From 93a51da0c53c08410aa68d588858e0cda844f7de Mon Sep 17 00:00:00 2001 From: lloyd Date: Sun, 17 Mar 2013 00:06:55 +0000 Subject: Move all AEAD modes to src/filters/aead --- src/filters/aead/aead.h | 38 ++++ src/filters/aead/eax/eax.cpp | 193 ++++++++++++++++++ src/filters/aead/eax/eax.h | 130 ++++++++++++ src/filters/aead/eax/info.txt | 7 + src/filters/aead/gcm/gcm.cpp | 253 ++++++++++++++++++++++++ src/filters/aead/gcm/gcm.h | 101 ++++++++++ src/filters/aead/gcm/info.txt | 6 + src/filters/aead/info.txt | 1 + src/filters/aead/ocb/info.txt | 6 + src/filters/aead/ocb/ocb.cpp | 423 ++++++++++++++++++++++++++++++++++++++++ src/filters/aead/ocb/ocb.h | 112 +++++++++++ src/filters/modes/aead/aead.h | 38 ---- src/filters/modes/aead/info.txt | 1 - src/filters/modes/eax/eax.cpp | 193 ------------------ src/filters/modes/eax/eax.h | 130 ------------ src/filters/modes/eax/info.txt | 7 - src/filters/modes/gcm/gcm.cpp | 253 ------------------------ src/filters/modes/gcm/gcm.h | 101 ---------- src/filters/modes/gcm/info.txt | 6 - src/filters/modes/ocb/info.txt | 7 - src/filters/modes/ocb/ocb.cpp | 423 ---------------------------------------- src/filters/modes/ocb/ocb.h | 112 ----------- 22 files changed, 1270 insertions(+), 1271 deletions(-) create mode 100644 src/filters/aead/aead.h create mode 100644 src/filters/aead/eax/eax.cpp create mode 100644 src/filters/aead/eax/eax.h create mode 100644 src/filters/aead/eax/info.txt create mode 100644 src/filters/aead/gcm/gcm.cpp create mode 100644 src/filters/aead/gcm/gcm.h create mode 100644 src/filters/aead/gcm/info.txt create mode 100644 src/filters/aead/info.txt create mode 100644 src/filters/aead/ocb/info.txt create mode 100644 src/filters/aead/ocb/ocb.cpp create mode 100644 src/filters/aead/ocb/ocb.h delete mode 100644 src/filters/modes/aead/aead.h delete mode 100644 src/filters/modes/aead/info.txt delete mode 100644 src/filters/modes/eax/eax.cpp delete mode 100644 src/filters/modes/eax/eax.h delete mode 100644 src/filters/modes/eax/info.txt delete mode 100644 src/filters/modes/gcm/gcm.cpp delete mode 100644 src/filters/modes/gcm/gcm.h delete mode 100644 src/filters/modes/gcm/info.txt delete mode 100644 src/filters/modes/ocb/info.txt delete mode 100644 src/filters/modes/ocb/ocb.cpp delete mode 100644 src/filters/modes/ocb/ocb.h (limited to 'src/filters') diff --git a/src/filters/aead/aead.h b/src/filters/aead/aead.h new file mode 100644 index 000000000..868008e5d --- /dev/null +++ b/src/filters/aead/aead.h @@ -0,0 +1,38 @@ +/* +* Interface for AEAD modes +* (C) 2013 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_AEAD_H__ +#define BOTAN_AEAD_H__ + +#include + +namespace Botan { + +class AEAD_Mode : public Keyed_Filter + { + public: + /** + * Set associated data that is not included in the ciphertext but + * that should be authenticated. Must be called after set_key + * and before end_msg. + * + * @param ad the associated data + * @param ad_len length of add in bytes + */ + virtual void set_associated_data(const byte ad[], size_t ad_len) = 0; + + virtual void set_nonce(const byte nonce[], size_t nonce_len) = 0; + + void set_iv(const InitializationVector& iv) override + { + set_nonce(iv.begin(), iv.length()); + } + }; + +} + +#endif diff --git a/src/filters/aead/eax/eax.cpp b/src/filters/aead/eax/eax.cpp new file mode 100644 index 000000000..57f62833b --- /dev/null +++ b/src/filters/aead/eax/eax.cpp @@ -0,0 +1,193 @@ +/* +* EAX Mode Encryption +* (C) 1999-2007 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include +#include +#include +#include +#include +#include + +namespace Botan { + +namespace { + +/* +* EAX MAC-based PRF +*/ +secure_vector eax_prf(byte tag, size_t BLOCK_SIZE, + MessageAuthenticationCode& mac, + const byte in[], size_t length) + { + for(size_t i = 0; i != BLOCK_SIZE - 1; ++i) + mac.update(0); + mac.update(tag); + mac.update(in, length); + return mac.final(); + } + +size_t eax_tag_size(size_t tag_size, const BlockCipher& cipher) + { + if(tag_size == 0) + return cipher.block_size(); + return (tag_size / 8); + } + +} + +/* +* EAX_Mode Constructor +*/ +EAX_Mode::EAX_Mode(BlockCipher* cipher, size_t tag_size, bool decrypting) : + Buffered_Filter(cipher->parallel_bytes(), + decrypting ? eax_tag_size(tag_size, *cipher) : 0), + BLOCK_SIZE(cipher->block_size()), + TAG_SIZE(eax_tag_size(tag_size, *cipher)), + cipher_name(cipher->name()), + ctr_buf(DEFAULT_BUFFERSIZE) + { + cmac.reset(new CMAC(cipher->clone())); + ctr.reset(new CTR_BE(cipher)); // CTR_BE takes ownership of cipher + + if(tag_size % 8 != 0 || TAG_SIZE == 0 || TAG_SIZE > cmac->output_length()) + throw Invalid_Argument(name() + ": Bad tag size " + std::to_string(tag_size)); + } + +/* +* Check if a keylength is valid for EAX +*/ +bool EAX_Mode::valid_keylength(size_t n) const + { + if(!ctr->valid_keylength(n)) + return false; + return true; + } + +/* +* Set the EAX key +*/ +void EAX_Mode::set_key(const SymmetricKey& key) + { + /* + * These could share the key schedule, which is one nice part of EAX, + * but it's much easier to ignore that here... + */ + ctr->set_key(key); + cmac->set_key(key); + + ad_mac = eax_prf(1, BLOCK_SIZE, *cmac, nullptr, 0); + } + +/* +* Do setup at the start of each message +*/ +void EAX_Mode::start_msg() + { + for(size_t i = 0; i != BLOCK_SIZE - 1; ++i) + cmac->update(0); + cmac->update(2); + } + +/* +* Set the EAX nonce +*/ +void EAX_Mode::set_nonce(const byte nonce[], size_t nonce_len) + { + nonce_mac = eax_prf(0, BLOCK_SIZE, *cmac, nonce, nonce_len); + ctr->set_iv(&nonce_mac[0], nonce_mac.size()); + } + +/* +* Set the EAX associated data +*/ +void EAX_Mode::set_associated_data(const byte ad[], size_t length) + { + ad_mac = eax_prf(1, BLOCK_SIZE, *cmac, ad, length); + } + +/* +* Return the name of this cipher mode +*/ +std::string EAX_Mode::name() const + { + return (cipher_name + "/EAX"); + } + +void EAX_Mode::write(const byte input[], size_t length) + { + Buffered_Filter::write(input, length); + } + +void EAX_Mode::end_msg() + { + Buffered_Filter::end_msg(); + } + +void EAX_Encryption::buffered_block(const byte input[], size_t length) + { + while(length) + { + size_t copied = std::min(length, ctr_buf.size()); + + ctr->cipher(input, &ctr_buf[0], copied); + cmac->update(&ctr_buf[0], copied); + + send(ctr_buf, copied); + + input += copied; + length -= copied; + } + } + +void EAX_Encryption::buffered_final(const byte input[], size_t input_length) + { + buffered_block(input, input_length); + + secure_vector data_mac = cmac->final(); + xor_buf(data_mac, nonce_mac, data_mac.size()); + xor_buf(data_mac, ad_mac, data_mac.size()); + + send(data_mac, TAG_SIZE); + } + +void EAX_Decryption::buffered_block(const byte input[], size_t length) + { + cmac->update(&input[0], length); + + while(length) + { + size_t copied = std::min(length, ctr_buf.size()); + + ctr->cipher(input, &ctr_buf[0], copied); + + send(ctr_buf, copied); + + input += copied; + length -= copied; + } + } + +void EAX_Decryption::buffered_final(const byte input[], size_t input_length) + { + BOTAN_ASSERT(input_length >= TAG_SIZE, "Have the tag as part of final input"); + + const byte* included_tag = &input[input_length - TAG_SIZE]; + input_length -= TAG_SIZE; + + if(input_length) // handle any remaining input + buffered_block(input, input_length); + + secure_vector mac = cmac->final(); + mac ^= nonce_mac; + mac ^= ad_mac; + + if(!same_mem(&mac[0], included_tag, TAG_SIZE)) + throw Integrity_Failure("EAX tag check failed"); + } + + +} diff --git a/src/filters/aead/eax/eax.h b/src/filters/aead/eax/eax.h new file mode 100644 index 000000000..e219487cb --- /dev/null +++ b/src/filters/aead/eax/eax.h @@ -0,0 +1,130 @@ +/* +* EAX Mode +* (C) 1999-2007,2013 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_EAX_H__ +#define BOTAN_EAX_H__ + +#include +#include +#include +#include +#include +#include + +namespace Botan { + +/** +* EAX Mode +*/ +class BOTAN_DLL EAX_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; + + void set_associated_data(const byte ad[], size_t ad_len) override; + + std::string name() const override; + + bool valid_keylength(size_t key_len) const override; + + // EAX supports arbitrary IV lengths + bool valid_iv_length(size_t) const override { return true; } + protected: + /** + * @param cipher the cipher to use + * @param tag_size is how big the auth tag will be + */ + EAX_Mode(BlockCipher* cipher, size_t tag_size, bool decrypting); + + void start_msg(); + + /** + * The block size of the underlying cipher + */ + const size_t BLOCK_SIZE; + + /** + * The requested tag name + */ + const size_t TAG_SIZE; + + /** + * The name of the cipher + */ + std::string cipher_name; + + /** + * The stream cipher (CTR mode) + */ + std::unique_ptr ctr; + + /** + * The MAC (CMAC) + */ + std::unique_ptr cmac; + + /** + * The MAC of the nonce + */ + secure_vector nonce_mac; + + /** + * The MAC of the associated data + */ + secure_vector ad_mac; + + /** + * A buffer for CTR mode encryption + */ + secure_vector ctr_buf; + private: + void write(const byte[], size_t); + void end_msg(); + }; + +/** +* EAX Encryption +*/ +class BOTAN_DLL EAX_Encryption : public EAX_Mode + { + public: + /** + * @param ciph the cipher to use + * @param tag_size is how big the auth tag will be + */ + EAX_Encryption(BlockCipher* ciph, size_t tag_size = 0) : + EAX_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; + }; + +/** +* EAX Decryption +*/ +class BOTAN_DLL EAX_Decryption : public EAX_Mode + { + public: + /** + * @param ciph the cipher to use + * @param tag_size is how big the auth tag will be + */ + EAX_Decryption(BlockCipher* cipher, size_t tag_size = 0) : + EAX_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/aead/eax/info.txt b/src/filters/aead/eax/info.txt new file mode 100644 index 000000000..09d92e724 --- /dev/null +++ b/src/filters/aead/eax/info.txt @@ -0,0 +1,7 @@ +define EAX + + +block +cmac +ctr + diff --git a/src/filters/aead/gcm/gcm.cpp b/src/filters/aead/gcm/gcm.cpp new file mode 100644 index 000000000..527310069 --- /dev/null +++ b/src/filters/aead/gcm/gcm.cpp @@ -0,0 +1,253 @@ +/* +* GCM Mode Encryption +* (C) 2013 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include +#include +#include +#include + +namespace Botan { + +secure_vector +gcm_multiply(const secure_vector& x, + const secure_vector& y) + { + static const u64bit R = 0xE100000000000000; + + u64bit V[2] = { + load_be(&y[0], 0), + load_be(&y[0], 1) + }; + + u64bit Z[2] = { 0, 0 }; + + for(size_t i = 0; i != 2; ++i) + { + u64bit X = load_be(&x[0], i); + + for(size_t j = 0; j != 64; ++j) + { + if(X >> 63) + { + Z[0] ^= V[0]; + Z[1] ^= V[1]; + } + + const u64bit r = (V[1] & 1) ? R : 0; + + V[1] = (V[0] << 63) | (V[1] >> 1); + V[0] = (V[0] >> 1) ^ r; + + X <<= 1; + } + } + + secure_vector out(16); + store_be(&out[0], Z[0], Z[1]); + return out; + } + +void ghash_update(const secure_vector& H, + secure_vector& ghash, + const byte input[], size_t length) + { + const size_t BS = 16; + + /* + 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); + + xor_buf(&ghash[0], &input[0], to_proc); + + ghash = gcm_multiply(ghash, H); + + input += to_proc; + length -= to_proc; + } + } + +void ghash_finalize(const secure_vector& H, + secure_vector& ghash, + size_t ad_len, size_t text_len) + { + secure_vector final_block(16); + store_be(&final_block[0], 8*ad_len, 8*text_len); + ghash_update(H, ghash, &final_block[0], final_block.size()); + } + +/* +* 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_mac(16), + m_ad_len(0), m_text_len(0), + m_ctr_buf(8 * cipher->parallel_bytes()) + { + if(cipher->block_size() != BS) + throw std::invalid_argument("OCB requires a 128 bit cipher so cannot be used with " + + cipher->name()); + + 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 zeros(BS); + 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 y0(BS); + + 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_enc_y0.resize(BS); + m_ctr->encipher(m_enc_y0); + } + +/* +* Do setup at the start of each message +*/ +void GCM_Mode::start_msg() + { + m_text_len = 0; + m_mac = 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(length, m_ctr_buf.size()); + + m_ctr->cipher(input, &m_ctr_buf[0], copied); + ghash_update(m_H, m_mac, &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_mac, m_ad_len, m_text_len); + + m_mac ^= m_enc_y0; + + send(m_mac, m_tag_size); + } + +void GCM_Decryption::buffered_block(const byte input[], size_t length) + { + while(length) + { + size_t copied = std::min(length, m_ctr_buf.size()); + + ghash_update(m_H, m_mac, &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_mac, m_ad_len, m_text_len); + + m_mac ^= m_enc_y0; + + if(!same_mem(&m_mac[0], included_tag, m_tag_size)) + throw Integrity_Failure("GCM tag check failed"); + } + + +} diff --git a/src/filters/aead/gcm/gcm.h b/src/filters/aead/gcm/gcm.h new file mode 100644 index 000000000..fa13597ce --- /dev/null +++ b/src/filters/aead/gcm/gcm.h @@ -0,0 +1,101 @@ +/* +* GCM Mode +* (C) 2013 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_GCM_H__ +#define BOTAN_GCM_H__ + +#include +#include +#include +#include +#include + +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); + + static const size_t BS = 16; + + const size_t m_tag_size; + const std::string m_cipher_name; + + std::unique_ptr m_ctr; + secure_vector m_H; + secure_vector m_H_ad; + secure_vector m_mac; + secure_vector m_enc_y0; + size_t m_ad_len, m_text_len; + secure_vector 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/aead/gcm/info.txt b/src/filters/aead/gcm/info.txt new file mode 100644 index 000000000..52e677a03 --- /dev/null +++ b/src/filters/aead/gcm/info.txt @@ -0,0 +1,6 @@ +define GCM + + +block +ctr + diff --git a/src/filters/aead/info.txt b/src/filters/aead/info.txt new file mode 100644 index 000000000..b43aab2f6 --- /dev/null +++ b/src/filters/aead/info.txt @@ -0,0 +1 @@ +define AEAD diff --git a/src/filters/aead/ocb/info.txt b/src/filters/aead/ocb/info.txt new file mode 100644 index 000000000..0ee41681d --- /dev/null +++ b/src/filters/aead/ocb/info.txt @@ -0,0 +1,6 @@ +define OCB + + +block +cmac + diff --git a/src/filters/aead/ocb/ocb.cpp b/src/filters/aead/ocb/ocb.cpp new file mode 100644 index 000000000..ebf440d32 --- /dev/null +++ b/src/filters/aead/ocb/ocb.cpp @@ -0,0 +1,423 @@ +/* +* OCB Mode +* (C) 2013 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include +#include +#include +#include +#include + +#include +#include + +namespace Botan { + +// Has to be in Botan namespace so unique_ptr can reference it +class L_computer + { + public: + L_computer(const BlockCipher& cipher) + { + m_L_star.resize(cipher.block_size()); + cipher.encrypt(m_L_star); + m_L_dollar = poly_double(star()); + m_L.push_back(poly_double(dollar())); + } + + const secure_vector& star() const { return m_L_star; } + + const secure_vector& dollar() const { return m_L_dollar; } + + const secure_vector& operator()(size_t i) const { return get(i); } + + const secure_vector& get(size_t i) const + { + while(m_L.size() <= i) + m_L.push_back(poly_double(m_L.back())); + + return m_L.at(i); + } + + private: + secure_vector poly_double(const secure_vector& in) const + { + return CMAC::poly_double(in, 0x87); + } + + secure_vector m_L_dollar, m_L_star; + mutable std::vector> m_L; + }; + +class Nonce_State + { + public: + Nonce_State(const BlockCipher& cipher) : m_cipher(cipher) {} + + secure_vector update_nonce(const byte nonce[], + size_t nonce_len); + + bool fresh_nonce() { bool b = false; std::swap(b, m_fresh); return b; } + private: + const BlockCipher& m_cipher; + secure_vector m_last_nonce; + secure_vector m_stretch; + bool m_fresh = false; + }; + +secure_vector +Nonce_State::update_nonce(const byte nonce[], size_t nonce_len) + { + const size_t BS = 16; + + BOTAN_ASSERT(nonce_len < BS, "Nonce is less than 128 bits"); + + secure_vector nonce_buf(BS); + + copy_mem(&nonce_buf[BS - nonce_len], nonce, nonce_len); + nonce_buf[BS - nonce_len - 1] = 1; + + const byte bottom = nonce_buf[15] & 0x3F; + nonce_buf[15] &= 0xC0; + + const bool need_new_stretch = (m_last_nonce != nonce_buf); + + if(need_new_stretch) + { + m_last_nonce = nonce_buf; + + m_cipher.encrypt(nonce_buf); + + for(size_t i = 0; i != 8; ++i) + nonce_buf.push_back(nonce_buf[i] ^ nonce_buf[i+1]); + + m_stretch = nonce_buf; + } + + // now set the offset from stretch and bottom + + const size_t shift_bytes = bottom / 8; + const size_t shift_bits = bottom % 8; + + secure_vector offset(BS); + for(size_t i = 0; i != BS; ++i) + { + offset[i] = (m_stretch[i+shift_bytes] << shift_bits); + offset[i] |= (m_stretch[i+shift_bytes+1] >> (8-shift_bits)); + } + + m_fresh = true; + return offset; + } + +namespace { + +/* +* OCB's HASH +*/ +secure_vector ocb_hash(const L_computer& L, + const BlockCipher& cipher, + const byte ad[], size_t ad_len) + { + const size_t BS = cipher.block_size(); + + secure_vector sum(BS); + secure_vector offset(BS); + + secure_vector buf(BS); + + const size_t ad_blocks = (ad_len / BS); + const size_t ad_remainder = (ad_len % BS); + + for(size_t i = 0; i != ad_blocks; ++i) + { + // this loop could run in parallel + offset ^= L(ctz(i+1)); + + buf = offset; + xor_buf(&buf[0], &ad[BS*i], BS); + + cipher.encrypt(buf); + + sum ^= buf; + } + + if(ad_remainder) + { + offset ^= L.star(); + + buf = offset; + xor_buf(&buf[0], &ad[BS*ad_blocks], ad_remainder); + buf[ad_len % BS] ^= 0x80; + + cipher.encrypt(buf); + + sum ^= buf; + } + + return sum; + } + +} + +OCB_Mode::OCB_Mode(BlockCipher* cipher, size_t tag_size, bool decrypting) : + Buffered_Filter(cipher->parallel_bytes(), decrypting ? tag_size : 0), + m_cipher(cipher), m_tag_size(tag_size), + m_ad_hash(BS), m_offset(BS), m_checksum(BS) + { + if(m_cipher->block_size() != BS) + throw std::invalid_argument("OCB requires a 128 bit cipher so cannot be used with " + + m_cipher->name()); + + if(m_tag_size != 16) // 64, 96 bits also supported + throw std::invalid_argument("OCB cannot produce a " + std::to_string(m_tag_size) + + " byte tag"); + + } + +OCB_Mode::~OCB_Mode() { /* for unique_ptr destructor */ } + +bool OCB_Mode::valid_keylength(size_t n) const + { + return m_cipher->valid_keylength(n); + } + +std::string OCB_Mode::name() const + { + return m_cipher->name() + "/OCB"; // include tag size + } + +void OCB_Mode::set_key(const SymmetricKey& key) + { + m_cipher->set_key(key); + m_L.reset(new L_computer(*m_cipher)); + m_nonce_state.reset(new Nonce_State(*m_cipher)); + } + +void OCB_Mode::set_nonce(const byte nonce[], size_t nonce_len) + { + if(!valid_iv_length(nonce_len)) + throw Invalid_IV_Length(name(), nonce_len); + + BOTAN_ASSERT(m_nonce_state, "A key was set"); + + m_offset = m_nonce_state->update_nonce(nonce, nonce_len); + } + +void OCB_Mode::start_msg() + { + BOTAN_ASSERT(m_nonce_state->fresh_nonce(), "Nonce state is fresh"); + } + +void OCB_Mode::set_associated_data(const byte ad[], size_t ad_len) + { + m_ad_hash = ocb_hash(*m_L, *m_cipher, &ad[0], ad_len); + } + +void OCB_Mode::write(const byte input[], size_t length) + { + Buffered_Filter::write(input, length); + } + +void OCB_Mode::end_msg() + { + Buffered_Filter::end_msg(); + } + +void OCB_Encryption::buffered_block(const byte input[], size_t input_length) + { + BOTAN_ASSERT(input_length % BS == 0, "Input length is an even number of blocks"); + + const size_t blocks = input_length / BS; + + const size_t par_bytes = m_cipher->parallel_bytes(); + + BOTAN_ASSERT(par_bytes % BS == 0, "Cipher is parallel in full blocks"); + + const size_t par_blocks = par_bytes / BS; + + const L_computer& L = *m_L; // convenient name + + secure_vector ctext_buf(par_bytes); + secure_vector csum_accum(par_bytes); + secure_vector offsets(par_bytes); + + size_t blocks_left = blocks; + + while(blocks_left) + { + const size_t to_proc = std::min(blocks_left, par_blocks); + const size_t proc_bytes = to_proc * BS; + + xor_buf(&csum_accum[0], &input[0], proc_bytes); + + for(size_t i = 0; i != to_proc; ++i) + { + m_offset ^= L(ctz(++m_block_index)); + copy_mem(&offsets[BS*i], &m_offset[0], BS); + } + + copy_mem(&ctext_buf[0], &input[0], proc_bytes); + + ctext_buf ^= offsets; + m_cipher->encrypt(ctext_buf); + ctext_buf ^= offsets; + + send(ctext_buf, proc_bytes); + + input += proc_bytes; + blocks_left -= to_proc; + } + + // fold into checksum + for(size_t i = 0; i != csum_accum.size(); ++i) + m_checksum[i % BS] ^= csum_accum[i]; + } + +void OCB_Encryption::buffered_final(const byte input[], size_t input_length) + { + if(input_length >= BS) + { + const size_t final_blocks = input_length / BS; + const size_t final_bytes = final_blocks * BS; + buffered_block(input, final_bytes); + input += final_bytes; + input_length -= final_bytes; + } + + if(input_length) + { + BOTAN_ASSERT(input_length < BS, "Only a partial block left"); + + xor_buf(&m_checksum[0], &input[0], input_length); + m_checksum[input_length] ^= 0x80; + + m_offset ^= m_L->star(); // Offset_* + + secure_vector buf(BS); + m_cipher->encrypt(m_offset, buf); + xor_buf(&buf[0], &input[0], input_length); + + send(buf, input_length); // final ciphertext + } + + // now compute the tag + secure_vector mac = m_offset; + mac ^= m_checksum; + mac ^= m_L->dollar(); + + m_cipher->encrypt(mac); + + mac ^= m_ad_hash; + + send(mac); + + zeroise(m_checksum); + zeroise(m_offset); + m_block_index = 0; + } + +void OCB_Decryption::buffered_block(const byte input[], size_t input_length) + { + BOTAN_ASSERT(input_length % BS == 0, "Input length is an even number of blocks"); + + const size_t blocks = input_length / BS; + + const size_t par_bytes = m_cipher->parallel_bytes(); + + BOTAN_ASSERT(par_bytes % BS == 0, "Cipher is parallel in full blocks"); + + const size_t par_blocks = par_bytes / BS; + + const L_computer& L = *m_L; // convenient name + + secure_vector ptext_buf(par_bytes); + secure_vector csum_accum(par_bytes); + secure_vector offsets(par_bytes); + + size_t blocks_left = blocks; + + while(blocks_left) + { + const size_t to_proc = std::min(blocks_left, par_blocks); + const size_t proc_bytes = to_proc * BS; + + for(size_t i = 0; i != to_proc; ++i) + { + m_offset ^= L(ctz(++m_block_index)); + copy_mem(&offsets[BS*i], &m_offset[0], BS); + } + + copy_mem(&ptext_buf[0], &input[0], proc_bytes); + + ptext_buf ^= offsets; + m_cipher->decrypt(ptext_buf); + ptext_buf ^= offsets; + + xor_buf(&csum_accum[0], &ptext_buf[0], proc_bytes); + + send(ptext_buf, proc_bytes); + + input += proc_bytes; + blocks_left -= to_proc; + } + + // fold into checksum + for(size_t i = 0; i != csum_accum.size(); ++i) + m_checksum[i % BS] ^= csum_accum[i]; + } + +void OCB_Decryption::buffered_final(const byte input[], size_t input_length) + { + BOTAN_ASSERT(input_length >= m_tag_size, "We have the tag"); + + const byte* included_tag = &input[input_length - m_tag_size]; + input_length -= m_tag_size; + + if(input_length >= BS) + { + const size_t final_blocks = input_length / BS; + const size_t final_bytes = final_blocks * BS; + buffered_block(input, final_bytes); + input += final_bytes; + input_length -= final_bytes; + } + + if(input_length) + { + BOTAN_ASSERT(input_length < BS, "Only a partial block left"); + + m_offset ^= m_L->star(); // Offset_* + + secure_vector buf(BS); + m_cipher->encrypt(m_offset, buf); // P_* + + xor_buf(&buf[0], &input[0], input_length); + + xor_buf(&m_checksum[0], &buf[0], input_length); + m_checksum[input_length] ^= 0x80; + + send(buf, input_length); // final plaintext + } + + // now compute the tag + secure_vector mac = m_offset; + mac ^= m_checksum; + mac ^= m_L->dollar(); + + m_cipher->encrypt(mac); + + mac ^= m_ad_hash; + + zeroise(m_checksum); + zeroise(m_offset); + m_block_index = 0; + + if(!same_mem(&mac[0], included_tag, m_tag_size)) + throw Integrity_Failure("OCB tag check failed"); + } + +} diff --git a/src/filters/aead/ocb/ocb.h b/src/filters/aead/ocb/ocb.h new file mode 100644 index 000000000..5e3c5cf0a --- /dev/null +++ b/src/filters/aead/ocb/ocb.h @@ -0,0 +1,112 @@ +/* +* OCB Mode +* (C) 2013 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_OCB_H__ +#define BOTAN_OCB_H__ + +#include +#include +#include +#include + +namespace Botan { + +class L_computer; +class Nonce_State; + +/** +* OCB Mode (base class for OCB_Encryption and OCB_Decryption). Note +* that OCB is patented, but is freely licensed in some circumstances. +* +* @see "The OCB Authenticated-Encryption Algorithm" internet draft + http://tools.ietf.org/html/draft-irtf-cfrg-ocb-00 +* @see Free Licenses http://www.cs.ucdavis.edu/~rogaway/ocb/license.htm +* @see OCB home page http://www.cs.ucdavis.edu/~rogaway/ocb +*/ +class BOTAN_DLL OCB_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; + + void set_associated_data(const byte ad[], size_t ad_len) override; + + bool valid_keylength(size_t n) const override; + + std::string name() const override; + + bool valid_iv_length(size_t length) const override + { + return (length > 0 && length < 16); + } + + ~OCB_Mode(); + + protected: + /** + * @param cipher the 128-bit block cipher to use + * @param tag_size is how big the auth tag will be + * @param decrypting true if decrypting + */ + OCB_Mode(BlockCipher* cipher, size_t tag_size, bool decrypting); + + static const size_t BS = 16; // intrinsic to OCB definition + + // fixme make these private + std::unique_ptr m_cipher; + std::unique_ptr m_L; + + size_t m_tag_size = 0; + size_t m_block_index = 0; + + secure_vector m_ad_hash; + secure_vector m_offset; + secure_vector m_checksum; + + private: + void write(const byte input[], size_t input_length) override; + void start_msg() override; + void end_msg() override; + + std::unique_ptr m_nonce_state; + }; + +class BOTAN_DLL OCB_Encryption : public OCB_Mode + { + public: + /** + * @param cipher the 128-bit block cipher to use + * @param tag_size is how big the auth tag will be + */ + OCB_Encryption(BlockCipher* cipher, size_t tag_size = 16) : + OCB_Mode(cipher, 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; + }; + +class BOTAN_DLL OCB_Decryption : public OCB_Mode + { + public: + /** + * @param cipher the 128-bit block cipher to use + * @param tag_size is how big the auth tag will be + */ + OCB_Decryption(BlockCipher* cipher, size_t tag_size = 16) : + OCB_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/aead/aead.h b/src/filters/modes/aead/aead.h deleted file mode 100644 index 868008e5d..000000000 --- a/src/filters/modes/aead/aead.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -* Interface for AEAD modes -* (C) 2013 Jack Lloyd -* -* Distributed under the terms of the Botan license -*/ - -#ifndef BOTAN_AEAD_H__ -#define BOTAN_AEAD_H__ - -#include - -namespace Botan { - -class AEAD_Mode : public Keyed_Filter - { - public: - /** - * Set associated data that is not included in the ciphertext but - * that should be authenticated. Must be called after set_key - * and before end_msg. - * - * @param ad the associated data - * @param ad_len length of add in bytes - */ - virtual void set_associated_data(const byte ad[], size_t ad_len) = 0; - - virtual void set_nonce(const byte nonce[], size_t nonce_len) = 0; - - void set_iv(const InitializationVector& iv) override - { - set_nonce(iv.begin(), iv.length()); - } - }; - -} - -#endif diff --git a/src/filters/modes/aead/info.txt b/src/filters/modes/aead/info.txt deleted file mode 100644 index b43aab2f6..000000000 --- a/src/filters/modes/aead/info.txt +++ /dev/null @@ -1 +0,0 @@ -define AEAD diff --git a/src/filters/modes/eax/eax.cpp b/src/filters/modes/eax/eax.cpp deleted file mode 100644 index 57f62833b..000000000 --- a/src/filters/modes/eax/eax.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* -* EAX Mode Encryption -* (C) 1999-2007 Jack Lloyd -* -* Distributed under the terms of the Botan license -*/ - -#include -#include -#include -#include -#include -#include - -namespace Botan { - -namespace { - -/* -* EAX MAC-based PRF -*/ -secure_vector eax_prf(byte tag, size_t BLOCK_SIZE, - MessageAuthenticationCode& mac, - const byte in[], size_t length) - { - for(size_t i = 0; i != BLOCK_SIZE - 1; ++i) - mac.update(0); - mac.update(tag); - mac.update(in, length); - return mac.final(); - } - -size_t eax_tag_size(size_t tag_size, const BlockCipher& cipher) - { - if(tag_size == 0) - return cipher.block_size(); - return (tag_size / 8); - } - -} - -/* -* EAX_Mode Constructor -*/ -EAX_Mode::EAX_Mode(BlockCipher* cipher, size_t tag_size, bool decrypting) : - Buffered_Filter(cipher->parallel_bytes(), - decrypting ? eax_tag_size(tag_size, *cipher) : 0), - BLOCK_SIZE(cipher->block_size()), - TAG_SIZE(eax_tag_size(tag_size, *cipher)), - cipher_name(cipher->name()), - ctr_buf(DEFAULT_BUFFERSIZE) - { - cmac.reset(new CMAC(cipher->clone())); - ctr.reset(new CTR_BE(cipher)); // CTR_BE takes ownership of cipher - - if(tag_size % 8 != 0 || TAG_SIZE == 0 || TAG_SIZE > cmac->output_length()) - throw Invalid_Argument(name() + ": Bad tag size " + std::to_string(tag_size)); - } - -/* -* Check if a keylength is valid for EAX -*/ -bool EAX_Mode::valid_keylength(size_t n) const - { - if(!ctr->valid_keylength(n)) - return false; - return true; - } - -/* -* Set the EAX key -*/ -void EAX_Mode::set_key(const SymmetricKey& key) - { - /* - * These could share the key schedule, which is one nice part of EAX, - * but it's much easier to ignore that here... - */ - ctr->set_key(key); - cmac->set_key(key); - - ad_mac = eax_prf(1, BLOCK_SIZE, *cmac, nullptr, 0); - } - -/* -* Do setup at the start of each message -*/ -void EAX_Mode::start_msg() - { - for(size_t i = 0; i != BLOCK_SIZE - 1; ++i) - cmac->update(0); - cmac->update(2); - } - -/* -* Set the EAX nonce -*/ -void EAX_Mode::set_nonce(const byte nonce[], size_t nonce_len) - { - nonce_mac = eax_prf(0, BLOCK_SIZE, *cmac, nonce, nonce_len); - ctr->set_iv(&nonce_mac[0], nonce_mac.size()); - } - -/* -* Set the EAX associated data -*/ -void EAX_Mode::set_associated_data(const byte ad[], size_t length) - { - ad_mac = eax_prf(1, BLOCK_SIZE, *cmac, ad, length); - } - -/* -* Return the name of this cipher mode -*/ -std::string EAX_Mode::name() const - { - return (cipher_name + "/EAX"); - } - -void EAX_Mode::write(const byte input[], size_t length) - { - Buffered_Filter::write(input, length); - } - -void EAX_Mode::end_msg() - { - Buffered_Filter::end_msg(); - } - -void EAX_Encryption::buffered_block(const byte input[], size_t length) - { - while(length) - { - size_t copied = std::min(length, ctr_buf.size()); - - ctr->cipher(input, &ctr_buf[0], copied); - cmac->update(&ctr_buf[0], copied); - - send(ctr_buf, copied); - - input += copied; - length -= copied; - } - } - -void EAX_Encryption::buffered_final(const byte input[], size_t input_length) - { - buffered_block(input, input_length); - - secure_vector data_mac = cmac->final(); - xor_buf(data_mac, nonce_mac, data_mac.size()); - xor_buf(data_mac, ad_mac, data_mac.size()); - - send(data_mac, TAG_SIZE); - } - -void EAX_Decryption::buffered_block(const byte input[], size_t length) - { - cmac->update(&input[0], length); - - while(length) - { - size_t copied = std::min(length, ctr_buf.size()); - - ctr->cipher(input, &ctr_buf[0], copied); - - send(ctr_buf, copied); - - input += copied; - length -= copied; - } - } - -void EAX_Decryption::buffered_final(const byte input[], size_t input_length) - { - BOTAN_ASSERT(input_length >= TAG_SIZE, "Have the tag as part of final input"); - - const byte* included_tag = &input[input_length - TAG_SIZE]; - input_length -= TAG_SIZE; - - if(input_length) // handle any remaining input - buffered_block(input, input_length); - - secure_vector mac = cmac->final(); - mac ^= nonce_mac; - mac ^= ad_mac; - - if(!same_mem(&mac[0], included_tag, TAG_SIZE)) - throw Integrity_Failure("EAX tag check failed"); - } - - -} diff --git a/src/filters/modes/eax/eax.h b/src/filters/modes/eax/eax.h deleted file mode 100644 index e219487cb..000000000 --- a/src/filters/modes/eax/eax.h +++ /dev/null @@ -1,130 +0,0 @@ -/* -* EAX Mode -* (C) 1999-2007,2013 Jack Lloyd -* -* Distributed under the terms of the Botan license -*/ - -#ifndef BOTAN_EAX_H__ -#define BOTAN_EAX_H__ - -#include -#include -#include -#include -#include -#include - -namespace Botan { - -/** -* EAX Mode -*/ -class BOTAN_DLL EAX_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; - - void set_associated_data(const byte ad[], size_t ad_len) override; - - std::string name() const override; - - bool valid_keylength(size_t key_len) const override; - - // EAX supports arbitrary IV lengths - bool valid_iv_length(size_t) const override { return true; } - protected: - /** - * @param cipher the cipher to use - * @param tag_size is how big the auth tag will be - */ - EAX_Mode(BlockCipher* cipher, size_t tag_size, bool decrypting); - - void start_msg(); - - /** - * The block size of the underlying cipher - */ - const size_t BLOCK_SIZE; - - /** - * The requested tag name - */ - const size_t TAG_SIZE; - - /** - * The name of the cipher - */ - std::string cipher_name; - - /** - * The stream cipher (CTR mode) - */ - std::unique_ptr ctr; - - /** - * The MAC (CMAC) - */ - std::unique_ptr cmac; - - /** - * The MAC of the nonce - */ - secure_vector nonce_mac; - - /** - * The MAC of the associated data - */ - secure_vector ad_mac; - - /** - * A buffer for CTR mode encryption - */ - secure_vector ctr_buf; - private: - void write(const byte[], size_t); - void end_msg(); - }; - -/** -* EAX Encryption -*/ -class BOTAN_DLL EAX_Encryption : public EAX_Mode - { - public: - /** - * @param ciph the cipher to use - * @param tag_size is how big the auth tag will be - */ - EAX_Encryption(BlockCipher* ciph, size_t tag_size = 0) : - EAX_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; - }; - -/** -* EAX Decryption -*/ -class BOTAN_DLL EAX_Decryption : public EAX_Mode - { - public: - /** - * @param ciph the cipher to use - * @param tag_size is how big the auth tag will be - */ - EAX_Decryption(BlockCipher* cipher, size_t tag_size = 0) : - EAX_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/eax/info.txt b/src/filters/modes/eax/info.txt deleted file mode 100644 index 09d92e724..000000000 --- a/src/filters/modes/eax/info.txt +++ /dev/null @@ -1,7 +0,0 @@ -define EAX - - -block -cmac -ctr - diff --git a/src/filters/modes/gcm/gcm.cpp b/src/filters/modes/gcm/gcm.cpp deleted file mode 100644 index 527310069..000000000 --- a/src/filters/modes/gcm/gcm.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/* -* GCM Mode Encryption -* (C) 2013 Jack Lloyd -* -* Distributed under the terms of the Botan license -*/ - -#include -#include -#include -#include - -namespace Botan { - -secure_vector -gcm_multiply(const secure_vector& x, - const secure_vector& y) - { - static const u64bit R = 0xE100000000000000; - - u64bit V[2] = { - load_be(&y[0], 0), - load_be(&y[0], 1) - }; - - u64bit Z[2] = { 0, 0 }; - - for(size_t i = 0; i != 2; ++i) - { - u64bit X = load_be(&x[0], i); - - for(size_t j = 0; j != 64; ++j) - { - if(X >> 63) - { - Z[0] ^= V[0]; - Z[1] ^= V[1]; - } - - const u64bit r = (V[1] & 1) ? R : 0; - - V[1] = (V[0] << 63) | (V[1] >> 1); - V[0] = (V[0] >> 1) ^ r; - - X <<= 1; - } - } - - secure_vector out(16); - store_be(&out[0], Z[0], Z[1]); - return out; - } - -void ghash_update(const secure_vector& H, - secure_vector& ghash, - const byte input[], size_t length) - { - const size_t BS = 16; - - /* - 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); - - xor_buf(&ghash[0], &input[0], to_proc); - - ghash = gcm_multiply(ghash, H); - - input += to_proc; - length -= to_proc; - } - } - -void ghash_finalize(const secure_vector& H, - secure_vector& ghash, - size_t ad_len, size_t text_len) - { - secure_vector final_block(16); - store_be(&final_block[0], 8*ad_len, 8*text_len); - ghash_update(H, ghash, &final_block[0], final_block.size()); - } - -/* -* 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_mac(16), - m_ad_len(0), m_text_len(0), - m_ctr_buf(8 * cipher->parallel_bytes()) - { - if(cipher->block_size() != BS) - throw std::invalid_argument("OCB requires a 128 bit cipher so cannot be used with " + - cipher->name()); - - 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 zeros(BS); - 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 y0(BS); - - 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_enc_y0.resize(BS); - m_ctr->encipher(m_enc_y0); - } - -/* -* Do setup at the start of each message -*/ -void GCM_Mode::start_msg() - { - m_text_len = 0; - m_mac = 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(length, m_ctr_buf.size()); - - m_ctr->cipher(input, &m_ctr_buf[0], copied); - ghash_update(m_H, m_mac, &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_mac, m_ad_len, m_text_len); - - m_mac ^= m_enc_y0; - - send(m_mac, m_tag_size); - } - -void GCM_Decryption::buffered_block(const byte input[], size_t length) - { - while(length) - { - size_t copied = std::min(length, m_ctr_buf.size()); - - ghash_update(m_H, m_mac, &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_mac, m_ad_len, m_text_len); - - m_mac ^= m_enc_y0; - - if(!same_mem(&m_mac[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 deleted file mode 100644 index fa13597ce..000000000 --- a/src/filters/modes/gcm/gcm.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -* GCM Mode -* (C) 2013 Jack Lloyd -* -* Distributed under the terms of the Botan license -*/ - -#ifndef BOTAN_GCM_H__ -#define BOTAN_GCM_H__ - -#include -#include -#include -#include -#include - -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); - - static const size_t BS = 16; - - const size_t m_tag_size; - const std::string m_cipher_name; - - std::unique_ptr m_ctr; - secure_vector m_H; - secure_vector m_H_ad; - secure_vector m_mac; - secure_vector m_enc_y0; - size_t m_ad_len, m_text_len; - secure_vector 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 deleted file mode 100644 index 52e677a03..000000000 --- a/src/filters/modes/gcm/info.txt +++ /dev/null @@ -1,6 +0,0 @@ -define GCM - - -block -ctr - diff --git a/src/filters/modes/ocb/info.txt b/src/filters/modes/ocb/info.txt deleted file mode 100644 index a98d7d54e..000000000 --- a/src/filters/modes/ocb/info.txt +++ /dev/null @@ -1,7 +0,0 @@ -define OCB - - -block -aead -cmac - diff --git a/src/filters/modes/ocb/ocb.cpp b/src/filters/modes/ocb/ocb.cpp deleted file mode 100644 index ebf440d32..000000000 --- a/src/filters/modes/ocb/ocb.cpp +++ /dev/null @@ -1,423 +0,0 @@ -/* -* OCB Mode -* (C) 2013 Jack Lloyd -* -* Distributed under the terms of the Botan license -*/ - -#include -#include -#include -#include -#include - -#include -#include - -namespace Botan { - -// Has to be in Botan namespace so unique_ptr can reference it -class L_computer - { - public: - L_computer(const BlockCipher& cipher) - { - m_L_star.resize(cipher.block_size()); - cipher.encrypt(m_L_star); - m_L_dollar = poly_double(star()); - m_L.push_back(poly_double(dollar())); - } - - const secure_vector& star() const { return m_L_star; } - - const secure_vector& dollar() const { return m_L_dollar; } - - const secure_vector& operator()(size_t i) const { return get(i); } - - const secure_vector& get(size_t i) const - { - while(m_L.size() <= i) - m_L.push_back(poly_double(m_L.back())); - - return m_L.at(i); - } - - private: - secure_vector poly_double(const secure_vector& in) const - { - return CMAC::poly_double(in, 0x87); - } - - secure_vector m_L_dollar, m_L_star; - mutable std::vector> m_L; - }; - -class Nonce_State - { - public: - Nonce_State(const BlockCipher& cipher) : m_cipher(cipher) {} - - secure_vector update_nonce(const byte nonce[], - size_t nonce_len); - - bool fresh_nonce() { bool b = false; std::swap(b, m_fresh); return b; } - private: - const BlockCipher& m_cipher; - secure_vector m_last_nonce; - secure_vector m_stretch; - bool m_fresh = false; - }; - -secure_vector -Nonce_State::update_nonce(const byte nonce[], size_t nonce_len) - { - const size_t BS = 16; - - BOTAN_ASSERT(nonce_len < BS, "Nonce is less than 128 bits"); - - secure_vector nonce_buf(BS); - - copy_mem(&nonce_buf[BS - nonce_len], nonce, nonce_len); - nonce_buf[BS - nonce_len - 1] = 1; - - const byte bottom = nonce_buf[15] & 0x3F; - nonce_buf[15] &= 0xC0; - - const bool need_new_stretch = (m_last_nonce != nonce_buf); - - if(need_new_stretch) - { - m_last_nonce = nonce_buf; - - m_cipher.encrypt(nonce_buf); - - for(size_t i = 0; i != 8; ++i) - nonce_buf.push_back(nonce_buf[i] ^ nonce_buf[i+1]); - - m_stretch = nonce_buf; - } - - // now set the offset from stretch and bottom - - const size_t shift_bytes = bottom / 8; - const size_t shift_bits = bottom % 8; - - secure_vector offset(BS); - for(size_t i = 0; i != BS; ++i) - { - offset[i] = (m_stretch[i+shift_bytes] << shift_bits); - offset[i] |= (m_stretch[i+shift_bytes+1] >> (8-shift_bits)); - } - - m_fresh = true; - return offset; - } - -namespace { - -/* -* OCB's HASH -*/ -secure_vector ocb_hash(const L_computer& L, - const BlockCipher& cipher, - const byte ad[], size_t ad_len) - { - const size_t BS = cipher.block_size(); - - secure_vector sum(BS); - secure_vector offset(BS); - - secure_vector buf(BS); - - const size_t ad_blocks = (ad_len / BS); - const size_t ad_remainder = (ad_len % BS); - - for(size_t i = 0; i != ad_blocks; ++i) - { - // this loop could run in parallel - offset ^= L(ctz(i+1)); - - buf = offset; - xor_buf(&buf[0], &ad[BS*i], BS); - - cipher.encrypt(buf); - - sum ^= buf; - } - - if(ad_remainder) - { - offset ^= L.star(); - - buf = offset; - xor_buf(&buf[0], &ad[BS*ad_blocks], ad_remainder); - buf[ad_len % BS] ^= 0x80; - - cipher.encrypt(buf); - - sum ^= buf; - } - - return sum; - } - -} - -OCB_Mode::OCB_Mode(BlockCipher* cipher, size_t tag_size, bool decrypting) : - Buffered_Filter(cipher->parallel_bytes(), decrypting ? tag_size : 0), - m_cipher(cipher), m_tag_size(tag_size), - m_ad_hash(BS), m_offset(BS), m_checksum(BS) - { - if(m_cipher->block_size() != BS) - throw std::invalid_argument("OCB requires a 128 bit cipher so cannot be used with " + - m_cipher->name()); - - if(m_tag_size != 16) // 64, 96 bits also supported - throw std::invalid_argument("OCB cannot produce a " + std::to_string(m_tag_size) + - " byte tag"); - - } - -OCB_Mode::~OCB_Mode() { /* for unique_ptr destructor */ } - -bool OCB_Mode::valid_keylength(size_t n) const - { - return m_cipher->valid_keylength(n); - } - -std::string OCB_Mode::name() const - { - return m_cipher->name() + "/OCB"; // include tag size - } - -void OCB_Mode::set_key(const SymmetricKey& key) - { - m_cipher->set_key(key); - m_L.reset(new L_computer(*m_cipher)); - m_nonce_state.reset(new Nonce_State(*m_cipher)); - } - -void OCB_Mode::set_nonce(const byte nonce[], size_t nonce_len) - { - if(!valid_iv_length(nonce_len)) - throw Invalid_IV_Length(name(), nonce_len); - - BOTAN_ASSERT(m_nonce_state, "A key was set"); - - m_offset = m_nonce_state->update_nonce(nonce, nonce_len); - } - -void OCB_Mode::start_msg() - { - BOTAN_ASSERT(m_nonce_state->fresh_nonce(), "Nonce state is fresh"); - } - -void OCB_Mode::set_associated_data(const byte ad[], size_t ad_len) - { - m_ad_hash = ocb_hash(*m_L, *m_cipher, &ad[0], ad_len); - } - -void OCB_Mode::write(const byte input[], size_t length) - { - Buffered_Filter::write(input, length); - } - -void OCB_Mode::end_msg() - { - Buffered_Filter::end_msg(); - } - -void OCB_Encryption::buffered_block(const byte input[], size_t input_length) - { - BOTAN_ASSERT(input_length % BS == 0, "Input length is an even number of blocks"); - - const size_t blocks = input_length / BS; - - const size_t par_bytes = m_cipher->parallel_bytes(); - - BOTAN_ASSERT(par_bytes % BS == 0, "Cipher is parallel in full blocks"); - - const size_t par_blocks = par_bytes / BS; - - const L_computer& L = *m_L; // convenient name - - secure_vector ctext_buf(par_bytes); - secure_vector csum_accum(par_bytes); - secure_vector offsets(par_bytes); - - size_t blocks_left = blocks; - - while(blocks_left) - { - const size_t to_proc = std::min(blocks_left, par_blocks); - const size_t proc_bytes = to_proc * BS; - - xor_buf(&csum_accum[0], &input[0], proc_bytes); - - for(size_t i = 0; i != to_proc; ++i) - { - m_offset ^= L(ctz(++m_block_index)); - copy_mem(&offsets[BS*i], &m_offset[0], BS); - } - - copy_mem(&ctext_buf[0], &input[0], proc_bytes); - - ctext_buf ^= offsets; - m_cipher->encrypt(ctext_buf); - ctext_buf ^= offsets; - - send(ctext_buf, proc_bytes); - - input += proc_bytes; - blocks_left -= to_proc; - } - - // fold into checksum - for(size_t i = 0; i != csum_accum.size(); ++i) - m_checksum[i % BS] ^= csum_accum[i]; - } - -void OCB_Encryption::buffered_final(const byte input[], size_t input_length) - { - if(input_length >= BS) - { - const size_t final_blocks = input_length / BS; - const size_t final_bytes = final_blocks * BS; - buffered_block(input, final_bytes); - input += final_bytes; - input_length -= final_bytes; - } - - if(input_length) - { - BOTAN_ASSERT(input_length < BS, "Only a partial block left"); - - xor_buf(&m_checksum[0], &input[0], input_length); - m_checksum[input_length] ^= 0x80; - - m_offset ^= m_L->star(); // Offset_* - - secure_vector buf(BS); - m_cipher->encrypt(m_offset, buf); - xor_buf(&buf[0], &input[0], input_length); - - send(buf, input_length); // final ciphertext - } - - // now compute the tag - secure_vector mac = m_offset; - mac ^= m_checksum; - mac ^= m_L->dollar(); - - m_cipher->encrypt(mac); - - mac ^= m_ad_hash; - - send(mac); - - zeroise(m_checksum); - zeroise(m_offset); - m_block_index = 0; - } - -void OCB_Decryption::buffered_block(const byte input[], size_t input_length) - { - BOTAN_ASSERT(input_length % BS == 0, "Input length is an even number of blocks"); - - const size_t blocks = input_length / BS; - - const size_t par_bytes = m_cipher->parallel_bytes(); - - BOTAN_ASSERT(par_bytes % BS == 0, "Cipher is parallel in full blocks"); - - const size_t par_blocks = par_bytes / BS; - - const L_computer& L = *m_L; // convenient name - - secure_vector ptext_buf(par_bytes); - secure_vector csum_accum(par_bytes); - secure_vector offsets(par_bytes); - - size_t blocks_left = blocks; - - while(blocks_left) - { - const size_t to_proc = std::min(blocks_left, par_blocks); - const size_t proc_bytes = to_proc * BS; - - for(size_t i = 0; i != to_proc; ++i) - { - m_offset ^= L(ctz(++m_block_index)); - copy_mem(&offsets[BS*i], &m_offset[0], BS); - } - - copy_mem(&ptext_buf[0], &input[0], proc_bytes); - - ptext_buf ^= offsets; - m_cipher->decrypt(ptext_buf); - ptext_buf ^= offsets; - - xor_buf(&csum_accum[0], &ptext_buf[0], proc_bytes); - - send(ptext_buf, proc_bytes); - - input += proc_bytes; - blocks_left -= to_proc; - } - - // fold into checksum - for(size_t i = 0; i != csum_accum.size(); ++i) - m_checksum[i % BS] ^= csum_accum[i]; - } - -void OCB_Decryption::buffered_final(const byte input[], size_t input_length) - { - BOTAN_ASSERT(input_length >= m_tag_size, "We have the tag"); - - const byte* included_tag = &input[input_length - m_tag_size]; - input_length -= m_tag_size; - - if(input_length >= BS) - { - const size_t final_blocks = input_length / BS; - const size_t final_bytes = final_blocks * BS; - buffered_block(input, final_bytes); - input += final_bytes; - input_length -= final_bytes; - } - - if(input_length) - { - BOTAN_ASSERT(input_length < BS, "Only a partial block left"); - - m_offset ^= m_L->star(); // Offset_* - - secure_vector buf(BS); - m_cipher->encrypt(m_offset, buf); // P_* - - xor_buf(&buf[0], &input[0], input_length); - - xor_buf(&m_checksum[0], &buf[0], input_length); - m_checksum[input_length] ^= 0x80; - - send(buf, input_length); // final plaintext - } - - // now compute the tag - secure_vector mac = m_offset; - mac ^= m_checksum; - mac ^= m_L->dollar(); - - m_cipher->encrypt(mac); - - mac ^= m_ad_hash; - - zeroise(m_checksum); - zeroise(m_offset); - m_block_index = 0; - - if(!same_mem(&mac[0], included_tag, m_tag_size)) - throw Integrity_Failure("OCB tag check failed"); - } - -} diff --git a/src/filters/modes/ocb/ocb.h b/src/filters/modes/ocb/ocb.h deleted file mode 100644 index 5e3c5cf0a..000000000 --- a/src/filters/modes/ocb/ocb.h +++ /dev/null @@ -1,112 +0,0 @@ -/* -* OCB Mode -* (C) 2013 Jack Lloyd -* -* Distributed under the terms of the Botan license -*/ - -#ifndef BOTAN_OCB_H__ -#define BOTAN_OCB_H__ - -#include -#include -#include -#include - -namespace Botan { - -class L_computer; -class Nonce_State; - -/** -* OCB Mode (base class for OCB_Encryption and OCB_Decryption). Note -* that OCB is patented, but is freely licensed in some circumstances. -* -* @see "The OCB Authenticated-Encryption Algorithm" internet draft - http://tools.ietf.org/html/draft-irtf-cfrg-ocb-00 -* @see Free Licenses http://www.cs.ucdavis.edu/~rogaway/ocb/license.htm -* @see OCB home page http://www.cs.ucdavis.edu/~rogaway/ocb -*/ -class BOTAN_DLL OCB_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; - - void set_associated_data(const byte ad[], size_t ad_len) override; - - bool valid_keylength(size_t n) const override; - - std::string name() const override; - - bool valid_iv_length(size_t length) const override - { - return (length > 0 && length < 16); - } - - ~OCB_Mode(); - - protected: - /** - * @param cipher the 128-bit block cipher to use - * @param tag_size is how big the auth tag will be - * @param decrypting true if decrypting - */ - OCB_Mode(BlockCipher* cipher, size_t tag_size, bool decrypting); - - static const size_t BS = 16; // intrinsic to OCB definition - - // fixme make these private - std::unique_ptr m_cipher; - std::unique_ptr m_L; - - size_t m_tag_size = 0; - size_t m_block_index = 0; - - secure_vector m_ad_hash; - secure_vector m_offset; - secure_vector m_checksum; - - private: - void write(const byte input[], size_t input_length) override; - void start_msg() override; - void end_msg() override; - - std::unique_ptr m_nonce_state; - }; - -class BOTAN_DLL OCB_Encryption : public OCB_Mode - { - public: - /** - * @param cipher the 128-bit block cipher to use - * @param tag_size is how big the auth tag will be - */ - OCB_Encryption(BlockCipher* cipher, size_t tag_size = 16) : - OCB_Mode(cipher, 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; - }; - -class BOTAN_DLL OCB_Decryption : public OCB_Mode - { - public: - /** - * @param cipher the 128-bit block cipher to use - * @param tag_size is how big the auth tag will be - */ - OCB_Decryption(BlockCipher* cipher, size_t tag_size = 16) : - OCB_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 -- cgit v1.2.3