From 425a2c2497387b7b5804738a77c757b93e630322 Mon Sep 17 00:00:00 2001 From: Matthias Gierlings Date: Wed, 3 Feb 2016 22:38:41 +0100 Subject: Added implementation for GMAC - Added GMAC class - Integrated GMAC into MAC-Class test bench. Run GMAC tests using ./botan-test mac --- src/lib/mac/gmac/gmac.cpp | 76 ++++++++++++++++++++++++++++++++ src/lib/mac/gmac/gmac.h | 77 ++++++++++++++++++++++++++++++++ src/lib/mac/gmac/info.txt | 6 +++ src/lib/mac/mac.cpp | 4 ++ src/lib/mac/mac.h | 22 +++++++++- src/lib/modes/aead/gcm/gcm.cpp | 4 +- src/lib/modes/aead/gcm/gcm.h | 99 ++++++++++++++++++++++-------------------- src/tests/data/mac/gmac.vec | 80 ++++++++++++++++++++++++++++++++++ src/tests/test_mac.cpp | 22 ++++++---- 9 files changed, 332 insertions(+), 58 deletions(-) create mode 100644 src/lib/mac/gmac/gmac.cpp create mode 100644 src/lib/mac/gmac/gmac.h create mode 100644 src/lib/mac/gmac/info.txt create mode 100644 src/tests/data/mac/gmac.vec diff --git a/src/lib/mac/gmac/gmac.cpp b/src/lib/mac/gmac/gmac.cpp new file mode 100644 index 000000000..5d82456ae --- /dev/null +++ b/src/lib/mac/gmac/gmac.cpp @@ -0,0 +1,76 @@ +/* + * GMAC + * (C) 2016 Matthias Gierlings, René Korthaus + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#include + +namespace Botan { + +GMAC* GMAC::make(const Spec& spec) + { + if(spec.arg_count() == 1) + { + if(auto bc = BlockCipher::create(spec.arg(0))) + return new GMAC(bc.release()); + } + return nullptr; + } + +GMAC::GMAC(BlockCipher* cipher) + : m_iv(), m_aad(), + m_gcm(GCM_Encryption(cipher)), m_cipher(cipher->clone()) + { + } + +void GMAC::clear() + { + m_gcm.clear(); + zeroise(m_iv); + zeroise(m_aad); + } + +std::string GMAC::name() const + { + return "GMAC(" + m_cipher->name() + ")"; + } + +size_t GMAC::output_length() const + { + return m_gcm.tag_size(); + } + +void GMAC::add_data(const byte input[], size_t length) + { + m_aad.insert(m_aad.end(), input, input + length); + } + +void GMAC::start(const std::vector& nonce) + { + m_iv.assign(nonce.begin(), nonce.end()); + } + +void GMAC::start(const secure_vector& nonce) + { + m_iv.assign(nonce.begin(), nonce.end()); + } + +void GMAC::final_result(byte mac[]) + { + secure_vector result; + m_gcm.set_associated_data(m_aad.data(), m_aad.size()); + m_gcm.start(m_iv); + m_gcm.finish(result); + std::copy(result.begin(), result.end(), mac); + + zeroise(m_aad); + m_aad.clear(); + } + +MessageAuthenticationCode* GMAC::clone() const + { + return new GMAC(m_cipher->clone()); + } +} diff --git a/src/lib/mac/gmac/gmac.h b/src/lib/mac/gmac/gmac.h new file mode 100644 index 000000000..d83236b32 --- /dev/null +++ b/src/lib/mac/gmac/gmac.h @@ -0,0 +1,77 @@ +/* + * GMAC + * (C) 2016 Matthias Gierlings, René Korthaus + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#ifndef BOTAN_GMAC_H__ +#define BOTAN_GMAC_H__ + +#include +#include +#include +#include + +namespace Botan { + +/** +* GMAC +*/ +class BOTAN_DLL GMAC : public MessageAuthenticationCode + { + public: + void clear() override; + std::string name() const override; + size_t output_length() const override; + MessageAuthenticationCode* clone() const override; + + /** + * Must be called to set the initialization vector prior to GMAC + * calculation. + * + * @param nonce Initialization vector. + */ + void start(const secure_vector& nonce); + + /** + * Must be called to set the initialization vector prior to GMAC + * calculation. + * + * @param nonce Initialization vector. + */ + void start(const std::vector& nonce); + + Key_Length_Specification key_spec() const + { + return m_gcm.key_spec(); + } + + /** + * Creates a new GMAC instance. + * + * @param cipher the underlying block cipher to use + */ + explicit GMAC(BlockCipher* cipher); + + static GMAC* make(const Spec& spec); + + GMAC(const GMAC&) = delete; + GMAC& operator=(const GMAC&) = delete; + private: + void add_data(const byte[], size_t) override; + void final_result(byte[]) override; + + void key_schedule(const byte key[], size_t size) override + { + m_gcm.set_key(key, size); + } + + secure_vector m_iv; + secure_vector m_aad; + GCM_Encryption m_gcm; + std::unique_ptr m_cipher; + }; + +} +#endif diff --git a/src/lib/mac/gmac/info.txt b/src/lib/mac/gmac/info.txt new file mode 100644 index 000000000..921690c93 --- /dev/null +++ b/src/lib/mac/gmac/info.txt @@ -0,0 +1,6 @@ +define GMAC 20160207 + + +gcm +mac + diff --git a/src/lib/mac/mac.cpp b/src/lib/mac/mac.cpp index 70807b39f..f2c5557c7 100644 --- a/src/lib/mac/mac.cpp +++ b/src/lib/mac/mac.cpp @@ -17,6 +17,10 @@ #include #endif +#if defined(BOTAN_HAS_GMAC) + #include +#endif + #if defined(BOTAN_HAS_HMAC) #include #endif diff --git a/src/lib/mac/mac.h b/src/lib/mac/mac.h index f3befc512..9c3614f33 100644 --- a/src/lib/mac/mac.h +++ b/src/lib/mac/mac.h @@ -59,7 +59,27 @@ class BOTAN_DLL MessageAuthenticationCode : public Buffered_Computation, virtual bool verify_mac(const byte in[], size_t length); /** - * @return a new object representing the same algorithm as *this + * Verify a MAC. + * @param in the MAC to verify as a byte array + * @return true if the MAC is valid, false otherwise + */ + virtual bool verify_mac(const std::vector& in) + { + return verify_mac(in.data(), in.size()); + } + + /** + * Verify a MAC. + * @param in the MAC to verify as a byte array + * @return true if the MAC is valid, false otherwise + */ + virtual bool verify_mac(const secure_vector& in) + { + return verify_mac(in.data(), in.size()); + } + + /** + * Get a new object representing the same algorithm as *this */ virtual MessageAuthenticationCode* clone() const = 0; diff --git a/src/lib/modes/aead/gcm/gcm.cpp b/src/lib/modes/aead/gcm/gcm.cpp index a73e5ee5b..6e1bd82f7 100644 --- a/src/lib/modes/aead/gcm/gcm.cpp +++ b/src/lib/modes/aead/gcm/gcm.cpp @@ -93,9 +93,9 @@ void GHASH::key_schedule(const byte key[], size_t length) m_text_len = 0; } -void GHASH::start(const byte nonce[], size_t len) +void GHASH::start(const secure_vector& nonce) { - m_nonce.assign(nonce, nonce + len); + m_nonce = nonce; m_ghash = m_H_ad; } diff --git a/src/lib/modes/aead/gcm/gcm.h b/src/lib/modes/aead/gcm/gcm.h index 6902bc1fa..964bd5062 100644 --- a/src/lib/modes/aead/gcm/gcm.h +++ b/src/lib/modes/aead/gcm/gcm.h @@ -14,7 +14,52 @@ namespace Botan { -class GHASH; +/** +* GCM's GHASH +* Maybe a Transform? +*/ +class BOTAN_DLL GHASH : public SymmetricAlgorithm + { + public: + void set_associated_data(const byte ad[], size_t ad_len); + + secure_vector nonce_hash(const byte nonce[], size_t len); + + void start(const secure_vector& nonce); + + /* + * Assumes input len is multiple of 16 + */ + void update(const byte in[], size_t len); + + secure_vector final(); + + Key_Length_Specification key_spec() const override + { return Key_Length_Specification(16); } + + size_t input_size() const { return m_text_len; } + + void clear() override; + + std::string name() const override { return "GHASH"; } + private: + void key_schedule(const byte key[], size_t key_len) override; + + void gcm_multiply(secure_vector& x) const; + + void ghash_update(secure_vector& x, + const byte input[], size_t input_len); + + void add_final_block(secure_vector& x, + size_t ad_len, size_t pt_len); + + secure_vector m_H; + secure_vector m_H_ad; + secure_vector m_nonce; + secure_vector m_ghash; + size_t m_ad_len = 0, m_text_len = 0; + }; + /** * GCM Mode @@ -28,6 +73,8 @@ class BOTAN_DLL GCM_Mode : public AEAD_Mode size_t update_granularity() const override; + void update(secure_vector& blocks, size_t offset = 0) override = 0; + Key_Length_Specification key_spec() const override; // GCM supports arbitrary nonce lengths @@ -35,6 +82,8 @@ class BOTAN_DLL GCM_Mode : public AEAD_Mode size_t tag_size() const override { return m_tag_size; } + size_t input_size() const { return m_ghash->input_size(); } + void clear() override; std::string provider() const override; @@ -51,7 +100,9 @@ class BOTAN_DLL GCM_Mode : public AEAD_Mode private: void start_msg(const byte nonce[], size_t nonce_len) override; + private: void key_schedule(const byte key[], size_t length) override; + secure_vector start_raw(const byte nonce[], size_t nonce_len) override; }; /** @@ -102,51 +153,5 @@ class BOTAN_DLL GCM_Decryption final : public GCM_Mode void finish(secure_vector& final_block, size_t offset = 0) override; }; - -/** -* GCM's GHASH -* Maybe a Transform? -*/ -class BOTAN_DLL GHASH : public SymmetricAlgorithm - { - public: - void set_associated_data(const byte ad[], size_t ad_len); - - secure_vector nonce_hash(const byte nonce[], size_t len); - - void start(const byte nonce[], size_t len); - - /* - * Assumes input len is multiple of 16 - */ - void update(const byte in[], size_t len); - - secure_vector final(); - - Key_Length_Specification key_spec() const override - { return Key_Length_Specification(16); } - - void clear() override; - - std::string name() const override { return "GHASH"; } - private: - void key_schedule(const byte key[], size_t key_len) override; - - void gcm_multiply(secure_vector& x) const; - - void ghash_update(secure_vector& x, - const byte input[], size_t input_len); - - void add_final_block(secure_vector& x, - size_t ad_len, size_t pt_len); - - secure_vector m_H; - secure_vector m_H_ad; - secure_vector m_nonce; - secure_vector m_ghash; - size_t m_ad_len = 0, m_text_len = 0; - }; - } - #endif diff --git a/src/tests/data/mac/gmac.vec b/src/tests/data/mac/gmac.vec new file mode 100644 index 000000000..47a94d553 --- /dev/null +++ b/src/tests/data/mac/gmac.vec @@ -0,0 +1,80 @@ +# Testvectors in this file have been generated using the Bouncy Castle Crypto +# API version 1.54 (https://www.bouncycastle.org) + +[GMAC(AES-128)] +IV = 000000000000000000000000 +Key = 00000000000000000000000000000000 +In = +Out = 58E2FCCEFA7E3061367F1D57A4E7455A + +IV = 000000000000000000000000 +Key = 00000000000000000000000000000000 +In = 00000000000000000000000000000000 +Out = 21C2EB20CD2214DBDF34C9B82ECB7ED2 + +IV = FFFFFFFFFFFFFFFFFFFFFFFF +Key = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +In = 00000000000000000000000000000000 +Out = B19E0699327D423B057C95D258AC3129 + +IV = 00000000 +Key = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +In = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +Out = 95394A79D3A4E94A419D2EA464005F79 + +IV = 860490BADBD9D69CB13322C8 +Key = 03E76EBD8695CAAA623817AEDF12A6FE +In = 06CEBA51C28BBEF65FF07B3E265A8905F950B6763498E2A35275E40B985EA459E3AEA0E7B8701E2EBC5C7FC73657F0FE25C4 +Out = 09A226837D34C6881BB1E4377C29F781 + +IV = 7F3726E3698857F573877988 +Key = 0679238B9E5FD7AFA8F4D5B3AF08671E +In = 90B3319E2973E670B392678CA177BB24CF0343D1F8AD0C6A2812C2C7A012D6667BC2F1B74F7F6EBDBE9491AE6AD36FDA0052 +Out = 02972297D35F920876FA42F3BC588D95 + +IV = 2B4F4D6E9C4AC0778DFE1185 +Key = C592DFFD2C340191E688C78BBCDE9B14 +In = D184E694CE693FA39E3EC026500B771B3C7F51A8F9E9B7DE609A6E34B92A967F26B453AEBD6011421CE21C4C500459997A32 +Out = 6174D3E5298EE97224ABA305196FB34E + +[GMAC(AES-192)] +IV = 000000000000000000000000 +Key = 000000000000000000000000000000000000000000000000 +In = +Out = CD33B28AC773F74BA00ED1F312572435 + +IV = 38709F4B067D1714DD63F428 +Key = F3582246D1DD6AE73F855C82B774E94E8143D9F8354A9F66 +In = E924266E9687AF9C8E1E23EBCF80AD90EE072B3FC2B57C020708AED47A5D69DF5748B985D8D78BF48D63A4149C3EF72440CC +Out = 749EE0A6FEE5DEC22E58C597E1CD047C + +IV = A47E708697F2B8D98E7F5CA1 +Key = 890EB916B61FD6101400ED06FECB89C342B95396E635B3D6 +In = E18155FDF859872A7491B847B80C5070A1FE9728E752129D54DBE616B4616FA4697CA45DF7C9292F845AC4CA3EE120701924 +Out = 2A73023EE22E85937CC61C8575E962D4 + +IV = 80947F9A7FE0AED56AFCE4CD +Key = 2622E5104C164548437A131BC45BA080E70BEF18AC1B0AAB +In = 485DDEF269A377AD7B29AD1E2055A08EA01E0DCA4B83F4DF5668C0197EFF6B8B835ED75BB4046813ADBAE0F900811119C5F4 +Out = B36E1D2CECBA1EAEB3C43CC3AAED9C15 + +[GMAC(AES-256)] +IV = 000000000000000000000000 +Key = 0000000000000000000000000000000000000000000000000000000000000000 +In = +Out = 530F8AFBC74536B9A963B4F1C4CB738B + +IV = 6A9588E108A7F8A01407476D +Key = 124A6263F56093DE70CBD45B2F57450B99DB7C068B0E11DA674D267CB739CF4C +In = F07D4DC8F1D32903DAFCDF1F3CA792AFC325D36D8E82E0EA0F6519AE81F4DF905F3DD78076F02FFE74341EB3E606A8ED6DF0 +Out = 9F82882851DD41F38334C3E5337C80AA + +IV = 551D960E1C2FBCB565E00E61 +Key = DB8F74ABDE797BF29215A9D1883E5BCE4B0334A2C7891F82D3DCC106EC026F7B +In = 6C4399B071B67B9F5495DA20F40D23A8A9FA86D3217D9226C43F7BA5E083C96280D13EA65324131631A2ED573F80568DC47B +Out = 1A55AC6CB46B8001BAA02BA64FAB7B89 + +IV = FB09C848E7ECE36527B7FB70 +Key = 0994C9E2A62E30A727BC69AE2DACC8823B00DD2888ECE29C2CB764A38FD30FBB +In = 4E1F2940DA4E4F5616304E7E398070FD106B32B68A0A47977CD008760F0972B5B519FD91C4AEDE49AC869D0766AF8C1A8309 +Out = FFF8F5311D7A16F78930F319EC3E9F8A diff --git a/src/tests/test_mac.cpp b/src/tests/test_mac.cpp index 6183e88f7..10236dfff 100644 --- a/src/tests/test_mac.cpp +++ b/src/tests/test_mac.cpp @@ -1,5 +1,6 @@ /* * (C) 2014,2015 Jack Lloyd +* (C) 2016 René Korthaus * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -10,6 +11,10 @@ #include #endif +#if defined(BOTAN_HAS_GMAC) + #include +#endif + namespace Botan_Tests { namespace { @@ -20,13 +25,14 @@ class Message_Auth_Tests : public Text_Based_Test { public: Message_Auth_Tests() : - Text_Based_Test("mac", {"Key", "In", "Out"}) {} + Text_Based_Test("mac", {"Key", "In", "Out"}, {"IV"}) {} Test::Result run_one_test(const std::string& algo, const VarMap& vars) override { const std::vector key = get_req_bin(vars, "Key"); const std::vector input = get_req_bin(vars, "In"); const std::vector expected = get_req_bin(vars, "Out"); + const std::vector iv = get_opt_bin(vars, "IV"); Test::Result result(algo); @@ -54,14 +60,14 @@ class Message_Auth_Tests : public Text_Based_Test result.test_eq(provider, mac->name(), algo); mac->set_key(key); - mac->update(input); - - result.test_eq(provider, "correct mac", mac->final(), expected); - // Test to make sure clear() resets what we need it to - mac->set_key( key ); - mac->update( "some discarded input"); - mac->clear(); +#if defined(BOTAN_HAS_GMAC) + // GMAC needs to set an IV + if(Botan::GMAC* gmac = dynamic_cast(mac.get())) + { + gmac->start(iv); + } +#endif // do the same to test verify_mac() mac->set_key(key); -- cgit v1.2.3 From 9ad816a5d8d74105558640b2f37baec50d8b920f Mon Sep 17 00:00:00 2001 From: Matthias Gierlings Date: Thu, 27 Oct 2016 19:00:23 +0200 Subject: Implements GMAC with GHASH. Adds support for Galois Message Authentication Code calculation based on GHASH, rather than GCM_Mode. --- src/build-data/policy/bsi.txt | 1 + src/lib/mac/gmac/gmac.cpp | 107 +++++++++++++++++++++++++++++------------ src/lib/mac/gmac/gmac.h | 35 ++++++++------ src/lib/mac/mac.cpp | 11 +++++ src/lib/modes/aead/gcm/gcm.cpp | 4 +- src/lib/modes/aead/gcm/gcm.h | 102 +++++++++++++++++++-------------------- src/tests/test_mac.cpp | 30 ++++++++++++ 7 files changed, 192 insertions(+), 98 deletions(-) diff --git a/src/build-data/policy/bsi.txt b/src/build-data/policy/bsi.txt index 7295f84d4..62be245d0 100644 --- a/src/build-data/policy/bsi.txt +++ b/src/build-data/policy/bsi.txt @@ -18,6 +18,7 @@ sha3 # mac cmac hmac +gmac # kdf kdf1_iso18033 diff --git a/src/lib/mac/gmac/gmac.cpp b/src/lib/mac/gmac/gmac.cpp index 5d82456ae..946e22cf0 100644 --- a/src/lib/mac/gmac/gmac.cpp +++ b/src/lib/mac/gmac/gmac.cpp @@ -9,27 +9,22 @@ namespace Botan { -GMAC* GMAC::make(const Spec& spec) - { - if(spec.arg_count() == 1) - { - if(auto bc = BlockCipher::create(spec.arg(0))) - return new GMAC(bc.release()); - } - return nullptr; - } - GMAC::GMAC(BlockCipher* cipher) - : m_iv(), m_aad(), - m_gcm(GCM_Encryption(cipher)), m_cipher(cipher->clone()) - { - } + : GHASH(), + m_aad_buf(), + m_cipher(cipher), + m_initialized(false) + {} void GMAC::clear() { - m_gcm.clear(); - zeroise(m_iv); - zeroise(m_aad); + GHASH::clear(); + m_H.resize(GCM_BS); + m_H_ad.resize(GCM_BS); + m_ghash.resize(GCM_BS); + m_cipher->clear(); + m_aad_buf.clear(); + m_initialized = false; } std::string GMAC::name() const @@ -39,38 +34,90 @@ std::string GMAC::name() const size_t GMAC::output_length() const { - return m_gcm.tag_size(); + return GCM_BS; + } + +void GMAC::add_data(const byte input[], size_t size) + { + m_ad_len += size; + + // buffer partial blocks till we received a full input block + // or final is called. + m_aad_buf.insert(m_aad_buf.end(), input, input + size); + if(m_aad_buf.size() >= GCM_BS) + { + // process all complete input blocks. + ghash_update(m_ghash, + m_aad_buf.data(), + m_aad_buf.size() - (m_aad_buf.size() % GCM_BS)); + + // remove all processed blocks from buffer. + m_aad_buf.erase(m_aad_buf.begin(), + m_aad_buf.end() - (m_aad_buf.size() % GCM_BS)); + } } -void GMAC::add_data(const byte input[], size_t length) +void GMAC::key_schedule(const byte key[], size_t size) { - m_aad.insert(m_aad.end(), input, input + length); + clear(); + m_cipher->set_key(key, size); + m_cipher->encrypt(m_H_ad.data(), m_H.data()); } void GMAC::start(const std::vector& nonce) { - m_iv.assign(nonce.begin(), nonce.end()); + start(nonce.data(), nonce.size()); } void GMAC::start(const secure_vector& nonce) { - m_iv.assign(nonce.begin(), nonce.end()); + start(nonce.data(), nonce.size()); + } + +void GMAC::start(const byte nonce[], size_t nonce_len) + { + secure_vector y0(GCM_BS); + + if(nonce_len == 12) + { + copy_mem(y0.data(), nonce, nonce_len); + y0[GCM_BS - 1] = 1; + } + else + { + ghash_update(y0, nonce, nonce_len); + add_final_block(y0, 0, nonce_len); + } + + secure_vector m_enc_y0(GCM_BS); + m_cipher->encrypt(y0.data(), m_enc_y0.data()); + GHASH::start(m_enc_y0.data(), m_enc_y0.size()); + m_initialized = true; } void GMAC::final_result(byte mac[]) { - secure_vector result; - m_gcm.set_associated_data(m_aad.data(), m_aad.size()); - m_gcm.start(m_iv); - m_gcm.finish(result); + // This ensures the GMAC computation has been initialized with a fresh + // nonce. The aim of this check is to prevent developers from re-using + // nonces (and potential nonce-reuse attacks). + BOTAN_ASSERT(m_initialized, + "The GMAC computation has not been initialized with a fresh " + "nonce."); + // process the rest of the aad buffer. Even if it is a partial block only + // ghash_update will process it properly. + if(m_aad_buf.size() > 0) + { + ghash_update(m_ghash, + m_aad_buf.data(), + m_aad_buf.size()); + } + secure_vector result = GHASH::final(); std::copy(result.begin(), result.end(), mac); - - zeroise(m_aad); - m_aad.clear(); + clear(); } MessageAuthenticationCode* GMAC::clone() const { - return new GMAC(m_cipher->clone()); + return new GMAC(BlockCipher::create(m_cipher->name()).release()); } } diff --git a/src/lib/mac/gmac/gmac.h b/src/lib/mac/gmac/gmac.h index d83236b32..b651c2e11 100644 --- a/src/lib/mac/gmac/gmac.h +++ b/src/lib/mac/gmac/gmac.h @@ -8,9 +8,9 @@ #ifndef BOTAN_GMAC_H__ #define BOTAN_GMAC_H__ -#include -#include #include +#include +#include #include namespace Botan { @@ -18,7 +18,9 @@ namespace Botan { /** * GMAC */ -class BOTAN_DLL GMAC : public MessageAuthenticationCode +class BOTAN_DLL GMAC : public MessageAuthenticationCode, + public GHASH + { public: void clear() override; @@ -26,6 +28,15 @@ class BOTAN_DLL GMAC : public MessageAuthenticationCode size_t output_length() const override; MessageAuthenticationCode* clone() const override; + /** + * Must be called to set the initialization vector prior to GMAC + * calculation. + * + * @param nonce Initialization vector. + * @param nonce_len size of initialization vector. + */ + void start(const byte nonce[], size_t nonce_len); + /** * Must be called to set the initialization vector prior to GMAC * calculation. @@ -44,7 +55,7 @@ class BOTAN_DLL GMAC : public MessageAuthenticationCode Key_Length_Specification key_spec() const { - return m_gcm.key_spec(); + return m_cipher->key_spec(); } /** @@ -54,23 +65,19 @@ class BOTAN_DLL GMAC : public MessageAuthenticationCode */ explicit GMAC(BlockCipher* cipher); - static GMAC* make(const Spec& spec); - GMAC(const GMAC&) = delete; GMAC& operator=(const GMAC&) = delete; + private: void add_data(const byte[], size_t) override; void final_result(byte[]) override; + void start_msg(const byte nonce[], size_t nonce_len); + void key_schedule(const byte key[], size_t size) override; - void key_schedule(const byte key[], size_t size) override - { - m_gcm.set_key(key, size); - } - - secure_vector m_iv; - secure_vector m_aad; - GCM_Encryption m_gcm; + static const size_t GCM_BS = 16; + secure_vector m_aad_buf; std::unique_ptr m_cipher; + bool m_initialized; }; } diff --git a/src/lib/mac/mac.cpp b/src/lib/mac/mac.cpp index f2c5557c7..2fa321a67 100644 --- a/src/lib/mac/mac.cpp +++ b/src/lib/mac/mac.cpp @@ -45,6 +45,17 @@ MessageAuthenticationCode::create(const std::string& algo_spec, { const SCAN_Name req(algo_spec); +#if defined(BOTAN_HAS_GMAC) + if(req.algo_name() == "GMAC" && req.arg_count() == 1) + { + if(provider.empty() || provider == "base") + { + if(auto bc = BlockCipher::create(req.arg(0))) + return std::unique_ptr(new GMAC(bc.release())); + } + } +#endif + #if defined(BOTAN_HAS_HMAC) if(req.algo_name() == "HMAC" && req.arg_count() == 1) { diff --git a/src/lib/modes/aead/gcm/gcm.cpp b/src/lib/modes/aead/gcm/gcm.cpp index 6e1bd82f7..a73e5ee5b 100644 --- a/src/lib/modes/aead/gcm/gcm.cpp +++ b/src/lib/modes/aead/gcm/gcm.cpp @@ -93,9 +93,9 @@ void GHASH::key_schedule(const byte key[], size_t length) m_text_len = 0; } -void GHASH::start(const secure_vector& nonce) +void GHASH::start(const byte nonce[], size_t len) { - m_nonce = nonce; + m_nonce.assign(nonce, nonce + len); m_ghash = m_H_ad; } diff --git a/src/lib/modes/aead/gcm/gcm.h b/src/lib/modes/aead/gcm/gcm.h index 964bd5062..6468cbd9c 100644 --- a/src/lib/modes/aead/gcm/gcm.h +++ b/src/lib/modes/aead/gcm/gcm.h @@ -14,52 +14,7 @@ namespace Botan { -/** -* GCM's GHASH -* Maybe a Transform? -*/ -class BOTAN_DLL GHASH : public SymmetricAlgorithm - { - public: - void set_associated_data(const byte ad[], size_t ad_len); - - secure_vector nonce_hash(const byte nonce[], size_t len); - - void start(const secure_vector& nonce); - - /* - * Assumes input len is multiple of 16 - */ - void update(const byte in[], size_t len); - - secure_vector final(); - - Key_Length_Specification key_spec() const override - { return Key_Length_Specification(16); } - - size_t input_size() const { return m_text_len; } - - void clear() override; - - std::string name() const override { return "GHASH"; } - private: - void key_schedule(const byte key[], size_t key_len) override; - - void gcm_multiply(secure_vector& x) const; - - void ghash_update(secure_vector& x, - const byte input[], size_t input_len); - - void add_final_block(secure_vector& x, - size_t ad_len, size_t pt_len); - - secure_vector m_H; - secure_vector m_H_ad; - secure_vector m_nonce; - secure_vector m_ghash; - size_t m_ad_len = 0, m_text_len = 0; - }; - +class GHASH; /** * GCM Mode @@ -73,8 +28,6 @@ class BOTAN_DLL GCM_Mode : public AEAD_Mode size_t update_granularity() const override; - void update(secure_vector& blocks, size_t offset = 0) override = 0; - Key_Length_Specification key_spec() const override; // GCM supports arbitrary nonce lengths @@ -82,8 +35,6 @@ class BOTAN_DLL GCM_Mode : public AEAD_Mode size_t tag_size() const override { return m_tag_size; } - size_t input_size() const { return m_ghash->input_size(); } - void clear() override; std::string provider() const override; @@ -100,9 +51,7 @@ class BOTAN_DLL GCM_Mode : public AEAD_Mode private: void start_msg(const byte nonce[], size_t nonce_len) override; - private: void key_schedule(const byte key[], size_t length) override; - secure_vector start_raw(const byte nonce[], size_t nonce_len) override; }; /** @@ -153,5 +102,54 @@ class BOTAN_DLL GCM_Decryption final : public GCM_Mode void finish(secure_vector& final_block, size_t offset = 0) override; }; + +/** +* GCM's GHASH +* Maybe a Transform? +*/ +class BOTAN_DLL GHASH : public SymmetricAlgorithm + { + public: + void set_associated_data(const byte ad[], size_t ad_len); + + secure_vector nonce_hash(const byte nonce[], size_t len); + + void start(const byte nonce[], size_t len); + + /* + * Assumes input len is multiple of 16 + */ + void update(const byte in[], size_t len); + + secure_vector final(); + + Key_Length_Specification key_spec() const override + { return Key_Length_Specification(16); } + + void clear() override; + + std::string name() const override { return "GHASH"; } + protected: + void ghash_update(secure_vector& x, + const byte input[], size_t input_len); + + void add_final_block(secure_vector& x, + size_t ad_len, size_t pt_len); + + secure_vector m_H; + secure_vector m_H_ad; + secure_vector m_ghash; + size_t m_ad_len = 0; + + private: + void key_schedule(const byte key[], size_t key_len) override; + + void gcm_multiply(secure_vector& x) const; + + secure_vector m_nonce; + size_t m_text_len = 0; + }; + } + #endif diff --git a/src/tests/test_mac.cpp b/src/tests/test_mac.cpp index 10236dfff..43b19b14e 100644 --- a/src/tests/test_mac.cpp +++ b/src/tests/test_mac.cpp @@ -69,8 +69,24 @@ class Message_Auth_Tests : public Text_Based_Test } #endif + mac->update(input); + + result.test_eq(provider, "correct mac", mac->final(), expected); + + // Test to make sure clear() resets what we need it to + mac->set_key( key ); + mac->update( "some discarded input"); + mac->clear(); + // do the same to test verify_mac() mac->set_key(key); +#if defined(BOTAN_HAS_GMAC) + // GMAC needs to set an IV + if(Botan::GMAC* gmac = dynamic_cast(mac.get())) + { + gmac->start(iv); + } +#endif mac->update(input); result.test_eq(provider + " correct mac", mac->verify_mac(expected.data(), expected.size()), true); @@ -78,6 +94,13 @@ class Message_Auth_Tests : public Text_Based_Test if(input.size() > 2) { mac->set_key(key); // Poly1305 requires the re-key +#if defined(BOTAN_HAS_GMAC) + // GMAC needs to set an IV + if(Botan::GMAC* gmac = dynamic_cast(mac.get())) + { + gmac->start(iv); + } +#endif mac->update(input[0]); mac->update(&input[1], input.size() - 2); mac->update(input[input.size()-1]); @@ -86,6 +109,13 @@ class Message_Auth_Tests : public Text_Based_Test // do the same to test verify_mac() mac->set_key(key); +#if defined(BOTAN_HAS_GMAC) + // GMAC needs to set an IV + if(Botan::GMAC* gmac = dynamic_cast(mac.get())) + { + gmac->start(iv); + } +#endif mac->update(input[ 0 ]); mac->update(&input[ 1 ], input.size() - 2); mac->update(input[ input.size() - 1 ]); -- cgit v1.2.3 From 8863848ad3b21ad6e2cf626b75e991484be61a9f Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Fri, 28 Oct 2016 14:14:16 -0400 Subject: Add MAC::start_msg, update GMAC GMAC needs a per-message nonce specified with `start`, and other MACs are capable of using nonces (Skein-MAC, for instance) so move this API up to MAC class. Change GMAC::clone to clone the owned cipher. --- src/lib/mac/gmac/gmac.cpp | 14 ++------------ src/lib/mac/gmac/gmac.h | 4 ++-- src/lib/mac/mac.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++ src/tests/test_mac.cpp | 39 ++++++------------------------------- 4 files changed, 59 insertions(+), 47 deletions(-) diff --git a/src/lib/mac/gmac/gmac.cpp b/src/lib/mac/gmac/gmac.cpp index 946e22cf0..4461cf370 100644 --- a/src/lib/mac/gmac/gmac.cpp +++ b/src/lib/mac/gmac/gmac.cpp @@ -64,17 +64,7 @@ void GMAC::key_schedule(const byte key[], size_t size) m_cipher->encrypt(m_H_ad.data(), m_H.data()); } -void GMAC::start(const std::vector& nonce) - { - start(nonce.data(), nonce.size()); - } - -void GMAC::start(const secure_vector& nonce) - { - start(nonce.data(), nonce.size()); - } - -void GMAC::start(const byte nonce[], size_t nonce_len) +void GMAC::start_msg(const byte nonce[], size_t nonce_len) { secure_vector y0(GCM_BS); @@ -118,6 +108,6 @@ void GMAC::final_result(byte mac[]) MessageAuthenticationCode* GMAC::clone() const { - return new GMAC(BlockCipher::create(m_cipher->name()).release()); + return new GMAC(m_cipher->clone()); } } diff --git a/src/lib/mac/gmac/gmac.h b/src/lib/mac/gmac/gmac.h index b651c2e11..b05c5451f 100644 --- a/src/lib/mac/gmac/gmac.h +++ b/src/lib/mac/gmac/gmac.h @@ -53,7 +53,7 @@ class BOTAN_DLL GMAC : public MessageAuthenticationCode, */ void start(const std::vector& nonce); - Key_Length_Specification key_spec() const + Key_Length_Specification key_spec() const override { return m_cipher->key_spec(); } @@ -71,7 +71,7 @@ class BOTAN_DLL GMAC : public MessageAuthenticationCode, private: void add_data(const byte[], size_t) override; void final_result(byte[]) override; - void start_msg(const byte nonce[], size_t nonce_len); + void start_msg(const byte nonce[], size_t nonce_len) override; void key_schedule(const byte key[], size_t size) override; static const size_t GCM_BS = 16; diff --git a/src/lib/mac/mac.h b/src/lib/mac/mac.h index 9c3614f33..d7808c1bf 100644 --- a/src/lib/mac/mac.h +++ b/src/lib/mac/mac.h @@ -50,6 +50,55 @@ class BOTAN_DLL MessageAuthenticationCode : public Buffered_Computation, virtual ~MessageAuthenticationCode() {} + /** + * Prepare for processing a message under the specified nonce + * + * Most MACs neither require nor support a nonce; for these algorithms + * calling `start_msg` is optional and calling it with anything other than + * an empty string is an error. One MAC which *requires* a per-message + * nonce be specified is GMAC. + * + * @param nonce the message nonce bytes + * @param nonce_len the size of len in bytes + * Default implementation simply rejects all non-empty nonces + * since most hash/MAC algorithms do not support randomization + */ + virtual void start_msg(const byte nonce[], size_t nonce_len) + { + BOTAN_UNUSED(nonce); + if(nonce_len > 0) + throw Invalid_IV_Length(name(), nonce_len); + } + + /** + * Begin processing a message with a nonce + * + * @param nonce the per message nonce + */ + template + void start(const std::vector& nonce) + { + start_msg(nonce.data(), nonce.size()); + } + + /** + * Begin processing a message. + * @param nonce the per message nonce + * @param nonce_len length of nonce + */ + void start(const byte nonce[], size_t nonce_len) + { + start_msg(nonce, nonce_len); + } + + /** + * Begin processing a message. + */ + void start() + { + return start_msg(nullptr, 0); + } + /** * Verify a MAC. * @param in the MAC to verify as a byte array diff --git a/src/tests/test_mac.cpp b/src/tests/test_mac.cpp index 43b19b14e..33972fabc 100644 --- a/src/tests/test_mac.cpp +++ b/src/tests/test_mac.cpp @@ -11,10 +11,6 @@ #include #endif -#if defined(BOTAN_HAS_GMAC) - #include -#endif - namespace Botan_Tests { namespace { @@ -60,14 +56,7 @@ class Message_Auth_Tests : public Text_Based_Test result.test_eq(provider, mac->name(), algo); mac->set_key(key); - -#if defined(BOTAN_HAS_GMAC) - // GMAC needs to set an IV - if(Botan::GMAC* gmac = dynamic_cast(mac.get())) - { - gmac->start(iv); - } -#endif + mac->start(iv); mac->update(input); @@ -80,13 +69,7 @@ class Message_Auth_Tests : public Text_Based_Test // do the same to test verify_mac() mac->set_key(key); -#if defined(BOTAN_HAS_GMAC) - // GMAC needs to set an IV - if(Botan::GMAC* gmac = dynamic_cast(mac.get())) - { - gmac->start(iv); - } -#endif + mac->start(iv); mac->update(input); result.test_eq(provider + " correct mac", mac->verify_mac(expected.data(), expected.size()), true); @@ -94,13 +77,8 @@ class Message_Auth_Tests : public Text_Based_Test if(input.size() > 2) { mac->set_key(key); // Poly1305 requires the re-key -#if defined(BOTAN_HAS_GMAC) - // GMAC needs to set an IV - if(Botan::GMAC* gmac = dynamic_cast(mac.get())) - { - gmac->start(iv); - } -#endif + mac->start(iv); + mac->update(input[0]); mac->update(&input[1], input.size() - 2); mac->update(input[input.size()-1]); @@ -109,13 +87,8 @@ class Message_Auth_Tests : public Text_Based_Test // do the same to test verify_mac() mac->set_key(key); -#if defined(BOTAN_HAS_GMAC) - // GMAC needs to set an IV - if(Botan::GMAC* gmac = dynamic_cast(mac.get())) - { - gmac->start(iv); - } -#endif + mac->start(iv); + mac->update(input[ 0 ]); mac->update(&input[ 1 ], input.size() - 2); mac->update(input[ input.size() - 1 ]); -- cgit v1.2.3