From b2746bc1b28ba9342be2ad781cd6f548a66a7d8e Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 8 Apr 2013 13:01:21 +0000 Subject: [PATCH] added unit test to make sure IV works as we expect. --- src/lib/synergy/CCryptoStream.cpp | 30 ++++- src/lib/synergy/CCryptoStream.h | 26 ++++- .../unittests/synergy/CCryptoStreamTests.cpp | 108 +++++++++++++++--- 3 files changed, 140 insertions(+), 24 deletions(-) diff --git a/src/lib/synergy/CCryptoStream.cpp b/src/lib/synergy/CCryptoStream.cpp index ef181836..ce661841 100644 --- a/src/lib/synergy/CCryptoStream.cpp +++ b/src/lib/synergy/CCryptoStream.cpp @@ -29,10 +29,10 @@ const byte g_iv2[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; using namespace CryptoPP; CCryptoStream::CCryptoStream(IEventQueue& eventQueue, synergy::IStream* stream, bool adoptStream) : - CStreamFilter(eventQueue, stream, adoptStream) -{ - m_encryption.SetKeyWithIV(g_key1, sizeof(g_key1), g_iv1); - m_decryption.SetKeyWithIV(g_key1, sizeof(g_key1), g_iv1); + CStreamFilter(eventQueue, stream, adoptStream), + m_key(NULL), + m_keyLength(0) +{ } CCryptoStream::~CCryptoStream() @@ -42,6 +42,7 @@ CCryptoStream::~CCryptoStream() UInt32 CCryptoStream::read(void* out, UInt32 n) { + assert(m_key != NULL); LOG((CLOG_DEBUG4 "crypto: read %i (decrypt)", n)); byte* cypher = new byte[n]; @@ -66,6 +67,7 @@ CCryptoStream::read(void* out, UInt32 n) void CCryptoStream::write(const void* in, UInt32 n) { + assert(m_key != NULL); LOG((CLOG_DEBUG4 "crypto: write %i (encrypt)", n)); logBuffer("plaintext", static_cast(const_cast(in)), n); @@ -75,6 +77,26 @@ CCryptoStream::write(const void* in, UInt32 n) getStream()->write(cypher, n); delete[] cypher; } + +void +CCryptoStream::setKeyWithIV(const byte* key, size_t length, const byte* iv) +{ + LOG((CLOG_DEBUG "crypto: key=%s (%i) iv=%s", key, length, iv)); + m_encryption.SetKeyWithIV(key, length, iv); + m_decryption.SetKeyWithIV(key, length, iv); + + m_key = key; + m_keyLength = length; +} + +void +CCryptoStream::setIV(const byte* iv) +{ + assert(m_key != NULL); + LOG((CLOG_DEBUG "crypto: new iv=%s", iv)); + m_encryption.SetKeyWithIV(m_key, m_keyLength, iv); + m_decryption.SetKeyWithIV(m_key, m_keyLength, iv); +} void CCryptoStream::logBuffer(const char* name, const byte* buf, int length) diff --git a/src/lib/synergy/CCryptoStream.h b/src/lib/synergy/CCryptoStream.h index 3b1cf0c8..8a6b4525 100644 --- a/src/lib/synergy/CCryptoStream.h +++ b/src/lib/synergy/CCryptoStream.h @@ -20,7 +20,7 @@ #include "BasicTypes.h" #include "CStreamFilter.h" #include "cryptopp562/gcm.h" -//#include "cryptopp562/modes.h" +#include "cryptopp562/modes.h" #include "cryptopp562/aes.h" //! Bidirectional encrypted stream @@ -48,12 +48,30 @@ public: */ virtual void write(const void* in, UInt32 n); + //! Set the key and IV + void setKeyWithIV(const byte* key, size_t length, const byte* iv); + + //! Set the IV + void setIV(const byte* iv); + private: - // TODO: allow user to change between GCM/CTR/CFB - CryptoPP::GCM::Encryption m_encryption; - CryptoPP::GCM::Decryption m_decryption; + // TODO: allow user to change the block cypher mode. + /* + For CBC and CFB, reusing an IV leaks some information about the first block of plaintext, + and about any common prefix shared by the two messages. For OFB and CTR, reusing an IV + completely destroys security. http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation + */ + CryptoPP::OFB_Mode::Encryption m_encryption; + CryptoPP::OFB_Mode::Decryption m_decryption; + //CryptoPP::CFB_Mode::Encryption m_encryption; + //CryptoPP::CFB_Mode::Decryption m_decryption; + //CryptoPP::GCM::Encryption m_encryption; + //CryptoPP::GCM::Decryption m_decryption; //CryptoPP::CTR_Mode::Encryption m_encryption; //CryptoPP::CTR_Mode::Decryption m_decryption; void logBuffer(const char* name, const byte* buf, int length); + + const byte* m_key; + size_t m_keyLength; }; diff --git a/src/test/unittests/synergy/CCryptoStreamTests.cpp b/src/test/unittests/synergy/CCryptoStreamTests.cpp index 2e444b61..a8d723f1 100644 --- a/src/test/unittests/synergy/CCryptoStreamTests.cpp +++ b/src/test/unittests/synergy/CCryptoStreamTests.cpp @@ -20,17 +20,20 @@ #include "CMockStream.h" #include "CMockEventQueue.h" #include "CPacketStreamFilter.h" + using ::testing::_; using ::testing::Invoke; using namespace std; -void assertWrite(const void* in, UInt32 n); -UInt8 mockRead(void* out, UInt32 n); +void write_assertWrite(const void* in, UInt32 n); +UInt8 read_mockRead(void* out, UInt32 n); void write4Read1_mockWrite(const void* in, UInt32 n); UInt8 write4Read1_mockRead(void* out, UInt32 n); void write1Read4_mockWrite(const void* in, UInt32 n); UInt8 write1Read4_mockRead(void* out, UInt32 n); +void readWriteIVChanged_mockWrite(const void* in, UInt32 n); +UInt8 readWriteIVChanged_mockRead(void* out, UInt32 n); UInt8 g_write4Read1_buffer[4]; UInt32 g_write4Read1_bufferIndex = 0; @@ -38,6 +41,11 @@ UInt32 g_write4Read1_bufferIndex = 0; UInt8 g_write1Read4_buffer[4]; UInt32 g_write1Read4_bufferIndex = 0; +UInt8 g_readWriteIVChanged_buffer[4]; + +const byte g_key[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; // +\0, 32-byte/256-bit key. +const byte g_iv[] = "bbbbbbbbbbbbbbb"; // +\0, AES block size = 16 + TEST(CCryptoTests, write) { const UInt32 size = 4; @@ -50,7 +58,7 @@ TEST(CCryptoTests, write) CMockEventQueue eventQueue; CMockStream innerStream(eventQueue); - ON_CALL(innerStream, write(_, _)).WillByDefault(Invoke(assertWrite)); + ON_CALL(innerStream, write(_, _)).WillByDefault(Invoke(write_assertWrite)); EXPECT_CALL(innerStream, write(_, _)).Times(1); EXPECT_CALL(innerStream, getEventTarget()).Times(3); EXPECT_CALL(eventQueue, removeHandlers(_)).Times(1); @@ -58,6 +66,7 @@ TEST(CCryptoTests, write) EXPECT_CALL(eventQueue, removeHandler(_, _)).Times(1); CCryptoStream cs(eventQueue, &innerStream, false); + cs.setKeyWithIV(g_key, sizeof(g_key), g_iv); cs.write(buffer, size); } @@ -66,7 +75,7 @@ TEST(CCryptoTests, read) CMockEventQueue eventQueue; CMockStream innerStream(eventQueue); - ON_CALL(innerStream, read(_, _)).WillByDefault(Invoke(mockRead)); + ON_CALL(innerStream, read(_, _)).WillByDefault(Invoke(read_mockRead)); EXPECT_CALL(innerStream, read(_, _)).Times(1); EXPECT_CALL(innerStream, getEventTarget()).Times(3); EXPECT_CALL(eventQueue, removeHandlers(_)).Times(1); @@ -74,6 +83,7 @@ TEST(CCryptoTests, read) EXPECT_CALL(eventQueue, removeHandler(_, _)).Times(1); CCryptoStream cs(eventQueue, &innerStream, false); + cs.setKeyWithIV(g_key, sizeof(g_key), g_iv); const UInt32 size = 4; UInt8* buffer = new UInt8[size]; @@ -100,12 +110,15 @@ TEST(CCryptoTests, write4Read1) EXPECT_CALL(eventQueue, removeHandler(_, _)).Times(2); CCryptoStream cs1(eventQueue, &innerStream, false); - CCryptoStream cs2(eventQueue, &innerStream, false); + cs1.setKeyWithIV(g_key, sizeof(g_key), g_iv); cs1.write("a", 1); cs1.write("b", 1); cs1.write("c", 1); cs1.write("d", 1); + + CCryptoStream cs2(eventQueue, &innerStream, false); + cs2.setKeyWithIV(g_key, sizeof(g_key), g_iv); UInt8 buffer[4]; cs2.read(buffer, 4); @@ -131,7 +144,7 @@ TEST(CCryptoTests, write1Read4) EXPECT_CALL(eventQueue, removeHandler(_, _)).Times(2); CCryptoStream cs1(eventQueue, &innerStream, false); - CCryptoStream cs2(eventQueue, &innerStream, false); + cs1.setKeyWithIV(g_key, sizeof(g_key), g_iv); UInt8 bufferIn[4]; bufferIn[0] = 'a'; @@ -139,6 +152,9 @@ TEST(CCryptoTests, write1Read4) bufferIn[2] = 'c'; bufferIn[3] = 'd'; cs1.write(bufferIn, 4); + + CCryptoStream cs2(eventQueue, &innerStream, false); + cs2.setKeyWithIV(g_key, sizeof(g_key), g_iv); UInt8 bufferOut[4]; cs2.read(&bufferOut[0], 1); @@ -152,24 +168,64 @@ TEST(CCryptoTests, write1Read4) EXPECT_EQ('d', bufferOut[3]); } +TEST(CCryptoTests, readWriteIVChanged) +{ + CMockEventQueue eventQueue; + CMockStream innerStream(eventQueue); + + ON_CALL(innerStream, write(_, _)).WillByDefault(Invoke(readWriteIVChanged_mockWrite)); + ON_CALL(innerStream, read(_, _)).WillByDefault(Invoke(readWriteIVChanged_mockRead)); + EXPECT_CALL(innerStream, write(_, _)).Times(1); + EXPECT_CALL(innerStream, read(_, _)).Times(1); + EXPECT_CALL(innerStream, getEventTarget()).Times(6); + EXPECT_CALL(eventQueue, removeHandlers(_)).Times(2); + EXPECT_CALL(eventQueue, adoptHandler(_, _, _)).Times(2); + EXPECT_CALL(eventQueue, removeHandler(_, _)).Times(2); + + const byte iv1[] = "bbbbbbbbbbbbbbb"; + const byte iv2[] = "ccccccccccccccc"; + + CCryptoStream cs1(eventQueue, &innerStream, false); + cs1.setKeyWithIV(g_key, sizeof(g_key), iv1); + + UInt8 bufferIn[4]; + bufferIn[0] = 'a'; + bufferIn[1] = 'b'; + bufferIn[2] = 'c'; + bufferIn[3] = 'd'; + cs1.write(bufferIn, 4); + + CCryptoStream cs2(eventQueue, &innerStream, false); + cs2.setKeyWithIV(g_key, sizeof(g_key), iv2); + + UInt8 bufferOut[4]; + cs2.read(bufferOut, 4); + + // assert that the values have changed. + EXPECT_NE('a', bufferOut[0]); + EXPECT_NE('b', bufferOut[1]); + EXPECT_NE('c', bufferOut[2]); + EXPECT_NE('d', bufferOut[3]); +} + void -assertWrite(const void* in, UInt32 n) +write_assertWrite(const void* in, UInt32 n) { UInt8* buffer = static_cast(const_cast(in)); - EXPECT_EQ(55, buffer[0]); - EXPECT_EQ(142, buffer[1]); - EXPECT_EQ(189, buffer[2]); - EXPECT_EQ(237, buffer[3]); + EXPECT_EQ(8, buffer[0]); + EXPECT_EQ(58, buffer[1]); + EXPECT_EQ(151, buffer[2]); + EXPECT_EQ(33, buffer[3]); } UInt8 -mockRead(void* out, UInt32 n) +read_mockRead(void* out, UInt32 n) { UInt8* buffer = static_cast(out); - buffer[0] = 55; - buffer[1] = 142; - buffer[2] = 189; - buffer[3] = 237; + buffer[0] = 8; + buffer[1] = 58; + buffer[2] = 151; + buffer[3] = 33; return n; } @@ -209,3 +265,23 @@ write1Read4_mockRead(void* out, UInt32 n) return 1; } +void +readWriteIVChanged_mockWrite(const void* in, UInt32 n) +{ + UInt8* buffer = static_cast(const_cast(in)); + g_readWriteIVChanged_buffer[0] = buffer[0]; + g_readWriteIVChanged_buffer[1] = buffer[1]; + g_readWriteIVChanged_buffer[2] = buffer[2]; + g_readWriteIVChanged_buffer[3] = buffer[3]; +} + +UInt8 +readWriteIVChanged_mockRead(void* out, UInt32 n) +{ + UInt8* buffer = static_cast(out); + buffer[0] = g_readWriteIVChanged_buffer[0]; + buffer[1] = g_readWriteIVChanged_buffer[1]; + buffer[2] = g_readWriteIVChanged_buffer[2]; + buffer[3] = g_readWriteIVChanged_buffer[3]; + return 4; +}