diff options
author | Jack Lloyd <[email protected]> | 2017-05-19 10:42:46 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-05-19 10:42:46 -0400 |
commit | 2165cda540bac5311f46ac63480c5138a03adaca (patch) | |
tree | 33fa4b52d4aef9a4b857092618e5756c649de1fd | |
parent | 7e1229d91c5768c532c2a3296d02b836ef58219b (diff) | |
parent | 2914fcfb736b0a156ee14e4775a587ad92171ca3 (diff) |
Merge GH #1044 Handle IV carryover in CBC, CFB, and stream ciphers
-rw-r--r-- | src/lib/block/aes/aes_ni/aes_ni.cpp | 12 | ||||
-rw-r--r-- | src/lib/filters/cipher_filter.cpp | 25 | ||||
-rw-r--r-- | src/lib/filters/cipher_filter.h | 14 | ||||
-rw-r--r-- | src/lib/modes/cbc/cbc.cpp | 2 | ||||
-rw-r--r-- | src/lib/modes/cfb/cfb.cpp | 15 | ||||
-rw-r--r-- | src/lib/modes/stream_mode.h | 5 | ||||
-rw-r--r-- | src/tests/data/stream/ctr.vec | 5 | ||||
-rw-r--r-- | src/tests/test_filters.cpp | 28 | ||||
-rw-r--r-- | src/tests/test_modes.cpp | 180 |
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 } |