Refactor and optimize cbc encryption/decryption
- Optimize and clean up encryption and decryption code. - Consolidate various CBC encryption/decryption schemes into a common class. - Make it a constructor argument whether cipher block chain is continuous across Encrypt/Decrypt calls. - Also align protected region size as required in CENC spec. Issue #77 Change-Id: I533d92ada3cd80933b532b9c3a1cca105ba66f8e
This commit is contained in:
parent
a2438554f6
commit
a9e5a2ff4f
|
@ -112,7 +112,7 @@ edash_packager::media::EncryptionMode GetEncryptionMode(
|
||||||
} else if (protection_scheme == "cbc1") {
|
} else if (protection_scheme == "cbc1") {
|
||||||
return edash_packager::media::kEncryptionModeAesCbc;
|
return edash_packager::media::kEncryptionModeAesCbc;
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR) << "Protection scheme is unknown.";
|
LOG(ERROR) << "Unknown protection scheme: " << protection_scheme;
|
||||||
return edash_packager::media::kEncryptionModeUnknown;
|
return edash_packager::media::kEncryptionModeUnknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -377,6 +377,12 @@ bool RunPackager(const StreamDescriptorList& stream_descriptors) {
|
||||||
EncryptionMode encryption_mode = GetEncryptionMode(FLAGS_protection_scheme);
|
EncryptionMode encryption_mode = GetEncryptionMode(FLAGS_protection_scheme);
|
||||||
if (encryption_mode == kEncryptionModeUnknown)
|
if (encryption_mode == kEncryptionModeUnknown)
|
||||||
return false;
|
return false;
|
||||||
|
if (encryption_mode == kEncryptionModeAesCbc && !FLAGS_iv.empty()) {
|
||||||
|
if (FLAGS_iv.size() != 16) {
|
||||||
|
LOG(ERROR) << "Iv size should be 16 bytes for CBC encryption mode.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!AssignFlagsFromProfile())
|
if (!AssignFlagsFromProfile())
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -27,6 +27,32 @@ namespace media {
|
||||||
AesDecryptor::AesDecryptor() {}
|
AesDecryptor::AesDecryptor() {}
|
||||||
AesDecryptor::~AesDecryptor() {}
|
AesDecryptor::~AesDecryptor() {}
|
||||||
|
|
||||||
|
bool AesDecryptor::Decrypt(const std::vector<uint8_t>& ciphertext,
|
||||||
|
std::vector<uint8_t>* plaintext) {
|
||||||
|
DCHECK(plaintext);
|
||||||
|
plaintext->resize(ciphertext.size());
|
||||||
|
size_t plaintext_size;
|
||||||
|
if (!DecryptInternal(ciphertext.data(), ciphertext.size(), plaintext->data(),
|
||||||
|
&plaintext_size))
|
||||||
|
return false;
|
||||||
|
plaintext->resize(plaintext_size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AesDecryptor::Decrypt(const std::string& ciphertext,
|
||||||
|
std::string* plaintext) {
|
||||||
|
DCHECK(plaintext);
|
||||||
|
plaintext->resize(ciphertext.size());
|
||||||
|
size_t plaintext_size;
|
||||||
|
if (!DecryptInternal(reinterpret_cast<const uint8_t*>(ciphertext.data()),
|
||||||
|
ciphertext.size(),
|
||||||
|
reinterpret_cast<uint8_t*>(string_as_array(plaintext)),
|
||||||
|
&plaintext_size))
|
||||||
|
return false;
|
||||||
|
plaintext->resize(plaintext_size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
AesCtrDecryptor::AesCtrDecryptor() {}
|
AesCtrDecryptor::AesCtrDecryptor() {}
|
||||||
|
|
||||||
AesCtrDecryptor::~AesCtrDecryptor() {}
|
AesCtrDecryptor::~AesCtrDecryptor() {}
|
||||||
|
@ -37,163 +63,119 @@ bool AesCtrDecryptor::InitializeWithIv(const std::vector<uint8_t>& key,
|
||||||
return encryptor_->InitializeWithIv(key, iv);
|
return encryptor_->InitializeWithIv(key, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For AES CTR, encryption and decryption are identical.
|
|
||||||
bool AesCtrDecryptor::Decrypt(const uint8_t* ciphertext,
|
|
||||||
size_t ciphertext_size,
|
|
||||||
uint8_t* plaintext) {
|
|
||||||
DCHECK(encryptor_);
|
|
||||||
return encryptor_->EncryptData(ciphertext, ciphertext_size, plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AesCtrDecryptor::Decrypt(const std::vector<uint8_t>& ciphertext,
|
|
||||||
std::vector<uint8_t>* plaintext) {
|
|
||||||
DCHECK(encryptor_);
|
|
||||||
return encryptor_->Encrypt(ciphertext, plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AesCtrDecryptor::Decrypt(const std::string& ciphertext,
|
|
||||||
std::string* plaintext) {
|
|
||||||
DCHECK(encryptor_);
|
|
||||||
return encryptor_->Encrypt(ciphertext, plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AesCtrDecryptor::SetIv(const std::vector<uint8_t>& iv) {
|
bool AesCtrDecryptor::SetIv(const std::vector<uint8_t>& iv) {
|
||||||
DCHECK(encryptor_);
|
DCHECK(encryptor_);
|
||||||
return encryptor_->SetIv(iv);
|
return encryptor_->SetIv(iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
AesCbcPkcs5Decryptor::AesCbcPkcs5Decryptor() {}
|
bool AesCtrDecryptor::DecryptInternal(const uint8_t* ciphertext,
|
||||||
AesCbcPkcs5Decryptor::~AesCbcPkcs5Decryptor() {}
|
size_t ciphertext_size,
|
||||||
|
uint8_t* plaintext,
|
||||||
|
size_t* plaintext_size) {
|
||||||
|
DCHECK(encryptor_);
|
||||||
|
*plaintext_size = ciphertext_size;
|
||||||
|
// For AES CTR, encryption and decryption are identical.
|
||||||
|
return encryptor_->Encrypt(ciphertext, ciphertext_size, plaintext);
|
||||||
|
}
|
||||||
|
|
||||||
bool AesCbcPkcs5Decryptor::InitializeWithIv(const std::vector<uint8_t>& key,
|
AesCbcDecryptor::AesCbcDecryptor(CbcPaddingScheme padding_scheme,
|
||||||
|
bool chain_across_calls)
|
||||||
|
: padding_scheme_(padding_scheme),
|
||||||
|
chain_across_calls_(chain_across_calls) {
|
||||||
|
if (padding_scheme_ != kNoPadding) {
|
||||||
|
CHECK(!chain_across_calls) << "cipher block chain across calls only makes "
|
||||||
|
"sense if the padding_scheme is kNoPadding.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AesCbcDecryptor::~AesCbcDecryptor() {}
|
||||||
|
|
||||||
|
bool AesCbcDecryptor::InitializeWithIv(const std::vector<uint8_t>& key,
|
||||||
const std::vector<uint8_t>& iv) {
|
const std::vector<uint8_t>& 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();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aes_key_.reset(new AES_KEY());
|
||||||
|
CHECK_EQ(AES_set_decrypt_key(key.data(), key.size() * 8, aes_key_.get()), 0);
|
||||||
|
|
||||||
|
return SetIv(iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AesCbcDecryptor::SetIv(const std::vector<uint8_t>& 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
aes_key_.reset(new AES_KEY());
|
|
||||||
CHECK_EQ(AES_set_decrypt_key(&key[0], key.size() * 8, aes_key_.get()), 0);
|
|
||||||
|
|
||||||
iv_ = iv;
|
iv_ = iv;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AesCbcPkcs5Decryptor::Decrypt(const uint8_t* ciphertext,
|
bool AesCbcDecryptor::DecryptInternal(const uint8_t* ciphertext,
|
||||||
size_t ciphertext_size,
|
size_t ciphertext_size,
|
||||||
uint8_t* plaintext) {
|
uint8_t* plaintext,
|
||||||
NOTIMPLEMENTED();
|
size_t* plaintext_size) {
|
||||||
return false;
|
DCHECK(plaintext_size);
|
||||||
}
|
DCHECK(aes_key_);
|
||||||
|
// Plaintext size is the same as ciphertext size except for pkcs5 padding.
|
||||||
bool AesCbcPkcs5Decryptor::Decrypt(const std::vector<uint8_t>& ciphertext,
|
// Will update later if using pkcs5 padding.
|
||||||
std::vector<uint8_t>* plaintext) {
|
*plaintext_size = ciphertext_size;
|
||||||
NOTIMPLEMENTED();
|
if (ciphertext_size == 0) {
|
||||||
return false;
|
if (padding_scheme_ == kPkcs5Padding) {
|
||||||
}
|
LOG(ERROR) << "Expected ciphertext to be at least " << AES_BLOCK_SIZE
|
||||||
|
<< " bytes with Pkcs5 padding";
|
||||||
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();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
DCHECK(plaintext);
|
DCHECK(plaintext);
|
||||||
DCHECK(aes_key_);
|
|
||||||
|
|
||||||
plaintext->resize(ciphertext.size());
|
std::vector<uint8_t> local_iv(iv_);
|
||||||
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(ciphertext.data()),
|
const size_t residual_block_size = ciphertext_size % AES_BLOCK_SIZE;
|
||||||
reinterpret_cast<uint8_t*>(string_as_array(plaintext)),
|
if (residual_block_size == 0) {
|
||||||
ciphertext.size(),
|
AES_cbc_encrypt(ciphertext, plaintext, ciphertext_size, aes_key_.get(),
|
||||||
aes_key_.get(),
|
local_iv.data(), AES_DECRYPT);
|
||||||
&iv_[0],
|
if (chain_across_calls_)
|
||||||
AES_DECRYPT);
|
iv_ = local_iv;
|
||||||
|
if (padding_scheme_ != kPkcs5Padding)
|
||||||
|
return true;
|
||||||
|
|
||||||
// Strip off PKCS5 padding bytes.
|
// Strip off PKCS5 padding bytes.
|
||||||
const uint8_t num_padding_bytes = (*plaintext)[plaintext->size() - 1];
|
const uint8_t num_padding_bytes = plaintext[ciphertext_size - 1];
|
||||||
if (num_padding_bytes > AES_BLOCK_SIZE) {
|
if (num_padding_bytes > AES_BLOCK_SIZE) {
|
||||||
LOG(ERROR) << "Padding length is too large : "
|
LOG(ERROR) << "Padding length is too large : "
|
||||||
<< static_cast<int>(num_padding_bytes);
|
<< static_cast<int>(num_padding_bytes);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
plaintext->resize(plaintext->size() - num_padding_bytes);
|
*plaintext_size -= num_padding_bytes;
|
||||||
return true;
|
return true;
|
||||||
}
|
} else if (padding_scheme_ != kCtsPadding) {
|
||||||
|
LOG(ERROR) << "Expecting cipher text size to be multiple of "
|
||||||
bool AesCbcPkcs5Decryptor::SetIv(const std::vector<uint8_t>& iv) {
|
<< AES_BLOCK_SIZE << ", got " << ciphertext_size;
|
||||||
if (iv.size() != AES_BLOCK_SIZE) {
|
|
||||||
LOG(ERROR) << "Invalid IV size: " << iv.size();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
iv_ = iv;
|
DCHECK(!chain_across_calls_);
|
||||||
return true;
|
DCHECK_EQ(padding_scheme_, kCtsPadding);
|
||||||
}
|
|
||||||
|
|
||||||
AesCbcCtsDecryptor::AesCbcCtsDecryptor() {}
|
|
||||||
AesCbcCtsDecryptor::~AesCbcCtsDecryptor() {}
|
|
||||||
|
|
||||||
bool AesCbcCtsDecryptor::InitializeWithIv(const std::vector<uint8_t>& key,
|
|
||||||
const std::vector<uint8_t>& 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
aes_key_.reset(new AES_KEY());
|
|
||||||
CHECK_EQ(AES_set_decrypt_key(&key[0], key.size() * 8, aes_key_.get()), 0);
|
|
||||||
|
|
||||||
iv_ = iv;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AesCbcCtsDecryptor::Decrypt(const uint8_t* ciphertext,
|
|
||||||
size_t ciphertext_size,
|
|
||||||
uint8_t* plaintext) {
|
|
||||||
DCHECK(ciphertext);
|
|
||||||
DCHECK(plaintext);
|
|
||||||
|
|
||||||
if (ciphertext_size < AES_BLOCK_SIZE) {
|
if (ciphertext_size < AES_BLOCK_SIZE) {
|
||||||
// Don't have a full block, leave unencrypted.
|
// Don't have a full block, leave unencrypted.
|
||||||
memcpy(plaintext, ciphertext, ciphertext_size);
|
memcpy(plaintext, ciphertext, ciphertext_size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> iv(iv_);
|
|
||||||
size_t residual_block_size = ciphertext_size % AES_BLOCK_SIZE;
|
|
||||||
|
|
||||||
if (residual_block_size == 0) {
|
|
||||||
// No residual block. No need to do ciphertext stealing.
|
|
||||||
AES_cbc_encrypt(ciphertext,
|
|
||||||
plaintext,
|
|
||||||
ciphertext_size,
|
|
||||||
aes_key_.get(),
|
|
||||||
&iv[0],
|
|
||||||
AES_DECRYPT);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// AES-CBC decrypt everything up to the next-to-last full block.
|
// AES-CBC decrypt everything up to the next-to-last full block.
|
||||||
size_t cbc_size = ciphertext_size - residual_block_size;
|
const size_t cbc_size = ciphertext_size - residual_block_size;
|
||||||
if (cbc_size > AES_BLOCK_SIZE) {
|
if (cbc_size > AES_BLOCK_SIZE) {
|
||||||
AES_cbc_encrypt(ciphertext,
|
AES_cbc_encrypt(ciphertext, plaintext, cbc_size - AES_BLOCK_SIZE,
|
||||||
plaintext,
|
aes_key_.get(), local_iv.data(), AES_DECRYPT);
|
||||||
cbc_size - AES_BLOCK_SIZE,
|
|
||||||
aes_key_.get(),
|
|
||||||
&iv[0],
|
|
||||||
AES_DECRYPT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint8_t* next_to_last_ciphertext_block =
|
||||||
|
ciphertext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE;
|
||||||
|
uint8_t* next_to_last_plaintext_block =
|
||||||
|
plaintext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE;
|
||||||
|
|
||||||
// Determine what the last IV should be so that we can "skip ahead" in the
|
// Determine what the last IV should be so that we can "skip ahead" in the
|
||||||
// CBC decryption.
|
// CBC decryption.
|
||||||
std::vector<uint8_t> last_iv(
|
std::vector<uint8_t> last_iv(
|
||||||
|
@ -203,64 +185,24 @@ bool AesCbcCtsDecryptor::Decrypt(const uint8_t* ciphertext,
|
||||||
|
|
||||||
// Decrypt the next-to-last block using the IV determined above. This decrypts
|
// Decrypt the next-to-last block using the IV determined above. This decrypts
|
||||||
// the residual block bits.
|
// the residual block bits.
|
||||||
AES_cbc_encrypt(
|
AES_cbc_encrypt(next_to_last_ciphertext_block, next_to_last_plaintext_block,
|
||||||
ciphertext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE,
|
AES_BLOCK_SIZE, aes_key_.get(), last_iv.data(), AES_DECRYPT);
|
||||||
plaintext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE,
|
|
||||||
AES_BLOCK_SIZE, aes_key_.get(), &last_iv[0], AES_DECRYPT);
|
|
||||||
|
|
||||||
// Swap back the residual block bits and the next-to-last full block.
|
// Swap back the residual block bits and the next-to-last block.
|
||||||
if (plaintext == ciphertext) {
|
if (plaintext == ciphertext) {
|
||||||
uint8_t* ptr1 = plaintext + ciphertext_size - residual_block_size;
|
std::swap_ranges(next_to_last_plaintext_block,
|
||||||
uint8_t* ptr2 = plaintext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE;
|
next_to_last_plaintext_block + residual_block_size,
|
||||||
for (size_t i = 0; i < residual_block_size; ++i) {
|
next_to_last_plaintext_block + AES_BLOCK_SIZE);
|
||||||
uint8_t temp = *ptr1;
|
|
||||||
*ptr1 = *ptr2;
|
|
||||||
*ptr2 = temp;
|
|
||||||
++ptr1;
|
|
||||||
++ptr2;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
uint8_t* residual_plaintext_block =
|
memcpy(next_to_last_plaintext_block + AES_BLOCK_SIZE,
|
||||||
plaintext + ciphertext_size - residual_block_size;
|
next_to_last_plaintext_block, residual_block_size);
|
||||||
memcpy(residual_plaintext_block, residual_plaintext_block - AES_BLOCK_SIZE,
|
memcpy(next_to_last_plaintext_block,
|
||||||
residual_block_size);
|
next_to_last_ciphertext_block + AES_BLOCK_SIZE, residual_block_size);
|
||||||
memcpy(residual_plaintext_block - AES_BLOCK_SIZE,
|
|
||||||
ciphertext + ciphertext_size - residual_block_size,
|
|
||||||
residual_block_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt the last full block.
|
// Decrypt the next-to-last full block.
|
||||||
AES_cbc_encrypt(
|
AES_cbc_encrypt(next_to_last_plaintext_block, next_to_last_plaintext_block,
|
||||||
plaintext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE,
|
AES_BLOCK_SIZE, aes_key_.get(), local_iv.data(), AES_DECRYPT);
|
||||||
plaintext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE,
|
|
||||||
AES_BLOCK_SIZE, aes_key_.get(), &iv[0], AES_DECRYPT);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AesCbcCtsDecryptor::Decrypt(const std::vector<uint8_t>& ciphertext,
|
|
||||||
std::vector<uint8_t>* plaintext) {
|
|
||||||
DCHECK(plaintext);
|
|
||||||
|
|
||||||
plaintext->resize(ciphertext.size(), 0);
|
|
||||||
if (ciphertext.empty())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return Decrypt(ciphertext.data(), ciphertext.size(), &(*plaintext)[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AesCbcCtsDecryptor::Decrypt(const std::string& ciphertext,
|
|
||||||
std::string* plaintext) {
|
|
||||||
NOTIMPLEMENTED();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AesCbcCtsDecryptor::SetIv(const std::vector<uint8_t>& iv) {
|
|
||||||
if (iv.size() != AES_BLOCK_SIZE) {
|
|
||||||
LOG(ERROR) << "Invalid IV size: " << iv.size();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
iv_ = iv;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,34 +27,42 @@ class AesDecryptor {
|
||||||
AesDecryptor();
|
AesDecryptor();
|
||||||
virtual ~AesDecryptor();
|
virtual ~AesDecryptor();
|
||||||
|
|
||||||
|
/// Initialize the decryptor with specified key and IV.
|
||||||
|
/// @return true on successful initialization, false otherwise.
|
||||||
virtual bool InitializeWithIv(const std::vector<uint8_t>& key,
|
virtual bool InitializeWithIv(const std::vector<uint8_t>& key,
|
||||||
const std::vector<uint8_t>& iv) = 0;
|
const std::vector<uint8_t>& iv) = 0;
|
||||||
|
|
||||||
/// @name Various forms of decrypt calls.
|
/// @name Various forms of decrypt calls.
|
||||||
/// The plaintext and ciphertext pointers can be the same address.
|
/// The plaintext and ciphertext pointers can be the same address.
|
||||||
/// @{
|
/// @{
|
||||||
virtual bool Decrypt(const uint8_t* ciphertext,
|
bool Decrypt(const std::vector<uint8_t>& ciphertext,
|
||||||
|
std::vector<uint8_t>* plaintext);
|
||||||
|
bool Decrypt(const std::string& ciphertext, std::string* plaintext);
|
||||||
|
bool Decrypt(const uint8_t* ciphertext,
|
||||||
size_t ciphertext_size,
|
size_t ciphertext_size,
|
||||||
uint8_t* plaintext) = 0;
|
uint8_t* plaintext) {
|
||||||
|
size_t plaintext_size;
|
||||||
virtual bool Decrypt(const std::vector<uint8_t>& ciphertext,
|
return DecryptInternal(ciphertext, ciphertext_size, plaintext,
|
||||||
std::vector<uint8_t>* plaintext) = 0;
|
&plaintext_size);
|
||||||
|
}
|
||||||
virtual bool Decrypt(const std::string& ciphertext,
|
|
||||||
std::string* plaintext) = 0;
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// Set IV. @a block_offset_ is reset to 0 on success.
|
/// Set IV.
|
||||||
/// @return true if successful, false if the input is invalid.
|
/// @return true if successful, false if the input is invalid.
|
||||||
virtual bool SetIv(const std::vector<uint8_t>& iv) = 0;
|
virtual bool SetIv(const std::vector<uint8_t>& iv) = 0;
|
||||||
|
|
||||||
const std::vector<uint8_t>& iv() const { return iv_; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Initialization vector, with size 8 or 16.
|
/// Internal implementation of decrypt function.
|
||||||
std::vector<uint8_t> iv_;
|
/// @param ciphertext points to the input ciphertext.
|
||||||
// Openssl AES_KEY.
|
/// @param ciphertext_size is the input ciphertext size.
|
||||||
scoped_ptr<AES_KEY> aes_key_;
|
/// @param[out] plaintext points to the output plaintext. @a plaintext and
|
||||||
|
/// @a ciphertext can point to the same address.
|
||||||
|
/// @param[out] plaintext_size contains the size of plaintext on success.
|
||||||
|
/// It should never be larger than @a ciphertext_size.
|
||||||
|
virtual bool DecryptInternal(const uint8_t* ciphertext,
|
||||||
|
size_t ciphertext_size,
|
||||||
|
uint8_t* plaintext,
|
||||||
|
size_t* plaintext_size) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_COPY_AND_ASSIGN(AesDecryptor);
|
DISALLOW_COPY_AND_ASSIGN(AesDecryptor);
|
||||||
|
@ -71,80 +79,57 @@ class AesCtrDecryptor : public AesDecryptor {
|
||||||
bool InitializeWithIv(const std::vector<uint8_t>& key,
|
bool InitializeWithIv(const std::vector<uint8_t>& key,
|
||||||
const std::vector<uint8_t>& iv) override;
|
const std::vector<uint8_t>& iv) override;
|
||||||
|
|
||||||
bool Decrypt(const uint8_t* ciphertext,
|
|
||||||
size_t ciphertext_size,
|
|
||||||
uint8_t* plaintext) override;
|
|
||||||
|
|
||||||
bool Decrypt(const std::vector<uint8_t>& ciphertext,
|
|
||||||
std::vector<uint8_t>* plaintext) override;
|
|
||||||
|
|
||||||
bool Decrypt(const std::string& ciphertext, std::string* plaintext) override;
|
|
||||||
|
|
||||||
bool SetIv(const std::vector<uint8_t>& iv) override;
|
bool SetIv(const std::vector<uint8_t>& iv) override;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
uint32_t block_offset() const { return encryptor_->block_offset(); }
|
uint32_t block_offset() const { return encryptor_->block_offset(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool DecryptInternal(const uint8_t* ciphertext,
|
||||||
|
size_t ciphertext_size,
|
||||||
|
uint8_t* plaintext,
|
||||||
|
size_t* plaintext_size) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
scoped_ptr<AesCtrEncryptor> encryptor_;
|
scoped_ptr<AesCtrEncryptor> encryptor_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(AesCtrDecryptor);
|
DISALLOW_COPY_AND_ASSIGN(AesCtrDecryptor);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Class which implements AES-CBC (Cipher block chaining) decryption with
|
// Class which implements AES-CBC (Cipher block chaining) decryption.
|
||||||
// PKCS#5 padding.
|
class AesCbcDecryptor : public AesDecryptor {
|
||||||
class AesCbcPkcs5Decryptor : public AesDecryptor {
|
|
||||||
public:
|
public:
|
||||||
AesCbcPkcs5Decryptor();
|
/// @param padding_scheme indicates the padding scheme used. Currently
|
||||||
~AesCbcPkcs5Decryptor() override;
|
/// supported schemes: kNoPadding, kPkcs5Padding, kCtsPadding.
|
||||||
|
/// @param chain_across_calls indicates whether there is a continuous cipher
|
||||||
|
/// block chain across calls for Decrypt function. If it is false, iv
|
||||||
|
/// is not updated across Decrypt function calls.
|
||||||
|
AesCbcDecryptor(CbcPaddingScheme padding_scheme, bool chain_across_calls);
|
||||||
|
~AesCbcDecryptor() override;
|
||||||
|
|
||||||
/// @name AesDecryptor implementation overrides.
|
/// @name AesDecryptor implementation overrides.
|
||||||
/// @{
|
/// @{
|
||||||
bool InitializeWithIv(const std::vector<uint8_t>& key,
|
bool InitializeWithIv(const std::vector<uint8_t>& key,
|
||||||
const std::vector<uint8_t>& iv) override;
|
const std::vector<uint8_t>& iv) override;
|
||||||
|
|
||||||
bool Decrypt(const uint8_t* ciphertext,
|
|
||||||
size_t ciphertext_size,
|
|
||||||
uint8_t* plaintext) override;
|
|
||||||
|
|
||||||
bool Decrypt(const std::vector<uint8_t>& ciphertext,
|
|
||||||
std::vector<uint8_t>* plaintext) override;
|
|
||||||
|
|
||||||
bool Decrypt(const std::string& ciphertext, std::string* plaintext) override;
|
|
||||||
|
|
||||||
bool SetIv(const std::vector<uint8_t>& iv) override;
|
bool SetIv(const std::vector<uint8_t>& iv) override;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
DISALLOW_COPY_AND_ASSIGN(AesCbcPkcs5Decryptor);
|
bool DecryptInternal(const uint8_t* ciphertext,
|
||||||
};
|
|
||||||
|
|
||||||
// Class which implements AES-CBC (Cipher block chaining) decryption with
|
|
||||||
// Ciphertext stealing.
|
|
||||||
class AesCbcCtsDecryptor : public AesDecryptor {
|
|
||||||
public:
|
|
||||||
AesCbcCtsDecryptor();
|
|
||||||
~AesCbcCtsDecryptor() override;
|
|
||||||
|
|
||||||
/// @name AesDecryptor implementation overrides.
|
|
||||||
/// @{
|
|
||||||
bool InitializeWithIv(const std::vector<uint8_t>& key,
|
|
||||||
const std::vector<uint8_t>& iv) override;
|
|
||||||
|
|
||||||
bool Decrypt(const uint8_t* ciphertext,
|
|
||||||
size_t ciphertext_size,
|
size_t ciphertext_size,
|
||||||
uint8_t* plaintext) override;
|
uint8_t* plaintext,
|
||||||
|
size_t* plaintext_size) override;
|
||||||
bool Decrypt(const std::vector<uint8_t>& ciphertext,
|
|
||||||
std::vector<uint8_t>* plaintext) override;
|
|
||||||
|
|
||||||
bool Decrypt(const std::string& ciphertext, std::string* plaintext) override;
|
|
||||||
|
|
||||||
bool SetIv(const std::vector<uint8_t>& iv) override;
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_COPY_AND_ASSIGN(AesCbcCtsDecryptor);
|
// Openssl AES_KEY.
|
||||||
|
scoped_ptr<AES_KEY> aes_key_;
|
||||||
|
// Initialization vector, must be 16 for CBC.
|
||||||
|
std::vector<uint8_t> iv_;
|
||||||
|
const CbcPaddingScheme padding_scheme_;
|
||||||
|
const bool chain_across_calls_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(AesCbcDecryptor);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
|
@ -32,9 +32,6 @@ bool IsKeySizeValidForAes(size_t key_size) {
|
||||||
return key_size == 16 || key_size == 24 || key_size == 32;
|
return key_size == 16 || key_size == 24 || key_size == 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CENC protection scheme uses 128-bit keys in counter mode.
|
|
||||||
const uint32_t kCencKeySize = 16;
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
|
@ -47,7 +44,7 @@ bool AesEncryptor::InitializeWithRandomIv(
|
||||||
const std::vector<uint8_t>& key,
|
const std::vector<uint8_t>& key,
|
||||||
uint8_t iv_size) {
|
uint8_t iv_size) {
|
||||||
std::vector<uint8_t> iv(iv_size, 0);
|
std::vector<uint8_t> iv(iv_size, 0);
|
||||||
if (RAND_bytes(&iv[0], iv_size) != 1) {
|
if (RAND_bytes(iv.data(), iv_size) != 1) {
|
||||||
LOG(ERROR) << "RAND_bytes failed with error: "
|
LOG(ERROR) << "RAND_bytes failed with error: "
|
||||||
<< ERR_error_string(ERR_get_error(), NULL);
|
<< ERR_error_string(ERR_get_error(), NULL);
|
||||||
return false;
|
return false;
|
||||||
|
@ -55,62 +52,98 @@ bool AesEncryptor::InitializeWithRandomIv(
|
||||||
return InitializeWithIv(key, iv);
|
return InitializeWithIv(key, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AesEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
|
||||||
|
const std::vector<uint8_t>& iv) {
|
||||||
|
if (!IsKeySizeValidForAes(key.size())) {
|
||||||
|
LOG(ERROR) << "Invalid AES key size: " << key.size();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
aes_key_.reset(new AES_KEY());
|
||||||
|
CHECK_EQ(AES_set_encrypt_key(key.data(), key.size() * 8, aes_key_.get()), 0);
|
||||||
|
return SetIv(iv);
|
||||||
|
}
|
||||||
|
|
||||||
bool AesEncryptor::Encrypt(const std::vector<uint8_t>& plaintext,
|
bool AesEncryptor::Encrypt(const std::vector<uint8_t>& plaintext,
|
||||||
std::vector<uint8_t>* ciphertext) {
|
std::vector<uint8_t>* ciphertext) {
|
||||||
if (plaintext.empty())
|
// Save plaintext size to make it work for in-place conversion, since the
|
||||||
return true;
|
// next statement will update the plaintext size.
|
||||||
ciphertext->resize(plaintext.size() + NumPaddingBytes(plaintext.size()));
|
const size_t plaintext_size = plaintext.size();
|
||||||
return EncryptData(plaintext.data(), plaintext.size(), ciphertext->data());
|
ciphertext->resize(plaintext_size + NumPaddingBytes(plaintext.size()));
|
||||||
|
return EncryptInternal(plaintext.data(), plaintext_size, ciphertext->data());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AesEncryptor::Encrypt(const std::string& plaintext,
|
bool AesEncryptor::Encrypt(const std::string& plaintext,
|
||||||
std::string* ciphertext) {
|
std::string* ciphertext) {
|
||||||
ciphertext->resize(plaintext.size() + NumPaddingBytes(plaintext.size()));
|
// Save plaintext size to make it work for in-place conversion, since the
|
||||||
return EncryptData(reinterpret_cast<const uint8_t*>(plaintext.data()),
|
// next statement will update the plaintext size.
|
||||||
plaintext.size(),
|
const size_t plaintext_size = plaintext.size();
|
||||||
|
ciphertext->resize(plaintext_size + NumPaddingBytes(plaintext.size()));
|
||||||
|
return EncryptInternal(
|
||||||
|
reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext_size,
|
||||||
reinterpret_cast<uint8_t*>(string_as_array(ciphertext)));
|
reinterpret_cast<uint8_t*>(string_as_array(ciphertext)));
|
||||||
}
|
}
|
||||||
|
|
||||||
AesCtrEncryptor::AesCtrEncryptor()
|
AesCtrEncryptor::AesCtrEncryptor()
|
||||||
: block_offset_(0),
|
: block_offset_(0),
|
||||||
encrypted_counter_(AES_BLOCK_SIZE, 0),
|
encrypted_counter_(AES_BLOCK_SIZE, 0),
|
||||||
counter_overflow_(false) {
|
counter_overflow_(false) {}
|
||||||
COMPILE_ASSERT(AES_BLOCK_SIZE == kCencKeySize,
|
|
||||||
cenc_key_size_should_be_the_same_as_aes_block_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
AesCtrEncryptor::~AesCtrEncryptor() {}
|
AesCtrEncryptor::~AesCtrEncryptor() {}
|
||||||
|
|
||||||
bool AesCtrEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
|
void AesCtrEncryptor::UpdateIv() {
|
||||||
const std::vector<uint8_t>& iv) {
|
block_offset_ = 0;
|
||||||
if (key.size() != kCencKeySize) {
|
|
||||||
LOG(ERROR) << "Invalid key size of " << key.size() << " for CENC.";
|
// As recommended in ISO/IEC FDIS 23001-7: CENC spec, for 64-bit (8-byte)
|
||||||
return false;
|
// IV_Sizes, initialization vectors for subsequent samples can be created by
|
||||||
|
// incrementing the initialization vector of the previous sample.
|
||||||
|
// For 128-bit (16-byte) IV_Sizes, initialization vectors for subsequent
|
||||||
|
// samples should be created by adding the block count of the previous sample
|
||||||
|
// to the initialization vector of the previous sample.
|
||||||
|
if (iv().size() == 8) {
|
||||||
|
counter_ = iv();
|
||||||
|
Increment64(&counter_[0]);
|
||||||
|
set_iv(counter_);
|
||||||
|
counter_.resize(AES_BLOCK_SIZE, 0);
|
||||||
|
} else {
|
||||||
|
DCHECK_EQ(16u, iv().size());
|
||||||
|
// Even though the block counter portion of the counter (bytes 8 to 15) is
|
||||||
|
// treated as a 64-bit number, it is recommended that the initialization
|
||||||
|
// vector is treated as a 128-bit number when calculating the next
|
||||||
|
// initialization vector from the previous one. The block counter portion
|
||||||
|
// is already incremented by number of blocks, the other 64 bits of the
|
||||||
|
// counter (bytes 0 to 7) is incremented here if the block counter portion
|
||||||
|
// has overflowed.
|
||||||
|
if (counter_overflow_)
|
||||||
|
Increment64(&counter_[0]);
|
||||||
|
set_iv(counter_);
|
||||||
}
|
}
|
||||||
|
counter_overflow_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AesCtrEncryptor::SetIv(const std::vector<uint8_t>& iv) {
|
||||||
if (!IsIvSizeValid(iv.size())) {
|
if (!IsIvSizeValid(iv.size())) {
|
||||||
LOG(ERROR) << "Invalid IV size: " << iv.size();
|
LOG(ERROR) << "Invalid IV size: " << iv.size();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
aes_key_.reset(new AES_KEY());
|
block_offset_ = 0;
|
||||||
CHECK_EQ(AES_set_encrypt_key(&key[0], AES_BLOCK_SIZE * 8, aes_key_.get()), 0);
|
set_iv(iv);
|
||||||
return SetIv(iv);
|
counter_ = iv;
|
||||||
|
counter_.resize(AES_BLOCK_SIZE, 0);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AesCtrEncryptor::NumPaddingBytes(size_t size) {
|
bool AesCtrEncryptor::EncryptInternal(const uint8_t* plaintext,
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AesCtrEncryptor::EncryptData(const uint8_t* plaintext,
|
|
||||||
size_t plaintext_size,
|
size_t plaintext_size,
|
||||||
uint8_t* ciphertext) {
|
uint8_t* ciphertext) {
|
||||||
DCHECK(plaintext);
|
DCHECK(plaintext);
|
||||||
DCHECK(ciphertext);
|
DCHECK(ciphertext);
|
||||||
DCHECK(aes_key_);
|
DCHECK(aes_key());
|
||||||
|
|
||||||
for (size_t i = 0; i < plaintext_size; ++i) {
|
for (size_t i = 0; i < plaintext_size; ++i) {
|
||||||
if (block_offset_ == 0) {
|
if (block_offset_ == 0) {
|
||||||
AES_encrypt(&counter_[0], &encrypted_counter_[0], aes_key_.get());
|
AES_encrypt(&counter_[0], &encrypted_counter_[0], aes_key());
|
||||||
// As mentioned in ISO/IEC FDIS 23001-7: CENC spec, of the 16 byte counter
|
// As mentioned in ISO/IEC FDIS 23001-7: CENC spec, of the 16 byte counter
|
||||||
// block, bytes 8 to 15 (i.e. the least significant bytes) are used as a
|
// block, bytes 8 to 15 (i.e. the least significant bytes) are used as a
|
||||||
// simple 64 bit unsigned integer that is incremented by one for each
|
// simple 64 bit unsigned integer that is incremented by one for each
|
||||||
|
@ -125,195 +158,111 @@ bool AesCtrEncryptor::EncryptData(const uint8_t* plaintext,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AesCtrEncryptor::UpdateIv() {
|
size_t AesCtrEncryptor::NumPaddingBytes(size_t size) const {
|
||||||
block_offset_ = 0;
|
// No padding needed for CTR.
|
||||||
|
|
||||||
// As recommended in ISO/IEC FDIS 23001-7: CENC spec, for 64-bit (8-byte)
|
|
||||||
// IV_Sizes, initialization vectors for subsequent samples can be created by
|
|
||||||
// incrementing the initialization vector of the previous sample.
|
|
||||||
// For 128-bit (16-byte) IV_Sizes, initialization vectors for subsequent
|
|
||||||
// samples should be created by adding the block count of the previous sample
|
|
||||||
// to the initialization vector of the previous sample.
|
|
||||||
if (iv_.size() == 8) {
|
|
||||||
Increment64(&iv_[0]);
|
|
||||||
counter_ = iv_;
|
|
||||||
counter_.resize(AES_BLOCK_SIZE, 0);
|
|
||||||
} else {
|
|
||||||
DCHECK_EQ(16u, iv_.size());
|
|
||||||
// Even though the block counter portion of the counter (bytes 8 to 15) is
|
|
||||||
// treated as a 64-bit number, it is recommended that the initialization
|
|
||||||
// vector is treated as a 128-bit number when calculating the next
|
|
||||||
// initialization vector from the previous one. The block counter portion
|
|
||||||
// is already incremented by number of blocks, the other 64 bits of the
|
|
||||||
// counter (bytes 0 to 7) is incremented here if the block counter portion
|
|
||||||
// has overflowed.
|
|
||||||
if (counter_overflow_)
|
|
||||||
Increment64(&counter_[0]);
|
|
||||||
iv_ = counter_;
|
|
||||||
}
|
|
||||||
counter_overflow_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AesCtrEncryptor::SetIv(const std::vector<uint8_t>& iv) {
|
|
||||||
if (!IsIvSizeValid(iv.size())) {
|
|
||||||
LOG(ERROR) << "Invalid IV size: " << iv.size();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
block_offset_ = 0;
|
|
||||||
counter_ = iv_ = iv;
|
|
||||||
counter_.resize(AES_BLOCK_SIZE, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
AesCbcPkcs5Encryptor::AesCbcPkcs5Encryptor() {}
|
|
||||||
AesCbcPkcs5Encryptor::~AesCbcPkcs5Encryptor() {}
|
|
||||||
|
|
||||||
bool AesCbcPkcs5Encryptor::InitializeWithIv(const std::vector<uint8_t>& key,
|
|
||||||
const std::vector<uint8_t>& 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
aes_key_.reset(new AES_KEY());
|
|
||||||
CHECK_EQ(AES_set_encrypt_key(&key[0], key.size() * 8, aes_key_.get()), 0);
|
|
||||||
|
|
||||||
iv_ = iv;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AesCbcPkcs5Encryptor::NumPaddingBytes(size_t size) {
|
|
||||||
return AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AesCbcPkcs5Encryptor::EncryptData(const uint8_t* plaintext,
|
|
||||||
size_t plaintext_size,
|
|
||||||
uint8_t* ciphertext) {
|
|
||||||
DCHECK(ciphertext);
|
|
||||||
DCHECK(aes_key_);
|
|
||||||
|
|
||||||
// Pad the input with PKCS5 padding.
|
|
||||||
// TODO(kqyang): Consider more efficient implementation.
|
|
||||||
memcpy(ciphertext, plaintext, plaintext_size);
|
|
||||||
for (size_t i = plaintext_size;
|
|
||||||
i < plaintext_size + NumPaddingBytes(plaintext_size); ++i) {
|
|
||||||
ciphertext[i] = NumPaddingBytes(plaintext_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> iv(iv_);
|
|
||||||
AES_cbc_encrypt(ciphertext, ciphertext,
|
|
||||||
plaintext_size + NumPaddingBytes(plaintext_size),
|
|
||||||
aes_key_.get(), &iv[0], AES_ENCRYPT);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AesCbcPkcs5Encryptor::UpdateIv() {}
|
|
||||||
|
|
||||||
bool AesCbcPkcs5Encryptor::SetIv(const std::vector<uint8_t>& 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_t>& key,
|
|
||||||
const std::vector<uint8_t>& 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
aes_key_.reset(new AES_KEY());
|
|
||||||
CHECK_EQ(AES_set_encrypt_key(&key[0], key.size() * 8, aes_key_.get()), 0);
|
|
||||||
|
|
||||||
iv_ = iv;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AesCbcCtsEncryptor::NumPaddingBytes(size_t size) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AesCbcCtsEncryptor::EncryptData(const uint8_t* plaintext,
|
AesCbcEncryptor::AesCbcEncryptor(CbcPaddingScheme padding_scheme,
|
||||||
size_t size,
|
bool chain_across_calls)
|
||||||
uint8_t* ciphertext) {
|
: padding_scheme_(padding_scheme),
|
||||||
DCHECK(plaintext);
|
chain_across_calls_(chain_across_calls) {
|
||||||
DCHECK(ciphertext);
|
if (padding_scheme_ != kNoPadding) {
|
||||||
|
CHECK(!chain_across_calls) << "cipher block chain across calls only makes "
|
||||||
if (size < AES_BLOCK_SIZE) {
|
"sense if the padding_scheme is kNoPadding.";
|
||||||
// Don't have a full block, leave unencrypted.
|
|
||||||
memcpy(ciphertext, plaintext, size);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
AesCbcEncryptor::~AesCbcEncryptor() {}
|
||||||
|
|
||||||
std::vector<uint8_t> iv(iv_);
|
void AesCbcEncryptor::UpdateIv() {
|
||||||
size_t residual_block_size = size % AES_BLOCK_SIZE;
|
// From CENC spec: CBC mode Initialization Vectors need not be unique per
|
||||||
size_t cbc_size = size - residual_block_size;
|
// sample or Subsample and may be generated randomly or sequentially, e.g.
|
||||||
|
// a per sample IV may be (1) equal to the cipher text of the last encrypted
|
||||||
// Encrypt everything but the residual block using CBC.
|
// cipher block (a continous cipher block chain across samples), or (2)
|
||||||
AES_cbc_encrypt(plaintext,
|
// generated by incrementing the previuos IV by the number of cipher blocks in the last
|
||||||
ciphertext,
|
// sample or (3) by a fixed amount. We use method (1) here. No separate IV
|
||||||
cbc_size,
|
// update is needed.
|
||||||
aes_key_.get(),
|
|
||||||
&iv[0],
|
|
||||||
AES_ENCRYPT);
|
|
||||||
if (residual_block_size == 0) {
|
|
||||||
// No residual block. No need to do ciphertext stealing.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zero-pad the residual block and encrypt using CBC.
|
|
||||||
std::vector<uint8_t> 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,
|
|
||||||
aes_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_t* 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);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AesCbcCtsEncryptor::UpdateIv() {}
|
bool AesCbcEncryptor::SetIv(const std::vector<uint8_t>& iv) {
|
||||||
|
|
||||||
bool AesCbcCtsEncryptor::SetIv(const std::vector<uint8_t>& 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
iv_ = iv;
|
set_iv(iv);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AesCbcEncryptor::EncryptInternal(const uint8_t* plaintext,
|
||||||
|
size_t plaintext_size,
|
||||||
|
uint8_t* ciphertext) {
|
||||||
|
DCHECK(aes_key());
|
||||||
|
|
||||||
|
const size_t residual_block_size = plaintext_size % AES_BLOCK_SIZE;
|
||||||
|
if (padding_scheme_ == kNoPadding && residual_block_size != 0) {
|
||||||
|
LOG(ERROR) << "Expecting input size to be multiple of " << AES_BLOCK_SIZE
|
||||||
|
<< ", got " << plaintext_size;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt everything but the residual block using CBC.
|
||||||
|
const size_t cbc_size = plaintext_size - residual_block_size;
|
||||||
|
std::vector<uint8_t> local_iv(iv());
|
||||||
|
if (cbc_size != 0) {
|
||||||
|
AES_cbc_encrypt(plaintext, ciphertext, cbc_size, aes_key(), local_iv.data(),
|
||||||
|
AES_ENCRYPT);
|
||||||
|
} else if (padding_scheme_ == kCtsPadding) {
|
||||||
|
// Don't have a full block, leave unencrypted.
|
||||||
|
memcpy(ciphertext, plaintext, plaintext_size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (residual_block_size == 0 && padding_scheme_ != kPkcs5Padding) {
|
||||||
|
if (chain_across_calls_)
|
||||||
|
set_iv(local_iv);
|
||||||
|
// No residual block. No need to do padding.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
DCHECK(!chain_across_calls_);
|
||||||
|
|
||||||
|
std::vector<uint8_t> residual_block(plaintext + cbc_size,
|
||||||
|
plaintext + plaintext_size);
|
||||||
|
DCHECK_EQ(residual_block.size(), residual_block_size);
|
||||||
|
uint8_t* residual_ciphertext_block = ciphertext + cbc_size;
|
||||||
|
|
||||||
|
if (padding_scheme_ == kPkcs5Padding) {
|
||||||
|
const size_t num_padding_bytes = AES_BLOCK_SIZE - residual_block_size;
|
||||||
|
DCHECK_EQ(num_padding_bytes, NumPaddingBytes(plaintext_size));
|
||||||
|
// Pad residue block with PKCS5 padding.
|
||||||
|
residual_block.resize(AES_BLOCK_SIZE, static_cast<char>(num_padding_bytes));
|
||||||
|
AES_cbc_encrypt(residual_block.data(), residual_ciphertext_block,
|
||||||
|
AES_BLOCK_SIZE, aes_key(), local_iv.data(), AES_ENCRYPT);
|
||||||
|
} else {
|
||||||
|
DCHECK_EQ(padding_scheme_, kCtsPadding);
|
||||||
|
|
||||||
|
// Zero-pad the residual block and encrypt using CBC.
|
||||||
|
residual_block.resize(AES_BLOCK_SIZE, 0);
|
||||||
|
AES_cbc_encrypt(residual_block.data(), residual_block.data(),
|
||||||
|
AES_BLOCK_SIZE, aes_key(), local_iv.data(), 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.
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AesCbcEncryptor::NumPaddingBytes(size_t size) const {
|
||||||
|
return (padding_scheme_ == kPkcs5Padding)
|
||||||
|
? (AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE))
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace edash_packager
|
} // namespace edash_packager
|
||||||
|
|
|
@ -29,52 +29,64 @@ class AesEncryptor {
|
||||||
/// Initialize the encryptor with specified key and a random generated IV
|
/// Initialize the encryptor with specified key and a random generated IV
|
||||||
/// of the specified size.
|
/// of the specified size.
|
||||||
/// @return true on successful initialization, false otherwise.
|
/// @return true on successful initialization, false otherwise.
|
||||||
virtual bool InitializeWithRandomIv(const std::vector<uint8_t>& key,
|
bool InitializeWithRandomIv(const std::vector<uint8_t>& key,
|
||||||
uint8_t iv_size);
|
uint8_t iv_size);
|
||||||
|
|
||||||
/// Initialize the encryptor with specified key and IV.
|
/// Initialize the encryptor with specified key and IV.
|
||||||
/// @return true on successful initialization, false otherwise.
|
/// @return true on successful initialization, false otherwise.
|
||||||
virtual bool InitializeWithIv(const std::vector<uint8_t>& key,
|
bool InitializeWithIv(const std::vector<uint8_t>& key,
|
||||||
const std::vector<uint8_t>& iv) = 0;
|
const std::vector<uint8_t>& iv);
|
||||||
|
|
||||||
virtual size_t NumPaddingBytes(size_t size) = 0;
|
/// @name Various forms of encrypt calls.
|
||||||
|
|
||||||
/// @name Various forms of encrypt and decrypt calls.
|
|
||||||
/// The plaintext and ciphertext pointers can be the same address.
|
/// The plaintext and ciphertext pointers can be the same address.
|
||||||
/// @{
|
|
||||||
virtual bool EncryptData(const uint8_t* plaintext,
|
|
||||||
size_t plaintext_size,
|
|
||||||
uint8_t* ciphertext) = 0;
|
|
||||||
|
|
||||||
bool Encrypt(const std::vector<uint8_t>& plaintext,
|
bool Encrypt(const std::vector<uint8_t>& plaintext,
|
||||||
std::vector<uint8_t>* ciphertext);
|
std::vector<uint8_t>* ciphertext);
|
||||||
|
|
||||||
bool Encrypt(const std::string& plaintext, std::string* ciphertext);
|
bool Encrypt(const std::string& plaintext, std::string* ciphertext);
|
||||||
|
bool Encrypt(const uint8_t* plaintext,
|
||||||
|
size_t plaintext_size,
|
||||||
|
uint8_t* ciphertext) {
|
||||||
|
return EncryptInternal(plaintext, plaintext_size, ciphertext);
|
||||||
|
}
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// Update IV for next sample.
|
/// Update IV for next sample.
|
||||||
/// As recommended in ISO/IEC FDIS 23001-7:
|
/// As recommended in ISO/IEC FDIS 23001-7:
|
||||||
/// IV need to be updated per sample for CENC.
|
/// IV need to be updated per sample for CENC.
|
||||||
/// IV need not be unique per sample for CBC mode.
|
|
||||||
virtual void UpdateIv() = 0;
|
virtual void UpdateIv() = 0;
|
||||||
|
|
||||||
/// Set IV.
|
/// Set IV.
|
||||||
/// @return true if successful, false if the input is invalid.
|
/// @return true if successful, false if the input is invalid.
|
||||||
virtual bool SetIv(const std::vector<uint8_t>& iv) = 0;
|
virtual bool SetIv(const std::vector<uint8_t>& iv) = 0;
|
||||||
|
|
||||||
|
/// @return The current iv.
|
||||||
const std::vector<uint8_t>& iv() const { return iv_; }
|
const std::vector<uint8_t>& iv() const { return iv_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/// Internal implementation of encrypt function.
|
||||||
|
/// @param plaintext points to the input plaintext.
|
||||||
|
/// @param plaintext_size is the size of input plaintext.
|
||||||
|
/// @param[out] ciphertext points to the output ciphertext. @a plaintext and
|
||||||
|
/// @a ciphertext can point to the same address.
|
||||||
|
virtual bool EncryptInternal(const uint8_t* plaintext,
|
||||||
|
size_t plaintext_size,
|
||||||
|
uint8_t* ciphertext) = 0;
|
||||||
|
/// @param size specifies the input plaintext size.
|
||||||
|
/// @returns The number of padding bytes needed for output ciphertext.
|
||||||
|
virtual size_t NumPaddingBytes(size_t size) const = 0;
|
||||||
|
|
||||||
|
void set_iv(const std::vector<uint8_t>& iv) { iv_ = iv; }
|
||||||
|
AES_KEY* aes_key() const { return aes_key_.get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
// Initialization vector, with size 8 or 16.
|
// Initialization vector, with size 8 or 16.
|
||||||
std::vector<uint8_t> iv_;
|
std::vector<uint8_t> iv_;
|
||||||
// Openssl AES_KEY.
|
// Openssl AES_KEY.
|
||||||
scoped_ptr<AES_KEY> aes_key_;
|
scoped_ptr<AES_KEY> aes_key_;
|
||||||
|
|
||||||
private:
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(AesEncryptor);
|
DISALLOW_COPY_AND_ASSIGN(AesEncryptor);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Class which implements AES-CTR counter-mode encryption/decryption.
|
// Class which implements AES-CTR counter-mode encryption.
|
||||||
class AesCtrEncryptor : public AesEncryptor {
|
class AesCtrEncryptor : public AesEncryptor {
|
||||||
public:
|
public:
|
||||||
AesCtrEncryptor();
|
AesCtrEncryptor();
|
||||||
|
@ -82,18 +94,6 @@ class AesCtrEncryptor : public AesEncryptor {
|
||||||
|
|
||||||
/// @name AesEncryptor implementation overrides.
|
/// @name AesEncryptor implementation overrides.
|
||||||
/// @{
|
/// @{
|
||||||
/// @param key should be 16 bytes in size as specified in CENC spec.
|
|
||||||
/// @param iv_size should be either 8 or 16 as specified in CENC spec.
|
|
||||||
/// @return true on successful initialization, false otherwise.
|
|
||||||
bool InitializeWithIv(const std::vector<uint8_t>& key,
|
|
||||||
const std::vector<uint8_t>& iv) override;
|
|
||||||
|
|
||||||
size_t NumPaddingBytes(size_t size) override;
|
|
||||||
|
|
||||||
bool EncryptData(const uint8_t* plaintext,
|
|
||||||
size_t plaintext_size,
|
|
||||||
uint8_t* ciphertext) override;
|
|
||||||
|
|
||||||
/// Update IV for next sample. @a block_offset_ is reset to 0.
|
/// Update IV for next sample. @a block_offset_ is reset to 0.
|
||||||
/// As recommended in ISO/IEC FDIS 23001-7: CENC spec,
|
/// As recommended in ISO/IEC FDIS 23001-7: CENC spec,
|
||||||
/// For 64-bit IV size, new_iv = old_iv + 1;
|
/// For 64-bit IV size, new_iv = old_iv + 1;
|
||||||
|
@ -105,6 +105,12 @@ class AesCtrEncryptor : public AesEncryptor {
|
||||||
|
|
||||||
uint32_t block_offset() const { return block_offset_; }
|
uint32_t block_offset() const { return block_offset_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool EncryptInternal(const uint8_t* plaintext,
|
||||||
|
size_t plaintext_size,
|
||||||
|
uint8_t* ciphertext) override;
|
||||||
|
size_t NumPaddingBytes(size_t size) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Current block offset.
|
// Current block offset.
|
||||||
uint32_t block_offset_;
|
uint32_t block_offset_;
|
||||||
|
@ -118,58 +124,43 @@ class AesCtrEncryptor : public AesEncryptor {
|
||||||
DISALLOW_COPY_AND_ASSIGN(AesCtrEncryptor);
|
DISALLOW_COPY_AND_ASSIGN(AesCtrEncryptor);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Class which implements AES-CBC (Cipher block chaining) encryption with
|
enum CbcPaddingScheme {
|
||||||
// PKCS#5 padding.
|
kNoPadding,
|
||||||
class AesCbcPkcs5Encryptor : public AesEncryptor {
|
kPkcs5Padding,
|
||||||
public:
|
kCtsPadding,
|
||||||
AesCbcPkcs5Encryptor();
|
|
||||||
~AesCbcPkcs5Encryptor() override;
|
|
||||||
|
|
||||||
/// @name AesEncryptor implementation overrides.
|
|
||||||
/// @{
|
|
||||||
bool InitializeWithIv(const std::vector<uint8_t>& key,
|
|
||||||
const std::vector<uint8_t>& iv) override;
|
|
||||||
|
|
||||||
size_t NumPaddingBytes(size_t size) override;
|
|
||||||
|
|
||||||
bool EncryptData(const uint8_t* plaintext,
|
|
||||||
size_t plaintext_size,
|
|
||||||
uint8_t* ciphertext) override;
|
|
||||||
|
|
||||||
void UpdateIv() override;
|
|
||||||
|
|
||||||
bool SetIv(const std::vector<uint8_t>& iv) override;
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
private:
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(AesCbcPkcs5Encryptor);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Class which implements AES-CBC (Cipher block chaining) encryption with
|
const bool kChainAcrossCalls = true;
|
||||||
// Ciphertext stealing.
|
|
||||||
class AesCbcCtsEncryptor : public AesEncryptor {
|
// Class which implements AES-CBC (Cipher block chaining) encryption.
|
||||||
|
class AesCbcEncryptor : public AesEncryptor {
|
||||||
public:
|
public:
|
||||||
AesCbcCtsEncryptor();
|
/// @param padding_scheme indicates the padding scheme used. Currently
|
||||||
~AesCbcCtsEncryptor() override;
|
/// supported schemes: kNoPadding, kPkcs5Padding, kCtsPadding.
|
||||||
|
/// @param chain_across_calls indicates whether there is a continuous cipher
|
||||||
|
/// block chain across calls for Encrypt function. If it is false, iv
|
||||||
|
/// is not updated across Encrypt function calls.
|
||||||
|
AesCbcEncryptor(CbcPaddingScheme padding_scheme, bool chain_across_calls);
|
||||||
|
~AesCbcEncryptor() override;
|
||||||
|
|
||||||
/// @name AesEncryptor implementation overrides.
|
/// @name AesEncryptor implementation overrides.
|
||||||
/// @{
|
/// @{
|
||||||
bool InitializeWithIv(const std::vector<uint8_t>& key,
|
|
||||||
const std::vector<uint8_t>& iv) override;
|
|
||||||
|
|
||||||
size_t NumPaddingBytes(size_t size) override;
|
|
||||||
|
|
||||||
bool EncryptData(const uint8_t* plaintext,
|
|
||||||
size_t plaintext_size,
|
|
||||||
uint8_t* ciphertext) override;
|
|
||||||
|
|
||||||
void UpdateIv() override;
|
void UpdateIv() override;
|
||||||
|
|
||||||
bool SetIv(const std::vector<uint8_t>& iv) override;
|
bool SetIv(const std::vector<uint8_t>& iv) override;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool EncryptInternal(const uint8_t* plaintext,
|
||||||
|
size_t plaintext_size,
|
||||||
|
uint8_t* ciphertext) override;
|
||||||
|
size_t NumPaddingBytes(size_t size) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_COPY_AND_ASSIGN(AesCbcCtsEncryptor);
|
const CbcPaddingScheme padding_scheme_;
|
||||||
|
const bool chain_across_calls_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(AesCbcEncryptor);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
|
@ -170,7 +170,7 @@ TEST_F(AesCtrEncryptorTest, NistTestCase) {
|
||||||
|
|
||||||
TEST_F(AesCtrEncryptorTest, NistTestCaseInplaceEncryptionDecryption) {
|
TEST_F(AesCtrEncryptorTest, NistTestCaseInplaceEncryptionDecryption) {
|
||||||
std::vector<uint8_t> buffer = plaintext_;
|
std::vector<uint8_t> buffer = plaintext_;
|
||||||
EXPECT_TRUE(encryptor_.EncryptData(&buffer[0], buffer.size(), &buffer[0]));
|
EXPECT_TRUE(encryptor_.Encrypt(&buffer[0], buffer.size(), &buffer[0]));
|
||||||
EXPECT_EQ(ciphertext_, buffer);
|
EXPECT_EQ(ciphertext_, buffer);
|
||||||
|
|
||||||
EXPECT_TRUE(decryptor_.SetIv(iv_));
|
EXPECT_TRUE(decryptor_.SetIv(iv_));
|
||||||
|
@ -211,12 +211,11 @@ TEST_F(AesCtrEncryptorTest, 128BitIVBoundaryCaseEncryption) {
|
||||||
|
|
||||||
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_max64));
|
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_max64));
|
||||||
std::vector<uint8_t> encrypted_verify(plaintext_.size(), 0);
|
std::vector<uint8_t> encrypted_verify(plaintext_.size(), 0);
|
||||||
EXPECT_TRUE(encryptor_.EncryptData(&plaintext_[0], kAesBlockSize,
|
EXPECT_TRUE(
|
||||||
&encrypted_verify[0]));
|
encryptor_.Encrypt(&plaintext_[0], kAesBlockSize, &encrypted_verify[0]));
|
||||||
std::vector<uint8_t> iv_zero(kIv128Zero, kIv128Zero + arraysize(kIv128Zero));
|
std::vector<uint8_t> iv_zero(kIv128Zero, kIv128Zero + arraysize(kIv128Zero));
|
||||||
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_zero));
|
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_zero));
|
||||||
EXPECT_TRUE(encryptor_.EncryptData(&plaintext_[kAesBlockSize],
|
EXPECT_TRUE(encryptor_.Encrypt(&plaintext_[kAesBlockSize], kAesBlockSize * 3,
|
||||||
kAesBlockSize * 3,
|
|
||||||
&encrypted_verify[kAesBlockSize]));
|
&encrypted_verify[kAesBlockSize]));
|
||||||
EXPECT_EQ(encrypted, encrypted_verify);
|
EXPECT_EQ(encrypted, encrypted_verify);
|
||||||
}
|
}
|
||||||
|
@ -254,7 +253,7 @@ TEST_P(AesCtrEncryptorSubsampleTest, NistTestCaseSubsamples) {
|
||||||
for (uint32_t i = 0, offset = 0; i < test_case->subsample_count; ++i) {
|
for (uint32_t i = 0, offset = 0; i < test_case->subsample_count; ++i) {
|
||||||
uint32_t len = test_case->subsample_sizes[i];
|
uint32_t len = test_case->subsample_sizes[i];
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
encryptor_.EncryptData(&plaintext_[offset], len, &encrypted[offset]));
|
encryptor_.Encrypt(&plaintext_[offset], len, &encrypted[offset]));
|
||||||
offset += len;
|
offset += len;
|
||||||
EXPECT_EQ(offset % kAesBlockSize, encryptor_.block_offset());
|
EXPECT_EQ(offset % kAesBlockSize, encryptor_.block_offset());
|
||||||
}
|
}
|
||||||
|
@ -301,30 +300,66 @@ INSTANTIATE_TEST_CASE_P(IvTestCases,
|
||||||
AesCtrEncryptorIvTest,
|
AesCtrEncryptorIvTest,
|
||||||
::testing::ValuesIn(kIvTestCases));
|
::testing::ValuesIn(kIvTestCases));
|
||||||
|
|
||||||
class AesCbcPkcs5EncryptorTestEncryptionDecryption : public testing::Test {
|
class AesCbcEncryptDecryptTest {
|
||||||
public:
|
public:
|
||||||
void TestEncryptionDecryption(const std::vector<uint8_t>& key,
|
AesCbcEncryptDecryptTest()
|
||||||
const std::vector<uint8_t>& iv,
|
: encryptor_(new AesCbcEncryptor(kPkcs5Padding, !kChainAcrossCalls)),
|
||||||
const std::string& plaintext,
|
decryptor_(new AesCbcDecryptor(kPkcs5Padding, !kChainAcrossCalls)),
|
||||||
const std::string& expected_ciphertext_hex) {
|
key_(kAesKey, kAesKey + arraysize(kAesKey)),
|
||||||
AesCbcPkcs5Encryptor encryptor;
|
iv_(kAesIv, kAesIv + arraysize(kAesIv)) {}
|
||||||
EXPECT_TRUE(encryptor.InitializeWithIv(key, iv));
|
|
||||||
|
|
||||||
std::string ciphertext;
|
void TestEncryptDecrypt(const std::vector<uint8_t>& plaintext,
|
||||||
encryptor.Encrypt(plaintext, &ciphertext);
|
const std::vector<uint8_t>& expected_ciphertext) {
|
||||||
EXPECT_EQ(expected_ciphertext_hex,
|
// Test Vector form.
|
||||||
base::HexEncode(ciphertext.data(), ciphertext.size()));
|
TestEncryptDecryptSeparateBuffers(plaintext, expected_ciphertext);
|
||||||
|
TestEncryptDecryptInPlace(plaintext, expected_ciphertext);
|
||||||
|
|
||||||
AesCbcPkcs5Decryptor decryptor;
|
// Test string form.
|
||||||
ASSERT_TRUE(decryptor.InitializeWithIv(key, iv));
|
std::string plaintext_str(plaintext.begin(), plaintext.end());
|
||||||
|
std::string expected_ciphertext_str(expected_ciphertext.begin(),
|
||||||
|
expected_ciphertext.end());
|
||||||
|
TestEncryptDecryptSeparateBuffers(plaintext_str, expected_ciphertext_str);
|
||||||
|
TestEncryptDecryptInPlace(plaintext_str, expected_ciphertext_str);
|
||||||
|
}
|
||||||
|
|
||||||
std::string decrypted;
|
protected:
|
||||||
EXPECT_TRUE(decryptor.Decrypt(ciphertext, &decrypted));
|
template <class T>
|
||||||
|
void TestEncryptDecryptSeparateBuffers(const T& plaintext,
|
||||||
|
const T& expected_ciphertext) {
|
||||||
|
ASSERT_TRUE(encryptor_->InitializeWithIv(key_, iv_));
|
||||||
|
ASSERT_TRUE(decryptor_->InitializeWithIv(key_, iv_));
|
||||||
|
|
||||||
|
T encrypted;
|
||||||
|
EXPECT_TRUE(encryptor_->Encrypt(plaintext, &encrypted));
|
||||||
|
EXPECT_EQ(expected_ciphertext, encrypted);
|
||||||
|
|
||||||
|
T decrypted;
|
||||||
|
EXPECT_TRUE(decryptor_->Decrypt(encrypted, &decrypted));
|
||||||
EXPECT_EQ(plaintext, decrypted);
|
EXPECT_EQ(plaintext, decrypted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void TestEncryptDecryptInPlace(const T& plaintext,
|
||||||
|
const T& expected_ciphertext) {
|
||||||
|
ASSERT_TRUE(encryptor_->InitializeWithIv(key_, iv_));
|
||||||
|
ASSERT_TRUE(decryptor_->InitializeWithIv(key_, iv_));
|
||||||
|
|
||||||
|
T buffer(plaintext);
|
||||||
|
EXPECT_TRUE(encryptor_->Encrypt(buffer, &buffer));
|
||||||
|
EXPECT_EQ(expected_ciphertext, buffer);
|
||||||
|
EXPECT_TRUE(decryptor_->Decrypt(buffer, &buffer));
|
||||||
|
EXPECT_EQ(plaintext, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
scoped_ptr<AesCbcEncryptor> encryptor_;
|
||||||
|
scoped_ptr<AesCbcDecryptor> decryptor_;
|
||||||
|
std::vector<uint8_t> key_;
|
||||||
|
std::vector<uint8_t> iv_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(AesCbcPkcs5EncryptorTestEncryptionDecryption, EncryptAES256CBC) {
|
class AesCbcTest : public AesCbcEncryptDecryptTest, public testing::Test {};
|
||||||
|
|
||||||
|
TEST_F(AesCbcTest, Aes256CbcPkcs5) {
|
||||||
// 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_t kAesCbcKey[] = {
|
static const uint8_t kAesCbcKey[] = {
|
||||||
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae,
|
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae,
|
||||||
|
@ -363,18 +398,17 @@ TEST_F(AesCbcPkcs5EncryptorTestEncryptionDecryption, EncryptAES256CBC) {
|
||||||
0x3f, 0x46, 0x17, 0x96, 0xd6, 0xb0, 0xd6, 0xb2,
|
0x3f, 0x46, 0x17, 0x96, 0xd6, 0xb0, 0xd6, 0xb2,
|
||||||
0xe0, 0xc2, 0xa7, 0x2b, 0x4d, 0x80, 0xe6, 0x44};
|
0xe0, 0xc2, 0xa7, 0x2b, 0x4d, 0x80, 0xe6, 0x44};
|
||||||
|
|
||||||
const std::vector<uint8_t> key(kAesCbcKey,
|
key_.assign(kAesCbcKey, kAesCbcKey + arraysize(kAesCbcKey));
|
||||||
kAesCbcKey + arraysize(kAesCbcKey));
|
iv_.assign(kAesCbcIv, kAesCbcIv + arraysize(kAesCbcIv));
|
||||||
const std::vector<uint8_t> iv(kAesCbcIv, kAesCbcIv + arraysize(kAesCbcIv));
|
const std::vector<uint8_t> plaintext(
|
||||||
const std::string plaintext(reinterpret_cast<const char*>(kAesCbcPlaintext),
|
kAesCbcPlaintext, kAesCbcPlaintext + arraysize(kAesCbcPlaintext));
|
||||||
sizeof(kAesCbcPlaintext));
|
const std::vector<uint8_t> expected_ciphertext(
|
||||||
const std::string expected_ciphertext_hex =
|
kAesCbcCiphertext, kAesCbcCiphertext + arraysize(kAesCbcCiphertext));
|
||||||
base::HexEncode(kAesCbcCiphertext, sizeof(kAesCbcCiphertext));
|
|
||||||
|
|
||||||
TestEncryptionDecryption(key, iv, plaintext, expected_ciphertext_hex);
|
TestEncryptDecrypt(plaintext, expected_ciphertext);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(AesCbcPkcs5EncryptorTestEncryptionDecryption, EncryptAES128CBCRegression) {
|
TEST_F(AesCbcTest, Aes128CbcPkcs5) {
|
||||||
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 =
|
||||||
|
@ -383,172 +417,201 @@ TEST_F(AesCbcPkcs5EncryptorTestEncryptionDecryption, EncryptAES128CBCRegression)
|
||||||
"D4A67A0BA33C30F207344D81D1E944BBE65587C3D7D9939A"
|
"D4A67A0BA33C30F207344D81D1E944BBE65587C3D7D9939A"
|
||||||
"C070C62B9C15A3EA312EA4AD1BC7929F4D3C16B03AD5ADA8";
|
"C070C62B9C15A3EA312EA4AD1BC7929F4D3C16B03AD5ADA8";
|
||||||
|
|
||||||
const std::vector<uint8_t> key(kKey.begin(), kKey.end());
|
key_.assign(kKey.begin(), kKey.end());
|
||||||
const std::vector<uint8_t> iv(kIv.begin(), kIv.end());
|
iv_.assign(kIv.begin(), kIv.end());
|
||||||
|
|
||||||
TestEncryptionDecryption(key, iv, kPlaintext, kExpectedCiphertextHex);
|
const std::vector<uint8_t> plaintext(kPlaintext.begin(), kPlaintext.end());
|
||||||
|
std::vector<uint8_t> expected_ciphertext;
|
||||||
|
ASSERT_TRUE(
|
||||||
|
base::HexStringToBytes(kExpectedCiphertextHex, &expected_ciphertext));
|
||||||
|
TestEncryptDecrypt(plaintext, expected_ciphertext);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(AesCbcPkcs5EncryptorTestEncryptionDecryption, EncryptAES192CBCRegression) {
|
TEST_F(AesCbcTest, Aes192CbcPkcs5) {
|
||||||
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";
|
||||||
const std::string kExpectedCiphertextHex = "78DE5D7C2714FC5C61346C5416F6C89A";
|
const std::string kExpectedCiphertextHex = "78DE5D7C2714FC5C61346C5416F6C89A";
|
||||||
|
|
||||||
const std::vector<uint8_t> key(kKey.begin(), kKey.end());
|
|
||||||
const std::vector<uint8_t> iv(kIv.begin(), kIv.end());
|
|
||||||
|
|
||||||
TestEncryptionDecryption(key, iv, kPlaintext, kExpectedCiphertextHex);
|
|
||||||
}
|
|
||||||
|
|
||||||
class AesCbcPkcs5EncryptorTest : public testing::Test {
|
|
||||||
public:
|
|
||||||
void SetUp() override {
|
|
||||||
const std::string kKey = "128=SixteenBytes";
|
|
||||||
const std::string kIv = "Sweet Sixteen IV";
|
|
||||||
key_.assign(kKey.begin(), kKey.end());
|
key_.assign(kKey.begin(), kKey.end());
|
||||||
iv_.assign(kIv.begin(), kIv.end());
|
iv_.assign(kIv.begin(), kIv.end());
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
const std::vector<uint8_t> plaintext(kPlaintext.begin(), kPlaintext.end());
|
||||||
std::vector<uint8_t> key_;
|
std::vector<uint8_t> expected_ciphertext;
|
||||||
std::vector<uint8_t> iv_;
|
ASSERT_TRUE(
|
||||||
};
|
base::HexStringToBytes(kExpectedCiphertextHex, &expected_ciphertext));
|
||||||
|
TestEncryptDecrypt(plaintext, expected_ciphertext);
|
||||||
TEST_F(AesCbcPkcs5EncryptorTest, UnsupportedKeySize) {
|
|
||||||
AesCbcPkcs5Encryptor encryptor;
|
|
||||||
EXPECT_FALSE(encryptor.InitializeWithIv(std::vector<uint8_t>(15, 0), iv_));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(AesCbcPkcs5EncryptorTest, UnsupportedIvSize) {
|
TEST_F(AesCbcTest, NoPaddingNoChainAcrossCalls) {
|
||||||
AesCbcPkcs5Encryptor encryptor;
|
const uint8_t kPlaintext[] = {
|
||||||
EXPECT_FALSE(encryptor.InitializeWithIv(key_, std::vector<uint8_t>(14, 0)));
|
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
|
||||||
}
|
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
|
||||||
|
};
|
||||||
|
const uint8_t kCiphertext[] = {
|
||||||
|
0x77, 0xcd, 0xe9, 0x1f, 0xe6, 0xdf, 0x9c, 0xbc,
|
||||||
|
0x5d, 0x0c, 0x98, 0xf9, 0x6e, 0xfd, 0x59, 0x0b,
|
||||||
|
};
|
||||||
|
|
||||||
TEST_F(AesCbcPkcs5EncryptorTest, EmptyEncrypt) {
|
std::vector<uint8_t> plaintext(kPlaintext,
|
||||||
AesCbcPkcs5Encryptor encryptor;
|
kPlaintext + arraysize(kPlaintext));
|
||||||
|
std::vector<uint8_t> ciphertext(kCiphertext,
|
||||||
|
kCiphertext + arraysize(kCiphertext));
|
||||||
|
|
||||||
|
AesCbcEncryptor encryptor(kNoPadding, !kChainAcrossCalls);
|
||||||
ASSERT_TRUE(encryptor.InitializeWithIv(key_, iv_));
|
ASSERT_TRUE(encryptor.InitializeWithIv(key_, iv_));
|
||||||
|
|
||||||
std::string ciphertext;
|
std::vector<uint8_t> encrypted;
|
||||||
std::string expected_ciphertext_hex = "8518B8878D34E7185E300D0FCC426396";
|
ASSERT_TRUE(encryptor.Encrypt(plaintext, &encrypted));
|
||||||
encryptor.Encrypt("", &ciphertext);
|
EXPECT_EQ(ciphertext, encrypted);
|
||||||
EXPECT_EQ(expected_ciphertext_hex,
|
// Iv should not have been updated.
|
||||||
base::HexEncode(ciphertext.data(), ciphertext.size()));
|
EXPECT_EQ(iv_, encryptor.iv());
|
||||||
}
|
ASSERT_TRUE(encryptor.Encrypt(plaintext, &encrypted));
|
||||||
|
EXPECT_EQ(ciphertext, encrypted);
|
||||||
|
|
||||||
TEST_F(AesCbcPkcs5EncryptorTest, CipherTextNotMultipleOfBlockSize) {
|
AesCbcDecryptor decryptor(kNoPadding, !kChainAcrossCalls);
|
||||||
AesCbcPkcs5Decryptor decryptor;
|
|
||||||
ASSERT_TRUE(decryptor.InitializeWithIv(key_, iv_));
|
ASSERT_TRUE(decryptor.InitializeWithIv(key_, iv_));
|
||||||
|
|
||||||
std::string plaintext;
|
std::vector<uint8_t> decrypted;
|
||||||
EXPECT_FALSE(decryptor.Decrypt("1", &plaintext));
|
ASSERT_TRUE(decryptor.Decrypt(ciphertext, &decrypted));
|
||||||
|
EXPECT_EQ(plaintext, decrypted);
|
||||||
|
// Iv should not have been updated.
|
||||||
|
EXPECT_EQ(iv_, encryptor.iv());
|
||||||
|
ASSERT_TRUE(decryptor.Decrypt(ciphertext, &decrypted));
|
||||||
|
EXPECT_EQ(plaintext, decrypted);
|
||||||
}
|
}
|
||||||
|
|
||||||
class AesCbcCtsEncryptorDecryptorTest : public testing::Test {
|
TEST_F(AesCbcTest, NoPaddingChainAcrossCalls) {
|
||||||
public:
|
const uint8_t kPlaintext[] = {
|
||||||
void SetUp() override {
|
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
|
||||||
key_.assign(kAesKey, kAesKey + arraysize(kAesKey));
|
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
|
||||||
iv_.assign(kAesIv, kAesIv + arraysize(kAesIv));
|
};
|
||||||
}
|
const uint8_t kCiphertext[] = {
|
||||||
|
0x77, 0xcd, 0xe9, 0x1f, 0xe6, 0xdf, 0x9c, 0xbc,
|
||||||
|
0x5d, 0x0c, 0x98, 0xf9, 0x6e, 0xfd, 0x59, 0x0b,
|
||||||
|
};
|
||||||
|
const uint8_t kCiphertext2[] = {
|
||||||
|
0xbd, 0xdd, 0xe4, 0x39, 0x52, 0x6f, 0x10, 0x0c,
|
||||||
|
0x95, 0x45, 0xc2, 0x74, 0xd4, 0xf7, 0xfd, 0x3f,
|
||||||
|
};
|
||||||
|
|
||||||
void TestEncryptDecryptSeparateBuffers(
|
std::vector<uint8_t> plaintext(kPlaintext,
|
||||||
const std::vector<uint8_t>& plaintext,
|
kPlaintext + arraysize(kPlaintext));
|
||||||
const std::vector<uint8_t>& expected_ciphertext) {
|
std::vector<uint8_t> ciphertext(kCiphertext,
|
||||||
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_));
|
kCiphertext + arraysize(kCiphertext));
|
||||||
ASSERT_TRUE(decryptor_.InitializeWithIv(key_, iv_));
|
std::vector<uint8_t> ciphertext2(kCiphertext2,
|
||||||
|
kCiphertext2 + arraysize(kCiphertext2));
|
||||||
|
|
||||||
|
AesCbcEncryptor encryptor(kNoPadding, kChainAcrossCalls);
|
||||||
|
ASSERT_TRUE(encryptor.InitializeWithIv(key_, iv_));
|
||||||
|
|
||||||
std::vector<uint8_t> encrypted;
|
std::vector<uint8_t> encrypted;
|
||||||
encryptor_.Encrypt(plaintext, &encrypted);
|
ASSERT_TRUE(encryptor.Encrypt(plaintext, &encrypted));
|
||||||
EXPECT_EQ(expected_ciphertext, encrypted);
|
EXPECT_EQ(ciphertext, encrypted);
|
||||||
|
// Iv should have been updated.
|
||||||
|
EXPECT_NE(iv_, encryptor.iv());
|
||||||
|
// If run encrypt again, the result will be different.
|
||||||
|
ASSERT_TRUE(encryptor.Encrypt(plaintext, &encrypted));
|
||||||
|
EXPECT_EQ(ciphertext2, encrypted);
|
||||||
|
|
||||||
|
AesCbcDecryptor decryptor(kNoPadding, kChainAcrossCalls);
|
||||||
|
ASSERT_TRUE(decryptor.InitializeWithIv(key_, iv_));
|
||||||
|
|
||||||
std::vector<uint8_t> decrypted;
|
std::vector<uint8_t> decrypted;
|
||||||
decryptor_.Decrypt(encrypted, &decrypted);
|
ASSERT_TRUE(decryptor.Decrypt(ciphertext, &decrypted));
|
||||||
EXPECT_EQ(plaintext, decrypted);
|
EXPECT_EQ(plaintext, decrypted);
|
||||||
}
|
// Iv should have been updated.
|
||||||
|
EXPECT_NE(iv_, encryptor.iv());
|
||||||
|
// If run decrypt on ciphertext2 now, it will return the original plaintext.
|
||||||
|
ASSERT_TRUE(decryptor.Decrypt(ciphertext2, &decrypted));
|
||||||
|
EXPECT_EQ(plaintext, decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
void TestEncryptDecryptInPlace(
|
TEST_F(AesCbcTest, UnsupportedKeySize) {
|
||||||
const std::vector<uint8_t>& plaintext,
|
EXPECT_FALSE(encryptor_->InitializeWithIv(std::vector<uint8_t>(15, 0), iv_));
|
||||||
const std::vector<uint8_t>& expected_ciphertext) {
|
EXPECT_FALSE(decryptor_->InitializeWithIv(std::vector<uint8_t>(15, 0), iv_));
|
||||||
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_));
|
}
|
||||||
ASSERT_TRUE(decryptor_.InitializeWithIv(key_, iv_));
|
|
||||||
|
|
||||||
std::vector<uint8_t> buffer(plaintext);
|
TEST_F(AesCbcTest, UnsupportedIvSize) {
|
||||||
encryptor_.Encrypt(buffer, &buffer);
|
EXPECT_FALSE(encryptor_->InitializeWithIv(key_, std::vector<uint8_t>(14, 0)));
|
||||||
EXPECT_EQ(expected_ciphertext, buffer);
|
EXPECT_FALSE(decryptor_->InitializeWithIv(key_, std::vector<uint8_t>(8, 0)));
|
||||||
decryptor_.Decrypt(buffer, &buffer);
|
}
|
||||||
EXPECT_EQ(plaintext, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
TEST_F(AesCbcTest, Pkcs5CipherTextNotMultipleOfBlockSize) {
|
||||||
std::vector<uint8_t> key_;
|
std::string plaintext;
|
||||||
std::vector<uint8_t> iv_;
|
ASSERT_TRUE(decryptor_->InitializeWithIv(key_, iv_));
|
||||||
AesCbcCtsEncryptor encryptor_;
|
EXPECT_FALSE(decryptor_->Decrypt("1", &plaintext));
|
||||||
AesCbcCtsDecryptor decryptor_;
|
}
|
||||||
|
|
||||||
|
TEST_F(AesCbcTest, Pkcs5CipherTextEmpty) {
|
||||||
|
std::string plaintext;
|
||||||
|
ASSERT_TRUE(decryptor_->InitializeWithIv(key_, iv_));
|
||||||
|
EXPECT_FALSE(decryptor_->Decrypt("", &plaintext));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CbcTestCase {
|
||||||
|
CbcPaddingScheme padding_scheme;
|
||||||
|
const char* plaintext_hex;
|
||||||
|
const char* expected_ciphertext_hex;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(AesCbcCtsEncryptorDecryptorTest, TestWithResidualBytes) {
|
const CbcTestCase kCbcTestCases[] = {
|
||||||
std::vector<uint8_t> plaintext;
|
// No padding with zero bytes.
|
||||||
ASSERT_TRUE(base::HexStringToBytes(
|
{kNoPadding, "", ""},
|
||||||
|
{kNoPadding,
|
||||||
|
"6bc1bee22e409f96e93d7e117393172a6bc1bee22e409f96e93d7e117393172a",
|
||||||
|
"77cde91fe6df9cbc5d0c98f96efd590bbddde439526f100c9545c274d4f7fd3f"},
|
||||||
|
// Pkcs5 padding with zero bytes.
|
||||||
|
{kPkcs5Padding, "", "f6a3569dea3cda208eb3d5792942612b"},
|
||||||
|
// Cts Padding with zero bytes.
|
||||||
|
{kCtsPadding, "", ""},
|
||||||
|
// Cts Padding with no encrypted blocks.
|
||||||
|
{kCtsPadding, "3f593e7a204a5e70f2", "3f593e7a204a5e70f2"},
|
||||||
|
// Cts padding with residual bytes.
|
||||||
|
{kCtsPadding,
|
||||||
"e0818f2dc7caaa9edf09285a0c1fca98d39e9b08a47ab6911c4bbdf27d94"
|
"e0818f2dc7caaa9edf09285a0c1fca98d39e9b08a47ab6911c4bbdf27d94"
|
||||||
"f917cdffc9ebb307141f23b0d3921e0ed7f86eb09381286f8e7a4f",
|
"f917cdffc9ebb307141f23b0d3921e0ed7f86eb09381286f8e7a4f",
|
||||||
&plaintext));
|
|
||||||
|
|
||||||
std::vector<uint8_t> ciphertext;
|
|
||||||
ASSERT_TRUE(base::HexStringToBytes(
|
|
||||||
"b40a0b8704c74e22e8030cad6f272b34ace54cc7c9c64b2018bbcf23df018"
|
"b40a0b8704c74e22e8030cad6f272b34ace54cc7c9c64b2018bbcf23df018"
|
||||||
"39b14899441cf74a9fb2f2b229a609146f31be8e8a826eb6e857e",
|
"39b14899441cf74a9fb2f2b229a609146f31be8e8a826eb6e857e"},
|
||||||
&ciphertext));
|
// Cts padding with even blocks.
|
||||||
|
{kCtsPadding,
|
||||||
TestEncryptDecryptSeparateBuffers(plaintext, ciphertext);
|
|
||||||
TestEncryptDecryptInPlace(plaintext, ciphertext);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(AesCbcCtsEncryptorDecryptorTest, TestEvenBlocks) {
|
|
||||||
std::vector<uint8_t> plaintext;
|
|
||||||
ASSERT_TRUE(base::HexStringToBytes(
|
|
||||||
"3f593e7a204a5e70f2814dca05aa49d36f2daddc9a24e0515802c539efc3"
|
"3f593e7a204a5e70f2814dca05aa49d36f2daddc9a24e0515802c539efc3"
|
||||||
"1094b3ad6c26d6f5c0e387545ce6a4c2c14d",
|
"1094b3ad6c26d6f5c0e387545ce6a4c2c14d",
|
||||||
&plaintext));
|
|
||||||
|
|
||||||
std::vector<uint8_t> ciphertext;
|
|
||||||
ASSERT_TRUE(base::HexStringToBytes(
|
|
||||||
"5f32cd0504b27b25ee04090d88d37d340c9c0a9fa50b05358b98fad4302ea"
|
"5f32cd0504b27b25ee04090d88d37d340c9c0a9fa50b05358b98fad4302ea"
|
||||||
"480148d8aa091f4e7d186a7223df153f6f7",
|
"480148d8aa091f4e7d186a7223df153f6f7"},
|
||||||
&ciphertext));
|
// Cts padding with one block and a half.
|
||||||
|
{kCtsPadding, "3f593e7a204a5e70f2814dca05aa49d36f2daddc9a4302ea",
|
||||||
|
"623fc113fe02ce85628deb58d652c6995f32cd0504b27b25"},
|
||||||
|
};
|
||||||
|
|
||||||
TestEncryptDecryptSeparateBuffers(plaintext, ciphertext);
|
class AesCbcCryptorVerificationTest
|
||||||
TestEncryptDecryptInPlace(plaintext, ciphertext);
|
: public AesCbcEncryptDecryptTest,
|
||||||
}
|
public ::testing::TestWithParam<CbcTestCase> {};
|
||||||
|
|
||||||
|
TEST_P(AesCbcCryptorVerificationTest, EncryptDecryptTest) {
|
||||||
|
encryptor_.reset(
|
||||||
|
new AesCbcEncryptor(GetParam().padding_scheme, !kChainAcrossCalls));
|
||||||
|
decryptor_.reset(
|
||||||
|
new AesCbcDecryptor(GetParam().padding_scheme, !kChainAcrossCalls));
|
||||||
|
|
||||||
TEST_F(AesCbcCtsEncryptorDecryptorTest, TestOneBlockAndAHalf) {
|
|
||||||
std::vector<uint8_t> plaintext;
|
std::vector<uint8_t> plaintext;
|
||||||
ASSERT_TRUE(base::HexStringToBytes(
|
std::string plaintext_hex(GetParam().plaintext_hex);
|
||||||
"3f593e7a204a5e70f2814dca05aa49d36f2daddc9a4302ea",
|
if (!plaintext_hex.empty())
|
||||||
&plaintext));
|
ASSERT_TRUE(base::HexStringToBytes(plaintext_hex, &plaintext));
|
||||||
|
|
||||||
std::vector<uint8_t> ciphertext;
|
std::vector<uint8_t> expected_ciphertext;
|
||||||
ASSERT_TRUE(base::HexStringToBytes(
|
std::string expected_ciphertext_hex(GetParam().expected_ciphertext_hex);
|
||||||
"623fc113fe02ce85628deb58d652c6995f32cd0504b27b25",
|
if (!expected_ciphertext_hex.empty()) {
|
||||||
&ciphertext));
|
ASSERT_TRUE(base::HexStringToBytes(GetParam().expected_ciphertext_hex,
|
||||||
|
&expected_ciphertext));
|
||||||
|
}
|
||||||
|
|
||||||
TestEncryptDecryptSeparateBuffers(plaintext, ciphertext);
|
TestEncryptDecrypt(plaintext, expected_ciphertext);
|
||||||
TestEncryptDecryptInPlace(plaintext, ciphertext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(AesCbcCtsEncryptorDecryptorTest, TestZeroEncryptedBlocks) {
|
INSTANTIATE_TEST_CASE_P(CbcTestCases,
|
||||||
std::vector<uint8_t> plaintext;
|
AesCbcCryptorVerificationTest,
|
||||||
ASSERT_TRUE(base::HexStringToBytes("3f593e7a204a5e70f2", &plaintext));
|
::testing::ValuesIn(kCbcTestCases));
|
||||||
|
|
||||||
TestEncryptDecryptSeparateBuffers(plaintext, plaintext);
|
|
||||||
TestEncryptDecryptInPlace(plaintext, plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(AesCbcCtsEncryptorDecryptorTest, TestZeroBytes) {
|
|
||||||
std::vector<uint8_t> plaintext;
|
|
||||||
|
|
||||||
TestEncryptDecryptSeparateBuffers(plaintext, plaintext);
|
|
||||||
TestEncryptDecryptInPlace(plaintext, plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace edash_packager
|
} // namespace edash_packager
|
||||||
|
|
|
@ -44,7 +44,7 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config,
|
||||||
aes_decryptor.reset(new AesCtrDecryptor);
|
aes_decryptor.reset(new AesCtrDecryptor);
|
||||||
break;
|
break;
|
||||||
case kEncryptionModeAesCbc:
|
case kEncryptionModeAesCbc:
|
||||||
aes_decryptor.reset(new AesCbcPkcs5Decryptor);
|
aes_decryptor.reset(new AesCbcDecryptor(kNoPadding, kChainAcrossCalls));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG(ERROR) << "Unsupported Decryption Mode: "
|
LOG(ERROR) << "Unsupported Decryption Mode: "
|
||||||
|
|
|
@ -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<AesCbcPkcs5Encryptor> encryptor)
|
scoped_ptr<AesCbcEncryptor> 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,8 @@ AesRequestSigner* AesRequestSigner::CreateSigner(const std::string& signer_name,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
scoped_ptr<AesCbcPkcs5Encryptor> encryptor(new AesCbcPkcs5Encryptor());
|
scoped_ptr<AesCbcEncryptor> encryptor(
|
||||||
|
new AesCbcEncryptor(kPkcs5Padding, !kChainAcrossCalls));
|
||||||
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());
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
class AesCbcPkcs5Encryptor;
|
class AesCbcEncryptor;
|
||||||
class RsaPrivateKey;
|
class RsaPrivateKey;
|
||||||
|
|
||||||
/// Abstract class used for signature generation.
|
/// Abstract class used for signature generation.
|
||||||
|
@ -56,9 +56,9 @@ class AesRequestSigner : public RequestSigner {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AesRequestSigner(const std::string& signer_name,
|
AesRequestSigner(const std::string& signer_name,
|
||||||
scoped_ptr<AesCbcPkcs5Encryptor> encryptor);
|
scoped_ptr<AesCbcEncryptor> encryptor);
|
||||||
|
|
||||||
scoped_ptr<AesCbcPkcs5Encryptor> aes_cbc_encryptor_;
|
scoped_ptr<AesCbcEncryptor> aes_cbc_encryptor_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(AesRequestSigner);
|
DISALLOW_COPY_AND_ASSIGN(AesRequestSigner);
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,7 +23,8 @@ namespace mp4 {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Generate 64bit IV by default.
|
// Generate 64bit IV by default.
|
||||||
const size_t kDefaultIvSize = 8u;
|
const size_t kDefaultIvSizeForCtr = 8u;
|
||||||
|
const size_t kDefaultIvSizeForCbc = 16u;
|
||||||
const size_t kCencBlockSize = 16u;
|
const size_t kCencBlockSize = 16u;
|
||||||
|
|
||||||
// Adds one or more subsamples to |*subsamples|. This may add more than one
|
// Adds one or more subsamples to |*subsamples|. This may add more than one
|
||||||
|
@ -175,16 +176,19 @@ void EncryptingFragmenter::FinalizeFragmentForEncryption() {
|
||||||
Status EncryptingFragmenter::CreateEncryptor() {
|
Status EncryptingFragmenter::CreateEncryptor() {
|
||||||
DCHECK(encryption_key_);
|
DCHECK(encryption_key_);
|
||||||
scoped_ptr<AesEncryptor> encryptor;
|
scoped_ptr<AesEncryptor> encryptor;
|
||||||
|
size_t default_iv_size = 0;
|
||||||
if (encryption_mode_ == kEncryptionModeAesCtr) {
|
if (encryption_mode_ == kEncryptionModeAesCtr) {
|
||||||
encryptor.reset(new AesCtrEncryptor);
|
encryptor.reset(new AesCtrEncryptor);
|
||||||
|
default_iv_size = kDefaultIvSizeForCtr;
|
||||||
} else if (encryption_mode_ == kEncryptionModeAesCbc) {
|
} else if (encryption_mode_ == kEncryptionModeAesCbc) {
|
||||||
encryptor.reset(new AesCbcPkcs5Encryptor);
|
encryptor.reset(new AesCbcEncryptor(kNoPadding, kChainAcrossCalls));
|
||||||
|
default_iv_size = kDefaultIvSizeForCbc;
|
||||||
} else {
|
} else {
|
||||||
return Status(error::MUXER_FAILURE, "Unsupported encryption mode.");
|
return Status(error::MUXER_FAILURE, "Unsupported encryption mode.");
|
||||||
}
|
}
|
||||||
const bool initialized = encryption_key_->iv.empty()
|
const bool initialized = encryption_key_->iv.empty()
|
||||||
? encryptor->InitializeWithRandomIv(
|
? encryptor->InitializeWithRandomIv(
|
||||||
encryption_key_->key, kDefaultIvSize)
|
encryption_key_->key, default_iv_size)
|
||||||
: encryptor->InitializeWithIv(
|
: encryptor->InitializeWithIv(
|
||||||
encryption_key_->key, encryption_key_->iv);
|
encryption_key_->key, encryption_key_->iv);
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
|
@ -195,7 +199,7 @@ Status EncryptingFragmenter::CreateEncryptor() {
|
||||||
|
|
||||||
void EncryptingFragmenter::EncryptBytes(uint8_t* data, uint32_t size) {
|
void EncryptingFragmenter::EncryptBytes(uint8_t* data, uint32_t size) {
|
||||||
DCHECK(encryptor_);
|
DCHECK(encryptor_);
|
||||||
CHECK(encryptor_->EncryptData(data, size, data));
|
CHECK(encryptor_->Encrypt(data, size, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
||||||
|
@ -223,8 +227,11 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
||||||
// encrypted bytes of each frame within the superframe must be block
|
// encrypted bytes of each frame within the superframe must be block
|
||||||
// aligned so that the counter state can be computed for each frame
|
// aligned so that the counter state can be computed for each frame
|
||||||
// within the superframe.
|
// within the superframe.
|
||||||
if (is_superframe) {
|
// For AES-CBC mode 'cbc1' scheme, clear data is sized appropriately so
|
||||||
uint16_t misalign_bytes = subsample.cipher_bytes % kCencBlockSize;
|
// that the cipher data is block aligned.
|
||||||
|
if (is_superframe || encryption_mode_ == kEncryptionModeAesCbc) {
|
||||||
|
const uint16_t misalign_bytes =
|
||||||
|
subsample.cipher_bytes % kCencBlockSize;
|
||||||
subsample.clear_bytes += misalign_bytes;
|
subsample.clear_bytes += misalign_bytes;
|
||||||
subsample.cipher_bytes -= misalign_bytes;
|
subsample.cipher_bytes -= misalign_bytes;
|
||||||
}
|
}
|
||||||
|
@ -258,10 +265,18 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
||||||
if (video_slice_header_size < 0)
|
if (video_slice_header_size < 0)
|
||||||
return Status(error::MUXER_FAILURE, "Failed to read slice header.");
|
return Status(error::MUXER_FAILURE, "Failed to read slice header.");
|
||||||
|
|
||||||
const uint64_t current_clear_bytes = nalu.header_size() +
|
uint64_t current_clear_bytes =
|
||||||
video_slice_header_size;
|
nalu.header_size() + video_slice_header_size;
|
||||||
const uint64_t cipher_bytes =
|
uint64_t cipher_bytes = nalu.payload_size() - video_slice_header_size;
|
||||||
nalu.payload_size() - video_slice_header_size;
|
|
||||||
|
// For AES-CBC mode 'cbc1' scheme, clear data is sized appropriately
|
||||||
|
// so that the cipher data is block aligned.
|
||||||
|
if (encryption_mode_ == kEncryptionModeAesCbc) {
|
||||||
|
const uint16_t misalign_bytes = cipher_bytes % kCencBlockSize;
|
||||||
|
current_clear_bytes += misalign_bytes;
|
||||||
|
cipher_bytes -= misalign_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
const uint8_t* nalu_data = nalu.data() + current_clear_bytes;
|
const uint8_t* nalu_data = nalu.data() + current_clear_bytes;
|
||||||
EncryptBytes(const_cast<uint8_t*>(nalu_data), cipher_bytes);
|
EncryptBytes(const_cast<uint8_t*>(nalu_data), cipher_bytes);
|
||||||
|
|
||||||
|
@ -285,7 +300,12 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
||||||
traf()->auxiliary_size.sample_info_sizes.push_back(
|
traf()->auxiliary_size.sample_info_sizes.push_back(
|
||||||
sample_encryption_entry.ComputeSize());
|
sample_encryption_entry.ComputeSize());
|
||||||
} else {
|
} else {
|
||||||
EncryptBytes(data, sample->data_size());
|
uint64_t encryption_data_size = sample->data_size();
|
||||||
|
// AES-CBC mode requires all encrypted cipher blocks to be 16 bytes. The
|
||||||
|
// partial blocks are left unencrypted.
|
||||||
|
if (encryption_mode_ == kEncryptionModeAesCbc)
|
||||||
|
encryption_data_size -= encryption_data_size % kCencBlockSize;
|
||||||
|
EncryptBytes(data, encryption_data_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
traf()->sample_encryption.sample_encryption_entries.push_back(
|
traf()->sample_encryption.sample_encryption_entries.push_back(
|
||||||
|
|
|
@ -78,7 +78,7 @@ Status Encryptor::EncryptFrame(scoped_refptr<MediaSample> sample,
|
||||||
uint8_t* sample_data = sample->writable_data();
|
uint8_t* sample_data = sample->writable_data();
|
||||||
|
|
||||||
// Encrypt the data in-place.
|
// Encrypt the data in-place.
|
||||||
if (!encryptor_->EncryptData(sample_data, sample_size, sample_data)) {
|
if (!encryptor_->Encrypt(sample_data, sample_size, sample_data)) {
|
||||||
return Status(error::MUXER_FAILURE, "Failed to encrypt the frame.");
|
return Status(error::MUXER_FAILURE, "Failed to encrypt the frame.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1114,7 +1114,7 @@ bool WvmMediaParser::ProcessEcm() {
|
||||||
encryption_key.key.begin(),
|
encryption_key.key.begin(),
|
||||||
encryption_key.key.begin() + kAssetKeySizeBytes);
|
encryption_key.key.begin() + kAssetKeySizeBytes);
|
||||||
std::vector<uint8_t> iv(kInitializationVectorSizeBytes);
|
std::vector<uint8_t> iv(kInitializationVectorSizeBytes);
|
||||||
AesCbcCtsDecryptor asset_decryptor;
|
AesCbcDecryptor asset_decryptor(kCtsPadding, !kChainAcrossCalls);
|
||||||
if (!asset_decryptor.InitializeWithIv(asset_key, iv)) {
|
if (!asset_decryptor.InitializeWithIv(asset_key, iv)) {
|
||||||
LOG(ERROR) << "Failed to initialize asset_decryptor.";
|
LOG(ERROR) << "Failed to initialize asset_decryptor.";
|
||||||
return false;
|
return false;
|
||||||
|
@ -1124,13 +1124,14 @@ bool WvmMediaParser::ProcessEcm() {
|
||||||
kEcmFlagsSizeBytes + kEcmContentKeySizeBytes +
|
kEcmFlagsSizeBytes + kEcmContentKeySizeBytes +
|
||||||
kEcmPaddingSizeBytes; // flags + contentKey + padding.
|
kEcmPaddingSizeBytes; // flags + contentKey + padding.
|
||||||
std::vector<uint8_t> content_key_buffer(content_key_buffer_size);
|
std::vector<uint8_t> content_key_buffer(content_key_buffer_size);
|
||||||
asset_decryptor.Decrypt(ecm_data, content_key_buffer_size,
|
CHECK(asset_decryptor.Decrypt(ecm_data, content_key_buffer_size,
|
||||||
content_key_buffer.data());
|
content_key_buffer.data()));
|
||||||
|
|
||||||
std::vector<uint8_t> decrypted_content_key_vec(
|
std::vector<uint8_t> decrypted_content_key_vec(
|
||||||
content_key_buffer.begin() + 4,
|
content_key_buffer.begin() + 4,
|
||||||
content_key_buffer.begin() + 20);
|
content_key_buffer.begin() + 20);
|
||||||
scoped_ptr<AesCbcCtsDecryptor> content_decryptor(new AesCbcCtsDecryptor);
|
scoped_ptr<AesCbcDecryptor> content_decryptor(
|
||||||
|
new AesCbcDecryptor(kCtsPadding, !kChainAcrossCalls));
|
||||||
if (!content_decryptor->InitializeWithIv(decrypted_content_key_vec, iv)) {
|
if (!content_decryptor->InitializeWithIv(decrypted_content_key_vec, iv)) {
|
||||||
LOG(ERROR) << "Failed to initialize content decryptor.";
|
LOG(ERROR) << "Failed to initialize content decryptor.";
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
class AesCbcCtsDecryptor;
|
class AesCbcDecryptor;
|
||||||
class KeySource;
|
class KeySource;
|
||||||
struct EncryptionKey;
|
struct EncryptionKey;
|
||||||
|
|
||||||
|
@ -252,7 +252,7 @@ class WvmMediaParser : public MediaParser {
|
||||||
std::deque<DemuxStreamIdMediaSample> media_sample_queue_;
|
std::deque<DemuxStreamIdMediaSample> media_sample_queue_;
|
||||||
std::vector<uint8_t> sample_data_;
|
std::vector<uint8_t> sample_data_;
|
||||||
KeySource* decryption_key_source_;
|
KeySource* decryption_key_source_;
|
||||||
scoped_ptr<AesCbcCtsDecryptor> content_decryptor_;
|
scoped_ptr<AesCbcDecryptor> content_decryptor_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(WvmMediaParser);
|
DISALLOW_COPY_AND_ASSIGN(WvmMediaParser);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue