aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Gierlings <[email protected]>2016-10-27 19:00:23 +0200
committerMatthias Gierlings <[email protected]>2016-10-28 10:49:38 +0200
commit9ad816a5d8d74105558640b2f37baec50d8b920f (patch)
tree8400746b4ce9915244c91aa4dcaa1cfb05b508ff
parent425a2c2497387b7b5804738a77c757b93e630322 (diff)
Implements GMAC with GHASH.
Adds support for Galois Message Authentication Code calculation based on GHASH, rather than GCM_Mode.
-rw-r--r--src/build-data/policy/bsi.txt1
-rw-r--r--src/lib/mac/gmac/gmac.cpp107
-rw-r--r--src/lib/mac/gmac/gmac.h35
-rw-r--r--src/lib/mac/mac.cpp11
-rw-r--r--src/lib/modes/aead/gcm/gcm.cpp4
-rw-r--r--src/lib/modes/aead/gcm/gcm.h102
-rw-r--r--src/tests/test_mac.cpp30
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<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());
}
}
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 <botan/types.h>
-#include <botan/mac.h>
#include <botan/gcm.h>
+#include <botan/mac.h>
+#include <botan/types.h>
#include <algorithm>
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;
@@ -31,6 +33,15 @@ class BOTAN_DLL GMAC : public MessageAuthenticationCode
* 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.
+ *
+ * @param nonce Initialization vector.
*/
void start(const secure_vector<byte>& nonce);
@@ -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<byte> m_iv;
- secure_vector<byte> m_aad;
- GCM_Encryption m_gcm;
+ static const size_t GCM_BS = 16;
+ secure_vector<byte> m_aad_buf;
std::unique_ptr<BlockCipher> 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<MessageAuthenticationCode>(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<byte>& 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<byte> nonce_hash(const byte nonce[], size_t len);
-
- void start(const secure_vector<byte>& nonce);
-
- /*
- * Assumes input len is multiple of 16
- */
- void update(const byte in[], size_t len);
-
- secure_vector<byte> 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<byte>& x) const;
-
- void ghash_update(secure_vector<byte>& x,
- const byte input[], size_t input_len);
-
- void add_final_block(secure_vector<byte>& x,
- size_t ad_len, size_t pt_len);
-
- secure_vector<byte> m_H;
- secure_vector<byte> m_H_ad;
- secure_vector<byte> m_nonce;
- secure_vector<byte> 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<byte>& 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<byte> 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<byte>& 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<byte> 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<byte> 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<byte>& x,
+ const byte input[], size_t input_len);
+
+ void add_final_block(secure_vector<byte>& x,
+ size_t ad_len, size_t pt_len);
+
+ secure_vector<byte> m_H;
+ secure_vector<byte> m_H_ad;
+ secure_vector<byte> 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<byte>& x) const;
+
+ secure_vector<byte> 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<Botan::GMAC*>(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<Botan::GMAC*>(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<Botan::GMAC*>(mac.get()))
+ {
+ gmac->start(iv);
+ }
+#endif
mac->update(input[ 0 ]);
mac->update(&input[ 1 ], input.size() - 2);
mac->update(input[ input.size() - 1 ]);