aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-05-19 10:42:46 -0400
committerJack Lloyd <[email protected]>2017-05-19 10:42:46 -0400
commit2165cda540bac5311f46ac63480c5138a03adaca (patch)
tree33fa4b52d4aef9a4b857092618e5756c649de1fd
parent7e1229d91c5768c532c2a3296d02b836ef58219b (diff)
parent2914fcfb736b0a156ee14e4775a587ad92171ca3 (diff)
Merge GH #1044 Handle IV carryover in CBC, CFB, and stream ciphers
-rw-r--r--src/lib/block/aes/aes_ni/aes_ni.cpp12
-rw-r--r--src/lib/filters/cipher_filter.cpp25
-rw-r--r--src/lib/filters/cipher_filter.h14
-rw-r--r--src/lib/modes/cbc/cbc.cpp2
-rw-r--r--src/lib/modes/cfb/cfb.cpp15
-rw-r--r--src/lib/modes/stream_mode.h5
-rw-r--r--src/tests/data/stream/ctr.vec5
-rw-r--r--src/tests/test_filters.cpp28
-rw-r--r--src/tests/test_modes.cpp180
9 files changed, 243 insertions, 43 deletions
diff --git a/src/lib/block/aes/aes_ni/aes_ni.cpp b/src/lib/block/aes/aes_ni/aes_ni.cpp
index 52f4e44a2..65c1dc300 100644
--- a/src/lib/block/aes/aes_ni/aes_ni.cpp
+++ b/src/lib/block/aes/aes_ni/aes_ni.cpp
@@ -109,6 +109,8 @@ __m128i aes_256_key_expansion(__m128i key, __m128i key2)
BOTAN_FUNC_ISA("ssse3,aes")
void AES_128::aesni_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
{
+ BOTAN_ASSERT(m_EK.empty() == false, "Key was set");
+
const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
__m128i* out_mm = reinterpret_cast<__m128i*>(out);
@@ -186,6 +188,8 @@ void AES_128::aesni_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks)
BOTAN_FUNC_ISA("ssse3,aes")
void AES_128::aesni_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
{
+ BOTAN_ASSERT(m_DK.empty() == false, "Key was set");
+
const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
__m128i* out_mm = reinterpret_cast<__m128i*>(out);
@@ -316,6 +320,8 @@ void AES_128::aesni_key_schedule(const uint8_t key[], size_t)
BOTAN_FUNC_ISA("ssse3,aes")
void AES_192::aesni_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
{
+ BOTAN_ASSERT(m_EK.empty() == false, "Key was set");
+
const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
__m128i* out_mm = reinterpret_cast<__m128i*>(out);
@@ -399,6 +405,8 @@ void AES_192::aesni_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks)
BOTAN_FUNC_ISA("ssse3,aes")
void AES_192::aesni_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
{
+ BOTAN_ASSERT(m_DK.empty() == false, "Key was set");
+
const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
__m128i* out_mm = reinterpret_cast<__m128i*>(out);
@@ -532,6 +540,8 @@ void AES_192::aesni_key_schedule(const uint8_t key[], size_t)
BOTAN_FUNC_ISA("ssse3,aes")
void AES_256::aesni_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
{
+ BOTAN_ASSERT(m_EK.empty() == false, "Key was set");
+
const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
__m128i* out_mm = reinterpret_cast<__m128i*>(out);
@@ -621,6 +631,8 @@ void AES_256::aesni_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks)
BOTAN_FUNC_ISA("ssse3,aes")
void AES_256::aesni_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
{
+ BOTAN_ASSERT(m_DK.empty() == false, "Key was set");
+
const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
__m128i* out_mm = reinterpret_cast<__m128i*>(out);
diff --git a/src/lib/filters/cipher_filter.cpp b/src/lib/filters/cipher_filter.cpp
index 1f3476694..646e596d8 100644
--- a/src/lib/filters/cipher_filter.cpp
+++ b/src/lib/filters/cipher_filter.cpp
@@ -1,6 +1,6 @@
/*
* Filter interface for Cipher_Modes
-* (C) 2013,2014 Jack Lloyd
+* (C) 2013,2014,2017 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -38,24 +38,9 @@ std::string Cipher_Mode_Filter::name() const
return m_mode->name();
}
-void Cipher_Mode_Filter::Nonce_State::update(const InitializationVector& iv)
- {
- m_nonce = unlock(iv.bits_of());
- m_fresh_nonce = true;
- }
-
-std::vector<uint8_t> Cipher_Mode_Filter::Nonce_State::get()
- {
- BOTAN_ASSERT(m_fresh_nonce, "The nonce is fresh for this message");
-
- if(!m_nonce.empty())
- m_fresh_nonce = false;
- return m_nonce;
- }
-
void Cipher_Mode_Filter::set_iv(const InitializationVector& iv)
{
- m_nonce.update(iv);
+ m_nonce = unlock(iv.bits_of());
}
void Cipher_Mode_Filter::set_key(const SymmetricKey& key)
@@ -85,7 +70,11 @@ void Cipher_Mode_Filter::end_msg()
void Cipher_Mode_Filter::start_msg()
{
- m_mode->start(m_nonce.get());
+ if(m_nonce.empty() && !m_mode->valid_nonce_length(0))
+ throw Invalid_State("Cipher " + m_mode->name() + " requires a fresh nonce for each message");
+
+ m_mode->start(m_nonce);
+ m_nonce.clear();
}
void Cipher_Mode_Filter::buffered_block(const uint8_t input[], size_t input_length)
diff --git a/src/lib/filters/cipher_filter.h b/src/lib/filters/cipher_filter.h
index 1675a15d7..eae70d9c0 100644
--- a/src/lib/filters/cipher_filter.h
+++ b/src/lib/filters/cipher_filter.h
@@ -46,20 +46,8 @@ class BOTAN_DLL Cipher_Mode_Filter : public Keyed_Filter,
void buffered_block(const uint8_t input[], size_t input_length) override;
void buffered_final(const uint8_t input[], size_t input_length) override;
- class Nonce_State
- {
- public:
- explicit Nonce_State(bool allow_null_nonce) : m_fresh_nonce(allow_null_nonce) {}
-
- void update(const InitializationVector& iv);
- std::vector<uint8_t> get();
- private:
- bool m_fresh_nonce;
- std::vector<uint8_t> m_nonce;
- };
-
- Nonce_State m_nonce;
std::unique_ptr<Cipher_Mode> m_mode;
+ std::vector<uint8_t> m_nonce;
secure_vector<uint8_t> m_buffer;
};
diff --git a/src/lib/modes/cbc/cbc.cpp b/src/lib/modes/cbc/cbc.cpp
index 188b4a0aa..fbe56da82 100644
--- a/src/lib/modes/cbc/cbc.cpp
+++ b/src/lib/modes/cbc/cbc.cpp
@@ -1,6 +1,6 @@
/*
* CBC Mode
-* (C) 1999-2007,2013 Jack Lloyd
+* (C) 1999-2007,2013,2017 Jack Lloyd
* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
diff --git a/src/lib/modes/cfb/cfb.cpp b/src/lib/modes/cfb/cfb.cpp
index 148e16c6c..56d234090 100644
--- a/src/lib/modes/cfb/cfb.cpp
+++ b/src/lib/modes/cfb/cfb.cpp
@@ -67,7 +67,7 @@ size_t CFB_Mode::default_nonce_length() const
bool CFB_Mode::valid_nonce_length(size_t n) const
{
- return (n == cipher().block_size());
+ return (n == 0 || n == cipher().block_size());
}
void CFB_Mode::key_schedule(const uint8_t key[], size_t length)
@@ -80,7 +80,18 @@ void CFB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
if(!valid_nonce_length(nonce_len))
throw Invalid_IV_Length(name(), nonce_len);
- m_shift_register.assign(nonce, nonce + nonce_len);
+ if(nonce_len == 0)
+ {
+ if(m_shift_register.empty())
+ {
+ throw Invalid_State("CFB requires a non-empty initial nonce");
+ }
+ }
+ else
+ {
+ m_shift_register.assign(nonce, nonce + nonce_len);
+ }
+
m_keystream_buf.resize(m_shift_register.size());
cipher().encrypt(m_shift_register, m_keystream_buf);
}
diff --git a/src/lib/modes/stream_mode.h b/src/lib/modes/stream_mode.h
index e32044a4b..27a94a7c7 100644
--- a/src/lib/modes/stream_mode.h
+++ b/src/lib/modes/stream_mode.h
@@ -56,7 +56,10 @@ class BOTAN_DLL Stream_Cipher_Mode : public Cipher_Mode
private:
void start_msg(const uint8_t nonce[], size_t nonce_len) override
{
- m_cipher->set_iv(nonce, nonce_len);
+ if(nonce_len > 0)
+ {
+ m_cipher->set_iv(nonce, nonce_len);
+ }
}
void key_schedule(const uint8_t key[], size_t length) override
diff --git a/src/tests/data/stream/ctr.vec b/src/tests/data/stream/ctr.vec
index a7275aa79..9fddf1cfc 100644
--- a/src/tests/data/stream/ctr.vec
+++ b/src/tests/data/stream/ctr.vec
@@ -286,6 +286,11 @@ Nonce = F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF
In = 6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710
Out = 874D6191B620E3261BEF6864990DB6CE9806F66B7970FDFF8617187BB9FFFDFF5AE4DF3EDBD5D35E5B4F09020DB03EAB1E031DDA2FBE03D1792170A0F3009CEE
+Key = 2B7E151628AED2A6ABF7158809CF4F3C
+Nonce = F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF
+In = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+Out = EC8CDF7398607CB0F2D21675EA9EA1E4362B7C3C6773516318A077D7FC5073AE6A2CC3787889374FBEB4C81B17BA6C44E89C399FF0F198C6D40A31DB156CABFE
+
Seek = 0
Key = 2B7E151628AED2A6ABF7158809CF4F3C
Nonce = F0F1F2F3F4F5F6F7F8F9FAFBFCFDFF
diff --git a/src/tests/test_filters.cpp b/src/tests/test_filters.cpp
index 3c50d94ce..6199f88e2 100644
--- a/src/tests/test_filters.cpp
+++ b/src/tests/test_filters.cpp
@@ -1,6 +1,6 @@
/*
* (C) 2016 Daniel Neus
-* 2016 Jack Lloyd
+* 2016,2017 Jack Lloyd
* 2017 René Korthaus
* 2017 Philippe Lieser
*
@@ -42,7 +42,7 @@ class Filter_Tests : public Test
results.push_back(test_pipe_hash());
results.push_back(test_pipe_mac());
results.push_back(test_pipe_stream());
- results.push_back(test_pipe_cipher());
+ results.push_back(test_pipe_cbc());
results.push_back(test_pipe_compress());
results.push_back(test_pipe_codec());
results.push_back(test_fork());
@@ -274,9 +274,9 @@ class Filter_Tests : public Test
return result;
}
- Test::Result test_pipe_cipher()
+ Test::Result test_pipe_cbc()
{
- Test::Result result("Pipe");
+ Test::Result result("Pipe CBC");
#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_MODE_CBC) && defined(BOTAN_HAS_CIPHER_MODE_PADDING)
Botan::Cipher_Mode_Filter* cipher =
@@ -303,13 +303,24 @@ class Filter_Tests : public Test
auto ciphertext = pipe.read_all();
result.test_eq("Bytes read after", pipe.get_bytes_read(), ciphertext.size());
- result.test_eq("Ciphertext", ciphertext, "9BDD7300E0CB61CA71FFF957A71605DB6836159C36781246A1ADF50982757F4B");
+ result.test_eq("Ciphertext", ciphertext, "9BDD7300E0CB61CA71FFF957A71605DB 6836159C36781246A1ADF50982757F4B");
+
+ pipe.process_msg("IV carryover");
+ auto ciphertext2 = pipe.read_all(1);
+ pipe.process_msg("IV carryover2");
+ auto ciphertext3 = pipe.read_all(2);
+
+ // These values tested against PyCrypto
+ result.test_eq("Ciphertext2", ciphertext2, "AA8D682958A4A044735DAC502B274DB2");
+ result.test_eq("Ciphertext3", ciphertext3, "1241B9976F73051BCF809525D6E86C25");
Botan::Cipher_Mode_Filter* dec_cipher =
new Botan::Cipher_Mode_Filter(Botan::get_cipher_mode("AES-128/CBC/PKCS7", Botan::DECRYPTION));
pipe.append(dec_cipher);
dec_cipher->set_key(Botan::SymmetricKey("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
dec_cipher->set_iv(Botan::InitializationVector("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"));
+
+ // reset the IV on the encryption filter
cipher->set_iv(Botan::InitializationVector("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"));
const std::vector<uint8_t> zeros_in(1024);
@@ -318,7 +329,7 @@ class Filter_Tests : public Test
pipe.write(src);
pipe.end_msg();
- pipe.set_default_msg(1);
+ pipe.set_default_msg(3);
result.test_eq("Bytes read", pipe.get_bytes_read(), 0);
Botan::secure_vector<uint8_t> zeros_out = pipe.read_all();
result.test_eq("Bytes read", pipe.get_bytes_read(), zeros_out.size());
@@ -440,7 +451,7 @@ class Filter_Tests : public Test
Test::Result test_pipe_stream()
{
- Test::Result result("Pipe");
+ Test::Result result("Pipe CTR");
#if defined(BOTAN_HAS_CTR_BE)
Botan::Keyed_Filter* aes = nullptr;
@@ -456,6 +467,9 @@ class Filter_Tests : public Test
result.test_eq("Message count", pipe.message_count(), 1);
result.test_eq("Ciphertext", pipe.read_all(), "FDFD6238F7C6");
+
+ pipe.process_msg("ABCDEF");
+ result.test_eq("Ciphertext", pipe.read_all(1), "8E72F1153514");
#endif
return result;
}
diff --git a/src/tests/test_modes.cpp b/src/tests/test_modes.cpp
index 74b04f7ac..015aafa16 100644
--- a/src/tests/test_modes.cpp
+++ b/src/tests/test_modes.cpp
@@ -1,5 +1,5 @@
/*
-* (C) 2014,2015 Jack Lloyd
+* (C) 2014,2015,2017 Jack Lloyd
* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
@@ -151,6 +151,184 @@ class Cipher_Mode_Tests : public Text_Based_Test
BOTAN_REGISTER_TEST("modes", Cipher_Mode_Tests);
+class Cipher_Mode_IV_Carry_Tests : public Test
+ {
+ public:
+ std::vector<Test::Result> run() override
+ {
+ std::vector<Test::Result> results;
+ results.push_back(test_cbc_iv_carry());
+ results.push_back(test_cfb_iv_carry());
+ results.push_back(test_ctr_iv_carry());
+ return results;
+ }
+
+ private:
+ Test::Result test_cbc_iv_carry()
+ {
+ Test::Result result("CBC IV carry");
+
+#if defined(BOTAN_HAS_MODE_CBC) && defined(BOTAN_HAS_AES)
+ std::unique_ptr<Botan::Cipher_Mode> enc(
+ Botan::get_cipher_mode("AES-128/CBC/PKCS7", Botan::ENCRYPTION));
+ std::unique_ptr<Botan::Cipher_Mode> dec(
+ Botan::get_cipher_mode("AES-128/CBC/PKCS7", Botan::DECRYPTION));
+
+ const std::vector<uint8_t> key(16, 0xAA);
+ const std::vector<uint8_t> iv(16, 0xAA);
+
+ Botan::secure_vector<uint8_t> msg1 =
+ Botan::hex_decode_locked("446F6E27742075736520706C61696E20434243206D6F6465");
+ Botan::secure_vector<uint8_t> msg2 =
+ Botan::hex_decode_locked("49562063617272796F766572");
+ Botan::secure_vector<uint8_t> msg3 =
+ Botan::hex_decode_locked("49562063617272796F76657232");
+
+ enc->set_key(key);
+ dec->set_key(key);
+
+ enc->start(iv);
+ enc->finish(msg1);
+ result.test_eq("First ciphertext", msg1,
+ "9BDD7300E0CB61CA71FFF957A71605DB6836159C36781246A1ADF50982757F4B");
+
+ enc->start();
+ enc->finish(msg2);
+
+ result.test_eq("Second ciphertext", msg2,
+ "AA8D682958A4A044735DAC502B274DB2");
+
+ enc->start();
+ enc->finish(msg3);
+
+ result.test_eq("Third ciphertext", msg3,
+ "1241B9976F73051BCF809525D6E86C25");
+
+ dec->start(iv);
+ dec->finish(msg1);
+
+ dec->start();
+ dec->finish(msg2);
+
+ dec->start();
+ dec->finish(msg3);
+ result.test_eq("Third plaintext", msg3, "49562063617272796F76657232");
+
+#endif
+ return result;
+ }
+
+ Test::Result test_cfb_iv_carry()
+ {
+ Test::Result result("CFB IV carry");
+#if defined(BOTAN_HAS_MODE_CFB) && defined(BOTAN_HAS_AES)
+ std::unique_ptr<Botan::Cipher_Mode> enc(
+ Botan::get_cipher_mode("AES-128/CFB(8)", Botan::ENCRYPTION));
+ std::unique_ptr<Botan::Cipher_Mode> dec(
+ Botan::get_cipher_mode("AES-128/CFB(8)", Botan::DECRYPTION));
+
+ const std::vector<uint8_t> key(16, 0xAA);
+ const std::vector<uint8_t> iv(16, 0xAB);
+
+ Botan::secure_vector<uint8_t> msg1 = Botan::hex_decode_locked("ABCDEF01234567");
+ Botan::secure_vector<uint8_t> msg2 = Botan::hex_decode_locked("0000123456ABCDEF");
+ Botan::secure_vector<uint8_t> msg3 = Botan::hex_decode_locked("012345");
+
+ enc->set_key(key);
+ dec->set_key(key);
+
+ enc->start(iv);
+ enc->finish(msg1);
+ result.test_eq("First ciphertext", msg1, "a51522387c4c9b");
+
+ enc->start();
+ enc->finish(msg2);
+
+ result.test_eq("Second ciphertext", msg2, "105457dc2e0649d4");
+
+ enc->start();
+ enc->finish(msg3);
+
+ result.test_eq("Third ciphertext", msg3, "53bd65");
+
+ dec->start(iv);
+ dec->finish(msg1);
+ result.test_eq("First plaintext", msg1, "ABCDEF01234567");
+
+ dec->start();
+ dec->finish(msg2);
+ result.test_eq("Second plaintext", msg2, "0000123456ABCDEF");
+
+ dec->start();
+ dec->finish(msg3);
+ result.test_eq("Third plaintext", msg3, "012345");
+#endif
+ return result;
+ }
+
+ Test::Result test_ctr_iv_carry()
+ {
+ Test::Result result("CTR IV carry");
+#if defined(BOTAN_HAS_CTR_BE) && defined(BOTAN_HAS_AES)
+
+ std::unique_ptr<Botan::Cipher_Mode> enc(
+ Botan::get_cipher_mode("AES-128/CTR-BE", Botan::ENCRYPTION));
+ std::unique_ptr<Botan::Cipher_Mode> dec(
+ Botan::get_cipher_mode("AES-128/CTR-BE", Botan::DECRYPTION));
+
+ const std::vector<uint8_t> key =
+ Botan::hex_decode("2B7E151628AED2A6ABF7158809CF4F3C");
+ const std::vector<uint8_t> iv =
+ Botan::hex_decode("F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF");
+
+ enc->set_key(key);
+ dec->set_key(key);
+
+ const std::vector<std::string> exp_ciphertext = {
+ "EC",
+ "8CDF",
+ "739860",
+ "7CB0F2D2",
+ "1675EA9EA1",
+ "E4362B7C3C67",
+ "73516318A077D7",
+ "FC5073AE6A2CC378",
+ "7889374FBEB4C81B17",
+ "BA6C44E89C399FF0F198C",
+ };
+
+ for(size_t i = 1; i != 10; ++i)
+ {
+ if(i == 1)
+ {
+ enc->start(iv);
+ dec->start(iv);
+ }
+ else
+ {
+ enc->start();
+ dec->start();
+ }
+
+ Botan::secure_vector<uint8_t> msg(i, 0);
+ enc->finish(msg);
+
+ result.test_eq("Ciphertext", msg, exp_ciphertext[i-1].c_str());
+
+ dec->finish(msg);
+
+ for(size_t i = 0; i != msg.size(); ++i)
+ result.test_eq("Plaintext zeros", static_cast<size_t>(msg[i]), 0);
+
+ }
+#endif
+ return result;
+ }
+ };
+
+
+BOTAN_REGISTER_TEST("iv_carryover", Cipher_Mode_IV_Carry_Tests);
+
#endif
}