From fd350847227dd71ea1122ac80eb6f3947d6cae13 Mon Sep 17 00:00:00 2001 From: Thomas Inskip Date: Tue, 9 Sep 2014 15:56:02 -0700 Subject: [PATCH] Implemented AES-CBC with Ciphertext Stealing for WVM decryption. Change-Id: I4a1ba4247bb6f055905663568699b06d7c18a968 --- media/base/aes_encryptor.cc | 247 +++++++++++++++++++++++++-- media/base/aes_encryptor.h | 106 +++++++++++- media/base/aes_encryptor_unittest.cc | 148 +++++++++++++--- media/base/request_signer.cc | 4 +- media/base/request_signer.h | 6 +- 5 files changed, 463 insertions(+), 48 deletions(-) diff --git a/media/base/aes_encryptor.cc b/media/base/aes_encryptor.cc index ee1904dfb5..5f5cc5e391 100644 --- a/media/base/aes_encryptor.cc +++ b/media/base/aes_encryptor.cc @@ -143,11 +143,11 @@ bool AesCtrEncryptor::SetIv(const std::vector& iv) { return true; } -AesCbcEncryptor::AesCbcEncryptor() {} -AesCbcEncryptor::~AesCbcEncryptor() {} +AesCbcPkcs5Encryptor::AesCbcPkcs5Encryptor() {} +AesCbcPkcs5Encryptor::~AesCbcPkcs5Encryptor() {} -bool AesCbcEncryptor::InitializeWithIv(const std::vector& key, - const std::vector& iv) { +bool AesCbcPkcs5Encryptor::InitializeWithIv(const std::vector& key, + const std::vector& iv) { if (!IsKeySizeValidForAes(key.size())) { LOG(ERROR) << "Invalid AES key size: " << key.size(); return false; @@ -164,8 +164,8 @@ bool AesCbcEncryptor::InitializeWithIv(const std::vector& key, return true; } -void AesCbcEncryptor::Encrypt(const std::string& plaintext, - std::string* ciphertext) { +void AesCbcPkcs5Encryptor::Encrypt(const std::string& plaintext, + std::string* ciphertext) { DCHECK(ciphertext); DCHECK(encrypt_key_); @@ -185,7 +185,7 @@ void AesCbcEncryptor::Encrypt(const std::string& plaintext, AES_ENCRYPT); } -bool AesCbcEncryptor::SetIv(const std::vector& iv) { +bool AesCbcPkcs5Encryptor::SetIv(const std::vector& iv) { if (iv.size() != AES_BLOCK_SIZE) { LOG(ERROR) << "Invalid IV size: " << iv.size(); return false; @@ -195,11 +195,11 @@ bool AesCbcEncryptor::SetIv(const std::vector& iv) { return true; } -AesCbcDecryptor::AesCbcDecryptor() {} -AesCbcDecryptor::~AesCbcDecryptor() {} +AesCbcPkcs5Decryptor::AesCbcPkcs5Decryptor() {} +AesCbcPkcs5Decryptor::~AesCbcPkcs5Decryptor() {} -bool AesCbcDecryptor::InitializeWithIv(const std::vector& key, - const std::vector& iv) { +bool AesCbcPkcs5Decryptor::InitializeWithIv(const std::vector& key, + const std::vector& iv) { if (!IsKeySizeValidForAes(key.size())) { LOG(ERROR) << "Invalid AES key size: " << key.size(); return false; @@ -216,8 +216,8 @@ bool AesCbcDecryptor::InitializeWithIv(const std::vector& key, return true; } -bool AesCbcDecryptor::Decrypt(const std::string& ciphertext, - std::string* plaintext) { +bool AesCbcPkcs5Decryptor::Decrypt(const std::string& ciphertext, + std::string* plaintext) { if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) { LOG(ERROR) << "Expecting cipher text size to be multiple of " << AES_BLOCK_SIZE << ", got " << ciphertext.size(); @@ -246,7 +246,226 @@ bool AesCbcDecryptor::Decrypt(const std::string& ciphertext, return true; } -bool AesCbcDecryptor::SetIv(const std::vector& iv) { +bool AesCbcPkcs5Decryptor::SetIv(const std::vector& iv) { + if (iv.size() != AES_BLOCK_SIZE) { + LOG(ERROR) << "Invalid IV size: " << iv.size(); + return false; + } + + iv_ = iv; + return true; +} + +AesCbcCtsEncryptor::AesCbcCtsEncryptor() {} +AesCbcCtsEncryptor::~AesCbcCtsEncryptor() {} + +bool AesCbcCtsEncryptor::InitializeWithIv(const std::vector& key, + const std::vector& iv) { + if (!IsKeySizeValidForAes(key.size())) { + LOG(ERROR) << "Invalid AES key size: " << key.size(); + return false; + } + if (iv.size() != AES_BLOCK_SIZE) { + LOG(ERROR) << "Invalid IV size: " << iv.size(); + return false; + } + + encrypt_key_.reset(new AES_KEY()); + CHECK_EQ(AES_set_encrypt_key(&key[0], key.size() * 8, encrypt_key_.get()), 0); + + iv_ = iv; + return true; +} + +void AesCbcCtsEncryptor::Encrypt(const uint8* plaintext, + size_t size, + uint8* ciphertext) { + DCHECK(plaintext); + DCHECK(ciphertext); + + if (size < AES_BLOCK_SIZE) { + // Don't have a full block, leave unencrypted. + memcpy(ciphertext, plaintext, size); + return; + } + + std::vector iv(iv_); + size_t residual_block_size = size % AES_BLOCK_SIZE; + size_t cbc_size = size - residual_block_size; + + // Encrypt everything but the residual block using CBC. + AES_cbc_encrypt(plaintext, + ciphertext, + cbc_size, + encrypt_key_.get(), + &iv[0], + AES_ENCRYPT); + if (residual_block_size == 0) { + // No residual block. No need to do ciphertext stealing. + return; + } + + // Zero-pad the residual block and encrypt using CBC. + std::vector residual_block(plaintext + size - residual_block_size, + plaintext + size); + residual_block.resize(AES_BLOCK_SIZE, 0); + AES_cbc_encrypt(&residual_block[0], + &residual_block[0], + AES_BLOCK_SIZE, + encrypt_key_.get(), + &iv[0], + AES_ENCRYPT); + + // Replace the last full block with the zero-padded, encrypted residual block, + // and replace the residual block with the equivalent portion of the last full + // encrypted block. It may appear that some encrypted bits of the last full + // block are lost, but they are not, as they were used as the IV when + // encrypting the zero-padded residual block. + uint8* residual_ciphertext_block = ciphertext + size - residual_block_size; + memcpy(residual_ciphertext_block, + residual_ciphertext_block - AES_BLOCK_SIZE, + residual_block_size); + memcpy(residual_ciphertext_block - AES_BLOCK_SIZE, + residual_block.data(), + AES_BLOCK_SIZE); +} + +void AesCbcCtsEncryptor::Encrypt(const std::vector& plaintext, + std::vector* ciphertext) { + DCHECK(ciphertext); + + ciphertext->resize(plaintext.size(), 0); + if (plaintext.empty()) + return; + + return Encrypt(plaintext.data(), plaintext.size(), &(*ciphertext)[0]); +} + +bool AesCbcCtsEncryptor::SetIv(const std::vector& iv) { + if (iv.size() != AES_BLOCK_SIZE) { + LOG(ERROR) << "Invalid IV size: " << iv.size(); + return false; + } + + iv_ = iv; + return true; +} + +AesCbcCtsDecryptor::AesCbcCtsDecryptor() {} +AesCbcCtsDecryptor::~AesCbcCtsDecryptor() {} + +bool AesCbcCtsDecryptor::InitializeWithIv(const std::vector& key, + const std::vector& iv) { + if (!IsKeySizeValidForAes(key.size())) { + LOG(ERROR) << "Invalid AES key size: " << key.size(); + return false; + } + if (iv.size() != AES_BLOCK_SIZE) { + LOG(ERROR) << "Invalid IV size: " << iv.size(); + return false; + } + + decrypt_key_.reset(new AES_KEY()); + CHECK_EQ(AES_set_decrypt_key(&key[0], key.size() * 8, decrypt_key_.get()), 0); + + iv_ = iv; + return true; +} + +void AesCbcCtsDecryptor::Decrypt(const uint8* ciphertext, + size_t size, + uint8* plaintext) { + DCHECK(ciphertext); + DCHECK(plaintext); + + if (size < AES_BLOCK_SIZE) { + // Don't have a full block, leave unencrypted. + memcpy(plaintext, ciphertext, size); + return; + } + + std::vector iv(iv_); + size_t residual_block_size = size % AES_BLOCK_SIZE; + + if (residual_block_size == 0) { + // No residual block. No need to do ciphertext stealing. + AES_cbc_encrypt(ciphertext, + plaintext, + size, + decrypt_key_.get(), + &iv[0], + AES_DECRYPT); + return; + } + + // AES-CBC decrypt everything up to the next-to-last full block. + size_t cbc_size = size - residual_block_size; + if (cbc_size > AES_BLOCK_SIZE) { + AES_cbc_encrypt(ciphertext, + plaintext, + cbc_size - AES_BLOCK_SIZE, + decrypt_key_.get(), + &iv[0], + AES_DECRYPT); + } + + // Determine what the last IV should be so that we can "skip ahead" in the + // CBC decryption. + std::vector last_iv(ciphertext + size - residual_block_size, + ciphertext + size); + last_iv.resize(AES_BLOCK_SIZE, 0); + + // Decrypt the next-to-last block using the IV determined above. This decrypts + // the residual block bits. + AES_cbc_encrypt(ciphertext + size - residual_block_size - AES_BLOCK_SIZE, + plaintext + size - residual_block_size - AES_BLOCK_SIZE, + AES_BLOCK_SIZE, + decrypt_key_.get(), + &last_iv[0], + AES_DECRYPT); + + // Swap back the residual block bits and the next-to-last full block. + if (plaintext == ciphertext) { + uint8* ptr1 = plaintext + size - residual_block_size; + uint8* ptr2 = plaintext + size - residual_block_size - AES_BLOCK_SIZE; + for (size_t i = 0; i < residual_block_size; ++i) { + uint8 temp = *ptr1; + *ptr1 = *ptr2; + *ptr2 = temp; + ++ptr1; + ++ptr2; + } + } else { + uint8* residual_plaintext_block = plaintext + size - residual_block_size; + memcpy(residual_plaintext_block, + residual_plaintext_block - AES_BLOCK_SIZE, + residual_block_size); + memcpy(residual_plaintext_block - AES_BLOCK_SIZE, + ciphertext + size - residual_block_size, + residual_block_size); + } + + // Decrypt the last full block. + AES_cbc_encrypt(plaintext + size - residual_block_size - AES_BLOCK_SIZE, + plaintext + size - residual_block_size - AES_BLOCK_SIZE, + AES_BLOCK_SIZE, + decrypt_key_.get(), + &iv[0], + AES_DECRYPT); +} + +void AesCbcCtsDecryptor::Decrypt(const std::vector& ciphertext, + std::vector* plaintext) { + DCHECK(plaintext); + + plaintext->resize(ciphertext.size(), 0); + if (ciphertext.empty()) + return; + + return Decrypt(ciphertext.data(), ciphertext.size(), &(*plaintext)[0]); +} + +bool AesCbcCtsDecryptor::SetIv(const std::vector& iv) { if (iv.size() != AES_BLOCK_SIZE) { LOG(ERROR) << "Invalid IV size: " << iv.size(); return false; diff --git a/media/base/aes_encryptor.h b/media/base/aes_encryptor.h index ca96be186e..ffc017e5c0 100644 --- a/media/base/aes_encryptor.h +++ b/media/base/aes_encryptor.h @@ -21,6 +21,7 @@ typedef struct aes_key_st AES_KEY; namespace edash_packager { namespace media { +// Class which implements AES-CTR counter-mode encryption/decryption. class AesCtrEncryptor { public: AesCtrEncryptor(); @@ -109,10 +110,12 @@ class AesCtrEncryptor { DISALLOW_COPY_AND_ASSIGN(AesCtrEncryptor); }; -class AesCbcEncryptor { +// Class which implements AES-CBC (Cipher block chaining) encryption with +// PKCS#5 padding. +class AesCbcPkcs5Encryptor { public: - AesCbcEncryptor(); - ~AesCbcEncryptor(); + AesCbcPkcs5Encryptor(); + ~AesCbcPkcs5Encryptor(); /// Initialize the encryptor with specified key and IV. /// @param key should be 128 bits or 192 bits or 256 bits in size as defined @@ -135,13 +138,15 @@ class AesCbcEncryptor { std::vector iv_; scoped_ptr encrypt_key_; - DISALLOW_COPY_AND_ASSIGN(AesCbcEncryptor); + DISALLOW_COPY_AND_ASSIGN(AesCbcPkcs5Encryptor); }; -class AesCbcDecryptor { +// Class which implements AES-CBC (Cipher block chaining) decryption with +// PKCS#5 padding. +class AesCbcPkcs5Decryptor { public: - AesCbcDecryptor(); - ~AesCbcDecryptor(); + AesCbcPkcs5Decryptor(); + ~AesCbcPkcs5Decryptor(); /// Initialize the decryptor with specified key and IV. /// @param key should be 128 bits or 192 bits or 256 bits in size as defined @@ -165,7 +170,92 @@ class AesCbcDecryptor { std::vector iv_; scoped_ptr decrypt_key_; - DISALLOW_COPY_AND_ASSIGN(AesCbcDecryptor); + DISALLOW_COPY_AND_ASSIGN(AesCbcPkcs5Decryptor); +}; + +// Class which implements AES-CBC (Cipher block chaining) encryption with +// Ciphertext stealing. +class AesCbcCtsEncryptor { + public: + AesCbcCtsEncryptor(); + ~AesCbcCtsEncryptor(); + + /// Initialize the encryptor with specified key and IV. + /// @param key should be 128 bits or 192 bits or 256 bits in size as defined + /// in AES spec. + /// @param iv should be 16 bytes in size. + /// @return true on successful initialization, false otherwise. + bool InitializeWithIv(const std::vector& key, + const std::vector& iv); + + /// @param plaintext points to the data to be encrypted. + /// @param size is the number of bytes to be encrypted. If less than 16 + /// bytes, it will be copied in the clear. + /// @param ciphertext should not be NULL. The buffer should be at least + /// @a size bytes in length. + void Encrypt(const uint8* plaintext, + size_t size, + uint8* ciphertext); + + /// @param plaintext contains the data to be encrypted. If less than 16 + /// bytes in size, it will be copied in the clear. + /// @param ciphertext should not be NULL. Caller retains ownership. + void Encrypt(const std::vector& plaintext, + std::vector* ciphertext); + + /// @param iv is the initialization vector. Should be 16 bytes in size. + /// @return true if successful, false if the input is invalid. + bool SetIv(const std::vector& iv); + + const std::vector& iv() const { return iv_; } + + private: + std::vector iv_; + scoped_ptr encrypt_key_; + + DISALLOW_COPY_AND_ASSIGN(AesCbcCtsEncryptor); +}; + +// Class which implements AES-CBC (Cipher block chaining) decryption with +// Ciphertext stealing. +class AesCbcCtsDecryptor { + public: + AesCbcCtsDecryptor(); + ~AesCbcCtsDecryptor(); + + /// Initialize the decryptor with specified key and IV. + /// @param key should be 128 bits or 192 bits or 256 bits in size as defined + /// in AES spec. + /// @param iv should be 16 bytes in size. + /// @return true on successful initialization, false otherwise. + bool InitializeWithIv(const std::vector& key, + const std::vector& iv); + + /// @param ciphertext points to the data to be decrypted. + /// @param size is the number of bytes to be decrypted. If less than 16 + /// bytes, it will be copied in the clear. + /// @param plaintext should not be NULL. The buffer should be at least + /// @a size bytes in length. + void Decrypt(const uint8* ciphertext, + size_t size, + uint8* plaintext); + + /// @param ciphertext contains the data to be decrypted. If less than 16 + /// bytes in size, it will be copied in the clear. + /// @param plaintext should not be NULL. Caller retains ownership. + void Decrypt(const std::vector& ciphertext, + std::vector* plaintext); + + /// @return true if successful, false if the input is invalid. + bool SetIv(const std::vector& iv); + + const std::vector& iv() const { return iv_; } + + private: + std::vector iv_; + scoped_ptr decrypt_key_; + + DISALLOW_COPY_AND_ASSIGN(AesCbcCtsDecryptor); }; } // namespace media diff --git a/media/base/aes_encryptor_unittest.cc b/media/base/aes_encryptor_unittest.cc index 419b0deb90..7765347818 100644 --- a/media/base/aes_encryptor_unittest.cc +++ b/media/base/aes_encryptor_unittest.cc @@ -17,11 +17,11 @@ const uint32 kAesBlockSize = 16; // From NIST SP 800-38a test case: - F.5.1 CTR-AES128.Encrypt // http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf -const uint8 kAesCtrKey[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, - 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}; +const uint8 kAesKey[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}; -const uint8 kAesCtrIv[] = {0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, - 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}; +const uint8 kAesIv[] = {0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}; const uint8 kAesCtrPlaintext[] = { // Block #1 @@ -136,8 +136,8 @@ namespace media { class AesCtrEncryptorTest : public testing::Test { public: virtual void SetUp() { - key_.assign(kAesCtrKey, kAesCtrKey + arraysize(kAesCtrKey)); - iv_.assign(kAesCtrIv, kAesCtrIv + arraysize(kAesCtrIv)); + key_.assign(kAesKey, kAesKey + arraysize(kAesKey)); + iv_.assign(kAesIv, kAesIv + arraysize(kAesIv)); plaintext_.assign(kAesCtrPlaintext, kAesCtrPlaintext + arraysize(kAesCtrPlaintext)); ciphertext_.assign(kAesCtrCiphertext, @@ -298,13 +298,13 @@ INSTANTIATE_TEST_CASE_P(IvTestCases, AesCtrEncryptorIvTest, ::testing::ValuesIn(kIvTestCases)); -class AesCbcEncryptorTestEncryptionDecryption : public testing::Test { +class AesCbcPkcs5EncryptorTestEncryptionDecryption : public testing::Test { public: void TestEncryptionDecryption(const std::vector& key, const std::vector& iv, const std::string& plaintext, const std::string& expected_ciphertext_hex) { - AesCbcEncryptor encryptor; + AesCbcPkcs5Encryptor encryptor; EXPECT_TRUE(encryptor.InitializeWithIv(key, iv)); std::string ciphertext; @@ -312,7 +312,7 @@ class AesCbcEncryptorTestEncryptionDecryption : public testing::Test { EXPECT_EQ(expected_ciphertext_hex, base::HexEncode(ciphertext.data(), ciphertext.size())); - AesCbcDecryptor decryptor; + AesCbcPkcs5Decryptor decryptor; ASSERT_TRUE(decryptor.InitializeWithIv(key, iv)); std::string decrypted; @@ -321,7 +321,7 @@ class AesCbcEncryptorTestEncryptionDecryption : public testing::Test { } }; -TEST_F(AesCbcEncryptorTestEncryptionDecryption, EncryptAES256CBC) { +TEST_F(AesCbcPkcs5EncryptorTestEncryptionDecryption, EncryptAES256CBC) { // NIST SP 800-38A test vector F.2.5 CBC-AES256.Encrypt. static const uint8 kAesCbcKey[] = { 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, @@ -370,7 +370,7 @@ TEST_F(AesCbcEncryptorTestEncryptionDecryption, EncryptAES256CBC) { TestEncryptionDecryption(key, iv, plaintext, expected_ciphertext_hex); } -TEST_F(AesCbcEncryptorTestEncryptionDecryption, EncryptAES128CBCRegression) { +TEST_F(AesCbcPkcs5EncryptorTestEncryptionDecryption, EncryptAES128CBCRegression) { const std::string kKey = "128=SixteenBytes"; const std::string kIv = "Sweet Sixteen IV"; const std::string kPlaintext = @@ -385,7 +385,7 @@ TEST_F(AesCbcEncryptorTestEncryptionDecryption, EncryptAES128CBCRegression) { TestEncryptionDecryption(key, iv, kPlaintext, kExpectedCiphertextHex); } -TEST_F(AesCbcEncryptorTestEncryptionDecryption, EncryptAES192CBCRegression) { +TEST_F(AesCbcPkcs5EncryptorTestEncryptionDecryption, EncryptAES192CBCRegression) { const std::string kKey = "192bitsIsTwentyFourByte!"; const std::string kIv = "Sweet Sixteen IV"; const std::string kPlaintext = "Small text"; @@ -397,7 +397,7 @@ TEST_F(AesCbcEncryptorTestEncryptionDecryption, EncryptAES192CBCRegression) { TestEncryptionDecryption(key, iv, kPlaintext, kExpectedCiphertextHex); } -class AesCbcEncryptorTest : public testing::Test { +class AesCbcPkcs5EncryptorTest : public testing::Test { public: virtual void SetUp() { const std::string kKey = "128=SixteenBytes"; @@ -411,18 +411,18 @@ class AesCbcEncryptorTest : public testing::Test { std::vector iv_; }; -TEST_F(AesCbcEncryptorTest, UnsupportedKeySize) { - AesCbcEncryptor encryptor; +TEST_F(AesCbcPkcs5EncryptorTest, UnsupportedKeySize) { + AesCbcPkcs5Encryptor encryptor; EXPECT_FALSE(encryptor.InitializeWithIv(std::vector(15, 0), iv_)); } -TEST_F(AesCbcEncryptorTest, UnsupportedIvSize) { - AesCbcEncryptor encryptor; +TEST_F(AesCbcPkcs5EncryptorTest, UnsupportedIvSize) { + AesCbcPkcs5Encryptor encryptor; EXPECT_FALSE(encryptor.InitializeWithIv(key_, std::vector(14, 0))); } -TEST_F(AesCbcEncryptorTest, EmptyEncrypt) { - AesCbcEncryptor encryptor; +TEST_F(AesCbcPkcs5EncryptorTest, EmptyEncrypt) { + AesCbcPkcs5Encryptor encryptor; ASSERT_TRUE(encryptor.InitializeWithIv(key_, iv_)); std::string ciphertext; @@ -432,13 +432,119 @@ TEST_F(AesCbcEncryptorTest, EmptyEncrypt) { base::HexEncode(ciphertext.data(), ciphertext.size())); } -TEST_F(AesCbcEncryptorTest, CipherTextNotMultipleOfBlockSize) { - AesCbcDecryptor decryptor; +TEST_F(AesCbcPkcs5EncryptorTest, CipherTextNotMultipleOfBlockSize) { + AesCbcPkcs5Decryptor decryptor; ASSERT_TRUE(decryptor.InitializeWithIv(key_, iv_)); std::string plaintext; EXPECT_FALSE(decryptor.Decrypt("1", &plaintext)); } +class AesCbcCtsEncryptorDecryptorTest : public testing::Test { + public: + virtual void SetUp() { + key_.assign(kAesKey, kAesKey + arraysize(kAesKey)); + iv_.assign(kAesIv, kAesIv + arraysize(kAesIv)); + } + + void TestEncryptDecryptSeparateBuffers( + const std::vector& plaintext, + const std::vector& expected_ciphertext) { + ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_)); + ASSERT_TRUE(decryptor_.InitializeWithIv(key_, iv_)); + + std::vector encrypted; + encryptor_.Encrypt(plaintext, &encrypted); + EXPECT_EQ(expected_ciphertext, encrypted); + + std::vector decrypted; + decryptor_.Decrypt(encrypted, &decrypted); + EXPECT_EQ(plaintext, decrypted); + } + + void TestEncryptDecryptInPlace( + const std::vector& plaintext, + const std::vector& expected_ciphertext) { + ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_)); + ASSERT_TRUE(decryptor_.InitializeWithIv(key_, iv_)); + + std::vector buffer(plaintext); + encryptor_.Encrypt(buffer, &buffer); + EXPECT_EQ(expected_ciphertext, buffer); + decryptor_.Decrypt(buffer, &buffer); + EXPECT_EQ(plaintext, buffer); + } + + protected: + std::vector key_; + std::vector iv_; + AesCbcCtsEncryptor encryptor_; + AesCbcCtsDecryptor decryptor_; +}; + +TEST_F(AesCbcCtsEncryptorDecryptorTest, TestWithResidualBytes) { + std::vector plaintext; + ASSERT_TRUE(base::HexStringToBytes( + "e0818f2dc7caaa9edf09285a0c1fca98d39e9b08a47ab6911c4bbdf27d94" + "f917cdffc9ebb307141f23b0d3921e0ed7f86eb09381286f8e7a4f", + &plaintext)); + + std::vector ciphertext; + ASSERT_TRUE(base::HexStringToBytes( + "b40a0b8704c74e22e8030cad6f272b34ace54cc7c9c64b2018bbcf23df018" + "39b14899441cf74a9fb2f2b229a609146f31be8e8a826eb6e857e", + &ciphertext)); + + TestEncryptDecryptSeparateBuffers(plaintext, ciphertext); + TestEncryptDecryptInPlace(plaintext, ciphertext); +} + +TEST_F(AesCbcCtsEncryptorDecryptorTest, TestEvenBlocks) { + std::vector plaintext; + ASSERT_TRUE(base::HexStringToBytes( + "3f593e7a204a5e70f2814dca05aa49d36f2daddc9a24e0515802c539efc3" + "1094b3ad6c26d6f5c0e387545ce6a4c2c14d", + &plaintext)); + + std::vector ciphertext; + ASSERT_TRUE(base::HexStringToBytes( + "5f32cd0504b27b25ee04090d88d37d340c9c0a9fa50b05358b98fad4302ea" + "480148d8aa091f4e7d186a7223df153f6f7", + &ciphertext)); + + TestEncryptDecryptSeparateBuffers(plaintext, ciphertext); + TestEncryptDecryptInPlace(plaintext, ciphertext); +} + +TEST_F(AesCbcCtsEncryptorDecryptorTest, TestOneBlockAndAHalf) { + std::vector plaintext; + ASSERT_TRUE(base::HexStringToBytes( + "3f593e7a204a5e70f2814dca05aa49d36f2daddc9a4302ea", + &plaintext)); + + std::vector ciphertext; + ASSERT_TRUE(base::HexStringToBytes( + "623fc113fe02ce85628deb58d652c6995f32cd0504b27b25", + &ciphertext)); + + TestEncryptDecryptSeparateBuffers(plaintext, ciphertext); + TestEncryptDecryptInPlace(plaintext, ciphertext); +} + +TEST_F(AesCbcCtsEncryptorDecryptorTest, TestZeroEncryptedBlocks) { + std::vector plaintext; + ASSERT_TRUE(base::HexStringToBytes("3f593e7a204a5e70f2", &plaintext)); + + TestEncryptDecryptSeparateBuffers(plaintext, plaintext); + TestEncryptDecryptInPlace(plaintext, plaintext); +} + +TEST_F(AesCbcCtsEncryptorDecryptorTest, TestZeroBytes) { + std::vector plaintext; + + TestEncryptDecryptSeparateBuffers(plaintext, plaintext); + TestEncryptDecryptInPlace(plaintext, plaintext); +} + } // namespace media } // namespace edash_packager diff --git a/media/base/request_signer.cc b/media/base/request_signer.cc index 1e85de3554..318e6195a7 100644 --- a/media/base/request_signer.cc +++ b/media/base/request_signer.cc @@ -19,7 +19,7 @@ RequestSigner::RequestSigner(const std::string& signer_name) RequestSigner::~RequestSigner() {} AesRequestSigner::AesRequestSigner(const std::string& signer_name, - scoped_ptr encryptor) + scoped_ptr encryptor) : RequestSigner(signer_name), aes_cbc_encryptor_(encryptor.Pass()) { DCHECK(aes_cbc_encryptor_); } @@ -39,7 +39,7 @@ AesRequestSigner* AesRequestSigner::CreateSigner(const std::string& signer_name, return NULL; } - scoped_ptr encryptor(new AesCbcEncryptor()); + scoped_ptr encryptor(new AesCbcPkcs5Encryptor()); if (!encryptor->InitializeWithIv(aes_key, iv)) return NULL; return new AesRequestSigner(signer_name, encryptor.Pass()); diff --git a/media/base/request_signer.h b/media/base/request_signer.h index 6603b5d344..7003b75ae4 100644 --- a/media/base/request_signer.h +++ b/media/base/request_signer.h @@ -15,7 +15,7 @@ namespace edash_packager { namespace media { -class AesCbcEncryptor; +class AesCbcPkcs5Encryptor; class RsaPrivateKey; /// Abstract class used for signature generation. @@ -57,9 +57,9 @@ class AesRequestSigner : public RequestSigner { private: AesRequestSigner(const std::string& signer_name, - scoped_ptr encryptor); + scoped_ptr encryptor); - scoped_ptr aes_cbc_encryptor_; + scoped_ptr aes_cbc_encryptor_; DISALLOW_COPY_AND_ASSIGN(AesRequestSigner); };