aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Neus <[email protected]>2016-07-20 22:26:26 +0200
committerDaniel Neus <[email protected]>2016-11-08 22:16:09 +0100
commit06b44d8ed339b3a467f10a326fd209b0b9496060 (patch)
tree24c3bf3f20ba697a658d6d009d0cdb7be8a3e41f
parent523b2a4ca48fa5cf04ea371aabe7167ce2e5cd13 (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()
-rw-r--r--src/lib/modes/aead/ccm/ccm.cpp9
-rw-r--r--src/lib/modes/aead/ccm/ccm.h3
-rw-r--r--src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp7
-rw-r--r--src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h4
-rw-r--r--src/lib/modes/aead/eax/eax.cpp32
-rw-r--r--src/lib/modes/aead/eax/eax.h6
-rw-r--r--src/lib/modes/aead/gcm/gcm.cpp17
-rw-r--r--src/lib/modes/aead/gcm/gcm.h5
-rw-r--r--src/lib/modes/aead/ocb/ocb.cpp12
-rw-r--r--src/lib/modes/aead/ocb/ocb.h3
-rw-r--r--src/lib/modes/aead/siv/siv.cpp9
-rw-r--r--src/lib/modes/aead/siv/siv.h3
-rw-r--r--src/lib/modes/cbc/cbc.cpp14
-rw-r--r--src/lib/modes/cbc/cbc.h7
-rw-r--r--src/lib/modes/cfb/cfb.cpp7
-rw-r--r--src/lib/modes/cfb/cfb.h3
-rw-r--r--src/lib/modes/cipher_mode.h11
-rw-r--r--src/lib/modes/ecb/ecb.cpp7
-rw-r--r--src/lib/modes/ecb/ecb.h4
-rw-r--r--src/lib/modes/stream_mode.h9
-rw-r--r--src/lib/modes/xts/xts.cpp6
-rw-r--r--src/lib/modes/xts/xts.h4
-rw-r--r--src/lib/tls/tls_cbc/tls_cbc.cpp12
-rw-r--r--src/lib/tls/tls_cbc/tls_cbc.h3
-rw-r--r--src/tests/test_aead.cpp287
-rw-r--r--src/tests/test_modes.cpp71
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();