diff options
author | Matthias Gierlings <[email protected]> | 2016-10-27 19:00:23 +0200 |
---|---|---|
committer | Matthias Gierlings <[email protected]> | 2016-10-28 10:49:38 +0200 |
commit | 9ad816a5d8d74105558640b2f37baec50d8b920f (patch) | |
tree | 8400746b4ce9915244c91aa4dcaa1cfb05b508ff /src/lib/mac/gmac/gmac.cpp | |
parent | 425a2c2497387b7b5804738a77c757b93e630322 (diff) |
Implements GMAC with GHASH.
Adds support for Galois Message Authentication Code calculation based on
GHASH, rather than GCM_Mode.
Diffstat (limited to 'src/lib/mac/gmac/gmac.cpp')
-rw-r--r-- | src/lib/mac/gmac/gmac.cpp | 107 |
1 files changed, 77 insertions, 30 deletions
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<byte>& nonce) { - m_iv.assign(nonce.begin(), nonce.end()); + start(nonce.data(), nonce.size()); } void GMAC::start(const secure_vector<byte>& 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<byte> 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<byte> 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<byte> 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<byte> 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()); } } |