diff options
author | lloyd <[email protected]> | 2013-03-16 21:23:50 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2013-03-16 21:23:50 +0000 |
commit | 01d1a8f734b9912301e4c0b6504c5ffa1d09e27a (patch) | |
tree | 0858c1e91d8a8161558a2d6ca4430853f8027564 | |
parent | aeb997bca6f6ce2856d1d346514f6e41167db957 (diff) |
Some work towards a (very slow) GCM implementation
Does not currently work due to some error in ghash, likely a
misunderstanding of the field representation.
-rw-r--r-- | checks/validate.dat | 15 | ||||
-rw-r--r-- | src/engine/core_engine/core_modes.cpp | 14 | ||||
-rw-r--r-- | src/filters/modes/gcm/gcm.cpp | 278 | ||||
-rw-r--r-- | src/filters/modes/gcm/gcm.h | 99 | ||||
-rw-r--r-- | src/filters/modes/gcm/info.txt | 6 |
5 files changed, 412 insertions, 0 deletions
diff --git a/checks/validate.dat b/checks/validate.dat index 2e748dfc7..affd59c7c 100644 --- a/checks/validate.dat +++ b/checks/validate.dat @@ -26291,6 +26291,21 @@ C61A0851AB4E515D11525B92E2B9D850:C825FC7C4D539DC74887CECC70884F37 60:710DABD24D400F3B6B:\ F956B879EC7F807F1FCB482B53623671:E64F90B4619D93137E6237929EABF297 +[AES-128/GCM] +#:58E2FCCEFA7E3061367F1D57A4E7455A:\ +#00000000000000000000000000000000:000000000000000000000000 + +00000000000000000000000000000000:\ +0388DACE60B6A392F328C2B971B2FE78AB6E47D42CEC13BDF53A67B21257BDDF:\ +00000000000000000000000000000000:000000000000000000000000 + +#D9313225F88406E5A55909C5AFF5269A86A7A9531534F7DA2E4C303D8A318A72\ +#1C3C0C95956809532FCF0E2449A6B525B16AEDF5AA0DE657BA637B391AAFD255:\ +#42831EC2217774244B7221B784D0D49CE3AA212F2C02A4E035C17E2329ACA12E\ +#21D514B25466931C7D8F6A5AAC84AA051BA30B396A0AAC973D58E091473F5985\ +#4D5C2AF327CD64A62CF35ABD2BA6FAB4:\ +#FEFFE9928665731C6D6A8F9467308308:CAFEBABEFACEDBADDECAF888 + [AES-128/XTS] # Vectors are from IEEE P1619 D11 diff --git a/src/engine/core_engine/core_modes.cpp b/src/engine/core_engine/core_modes.cpp index 08124a3a0..b30097eaa 100644 --- a/src/engine/core_engine/core_modes.cpp +++ b/src/engine/core_engine/core_modes.cpp @@ -44,6 +44,10 @@ #include <botan/ocb.h> #endif +#if defined(BOTAN_HAS_GCM) + #include <botan/gcm.h> +#endif + #if defined(BOTAN_HAS_XTS) #include <botan/xts.h> #endif @@ -141,6 +145,16 @@ Keyed_Filter* get_cipher_mode(const BlockCipher* block_cipher, } #endif +#if defined(BOTAN_HAS_GCM) + if(mode == "GCM") + { + if(direction == ENCRYPTION) + return new GCM_Encryption(block_cipher->clone(), 16); + else + return new GCM_Decryption(block_cipher->clone(), 16); + } +#endif + #if defined(BOTAN_HAS_XTS) if(mode == "XTS") { diff --git a/src/filters/modes/gcm/gcm.cpp b/src/filters/modes/gcm/gcm.cpp new file mode 100644 index 000000000..c88a90cdf --- /dev/null +++ b/src/filters/modes/gcm/gcm.cpp @@ -0,0 +1,278 @@ +/* +* GCM Mode Encryption +* (C) 2013 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/gcm.h> +#include <botan/ctr.h> +#include <botan/internal/xor_buf.h> +#include <botan/loadstor.h> + +#include <iostream> +#include <botan/hex.h> + +namespace Botan { + +void dump(const std::string& name, const byte x[], size_t x_len) + { + std::cout << name << " = " << hex_encode(x, x_len) << "\n"; + } + +void dump(const std::string& name, const secure_vector<byte>& x) + { + dump(name, &x[0], x.size()); + } + +bool rightshift(secure_vector<byte>& x) + { + byte carry = 0; + + for(size_t i = 0; i != x.size(); ++i) + { + byte carry2 = x[i] & 1; + x[i] = (x[i] >> 1) | (carry << 7); + carry = carry2; + } + + return carry; + } + +bool get_bit(const secure_vector<byte>& x, size_t bit) + { + const byte b = x[16 - (bit / 8)]; + + return (b >> (bit % 8)) & 0x01; + } + +secure_vector<byte> +gcm_multiply(const secure_vector<byte>& x, + const secure_vector<byte>& y) + { + secure_vector<byte> z(x.size()); + secure_vector<byte> v = x; + + for(size_t i = 0; i != 128; ++i) + { + if(get_bit(y, i)) + z ^= v; + + const bool highbit = get_bit(v, 127); + + const bool carry = rightshift(v); + BOTAN_ASSERT(carry == highbit, "That makes sense"); + + if(highbit) + v[0] ^= 0xE1; + } + + return z; + } + +void ghash_update(const secure_vector<byte>& H, + secure_vector<byte>& ghash, + const byte input[], size_t length) + { + const size_t BS = 16; + + dump("H", H); + + dump("ghash-in", ghash); + + /* + This assumes if less than block size input then we're just on the + final block and should pad with zeros + */ + while(length) + { + const size_t to_proc = std::min(length, BS); + + dump("input", input, to_proc); + + xor_buf(&ghash[0], &input[0], to_proc); + + ghash = gcm_multiply(ghash, H); + + dump("X", ghash); + + input += to_proc; + length -= to_proc; + } + } + +void ghash_finalize(const secure_vector<byte>& H, + secure_vector<byte>& ghash, + size_t ad_len, size_t text_len) + { + secure_vector<byte> final_block(16); + store_be<u64bit>(&final_block[0], 8*ad_len, 8*text_len); + dump("lens", final_block); + ghash_update(H, ghash, &final_block[0], final_block.size()); + dump("final ghash", ghash); + } + +/* +* GCM_Mode Constructor +*/ +GCM_Mode::GCM_Mode(BlockCipher* cipher, size_t tag_size, bool decrypting) : + Buffered_Filter(cipher->parallel_bytes(), decrypting ? tag_size : 0), + m_tag_size(tag_size), m_cipher_name(cipher->name()), + m_H(16), m_H_ad(16), m_H_current(16), + m_ad_len(0), m_text_len(0), + m_ctr_buf(8 * cipher->parallel_bytes()) + { + m_ctr.reset(new CTR_BE(cipher)); // CTR_BE takes ownership of cipher + + if(m_tag_size < 8 || m_tag_size > 16) + throw Invalid_Argument(name() + ": Bad tag size " + std::to_string(m_tag_size)); + } + +/* +* Check if a keylength is valid for GCM +*/ +bool GCM_Mode::valid_keylength(size_t n) const + { + if(!m_ctr->valid_keylength(n)) + return false; + return true; + } + +void GCM_Mode::set_key(const SymmetricKey& key) + { + m_ctr->set_key(key); + + const std::vector<byte> zeros(16); + m_ctr->set_iv(&zeros[0], zeros.size()); + + zeroise(m_H); + m_ctr->cipher(&m_H[0], &m_H[0], m_H.size()); + } + +/* +* Set the GCM associated data +*/ +void GCM_Mode::set_associated_data(const byte ad[], size_t ad_len) + { + zeroise(m_H_ad); + + ghash_update(m_H, m_H_ad, ad, ad_len); + m_ad_len = ad_len; + } + +/* +* Set the GCM nonce +*/ +void GCM_Mode::set_nonce(const byte nonce[], size_t nonce_len) + { + secure_vector<byte> y0(16); + + if(nonce_len == 12) + { + copy_mem(&y0[0], nonce, nonce_len); + y0[15] = 1; + } + else + { + ghash_update(m_H, y0, nonce, nonce_len); + ghash_finalize(m_H, y0, 0, nonce_len); + } + + m_ctr->set_iv(&y0[0], y0.size()); + + m_y0_cipher.resize(16); + m_ctr->cipher(&m_y0_cipher[0], &m_y0_cipher[0], m_y0_cipher.size()); + } + +/* +* Do setup at the start of each message +*/ +void GCM_Mode::start_msg() + { + m_text_len = 0; + m_H_current = m_H_ad; + } + +/* +* Return the name of this cipher mode +*/ +std::string GCM_Mode::name() const + { + return (m_cipher_name + "/GCM"); + } + +void GCM_Mode::write(const byte input[], size_t length) + { + Buffered_Filter::write(input, length); + } + +void GCM_Mode::end_msg() + { + Buffered_Filter::end_msg(); + } + +void GCM_Encryption::buffered_block(const byte input[], size_t length) + { + while(length) + { + size_t copied = std::min<size_t>(length, m_ctr_buf.size()); + + m_ctr->cipher(input, &m_ctr_buf[0], copied); + ghash_update(m_H, m_H_current, &m_ctr_buf[0], copied); + m_text_len += copied; + + send(m_ctr_buf, copied); + + input += copied; + length -= copied; + } + } + +void GCM_Encryption::buffered_final(const byte input[], size_t input_length) + { + buffered_block(input, input_length); + + ghash_finalize(m_H, m_H_current, m_ad_len, m_text_len); + + m_H_current ^= m_y0_cipher; + + send(m_H_current, m_tag_size); + } + +void GCM_Decryption::buffered_block(const byte input[], size_t length) + { + while(length) + { + size_t copied = std::min<size_t>(length, m_ctr_buf.size()); + + ghash_update(m_H, m_H_current, &input[0], copied); + m_ctr->cipher(input, &m_ctr_buf[0], copied); + m_text_len += copied; + + send(m_ctr_buf, copied); + + input += copied; + length -= copied; + } + } + +void GCM_Decryption::buffered_final(const byte input[], size_t input_length) + { + BOTAN_ASSERT(input_length >= m_tag_size, "Have the tag as part of final input"); + + const byte* included_tag = &input[input_length - m_tag_size]; + input_length -= m_tag_size; + + if(input_length) // handle any remaining input + buffered_block(input, input_length); + + ghash_finalize(m_H, m_H_current, m_ad_len, m_text_len); + + m_H_current ^= m_y0_cipher; + + if(!same_mem(&m_H_current[0], included_tag, m_tag_size)) + throw Integrity_Failure("GCM tag check failed"); + } + + +} diff --git a/src/filters/modes/gcm/gcm.h b/src/filters/modes/gcm/gcm.h new file mode 100644 index 000000000..3d00577aa --- /dev/null +++ b/src/filters/modes/gcm/gcm.h @@ -0,0 +1,99 @@ +/* +* GCM Mode +* (C) 2013 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_GCM_H__ +#define BOTAN_GCM_H__ + +#include <botan/aead.h> +#include <botan/buf_filt.h> +#include <botan/block_cipher.h> +#include <botan/stream_cipher.h> +#include <memory> + +namespace Botan { + +/** +* GCM Mode +*/ +class BOTAN_DLL GCM_Mode : public AEAD_Mode, + private Buffered_Filter + { + public: + void set_key(const SymmetricKey& key) override; + + void set_nonce(const byte nonce[], size_t nonce_len) override; + + /** + * @note must be called before start_msg or not at all + */ + void set_associated_data(const byte ad[], size_t ad_len) override; + + bool valid_keylength(size_t key_len) const override; + + // GCM supports arbitrary IV lengths + bool valid_iv_length(size_t) const override { return true; } + + std::string name() const override; + protected: + GCM_Mode(BlockCipher* cipher, size_t tag_size, bool decrypting); + + const size_t m_tag_size; + const std::string m_cipher_name; + + std::unique_ptr<StreamCipher> m_ctr; + secure_vector<byte> m_H; + secure_vector<byte> m_H_ad; + secure_vector<byte> m_H_current; + secure_vector<byte> m_y0_cipher; + size_t m_ad_len, m_text_len; + secure_vector<byte> m_ctr_buf; + + private: + void write(const byte[], size_t); + void start_msg(); + void end_msg(); + }; + +/** +* GCM Encryption +*/ +class BOTAN_DLL GCM_Encryption : public GCM_Mode + { + public: + /** + * @param ciph the cipher to use + * @param tag_size is how big the auth tag will be + */ + GCM_Encryption(BlockCipher* ciph, size_t tag_size = 16) : + GCM_Mode(ciph, tag_size, false) {} + + private: + void buffered_block(const byte input[], size_t input_length) override; + void buffered_final(const byte input[], size_t input_length) override; + }; + +/** +* GCM Decryption +*/ +class BOTAN_DLL GCM_Decryption : public GCM_Mode + { + public: + /** + * @param ciph the cipher to use + * @param tag_size is how big the auth tag will be + */ + GCM_Decryption(BlockCipher* cipher, size_t tag_size = 16) : + GCM_Mode(cipher, tag_size, true) {} + + private: + void buffered_block(const byte input[], size_t input_length) override; + void buffered_final(const byte input[], size_t input_length) override; + }; + +} + +#endif diff --git a/src/filters/modes/gcm/info.txt b/src/filters/modes/gcm/info.txt new file mode 100644 index 000000000..52e677a03 --- /dev/null +++ b/src/filters/modes/gcm/info.txt @@ -0,0 +1,6 @@ +define GCM + +<requires> +block +ctr +</requires> |