Implemented AES-CBC with Ciphertext Stealing for WVM decryption.

Change-Id: I4a1ba4247bb6f055905663568699b06d7c18a968
This commit is contained in:
Thomas Inskip 2014-09-09 15:56:02 -07:00 committed by Ramji Chandramouli
parent 3114ee945d
commit fd35084722
5 changed files with 463 additions and 48 deletions

View File

@ -143,10 +143,10 @@ bool AesCtrEncryptor::SetIv(const std::vector<uint8>& iv) {
return true; return true;
} }
AesCbcEncryptor::AesCbcEncryptor() {} AesCbcPkcs5Encryptor::AesCbcPkcs5Encryptor() {}
AesCbcEncryptor::~AesCbcEncryptor() {} AesCbcPkcs5Encryptor::~AesCbcPkcs5Encryptor() {}
bool AesCbcEncryptor::InitializeWithIv(const std::vector<uint8>& key, bool AesCbcPkcs5Encryptor::InitializeWithIv(const std::vector<uint8>& key,
const std::vector<uint8>& iv) { const std::vector<uint8>& iv) {
if (!IsKeySizeValidForAes(key.size())) { if (!IsKeySizeValidForAes(key.size())) {
LOG(ERROR) << "Invalid AES key size: " << key.size(); LOG(ERROR) << "Invalid AES key size: " << key.size();
@ -164,7 +164,7 @@ bool AesCbcEncryptor::InitializeWithIv(const std::vector<uint8>& key,
return true; return true;
} }
void AesCbcEncryptor::Encrypt(const std::string& plaintext, void AesCbcPkcs5Encryptor::Encrypt(const std::string& plaintext,
std::string* ciphertext) { std::string* ciphertext) {
DCHECK(ciphertext); DCHECK(ciphertext);
DCHECK(encrypt_key_); DCHECK(encrypt_key_);
@ -185,7 +185,7 @@ void AesCbcEncryptor::Encrypt(const std::string& plaintext,
AES_ENCRYPT); AES_ENCRYPT);
} }
bool AesCbcEncryptor::SetIv(const std::vector<uint8>& iv) { bool AesCbcPkcs5Encryptor::SetIv(const std::vector<uint8>& iv) {
if (iv.size() != AES_BLOCK_SIZE) { if (iv.size() != AES_BLOCK_SIZE) {
LOG(ERROR) << "Invalid IV size: " << iv.size(); LOG(ERROR) << "Invalid IV size: " << iv.size();
return false; return false;
@ -195,10 +195,10 @@ bool AesCbcEncryptor::SetIv(const std::vector<uint8>& iv) {
return true; return true;
} }
AesCbcDecryptor::AesCbcDecryptor() {} AesCbcPkcs5Decryptor::AesCbcPkcs5Decryptor() {}
AesCbcDecryptor::~AesCbcDecryptor() {} AesCbcPkcs5Decryptor::~AesCbcPkcs5Decryptor() {}
bool AesCbcDecryptor::InitializeWithIv(const std::vector<uint8>& key, bool AesCbcPkcs5Decryptor::InitializeWithIv(const std::vector<uint8>& key,
const std::vector<uint8>& iv) { const std::vector<uint8>& iv) {
if (!IsKeySizeValidForAes(key.size())) { if (!IsKeySizeValidForAes(key.size())) {
LOG(ERROR) << "Invalid AES key size: " << key.size(); LOG(ERROR) << "Invalid AES key size: " << key.size();
@ -216,7 +216,7 @@ bool AesCbcDecryptor::InitializeWithIv(const std::vector<uint8>& key,
return true; return true;
} }
bool AesCbcDecryptor::Decrypt(const std::string& ciphertext, bool AesCbcPkcs5Decryptor::Decrypt(const std::string& ciphertext,
std::string* plaintext) { std::string* plaintext) {
if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) { if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) {
LOG(ERROR) << "Expecting cipher text size to be multiple of " LOG(ERROR) << "Expecting cipher text size to be multiple of "
@ -246,7 +246,226 @@ bool AesCbcDecryptor::Decrypt(const std::string& ciphertext,
return true; return true;
} }
bool AesCbcDecryptor::SetIv(const std::vector<uint8>& iv) { bool AesCbcPkcs5Decryptor::SetIv(const std::vector<uint8>& 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<uint8>& key,
const std::vector<uint8>& 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<uint8> 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<uint8> 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<uint8>& plaintext,
std::vector<uint8>* 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<uint8>& 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<uint8>& key,
const std::vector<uint8>& 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<uint8> 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<uint8> 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<uint8>& ciphertext,
std::vector<uint8>* 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<uint8>& iv) {
if (iv.size() != AES_BLOCK_SIZE) { if (iv.size() != AES_BLOCK_SIZE) {
LOG(ERROR) << "Invalid IV size: " << iv.size(); LOG(ERROR) << "Invalid IV size: " << iv.size();
return false; return false;

View File

@ -21,6 +21,7 @@ typedef struct aes_key_st AES_KEY;
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
// Class which implements AES-CTR counter-mode encryption/decryption.
class AesCtrEncryptor { class AesCtrEncryptor {
public: public:
AesCtrEncryptor(); AesCtrEncryptor();
@ -109,10 +110,12 @@ class AesCtrEncryptor {
DISALLOW_COPY_AND_ASSIGN(AesCtrEncryptor); DISALLOW_COPY_AND_ASSIGN(AesCtrEncryptor);
}; };
class AesCbcEncryptor { // Class which implements AES-CBC (Cipher block chaining) encryption with
// PKCS#5 padding.
class AesCbcPkcs5Encryptor {
public: public:
AesCbcEncryptor(); AesCbcPkcs5Encryptor();
~AesCbcEncryptor(); ~AesCbcPkcs5Encryptor();
/// Initialize the encryptor with specified key and IV. /// Initialize the encryptor with specified key and IV.
/// @param key should be 128 bits or 192 bits or 256 bits in size as defined /// @param key should be 128 bits or 192 bits or 256 bits in size as defined
@ -135,13 +138,15 @@ class AesCbcEncryptor {
std::vector<uint8> iv_; std::vector<uint8> iv_;
scoped_ptr<AES_KEY> encrypt_key_; scoped_ptr<AES_KEY> 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: public:
AesCbcDecryptor(); AesCbcPkcs5Decryptor();
~AesCbcDecryptor(); ~AesCbcPkcs5Decryptor();
/// Initialize the decryptor with specified key and IV. /// Initialize the decryptor with specified key and IV.
/// @param key should be 128 bits or 192 bits or 256 bits in size as defined /// @param key should be 128 bits or 192 bits or 256 bits in size as defined
@ -165,7 +170,92 @@ class AesCbcDecryptor {
std::vector<uint8> iv_; std::vector<uint8> iv_;
scoped_ptr<AES_KEY> decrypt_key_; scoped_ptr<AES_KEY> 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<uint8>& key,
const std::vector<uint8>& 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<uint8>& plaintext,
std::vector<uint8>* 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<uint8>& iv);
const std::vector<uint8>& iv() const { return iv_; }
private:
std::vector<uint8> iv_;
scoped_ptr<AES_KEY> 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<uint8>& key,
const std::vector<uint8>& 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<uint8>& ciphertext,
std::vector<uint8>* plaintext);
/// @return true if successful, false if the input is invalid.
bool SetIv(const std::vector<uint8>& iv);
const std::vector<uint8>& iv() const { return iv_; }
private:
std::vector<uint8> iv_;
scoped_ptr<AES_KEY> decrypt_key_;
DISALLOW_COPY_AND_ASSIGN(AesCbcCtsDecryptor);
}; };
} // namespace media } // namespace media

View File

@ -17,10 +17,10 @@ const uint32 kAesBlockSize = 16;
// From NIST SP 800-38a test case: - F.5.1 CTR-AES128.Encrypt // From NIST SP 800-38a test case: - F.5.1 CTR-AES128.Encrypt
// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf // http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
const uint8 kAesCtrKey[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, const uint8 kAesKey[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}; 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
const uint8 kAesCtrIv[] = {0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, const uint8 kAesIv[] = {0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}; 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff};
const uint8 kAesCtrPlaintext[] = { const uint8 kAesCtrPlaintext[] = {
@ -136,8 +136,8 @@ namespace media {
class AesCtrEncryptorTest : public testing::Test { class AesCtrEncryptorTest : public testing::Test {
public: public:
virtual void SetUp() { virtual void SetUp() {
key_.assign(kAesCtrKey, kAesCtrKey + arraysize(kAesCtrKey)); key_.assign(kAesKey, kAesKey + arraysize(kAesKey));
iv_.assign(kAesCtrIv, kAesCtrIv + arraysize(kAesCtrIv)); iv_.assign(kAesIv, kAesIv + arraysize(kAesIv));
plaintext_.assign(kAesCtrPlaintext, plaintext_.assign(kAesCtrPlaintext,
kAesCtrPlaintext + arraysize(kAesCtrPlaintext)); kAesCtrPlaintext + arraysize(kAesCtrPlaintext));
ciphertext_.assign(kAesCtrCiphertext, ciphertext_.assign(kAesCtrCiphertext,
@ -298,13 +298,13 @@ INSTANTIATE_TEST_CASE_P(IvTestCases,
AesCtrEncryptorIvTest, AesCtrEncryptorIvTest,
::testing::ValuesIn(kIvTestCases)); ::testing::ValuesIn(kIvTestCases));
class AesCbcEncryptorTestEncryptionDecryption : public testing::Test { class AesCbcPkcs5EncryptorTestEncryptionDecryption : public testing::Test {
public: public:
void TestEncryptionDecryption(const std::vector<uint8>& key, void TestEncryptionDecryption(const std::vector<uint8>& key,
const std::vector<uint8>& iv, const std::vector<uint8>& iv,
const std::string& plaintext, const std::string& plaintext,
const std::string& expected_ciphertext_hex) { const std::string& expected_ciphertext_hex) {
AesCbcEncryptor encryptor; AesCbcPkcs5Encryptor encryptor;
EXPECT_TRUE(encryptor.InitializeWithIv(key, iv)); EXPECT_TRUE(encryptor.InitializeWithIv(key, iv));
std::string ciphertext; std::string ciphertext;
@ -312,7 +312,7 @@ class AesCbcEncryptorTestEncryptionDecryption : public testing::Test {
EXPECT_EQ(expected_ciphertext_hex, EXPECT_EQ(expected_ciphertext_hex,
base::HexEncode(ciphertext.data(), ciphertext.size())); base::HexEncode(ciphertext.data(), ciphertext.size()));
AesCbcDecryptor decryptor; AesCbcPkcs5Decryptor decryptor;
ASSERT_TRUE(decryptor.InitializeWithIv(key, iv)); ASSERT_TRUE(decryptor.InitializeWithIv(key, iv));
std::string decrypted; 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. // NIST SP 800-38A test vector F.2.5 CBC-AES256.Encrypt.
static const uint8 kAesCbcKey[] = { static const uint8 kAesCbcKey[] = {
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 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); TestEncryptionDecryption(key, iv, plaintext, expected_ciphertext_hex);
} }
TEST_F(AesCbcEncryptorTestEncryptionDecryption, EncryptAES128CBCRegression) { TEST_F(AesCbcPkcs5EncryptorTestEncryptionDecryption, EncryptAES128CBCRegression) {
const std::string kKey = "128=SixteenBytes"; const std::string kKey = "128=SixteenBytes";
const std::string kIv = "Sweet Sixteen IV"; const std::string kIv = "Sweet Sixteen IV";
const std::string kPlaintext = const std::string kPlaintext =
@ -385,7 +385,7 @@ TEST_F(AesCbcEncryptorTestEncryptionDecryption, EncryptAES128CBCRegression) {
TestEncryptionDecryption(key, iv, kPlaintext, kExpectedCiphertextHex); TestEncryptionDecryption(key, iv, kPlaintext, kExpectedCiphertextHex);
} }
TEST_F(AesCbcEncryptorTestEncryptionDecryption, EncryptAES192CBCRegression) { TEST_F(AesCbcPkcs5EncryptorTestEncryptionDecryption, EncryptAES192CBCRegression) {
const std::string kKey = "192bitsIsTwentyFourByte!"; const std::string kKey = "192bitsIsTwentyFourByte!";
const std::string kIv = "Sweet Sixteen IV"; const std::string kIv = "Sweet Sixteen IV";
const std::string kPlaintext = "Small text"; const std::string kPlaintext = "Small text";
@ -397,7 +397,7 @@ TEST_F(AesCbcEncryptorTestEncryptionDecryption, EncryptAES192CBCRegression) {
TestEncryptionDecryption(key, iv, kPlaintext, kExpectedCiphertextHex); TestEncryptionDecryption(key, iv, kPlaintext, kExpectedCiphertextHex);
} }
class AesCbcEncryptorTest : public testing::Test { class AesCbcPkcs5EncryptorTest : public testing::Test {
public: public:
virtual void SetUp() { virtual void SetUp() {
const std::string kKey = "128=SixteenBytes"; const std::string kKey = "128=SixteenBytes";
@ -411,18 +411,18 @@ class AesCbcEncryptorTest : public testing::Test {
std::vector<uint8> iv_; std::vector<uint8> iv_;
}; };
TEST_F(AesCbcEncryptorTest, UnsupportedKeySize) { TEST_F(AesCbcPkcs5EncryptorTest, UnsupportedKeySize) {
AesCbcEncryptor encryptor; AesCbcPkcs5Encryptor encryptor;
EXPECT_FALSE(encryptor.InitializeWithIv(std::vector<uint8>(15, 0), iv_)); EXPECT_FALSE(encryptor.InitializeWithIv(std::vector<uint8>(15, 0), iv_));
} }
TEST_F(AesCbcEncryptorTest, UnsupportedIvSize) { TEST_F(AesCbcPkcs5EncryptorTest, UnsupportedIvSize) {
AesCbcEncryptor encryptor; AesCbcPkcs5Encryptor encryptor;
EXPECT_FALSE(encryptor.InitializeWithIv(key_, std::vector<uint8>(14, 0))); EXPECT_FALSE(encryptor.InitializeWithIv(key_, std::vector<uint8>(14, 0)));
} }
TEST_F(AesCbcEncryptorTest, EmptyEncrypt) { TEST_F(AesCbcPkcs5EncryptorTest, EmptyEncrypt) {
AesCbcEncryptor encryptor; AesCbcPkcs5Encryptor encryptor;
ASSERT_TRUE(encryptor.InitializeWithIv(key_, iv_)); ASSERT_TRUE(encryptor.InitializeWithIv(key_, iv_));
std::string ciphertext; std::string ciphertext;
@ -432,13 +432,119 @@ TEST_F(AesCbcEncryptorTest, EmptyEncrypt) {
base::HexEncode(ciphertext.data(), ciphertext.size())); base::HexEncode(ciphertext.data(), ciphertext.size()));
} }
TEST_F(AesCbcEncryptorTest, CipherTextNotMultipleOfBlockSize) { TEST_F(AesCbcPkcs5EncryptorTest, CipherTextNotMultipleOfBlockSize) {
AesCbcDecryptor decryptor; AesCbcPkcs5Decryptor decryptor;
ASSERT_TRUE(decryptor.InitializeWithIv(key_, iv_)); ASSERT_TRUE(decryptor.InitializeWithIv(key_, iv_));
std::string plaintext; std::string plaintext;
EXPECT_FALSE(decryptor.Decrypt("1", &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<uint8>& plaintext,
const std::vector<uint8>& expected_ciphertext) {
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_));
ASSERT_TRUE(decryptor_.InitializeWithIv(key_, iv_));
std::vector<uint8> encrypted;
encryptor_.Encrypt(plaintext, &encrypted);
EXPECT_EQ(expected_ciphertext, encrypted);
std::vector<uint8> decrypted;
decryptor_.Decrypt(encrypted, &decrypted);
EXPECT_EQ(plaintext, decrypted);
}
void TestEncryptDecryptInPlace(
const std::vector<uint8>& plaintext,
const std::vector<uint8>& expected_ciphertext) {
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_));
ASSERT_TRUE(decryptor_.InitializeWithIv(key_, iv_));
std::vector<uint8> buffer(plaintext);
encryptor_.Encrypt(buffer, &buffer);
EXPECT_EQ(expected_ciphertext, buffer);
decryptor_.Decrypt(buffer, &buffer);
EXPECT_EQ(plaintext, buffer);
}
protected:
std::vector<uint8> key_;
std::vector<uint8> iv_;
AesCbcCtsEncryptor encryptor_;
AesCbcCtsDecryptor decryptor_;
};
TEST_F(AesCbcCtsEncryptorDecryptorTest, TestWithResidualBytes) {
std::vector<uint8> plaintext;
ASSERT_TRUE(base::HexStringToBytes(
"e0818f2dc7caaa9edf09285a0c1fca98d39e9b08a47ab6911c4bbdf27d94"
"f917cdffc9ebb307141f23b0d3921e0ed7f86eb09381286f8e7a4f",
&plaintext));
std::vector<uint8> ciphertext;
ASSERT_TRUE(base::HexStringToBytes(
"b40a0b8704c74e22e8030cad6f272b34ace54cc7c9c64b2018bbcf23df018"
"39b14899441cf74a9fb2f2b229a609146f31be8e8a826eb6e857e",
&ciphertext));
TestEncryptDecryptSeparateBuffers(plaintext, ciphertext);
TestEncryptDecryptInPlace(plaintext, ciphertext);
}
TEST_F(AesCbcCtsEncryptorDecryptorTest, TestEvenBlocks) {
std::vector<uint8> plaintext;
ASSERT_TRUE(base::HexStringToBytes(
"3f593e7a204a5e70f2814dca05aa49d36f2daddc9a24e0515802c539efc3"
"1094b3ad6c26d6f5c0e387545ce6a4c2c14d",
&plaintext));
std::vector<uint8> ciphertext;
ASSERT_TRUE(base::HexStringToBytes(
"5f32cd0504b27b25ee04090d88d37d340c9c0a9fa50b05358b98fad4302ea"
"480148d8aa091f4e7d186a7223df153f6f7",
&ciphertext));
TestEncryptDecryptSeparateBuffers(plaintext, ciphertext);
TestEncryptDecryptInPlace(plaintext, ciphertext);
}
TEST_F(AesCbcCtsEncryptorDecryptorTest, TestOneBlockAndAHalf) {
std::vector<uint8> plaintext;
ASSERT_TRUE(base::HexStringToBytes(
"3f593e7a204a5e70f2814dca05aa49d36f2daddc9a4302ea",
&plaintext));
std::vector<uint8> ciphertext;
ASSERT_TRUE(base::HexStringToBytes(
"623fc113fe02ce85628deb58d652c6995f32cd0504b27b25",
&ciphertext));
TestEncryptDecryptSeparateBuffers(plaintext, ciphertext);
TestEncryptDecryptInPlace(plaintext, ciphertext);
}
TEST_F(AesCbcCtsEncryptorDecryptorTest, TestZeroEncryptedBlocks) {
std::vector<uint8> plaintext;
ASSERT_TRUE(base::HexStringToBytes("3f593e7a204a5e70f2", &plaintext));
TestEncryptDecryptSeparateBuffers(plaintext, plaintext);
TestEncryptDecryptInPlace(plaintext, plaintext);
}
TEST_F(AesCbcCtsEncryptorDecryptorTest, TestZeroBytes) {
std::vector<uint8> plaintext;
TestEncryptDecryptSeparateBuffers(plaintext, plaintext);
TestEncryptDecryptInPlace(plaintext, plaintext);
}
} // namespace media } // namespace media
} // namespace edash_packager } // namespace edash_packager

View File

@ -19,7 +19,7 @@ RequestSigner::RequestSigner(const std::string& signer_name)
RequestSigner::~RequestSigner() {} RequestSigner::~RequestSigner() {}
AesRequestSigner::AesRequestSigner(const std::string& signer_name, AesRequestSigner::AesRequestSigner(const std::string& signer_name,
scoped_ptr<AesCbcEncryptor> encryptor) scoped_ptr<AesCbcPkcs5Encryptor> encryptor)
: RequestSigner(signer_name), aes_cbc_encryptor_(encryptor.Pass()) { : RequestSigner(signer_name), aes_cbc_encryptor_(encryptor.Pass()) {
DCHECK(aes_cbc_encryptor_); DCHECK(aes_cbc_encryptor_);
} }
@ -39,7 +39,7 @@ AesRequestSigner* AesRequestSigner::CreateSigner(const std::string& signer_name,
return NULL; return NULL;
} }
scoped_ptr<AesCbcEncryptor> encryptor(new AesCbcEncryptor()); scoped_ptr<AesCbcPkcs5Encryptor> encryptor(new AesCbcPkcs5Encryptor());
if (!encryptor->InitializeWithIv(aes_key, iv)) if (!encryptor->InitializeWithIv(aes_key, iv))
return NULL; return NULL;
return new AesRequestSigner(signer_name, encryptor.Pass()); return new AesRequestSigner(signer_name, encryptor.Pass());

View File

@ -15,7 +15,7 @@
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
class AesCbcEncryptor; class AesCbcPkcs5Encryptor;
class RsaPrivateKey; class RsaPrivateKey;
/// Abstract class used for signature generation. /// Abstract class used for signature generation.
@ -57,9 +57,9 @@ class AesRequestSigner : public RequestSigner {
private: private:
AesRequestSigner(const std::string& signer_name, AesRequestSigner(const std::string& signer_name,
scoped_ptr<AesCbcEncryptor> encryptor); scoped_ptr<AesCbcPkcs5Encryptor> encryptor);
scoped_ptr<AesCbcEncryptor> aes_cbc_encryptor_; scoped_ptr<AesCbcPkcs5Encryptor> aes_cbc_encryptor_;
DISALLOW_COPY_AND_ASSIGN(AesRequestSigner); DISALLOW_COPY_AND_ASSIGN(AesRequestSigner);
}; };