diff options
author | Daniel Neus <[email protected]> | 2016-07-20 22:26:26 +0200 |
---|---|---|
committer | Daniel Neus <[email protected]> | 2016-11-08 22:16:09 +0100 |
commit | 06b44d8ed339b3a467f10a326fd209b0b9496060 (patch) | |
tree | 24c3bf3f20ba697a658d6d009d0cdb7be8a3e41f | |
parent | 523b2a4ca48fa5cf04ea371aabe7167ce2e5cd13 (diff) |
Cipher_Mode and AEAD_Mode improvements
See PR #552
- Add Cipher_Mode::reset() which resets just the message specific state and allows encrypting again under the existing key
- In Cipher_Mode::clear() (at some planes) use cipher->clear() instead of resetting the pointer which would make the cipher object unusable
- EAX_Decryption::output_length() bugfix?! Now its possible to decrypt an empty ciphertext (just a tag)
- Bugfix for GCM_Decryption::finish()
- set tag length in GCM_Mode::name()
- Cipher_Mode tests: add tests for reset()and process()
- AEAD_Mode tests: add tests for reset(), clear(), update() and process()
26 files changed, 491 insertions, 64 deletions
diff --git a/src/lib/modes/aead/ccm/ccm.cpp b/src/lib/modes/aead/ccm/ccm.cpp index 81b9f4943..de639f23a 100644 --- a/src/lib/modes/aead/ccm/ccm.cpp +++ b/src/lib/modes/aead/ccm/ccm.cpp @@ -1,6 +1,7 @@ /* * CCM Mode Encryption * (C) 2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -33,7 +34,13 @@ CCM_Mode::CCM_Mode(BlockCipher* cipher, size_t tag_size, size_t L) : void CCM_Mode::clear() { - m_cipher.reset(); + m_cipher->clear(); + reset(); + } + +void CCM_Mode::reset() + { + m_nonce.clear(); m_msg_buf.clear(); m_ad_buf.clear(); } diff --git a/src/lib/modes/aead/ccm/ccm.h b/src/lib/modes/aead/ccm/ccm.h index 7484b500a..2a17595e7 100644 --- a/src/lib/modes/aead/ccm/ccm.h +++ b/src/lib/modes/aead/ccm/ccm.h @@ -1,6 +1,7 @@ /* * CCM Mode * (C) 2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -38,6 +39,8 @@ class BOTAN_DLL CCM_Mode : public AEAD_Mode void clear() override; + void reset() override; + size_t tag_size() const override { return m_tag_size; } protected: diff --git a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp index d2f16c225..197d6f921 100644 --- a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp +++ b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp @@ -1,6 +1,7 @@ /* * ChaCha20Poly1305 AEAD * (C) 2014,2016 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -26,8 +27,14 @@ void ChaCha20Poly1305_Mode::clear() { m_chacha->clear(); m_poly1305->clear(); + reset(); + } + +void ChaCha20Poly1305_Mode::reset() + { m_ad.clear(); m_ctext_len = 0; + m_nonce_len = 0; } void ChaCha20Poly1305_Mode::key_schedule(const byte key[], size_t length) diff --git a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h index 553508854..f58bd48ac 100644 --- a/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h +++ b/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h @@ -1,6 +1,7 @@ /* * ChaCha20Poly1305 AEAD * (C) 2014 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -37,6 +38,9 @@ class BOTAN_DLL ChaCha20Poly1305_Mode : public AEAD_Mode size_t tag_size() const override { return 16; } void clear() override; + + void reset() override; + protected: std::unique_ptr<StreamCipher> m_chacha; std::unique_ptr<MessageAuthenticationCode> m_poly1305; diff --git a/src/lib/modes/aead/eax/eax.cpp b/src/lib/modes/aead/eax/eax.cpp index c76f15b48..ba52efcfd 100644 --- a/src/lib/modes/aead/eax/eax.cpp +++ b/src/lib/modes/aead/eax/eax.cpp @@ -1,6 +1,7 @@ /* * EAX Mode Encryption * (C) 1999-2007 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -22,7 +23,9 @@ secure_vector<byte> eax_prf(byte tag, size_t block_size, 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(); @@ -45,11 +48,16 @@ EAX_Mode::EAX_Mode(BlockCipher* cipher, size_t tag_size) : void EAX_Mode::clear() { - m_cipher.reset(); - m_ctr.reset(); - m_cmac.reset(); - zeroise(m_ad_mac); - zeroise(m_nonce_mac); + m_cipher->clear(); + m_ctr->clear(); + m_cmac->clear(); + reset(); + } + +void EAX_Mode::reset() + { + m_ad_mac.clear(); + m_nonce_mac.clear(); } std::string EAX_Mode::name() const @@ -78,8 +86,6 @@ void EAX_Mode::key_schedule(const byte key[], size_t length) */ m_ctr->set_key(key, length); m_cmac->set_key(key, length); - - m_ad_mac = eax_prf(1, block_size(), *m_cmac, nullptr, 0); } /* @@ -117,6 +123,12 @@ void EAX_Encryption::finish(secure_vector<byte>& buffer, size_t offset) secure_vector<byte> data_mac = m_cmac->final(); xor_buf(data_mac, m_nonce_mac, data_mac.size()); + + if(m_ad_mac.empty()) + { + m_ad_mac = eax_prf(1, block_size(), *m_cmac, nullptr, 0); + } + xor_buf(data_mac, m_ad_mac, data_mac.size()); buffer += std::make_pair(data_mac.data(), tag_size()); @@ -149,6 +161,12 @@ void EAX_Decryption::finish(secure_vector<byte>& buffer, size_t offset) secure_vector<byte> mac = m_cmac->final(); mac ^= m_nonce_mac; + + if(m_ad_mac.empty()) + { + m_ad_mac = eax_prf(1, block_size(), *m_cmac, nullptr, 0); + } + mac ^= m_ad_mac; if(!same_mem(mac.data(), included_tag, tag_size())) diff --git a/src/lib/modes/aead/eax/eax.h b/src/lib/modes/aead/eax/eax.h index 0dedefe07..c0b6bcf42 100644 --- a/src/lib/modes/aead/eax/eax.h +++ b/src/lib/modes/aead/eax/eax.h @@ -1,6 +1,7 @@ /* * EAX Mode * (C) 1999-2007,2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -35,6 +36,9 @@ class BOTAN_DLL EAX_Mode : public AEAD_Mode size_t tag_size() const override { return m_tag_size; } void clear() override; + + void reset() override; + protected: /** * @param cipher the cipher to use @@ -97,7 +101,7 @@ class BOTAN_DLL EAX_Decryption final : public EAX_Mode size_t output_length(size_t input_length) const override { - BOTAN_ASSERT(input_length > tag_size(), "Sufficient input"); + BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input"); return input_length - tag_size(); } diff --git a/src/lib/modes/aead/gcm/gcm.cpp b/src/lib/modes/aead/gcm/gcm.cpp index a73e5ee5b..e0bc59a8d 100644 --- a/src/lib/modes/aead/gcm/gcm.cpp +++ b/src/lib/modes/aead/gcm/gcm.cpp @@ -1,6 +1,7 @@ /* * GCM Mode Encryption * (C) 2013,2015 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -150,8 +151,14 @@ secure_vector<byte> GHASH::nonce_hash(const byte nonce[], size_t nonce_len) void GHASH::clear() { zeroise(m_H); + reset(); + } + +void GHASH::reset() + { zeroise(m_H_ad); m_ghash.clear(); + m_nonce.clear(); m_text_len = m_ad_len = 0; } @@ -177,11 +184,17 @@ void GCM_Mode::clear() { m_ctr->clear(); m_ghash->clear(); + reset(); + } + +void GCM_Mode::reset() + { + m_ghash->reset(); } std::string GCM_Mode::name() const { - return (m_cipher_name + "/GCM"); + return (m_cipher_name + "/GCM(" + std::to_string(tag_size()) + ")"); } std::string GCM_Mode::provider() const @@ -294,7 +307,7 @@ void GCM_Decryption::finish(secure_vector<byte>& buffer, size_t offset) auto mac = m_ghash->final(); - const byte* included_tag = &buffer[remaining]; + const byte* included_tag = &buffer[remaining+offset]; if(!same_mem(mac.data(), included_tag, tag_size())) throw Integrity_Failure("GCM tag check failed"); diff --git a/src/lib/modes/aead/gcm/gcm.h b/src/lib/modes/aead/gcm/gcm.h index 6468cbd9c..463e69a3b 100644 --- a/src/lib/modes/aead/gcm/gcm.h +++ b/src/lib/modes/aead/gcm/gcm.h @@ -1,6 +1,7 @@ /* * GCM Mode * (C) 2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -37,6 +38,8 @@ class BOTAN_DLL GCM_Mode : public AEAD_Mode void clear() override; + void reset() override; + std::string provider() const override; protected: GCM_Mode(BlockCipher* cipher, size_t tag_size); @@ -128,6 +131,8 @@ class BOTAN_DLL GHASH : public SymmetricAlgorithm void clear() override; + void reset(); + std::string name() const override { return "GHASH"; } protected: void ghash_update(secure_vector<byte>& x, diff --git a/src/lib/modes/aead/ocb/ocb.cpp b/src/lib/modes/aead/ocb/ocb.cpp index 0ce2b6f00..c530dda5d 100644 --- a/src/lib/modes/aead/ocb/ocb.cpp +++ b/src/lib/modes/aead/ocb/ocb.cpp @@ -1,6 +1,7 @@ /* * OCB Mode * (C) 2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -129,12 +130,19 @@ OCB_Mode::~OCB_Mode() { /* for unique_ptr destructor */ } void OCB_Mode::clear() { - m_cipher.reset(); - m_L.reset(); + m_cipher->clear(); + m_L.reset(); // add clear here? + reset(); + } +void OCB_Mode::reset() + { + m_block_index = 0; zeroise(m_ad_hash); zeroise(m_offset); zeroise(m_checksum); + m_last_nonce.clear(); + m_stretch.clear(); } bool OCB_Mode::valid_nonce_length(size_t length) const diff --git a/src/lib/modes/aead/ocb/ocb.h b/src/lib/modes/aead/ocb/ocb.h index 4daa7a81b..ce9d29f1b 100644 --- a/src/lib/modes/aead/ocb/ocb.h +++ b/src/lib/modes/aead/ocb/ocb.h @@ -1,6 +1,7 @@ /* * OCB Mode * (C) 2013,2014 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -41,6 +42,8 @@ class BOTAN_DLL OCB_Mode : public AEAD_Mode void clear() override; + void reset() override; + ~OCB_Mode(); protected: /** diff --git a/src/lib/modes/aead/siv/siv.cpp b/src/lib/modes/aead/siv/siv.cpp index ce20f3ada..373a2627c 100644 --- a/src/lib/modes/aead/siv/siv.cpp +++ b/src/lib/modes/aead/siv/siv.cpp @@ -1,6 +1,7 @@ /* * SIV Mode Encryption * (C) 2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -23,7 +24,13 @@ SIV_Mode::SIV_Mode(BlockCipher* cipher) : void SIV_Mode::clear() { - m_ctr.reset(); + m_ctr->clear(); + m_cmac->clear(); + reset(); + } + +void SIV_Mode::reset() + { m_nonce.clear(); m_msg_buf.clear(); m_ad_macs.clear(); diff --git a/src/lib/modes/aead/siv/siv.h b/src/lib/modes/aead/siv/siv.h index ca3e7df37..71990ef96 100644 --- a/src/lib/modes/aead/siv/siv.h +++ b/src/lib/modes/aead/siv/siv.h @@ -1,6 +1,7 @@ /* * SIV Mode * (C) 2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -46,6 +47,8 @@ class BOTAN_DLL SIV_Mode : public AEAD_Mode void clear() override; + void reset() override; + size_t tag_size() const override { return 16; } protected: diff --git a/src/lib/modes/cbc/cbc.cpp b/src/lib/modes/cbc/cbc.cpp index 7e1fe4d0f..592ff95e9 100644 --- a/src/lib/modes/cbc/cbc.cpp +++ b/src/lib/modes/cbc/cbc.cpp @@ -1,6 +1,7 @@ /* * CBC Mode * (C) 1999-2007,2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -25,7 +26,12 @@ CBC_Mode::CBC_Mode(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) : void CBC_Mode::clear() { m_cipher->clear(); - m_state.clear(); + reset(); + } + +void CBC_Mode::reset() + { + zeroise(m_state); } std::string CBC_Mode::name() const @@ -239,6 +245,12 @@ void CBC_Decryption::finish(secure_vector<byte>& buffer, size_t offset) buffer.resize(buffer.size() - pad_bytes); // remove padding } +void CBC_Decryption::reset() + { + zeroise(state()); + zeroise(m_tempbuf); + } + bool CTS_Decryption::valid_nonce_length(size_t n) const { return (n == cipher().block_size()); diff --git a/src/lib/modes/cbc/cbc.h b/src/lib/modes/cbc/cbc.h index c6b6e4e4b..1b7cbd323 100644 --- a/src/lib/modes/cbc/cbc.h +++ b/src/lib/modes/cbc/cbc.h @@ -1,6 +1,7 @@ /* * CBC mode * (C) 1999-2007,2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -31,6 +32,9 @@ class BOTAN_DLL CBC_Mode : public Cipher_Mode bool valid_nonce_length(size_t n) const override; void clear() override; + + void reset() override; + protected: CBC_Mode(BlockCipher* cipher, BlockCipherModePaddingMethod* padding); @@ -118,6 +122,9 @@ class BOTAN_DLL CBC_Decryption : public CBC_Mode size_t output_length(size_t input_length) const override; size_t minimum_final_size() const override; + + void reset() override; + private: secure_vector<byte> m_tempbuf; }; diff --git a/src/lib/modes/cfb/cfb.cpp b/src/lib/modes/cfb/cfb.cpp index 793bfaf46..2d1477e27 100644 --- a/src/lib/modes/cfb/cfb.cpp +++ b/src/lib/modes/cfb/cfb.cpp @@ -1,6 +1,7 @@ /* * CFB Mode * (C) 1999-2007,2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -22,7 +23,13 @@ CFB_Mode::CFB_Mode(BlockCipher* cipher, size_t feedback_bits) : void CFB_Mode::clear() { m_cipher->clear(); + reset(); + } + +void CFB_Mode::reset() + { m_shift_register.clear(); + m_keystream_buf.clear(); } std::string CFB_Mode::name() const diff --git a/src/lib/modes/cfb/cfb.h b/src/lib/modes/cfb/cfb.h index 318bdab64..18611f3f2 100644 --- a/src/lib/modes/cfb/cfb.h +++ b/src/lib/modes/cfb/cfb.h @@ -1,6 +1,7 @@ /* * CFB mode * (C) 1999-2007,2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -34,6 +35,8 @@ class BOTAN_DLL CFB_Mode : public Cipher_Mode bool valid_nonce_length(size_t n) const override; void clear() override; + + void reset() override; protected: CFB_Mode(BlockCipher* cipher, size_t feedback_bits); diff --git a/src/lib/modes/cipher_mode.h b/src/lib/modes/cipher_mode.h index 7c0f8fc57..8bf58f10a 100644 --- a/src/lib/modes/cipher_mode.h +++ b/src/lib/modes/cipher_mode.h @@ -65,7 +65,7 @@ class BOTAN_DLL Cipher_Mode * * Processes msg in place and returns bytes written. Normally * this will be either msg_len (indicating the entire message was - * processes) or for certain AEAD modes zero (indicating that the + * processed) or for certain AEAD modes zero (indicating that the * mode requires the entire message be processed in one pass). * * @param msg the message to be processed @@ -127,9 +127,18 @@ class BOTAN_DLL Cipher_Mode virtual std::string name() const = 0; + /** + * Zeroise all state + * See also reset_msg() + */ virtual void clear() = 0; /** + * Resets just the message specific state and allows encrypting again under the existing key + */ + virtual void reset() = 0; + + /** * @return true iff this mode provides authentication as well as * confidentiality. */ diff --git a/src/lib/modes/ecb/ecb.cpp b/src/lib/modes/ecb/ecb.cpp index b39682fdf..78dff5ffa 100644 --- a/src/lib/modes/ecb/ecb.cpp +++ b/src/lib/modes/ecb/ecb.cpp @@ -1,6 +1,7 @@ /* * ECB Mode * (C) 1999-2009,2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -25,6 +26,12 @@ void ECB_Mode::clear() m_cipher->clear(); } +void ECB_Mode::reset() + { + // no msg state here + return; + } + std::string ECB_Mode::name() const { return cipher().name() + "/ECB/" + padding().name(); diff --git a/src/lib/modes/ecb/ecb.h b/src/lib/modes/ecb/ecb.h index 4d2a11d05..9fc17a80d 100644 --- a/src/lib/modes/ecb/ecb.h +++ b/src/lib/modes/ecb/ecb.h @@ -1,6 +1,7 @@ /* * ECB Mode * (C) 1999-2009,2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -31,6 +32,9 @@ class BOTAN_DLL ECB_Mode : public Cipher_Mode bool valid_nonce_length(size_t n) const override; void clear() override; + + void reset() override; + protected: ECB_Mode(BlockCipher* cipher, BlockCipherModePaddingMethod* padding); diff --git a/src/lib/modes/stream_mode.h b/src/lib/modes/stream_mode.h index 3a0c8574c..83b0543c9 100644 --- a/src/lib/modes/stream_mode.h +++ b/src/lib/modes/stream_mode.h @@ -1,5 +1,6 @@ /* * (C) 2015 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -44,7 +45,13 @@ class BOTAN_DLL Stream_Cipher_Mode : public Cipher_Mode std::string name() const override { return m_cipher->name(); } - void clear() override { return m_cipher->clear(); } + void clear() override + { + m_cipher->clear(); + reset(); + } + + void reset() override { /* no msg state */ return; } private: void start_msg(const byte nonce[], size_t nonce_len) override diff --git a/src/lib/modes/xts/xts.cpp b/src/lib/modes/xts/xts.cpp index 4b697ae6c..13dc932ea 100644 --- a/src/lib/modes/xts/xts.cpp +++ b/src/lib/modes/xts/xts.cpp @@ -1,6 +1,7 @@ /* * XTS Mode * (C) 2009,2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -61,6 +62,11 @@ void XTS_Mode::clear() { m_cipher->clear(); m_tweak_cipher->clear(); + reset(); + } + +void XTS_Mode::reset() + { zeroise(m_tweak); } diff --git a/src/lib/modes/xts/xts.h b/src/lib/modes/xts/xts.h index 1216251c2..6d53b4312 100644 --- a/src/lib/modes/xts/xts.h +++ b/src/lib/modes/xts/xts.h @@ -1,6 +1,7 @@ /* * XTS mode, from IEEE P1619 * (C) 2009,2013 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -32,6 +33,9 @@ class BOTAN_DLL XTS_Mode : public Cipher_Mode bool valid_nonce_length(size_t n) const override; void clear() override; + + void reset() override; + protected: explicit XTS_Mode(BlockCipher* cipher); diff --git a/src/lib/tls/tls_cbc/tls_cbc.cpp b/src/lib/tls/tls_cbc/tls_cbc.cpp index ef397e44d..bd9ce2528 100644 --- a/src/lib/tls/tls_cbc/tls_cbc.cpp +++ b/src/lib/tls/tls_cbc/tls_cbc.cpp @@ -1,8 +1,9 @@ /* * TLS CBC Record Handling * (C) 2012,2013,2014,2015,2016 Jack Lloyd -* 2016 Juraj Somorovsky -* 2016 Matthias Gierlings +* (C) 2016 Juraj Somorovsky +* (C) 2016 Matthias Gierlings +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -46,7 +47,14 @@ void TLS_CBC_HMAC_AEAD_Mode::clear() { cipher().clear(); mac().clear(); + reset(); + } + +void TLS_CBC_HMAC_AEAD_Mode::reset() + { cbc_state().clear(); + m_ad.clear(); + m_msg.clear(); } std::string TLS_CBC_HMAC_AEAD_Mode::name() const diff --git a/src/lib/tls/tls_cbc/tls_cbc.h b/src/lib/tls/tls_cbc/tls_cbc.h index 846774998..c448879fb 100644 --- a/src/lib/tls/tls_cbc/tls_cbc.h +++ b/src/lib/tls/tls_cbc/tls_cbc.h @@ -1,6 +1,7 @@ /* * TLS CBC+HMAC AEAD * (C) 2016 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -41,6 +42,8 @@ class TLS_CBC_HMAC_AEAD_Mode : public AEAD_Mode void clear() override final; + void reset() override final; + protected: TLS_CBC_HMAC_AEAD_Mode(const std::string& cipher_name, size_t cipher_keylen, diff --git a/src/tests/test_aead.cpp b/src/tests/test_aead.cpp index e7756d5bb..1558c0d30 100644 --- a/src/tests/test_aead.cpp +++ b/src/tests/test_aead.cpp @@ -1,5 +1,6 @@ /* * (C) 2014,2015,2016 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -7,7 +8,7 @@ #include "tests.h" #if defined(BOTAN_HAS_AEAD_MODES) -#include <botan/aead.h> + #include <botan/aead.h> #endif namespace Botan_Tests { @@ -23,76 +24,237 @@ class AEAD_Tests : public Text_Based_Test Text_Based_Test("aead", {"Key", "Nonce", "In", "Out"}, {"AD"}) {} - Test::Result run_one_test(const std::string& algo, const VarMap& vars) override + Test::Result test_enc(const std::vector<uint8_t>& key, const std::vector<uint8_t>& nonce, + const std::vector<uint8_t>& input, const std::vector<uint8_t>& expected, + const std::vector<uint8_t>& ad, const std::string& algo) { - const std::vector<uint8_t> key = get_req_bin(vars, "Key"); - const std::vector<uint8_t> nonce = get_opt_bin(vars, "Nonce"); - 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> ad = get_opt_bin(vars, "AD"); - Test::Result result(algo); std::unique_ptr<Botan::AEAD_Mode> enc(Botan::get_aead(algo, Botan::ENCRYPTION)); - std::unique_ptr<Botan::AEAD_Mode> dec(Botan::get_aead(algo, Botan::DECRYPTION)); - if(!enc || !dec) - { - result.note_missing(algo); - return result; - } + // First some tests for reset() to make sure it resets what we need it to + // set garbage values + enc->set_key(mutate_vec(key)); + enc->set_ad(mutate_vec(ad)); + enc->start(mutate_vec(nonce)); + + Botan::secure_vector<byte> garbage = Test::rng().random_vec(enc->update_granularity()); + enc->update(garbage); + + // reset message specific state + enc->reset(); + // now try to encrypt with correct values enc->set_key(key); - enc->set_associated_data_vec(ad); + enc->set_ad(ad); enc->start(nonce); Botan::secure_vector<uint8_t> buf(input.begin(), input.end()); - // TODO: should first update if possible - enc->finish(buf); - result.test_eq("encrypt", buf, expected); + // have to check here first if input is empty if not we can test update() and eventually process() + if(buf.empty()) + { + enc->finish(buf); + result.test_eq("encrypt with empty input", buf, expected); + } + else + { + // test finish() with full input + enc->finish(buf); + result.test_eq("encrypt", buf, expected); + + // additionally test update() if possible + const size_t update_granularity = enc->update_granularity(); + if(input.size() > update_granularity) + { + // reset state first + enc->reset(); + + enc->set_ad(ad); + enc->start(nonce); + + buf.assign(input.begin(), input.end()); + size_t input_length = buf.size(); + size_t offset = 0; + uint8_t* p = buf.data(); + Botan::secure_vector<uint8_t> block(update_granularity); + Botan::secure_vector<uint8_t> ciphertext(enc->output_length(buf.size())); + while(input_length > update_granularity && ((input_length - update_granularity) >= enc->minimum_final_size())) + { + block.assign(p, p + update_granularity); + enc->update(block); + p += update_granularity; + input_length -= update_granularity; + buffer_insert(ciphertext, 0 + offset, block); + offset += update_granularity; + } + + // encrypt remaining bytes + block.assign(p, p + input_length); + enc->finish(block); + buffer_insert(ciphertext, 0 + offset, block); + + result.test_eq("encrypt", ciphertext, expected); + } + + // additionally test process() if possible + size_t min_final_bytes = enc->minimum_final_size(); + if(input.size() > (update_granularity + min_final_bytes)) + { + // again reset state first + enc->reset(); + + enc->set_ad(ad); + enc->start(nonce); + + buf.assign(input.begin(), input.end()); - buf.assign(expected.begin(), expected.end()); + // we can process at max input.size() + const size_t max_blocks_to_process = (input.size() - min_final_bytes) / update_granularity; + const size_t bytes_to_process = max_blocks_to_process * update_granularity; + const size_t bytes_written = enc->process(buf.data(), bytes_to_process); + + result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process); + + const size_t remaining = input.size() - bytes_to_process; + + enc->finish(buf, bytes_to_process); + result.test_eq("encrypt", buf, expected); + } + } + return result; + } + + Test::Result test_dec(const std::vector<uint8_t>& key, const std::vector<uint8_t>& nonce, + const std::vector<uint8_t>& input, const std::vector<uint8_t>& expected, + const std::vector<uint8_t>& ad, const std::string& algo) + { + Test::Result result(algo); + + std::unique_ptr<Botan::AEAD_Mode> dec(Botan::get_aead(algo, Botan::DECRYPTION)); + + // First some tests for reset() to make sure it resets what we need it to + // set garbage values + dec->set_key(mutate_vec(key)); + dec->set_ad(mutate_vec(ad)); + dec->start(mutate_vec(nonce)); + + Botan::secure_vector<byte> garbage = Test::rng().random_vec(dec->update_granularity()); + dec->update(garbage); + + // reset message specific state + dec->reset(); + + Botan::secure_vector<uint8_t> buf(input.begin(), input.end()); try { + // now try to decrypt with correct values dec->set_key(key); - dec->set_associated_data_vec(ad); + dec->set_ad(ad); dec->start(nonce); + + // test finish() with full input dec->finish(buf); + result.test_eq("decrypt", buf, expected); + + // additionally test update() if possible + const size_t update_granularity = dec->update_granularity(); + if(input.size() > update_granularity) + { + // reset state first + dec->reset(); + + dec->set_ad(ad); + dec->start(nonce); + + buf.assign(input.begin(), input.end()); + size_t input_length = buf.size(); + size_t offset = 0; + uint8_t* p = buf.data(); + Botan::secure_vector<uint8_t> block(update_granularity); + Botan::secure_vector<uint8_t> plaintext(dec->output_length(buf.size())); + while((input_length > update_granularity) && ((input_length - update_granularity) >= dec->minimum_final_size())) + { + block.assign(p, p + update_granularity); + dec->update(block); + p += update_granularity; + input_length -= update_granularity; + buffer_insert(plaintext, 0 + offset, block); + offset += update_granularity; + } + + // decrypt remaining bytes + block.assign(p, p + input_length); + dec->finish(block); + buffer_insert(plaintext, 0 + offset, block); + + result.test_eq("decrypt", plaintext, expected); + } + + // additionally test process() if possible + const size_t min_final_size = dec->minimum_final_size(); + if(input.size() > (update_granularity + min_final_size)) + { + // again reset state first + dec->reset(); + + dec->set_ad(ad); + dec->start(nonce); + + buf.assign(input.begin(), input.end()); + + // we can process at max input.size() + const size_t max_blocks_to_process = (input.size() - min_final_size) / update_granularity; + const size_t bytes_to_process = max_blocks_to_process * update_granularity; + + const size_t bytes_written = dec->process(buf.data(), bytes_to_process); + + result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process); + + const size_t remaining = input.size() - bytes_to_process; + + dec->finish(buf, bytes_to_process); + result.test_eq("decrypt", buf, expected); + } + } catch(Botan::Exception& e) { - result.test_failure("Failure processing AEAD ciphertext"); + result.test_failure("Failure processing AEAD ciphertext", e.what()); } - if(enc->authenticated()) - { - const std::vector<byte> mutated_input = mutate_vec(expected, true); - buf.assign(mutated_input.begin(), mutated_input.end()); + // test decryption with modified ciphertext + const std::vector<byte> mutated_input = mutate_vec(input, true); + buf.assign(mutated_input.begin(), mutated_input.end()); - dec->start(nonce); + dec->reset(); - try - { - dec->finish(buf); - result.test_failure("accepted modified message", mutated_input); - } - catch(Botan::Integrity_Failure&) - { - result.test_note("correctly rejected modified message"); - } - catch(std::exception& e) - { - result.test_failure("unexpected error while rejecting modified message", e.what()); - } + dec->set_ad(ad); + dec->start(nonce); + + try + { + dec->finish(buf); + result.test_failure("accepted modified message", mutated_input); + } + catch(Botan::Integrity_Failure&) + { + result.test_success("correctly rejected modified message"); + } + catch(std::exception& e) + { + result.test_failure("unexpected error while rejecting modified message", e.what()); } + // test decryption with modified nonce if(nonce.size() > 0) { - buf.assign(expected.begin(), expected.end()); + buf.assign(input.begin(), input.end()); std::vector<byte> bad_nonce = mutate_vec(nonce); + dec->reset(); + dec->set_ad(ad); dec->start(bad_nonce); try @@ -102,7 +264,7 @@ class AEAD_Tests : public Text_Based_Test } catch(Botan::Integrity_Failure&) { - result.test_note("correctly rejected modified nonce"); + result.test_success("correctly rejected modified nonce"); } catch(std::exception& e) { @@ -110,21 +272,23 @@ class AEAD_Tests : public Text_Based_Test } } + // test decryption with modified associated_data const std::vector<byte> bad_ad = mutate_vec(ad, true); - dec->set_associated_data_vec(bad_ad); + dec->reset(); + dec->set_ad(bad_ad); dec->start(nonce); try { - buf.assign(expected.begin(), expected.end()); + buf.assign(input.begin(), input.end()); dec->finish(buf); result.test_failure("accepted message with modified ad", bad_ad); } catch(Botan::Integrity_Failure&) { - result.test_note("correctly rejected modified ad"); + result.test_success("correctly rejected modified ad"); } catch(std::exception& e) { @@ -133,6 +297,41 @@ class AEAD_Tests : public Text_Based_Test return result; } + + 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> nonce = get_opt_bin(vars, "Nonce"); + 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> ad = get_opt_bin(vars, "AD"); + + Test::Result result(algo); + + std::unique_ptr<Botan::AEAD_Mode> enc(Botan::get_aead(algo, Botan::ENCRYPTION)); + std::unique_ptr<Botan::AEAD_Mode> dec(Botan::get_aead(algo, Botan::DECRYPTION)); + + if(!enc || !dec) + { + result.note_missing(algo); + return result; + } + + // must be authenticated + result.test_eq("Encryption algo is an authenticated mode", enc->authenticated(), true); + result.test_eq("Decryption algo is an authenticated mode", dec->authenticated(), true); + + // test enc + result.merge(test_enc(key, nonce, input, expected, ad, algo)); + + // test dec + result.merge(test_dec(key, nonce, expected, input, ad, algo)); + + enc->clear(); + dec->clear(); + + return result; + } }; BOTAN_REGISTER_TEST("aead", AEAD_Tests); diff --git a/src/tests/test_modes.cpp b/src/tests/test_modes.cpp index ada4d5b82..4949d1c98 100644 --- a/src/tests/test_modes.cpp +++ b/src/tests/test_modes.cpp @@ -1,5 +1,6 @@ /* * (C) 2014,2015 Jack Lloyd +* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -44,22 +45,90 @@ class Cipher_Mode_Tests : public Text_Based_Test result.test_eq("mode not authenticated", enc->authenticated(), false); + // Test to make sure reset() resets what we need it to + enc->set_key(mutate_vec(key)); + Botan::secure_vector<byte> garbage = Test::rng().random_vec(enc->update_granularity()); + enc->start(mutate_vec(nonce)); + enc->update(garbage); + + enc->reset(); + enc->set_key(key); enc->start(nonce); Botan::secure_vector<uint8_t> buf(input.begin(), input.end()); // TODO: should first update if possible enc->finish(buf); - result.test_eq("encrypt", buf, expected); + // additionally test process() if possible + size_t update_granularity = enc->update_granularity(); + size_t input_length = input.size(); + size_t min_final_bytes = enc->minimum_final_size(); + if(input_length > (update_granularity + min_final_bytes)) + { + // reset state first + enc->reset(); + + enc->start(nonce); + buf.assign(input.begin(), input.end()); + + // we can process at max input_length + const size_t max_blocks_to_process = (input_length - min_final_bytes) / update_granularity; + const size_t bytes_to_process = max_blocks_to_process * update_granularity; + + const size_t bytes_written = enc->process(buf.data(), bytes_to_process); + + result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process); + + const size_t remaining = input_length - bytes_to_process; + + enc->finish(buf, bytes_to_process); + result.test_eq("encrypt", buf, expected); + } + + // decryption buf.assign(expected.begin(), expected.end()); + // Test to make sure reset() resets what we need it to + dec->set_key(mutate_vec(key)); + garbage = Test::rng().random_vec(dec->update_granularity()); + dec->start(mutate_vec(nonce)); + dec->update(garbage); + + dec->reset(); + dec->set_key(key); dec->start(nonce); dec->finish(buf); result.test_eq("decrypt", buf, input); + // additionally test process() if possible + update_granularity = dec->update_granularity(); + input_length = expected.size(); + min_final_bytes = dec->minimum_final_size(); + if(input_length > (update_granularity + min_final_bytes)) + { + // reset state first + dec->reset(); + + dec->start(nonce); + buf.assign(expected.begin(), expected.end()); + + // we can process at max input_length + const size_t max_blocks_to_process = (input_length - min_final_bytes) / update_granularity; + const size_t bytes_to_process = max_blocks_to_process * update_granularity; + + const size_t bytes_written = dec->process(buf.data(), bytes_to_process); + + result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process); + + const size_t remaining = input_length - bytes_to_process; + + dec->finish(buf, bytes_to_process); + result.test_eq("decrypt", buf, input); + } + enc->clear(); dec->clear(); |