aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2016-10-28 16:25:38 -0400
committerJack Lloyd <[email protected]>2016-10-28 16:25:38 -0400
commit90269f18d54897e890bf305d4db76d136ca1aeea (patch)
tree9f2fb556cf1e5a5cfb00b68fd8f79e4dc246dce9
parenta6b65a6f2a8cf019b6589aaee6c49cc125d76bce (diff)
parent8863848ad3b21ad6e2cf626b75e991484be61a9f (diff)
Merge GH #691/#488 Add GMAC. Add MessageAuthenticationCode::start_msg
-rw-r--r--src/build-data/policy/bsi.txt1
-rw-r--r--src/lib/mac/gmac/gmac.cpp113
-rw-r--r--src/lib/mac/gmac/gmac.h84
-rw-r--r--src/lib/mac/gmac/info.txt6
-rw-r--r--src/lib/mac/mac.cpp15
-rw-r--r--src/lib/mac/mac.h71
-rw-r--r--src/lib/modes/aead/gcm/gcm.h17
-rw-r--r--src/tests/data/mac/gmac.vec80
-rw-r--r--src/tests/test_mac.cpp11
9 files changed, 389 insertions, 9 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
new file mode 100644
index 000000000..4461cf370
--- /dev/null
+++ b/src/lib/mac/gmac/gmac.cpp
@@ -0,0 +1,113 @@
+/*
+ * GMAC
+ * (C) 2016 Matthias Gierlings, René Korthaus
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ */
+
+#include <botan/gmac.h>
+
+namespace Botan {
+
+GMAC::GMAC(BlockCipher* cipher)
+ : GHASH(),
+ m_aad_buf(),
+ m_cipher(cipher),
+ m_initialized(false)
+ {}
+
+void GMAC::clear()
+ {
+ 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
+ {
+ return "GMAC(" + m_cipher->name() + ")";
+ }
+
+size_t GMAC::output_length() const
+ {
+ 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::key_schedule(const byte key[], size_t size)
+ {
+ clear();
+ m_cipher->set_key(key, size);
+ m_cipher->encrypt(m_H_ad.data(), m_H.data());
+ }
+
+void GMAC::start_msg(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[])
+ {
+ // 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);
+ 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..b05c5451f
--- /dev/null
+++ b/src/lib/mac/gmac/gmac.h
@@ -0,0 +1,84 @@
+/*
+ * 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 <botan/gcm.h>
+#include <botan/mac.h>
+#include <botan/types.h>
+#include <algorithm>
+
+namespace Botan {
+
+/**
+* GMAC
+*/
+class BOTAN_DLL GMAC : public MessageAuthenticationCode,
+ public GHASH
+
+ {
+ 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.
+ * @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);
+
+ /**
+ * Must be called to set the initialization vector prior to GMAC
+ * calculation.
+ *
+ * @param nonce Initialization vector.
+ */
+ void start(const std::vector<byte>& nonce);
+
+ Key_Length_Specification key_spec() const override
+ {
+ return m_cipher->key_spec();
+ }
+
+ /**
+ * Creates a new GMAC instance.
+ *
+ * @param cipher the underlying block cipher to use
+ */
+ explicit GMAC(BlockCipher* cipher);
+
+ 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) override;
+ void key_schedule(const byte key[], size_t size) override;
+
+ static const size_t GCM_BS = 16;
+ secure_vector<byte> m_aad_buf;
+ std::unique_ptr<BlockCipher> m_cipher;
+ bool m_initialized;
+ };
+
+}
+#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
+
+<requires>
+gcm
+mac
+</requires>
diff --git a/src/lib/mac/mac.cpp b/src/lib/mac/mac.cpp
index 70807b39f..2fa321a67 100644
--- a/src/lib/mac/mac.cpp
+++ b/src/lib/mac/mac.cpp
@@ -17,6 +17,10 @@
#include <botan/cmac.h>
#endif
+#if defined(BOTAN_HAS_GMAC)
+ #include <botan/gmac.h>
+#endif
+
#if defined(BOTAN_HAS_HMAC)
#include <botan/hmac.h>
#endif
@@ -41,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/mac/mac.h b/src/lib/mac/mac.h
index f3befc512..d7808c1bf 100644
--- a/src/lib/mac/mac.h
+++ b/src/lib/mac/mac.h
@@ -51,6 +51,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<typename Alloc>
+ void start(const std::vector<byte, Alloc>& 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
* @param length the length of param in
@@ -59,7 +108,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<byte>& 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<byte>& 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.h b/src/lib/modes/aead/gcm/gcm.h
index 6902bc1fa..6468cbd9c 100644
--- a/src/lib/modes/aead/gcm/gcm.h
+++ b/src/lib/modes/aead/gcm/gcm.h
@@ -129,11 +129,7 @@ class BOTAN_DLL GHASH : public SymmetricAlgorithm
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;
-
+ protected:
void ghash_update(secure_vector<byte>& x,
const byte input[], size_t input_len);
@@ -142,9 +138,16 @@ class BOTAN_DLL GHASH : public SymmetricAlgorithm
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;
+ 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;
};
}
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..33972fabc 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)
*/
@@ -20,13 +21,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<uint8_t> key = get_req_bin(vars, "Key");
const std::vector<uint8_t> input = get_req_bin(vars, "In");
const std::vector<uint8_t> expected = get_req_bin(vars, "Out");
+ const std::vector<uint8_t> iv = get_opt_bin(vars, "IV");
Test::Result result(algo);
@@ -54,6 +56,8 @@ class Message_Auth_Tests : public Text_Based_Test
result.test_eq(provider, mac->name(), algo);
mac->set_key(key);
+ mac->start(iv);
+
mac->update(input);
result.test_eq(provider, "correct mac", mac->final(), expected);
@@ -65,6 +69,7 @@ class Message_Auth_Tests : public Text_Based_Test
// do the same to test verify_mac()
mac->set_key(key);
+ mac->start(iv);
mac->update(input);
result.test_eq(provider + " correct mac", mac->verify_mac(expected.data(), expected.size()), true);
@@ -72,6 +77,8 @@ class Message_Auth_Tests : public Text_Based_Test
if(input.size() > 2)
{
mac->set_key(key); // Poly1305 requires the re-key
+ mac->start(iv);
+
mac->update(input[0]);
mac->update(&input[1], input.size() - 2);
mac->update(input[input.size()-1]);
@@ -80,6 +87,8 @@ class Message_Auth_Tests : public Text_Based_Test
// do the same to test verify_mac()
mac->set_key(key);
+ mac->start(iv);
+
mac->update(input[ 0 ]);
mac->update(&input[ 1 ], input.size() - 2);
mac->update(input[ input.size() - 1 ]);