Fix a few problems in AesCryptor classes

- CBC cryptors should accept IV of size 8 bytes - it will be zero
  extended to 16 bytes.
- Fixed iv() not updated problem in AesPatternCryptor.
- Replace kChainAcrossCalls with ConstantIvFlag enum flags.

Change-Id: I3fb4de0e8abbe891e6271e779373ba53f8df660d
This commit is contained in:
KongQun Yang 2016-04-13 10:52:41 -07:00 committed by Kongqun Yang
parent 66b82f87dd
commit 13202f91b6
17 changed files with 276 additions and 256 deletions

View File

@ -50,6 +50,14 @@ bool ValidateFixedCryptoFlags() {
"--enable_fixed_key_encryption")) { "--enable_fixed_key_encryption")) {
success = false; success = false;
} }
if (!FLAGS_iv.empty()) {
if (FLAGS_iv.size() != 8 * 2 && FLAGS_iv.size() != 16 * 2) {
PrintError(
"--iv should be either 8 bytes (16 hex digits) or 16 bytes (32 hex "
"digits).");
success = false;
}
}
// --pssh is associated with --enable_fix_key_encryption. // --pssh is associated with --enable_fix_key_encryption.
if (!ValidateFlag("pssh", if (!ValidateFlag("pssh",

View File

@ -384,12 +384,6 @@ bool RunPackager(const StreamDescriptorList& stream_descriptors) {
const FourCC protection_scheme = GetProtectionScheme(FLAGS_protection_scheme); const FourCC protection_scheme = GetProtectionScheme(FLAGS_protection_scheme);
if (protection_scheme == FOURCC_NULL) if (protection_scheme == FOURCC_NULL)
return false; return false;
if (protection_scheme == FOURCC_cbc1 || protection_scheme == FOURCC_cbcs) {
if (!FLAGS_iv.empty() && 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;

View File

@ -61,7 +61,7 @@ DEFINE_string(protection_scheme,
"protection schemes 'cens' or 'cbcs'. Note that if a " "protection schemes 'cens' or 'cbcs'. Note that if a "
"pattern-based protection scheme only applies to video stream; " "pattern-based protection scheme only applies to video stream; "
"audio stream will be encrypted using the corresponding " "audio stream will be encrypted using the corresponding "
"non-pattern-based encryption schemes, i.e. 'cenc' for 'cens', " "non-pattern-based protection schemes, i.e. 'cenc' for 'cens', "
"'cbc1' for 'cbcs'."); "'cbc1' for 'cbcs'.");
namespace edash_packager { namespace edash_packager {

View File

@ -6,6 +6,9 @@
#include "packager/media/base/aes_cryptor.h" #include "packager/media/base/aes_cryptor.h"
#include <string>
#include <vector>
#include <openssl/aes.h> #include <openssl/aes.h>
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/rand.h> #include <openssl/rand.h>
@ -13,10 +16,24 @@
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/base/stl_util.h" #include "packager/base/stl_util.h"
namespace {
// According to ISO/IEC 23001-7:2016 CENC spec, IV should be either
// 64-bit (8-byte) or 128-bit (16-byte).
bool IsIvSizeValid(size_t iv_size) {
return iv_size == 8 || iv_size == 16;
}
} // namespace
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
AesCryptor::AesCryptor() : aes_key_(new AES_KEY) {} AesCryptor::AesCryptor(ConstantIvFlag constant_iv_flag)
: aes_key_(new AES_KEY),
constant_iv_flag_(constant_iv_flag),
num_crypt_bytes_(0) {}
AesCryptor::~AesCryptor() {} AesCryptor::~AesCryptor() {}
bool AesCryptor::Crypt(const std::vector<uint8_t>& text, bool AesCryptor::Crypt(const std::vector<uint8_t>& text,
@ -26,8 +43,7 @@ bool AesCryptor::Crypt(const std::vector<uint8_t>& text,
const size_t text_size = text.size(); const size_t text_size = text.size();
crypt_text->resize(text_size + NumPaddingBytes(text_size)); crypt_text->resize(text_size + NumPaddingBytes(text_size));
size_t crypt_text_size = crypt_text->size(); size_t crypt_text_size = crypt_text->size();
if (!CryptInternal(text.data(), text_size, crypt_text->data(), if (!Crypt(text.data(), text_size, crypt_text->data(), &crypt_text_size)) {
&crypt_text_size)) {
return false; return false;
} }
DCHECK_LE(crypt_text_size, crypt_text->size()); DCHECK_LE(crypt_text_size, crypt_text->size());
@ -41,18 +57,53 @@ bool AesCryptor::Crypt(const std::string& text, std::string* crypt_text) {
const size_t text_size = text.size(); const size_t text_size = text.size();
crypt_text->resize(text_size + NumPaddingBytes(text_size)); crypt_text->resize(text_size + NumPaddingBytes(text_size));
size_t crypt_text_size = crypt_text->size(); size_t crypt_text_size = crypt_text->size();
if (!CryptInternal(reinterpret_cast<const uint8_t*>(text.data()), text_size, if (!Crypt(reinterpret_cast<const uint8_t*>(text.data()), text_size,
reinterpret_cast<uint8_t*>(string_as_array(crypt_text)), reinterpret_cast<uint8_t*>(string_as_array(crypt_text)),
&crypt_text_size)) &crypt_text_size))
return false; return false;
DCHECK_LE(crypt_text_size, crypt_text->size()); DCHECK_LE(crypt_text_size, crypt_text->size());
crypt_text->resize(crypt_text_size); crypt_text->resize(crypt_text_size);
return true; return true;
} }
size_t AesCryptor::NumPaddingBytes(size_t size) const { bool AesCryptor::SetIv(const std::vector<uint8_t>& iv) {
// No padding by default. if (!IsIvSizeValid(iv.size())) {
return 0; LOG(ERROR) << "Invalid IV size: " << iv.size();
return false;
}
iv_ = iv;
num_crypt_bytes_ = 0;
SetIvInternal();
return true;
}
void AesCryptor::UpdateIv() {
if (constant_iv_flag_ == kUseConstantIv)
return;
uint64_t increment = 0;
// As recommended in ISO/IEC 23001-7:2016 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.
// There is no official recommendation of how IV for next sample should be
// generated for CBC mode. We use the same generation algorithm as CTR here.
if (iv_.size() == 8) {
increment = 1;
} else {
DCHECK_EQ(16u, iv_.size());
increment = (num_crypt_bytes_ + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE;
}
for (int i = iv_.size() - 1; increment > 0 && i >= 0; --i) {
increment += iv_[i];
iv_[i] = increment & 0xFF;
increment >>= 8;
}
num_crypt_bytes_ = 0;
SetIvInternal();
} }
bool AesCryptor::GenerateRandomIv(FourCC protection_scheme, bool AesCryptor::GenerateRandomIv(FourCC protection_scheme,
@ -74,6 +125,11 @@ bool AesCryptor::GenerateRandomIv(FourCC protection_scheme,
return true; return true;
} }
size_t AesCryptor::NumPaddingBytes(size_t size) const {
// No padding by default.
return 0;
}
} // namespace media } // namespace media
} // namespace edash_packager } // namespace edash_packager

View File

@ -24,7 +24,18 @@ namespace media {
// implementations. // implementations.
class AesCryptor { class AesCryptor {
public: public:
AesCryptor(); enum ConstantIvFlag {
kUseConstantIv,
kDontUseConstantIv,
};
/// @param constant_iv_flag indicates whether a constant iv is used,
/// kUseConstantIv means that the same iv is used for all Crypt calls
/// until iv is changed via SetIv; otherwise, iv can be incremented
/// (for counter mode) or chained (for cipher block chaining mode)
/// internally inside Crypt call, i.e. iv will be updated across Crypt
/// calls.
explicit AesCryptor(ConstantIvFlag constant_iv_flag);
virtual ~AesCryptor(); virtual ~AesCryptor();
/// Initialize the cryptor with specified key and IV. /// Initialize the cryptor with specified key and IV.
@ -43,18 +54,28 @@ class AesCryptor {
/// @param crypt_text should have at least @a text_size bytes. /// @param crypt_text should have at least @a text_size bytes.
bool Crypt(const uint8_t* text, size_t text_size, uint8_t* crypt_text) { bool Crypt(const uint8_t* text, size_t text_size, uint8_t* crypt_text) {
size_t crypt_text_size = text_size; size_t crypt_text_size = text_size;
return CryptInternal(text, text_size, crypt_text, &crypt_text_size); return Crypt(text, text_size, crypt_text, &crypt_text_size);
}
bool Crypt(const uint8_t* text,
size_t text_size,
uint8_t* crypt_text,
size_t* crypt_text_size) {
if (constant_iv_flag_ == kUseConstantIv)
SetIvInternal();
else
num_crypt_bytes_ += text_size;
return CryptInternal(text, text_size, crypt_text, crypt_text_size);
} }
/// @} /// @}
/// 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; bool SetIv(const std::vector<uint8_t>& iv);
/// Update IV for next sample. As recommended in ISO/IEC 23001-7: IV need to /// Update IV for next sample. As recommended in ISO/IEC 23001-7: IV need to
/// be updated per sample for CENC. /// be updated per sample for CENC.
/// This is used by encryptors only. /// This is used by encryptors only. It is a NOP if using kUseConstantIv.
virtual void UpdateIv() = 0; void UpdateIv();
/// @return The current iv. /// @return The current iv.
const std::vector<uint8_t>& iv() const { return iv_; } const std::vector<uint8_t>& iv() const { return iv_; }
@ -67,7 +88,6 @@ class AesCryptor {
std::vector<uint8_t>* iv); std::vector<uint8_t>* iv);
protected: protected:
void set_iv(const std::vector<uint8_t>& iv) { iv_ = iv; }
const AES_KEY* aes_key() const { return aes_key_.get(); } const AES_KEY* aes_key() const { return aes_key_.get(); }
AES_KEY* mutable_aes_key() { return aes_key_.get(); } AES_KEY* mutable_aes_key() { return aes_key_.get(); }
@ -87,16 +107,27 @@ class AesCryptor {
uint8_t* crypt_text, uint8_t* crypt_text,
size_t* crypt_text_size) = 0; size_t* crypt_text_size) = 0;
// Internal implementation of SetIv, which setup internal iv.
virtual void SetIvInternal() = 0;
// |size| specifies the input text size. // |size| specifies the input text size.
// Return the number of padding bytes needed. // Return the number of padding bytes needed.
// Note: No paddings should be needed except for pkcs5-cbc encryptor. // Note: No paddings should be needed except for pkcs5-cbc encryptor.
virtual size_t NumPaddingBytes(size_t size) const; virtual size_t NumPaddingBytes(size_t size) const;
// Initialization vector, with size 8 or 16.
std::vector<uint8_t> iv_;
// Openssl AES_KEY. // Openssl AES_KEY.
scoped_ptr<AES_KEY> aes_key_; scoped_ptr<AES_KEY> aes_key_;
// Indicates whether a constant iv is used. Internal iv will be reset to
// |iv_| before calling Crypt if that is the case.
const ConstantIvFlag constant_iv_flag_;
// Initialization vector from by SetIv or InitializeWithIv, with size 8 or 16
// bytes.
std::vector<uint8_t> iv_;
// Tracks number of crypt bytes. It is used to calculate how many blocks
// should iv advance in UpdateIv(). It will be reset to 0 after iv is updated.
size_t num_crypt_bytes_;
DISALLOW_COPY_AND_ASSIGN(AesCryptor); DISALLOW_COPY_AND_ASSIGN(AesCryptor);
}; };

View File

@ -220,6 +220,20 @@ TEST_F(AesCtrEncryptorTest, 128BitIVBoundaryCaseEncryption) {
EXPECT_EQ(encrypted, encrypted_verify); EXPECT_EQ(encrypted, encrypted_verify);
} }
TEST_F(AesCtrEncryptorTest, 64BitIvUpdate) {
std::vector<uint8_t> iv_zero(kIv64Zero, kIv64Zero + arraysize(kIv64Zero));
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_zero));
// There are four blocks of text in |plaintext_|, but since iv is 8 bytes,
// iv should only be incremented by one when UpdateIv() is called.
std::vector<uint8_t> encrypted;
ASSERT_TRUE(encryptor_.Crypt(plaintext_, &encrypted));
std::vector<uint8_t> iv_one(kIv64One, kIv64One + arraysize(kIv64One));
encryptor_.UpdateIv();
EXPECT_EQ(iv_one, encryptor_.iv());
}
TEST_F(AesCtrEncryptorTest, GenerateRandomIv) { TEST_F(AesCtrEncryptorTest, GenerateRandomIv) {
const uint8_t kCencIvSize = 8; const uint8_t kCencIvSize = 8;
std::vector<uint8_t> iv; std::vector<uint8_t> iv;
@ -297,8 +311,10 @@ INSTANTIATE_TEST_CASE_P(IvTestCases,
class AesCbcTest : public ::testing::Test { class AesCbcTest : public ::testing::Test {
public: public:
AesCbcTest() AesCbcTest()
: encryptor_(new AesCbcEncryptor(kPkcs5Padding, !kChainAcrossCalls)), : encryptor_(
decryptor_(new AesCbcDecryptor(kPkcs5Padding, !kChainAcrossCalls)), new AesCbcEncryptor(kPkcs5Padding, AesCryptor::kUseConstantIv)),
decryptor_(
new AesCbcDecryptor(kPkcs5Padding, AesCryptor::kUseConstantIv)),
key_(kAesKey, kAesKey + arraysize(kAesKey)), key_(kAesKey, kAesKey + arraysize(kAesKey)),
iv_(kAesIv, kAesIv + arraysize(kAesIv)) {} iv_(kAesIv, kAesIv + arraysize(kAesIv)) {}
@ -450,25 +466,21 @@ TEST_F(AesCbcTest, NoPaddingNoChainAcrossCalls) {
std::vector<uint8_t> ciphertext(kCiphertext, std::vector<uint8_t> ciphertext(kCiphertext,
kCiphertext + arraysize(kCiphertext)); kCiphertext + arraysize(kCiphertext));
AesCbcEncryptor encryptor(kNoPadding, !kChainAcrossCalls); AesCbcEncryptor encryptor(kNoPadding, AesCryptor::kUseConstantIv);
ASSERT_TRUE(encryptor.InitializeWithIv(key_, iv_)); ASSERT_TRUE(encryptor.InitializeWithIv(key_, iv_));
std::vector<uint8_t> encrypted; std::vector<uint8_t> encrypted;
ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted)); ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted));
EXPECT_EQ(ciphertext, encrypted); EXPECT_EQ(ciphertext, encrypted);
// Iv should not have been updated.
EXPECT_EQ(iv_, encryptor.iv());
ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted)); ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted));
EXPECT_EQ(ciphertext, encrypted); EXPECT_EQ(ciphertext, encrypted);
AesCbcDecryptor decryptor(kNoPadding, !kChainAcrossCalls); AesCbcDecryptor decryptor(kNoPadding, AesCryptor::kUseConstantIv);
ASSERT_TRUE(decryptor.InitializeWithIv(key_, iv_)); ASSERT_TRUE(decryptor.InitializeWithIv(key_, iv_));
std::vector<uint8_t> decrypted; std::vector<uint8_t> decrypted;
ASSERT_TRUE(decryptor.Crypt(ciphertext, &decrypted)); ASSERT_TRUE(decryptor.Crypt(ciphertext, &decrypted));
EXPECT_EQ(plaintext, decrypted); EXPECT_EQ(plaintext, decrypted);
// Iv should not have been updated.
EXPECT_EQ(iv_, encryptor.iv());
ASSERT_TRUE(decryptor.Crypt(ciphertext, &decrypted)); ASSERT_TRUE(decryptor.Crypt(ciphertext, &decrypted));
EXPECT_EQ(plaintext, decrypted); EXPECT_EQ(plaintext, decrypted);
} }
@ -494,26 +506,23 @@ TEST_F(AesCbcTest, NoPaddingChainAcrossCalls) {
std::vector<uint8_t> ciphertext2(kCiphertext2, std::vector<uint8_t> ciphertext2(kCiphertext2,
kCiphertext2 + arraysize(kCiphertext2)); kCiphertext2 + arraysize(kCiphertext2));
AesCbcEncryptor encryptor(kNoPadding, kChainAcrossCalls); AesCbcEncryptor encryptor(kNoPadding, AesCryptor::kDontUseConstantIv);
ASSERT_TRUE(encryptor.InitializeWithIv(key_, iv_)); ASSERT_TRUE(encryptor.InitializeWithIv(key_, iv_));
std::vector<uint8_t> encrypted; std::vector<uint8_t> encrypted;
ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted)); ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted));
EXPECT_EQ(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. // If run encrypt again, the result will be different.
ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted)); ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted));
EXPECT_NE(ciphertext, ciphertext2);
EXPECT_EQ(ciphertext2, encrypted); EXPECT_EQ(ciphertext2, encrypted);
AesCbcDecryptor decryptor(kNoPadding, kChainAcrossCalls); AesCbcDecryptor decryptor(kNoPadding, AesCryptor::kDontUseConstantIv);
ASSERT_TRUE(decryptor.InitializeWithIv(key_, iv_)); ASSERT_TRUE(decryptor.InitializeWithIv(key_, iv_));
std::vector<uint8_t> decrypted; std::vector<uint8_t> decrypted;
ASSERT_TRUE(decryptor.Crypt(ciphertext, &decrypted)); ASSERT_TRUE(decryptor.Crypt(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. // If run decrypt on ciphertext2 now, it will return the original plaintext.
ASSERT_TRUE(decryptor.Crypt(ciphertext2, &decrypted)); ASSERT_TRUE(decryptor.Crypt(ciphertext2, &decrypted));
EXPECT_EQ(plaintext, decrypted); EXPECT_EQ(plaintext, decrypted);
@ -524,9 +533,11 @@ TEST_F(AesCbcTest, UnsupportedKeySize) {
EXPECT_FALSE(decryptor_->InitializeWithIv(std::vector<uint8_t>(15, 0), iv_)); EXPECT_FALSE(decryptor_->InitializeWithIv(std::vector<uint8_t>(15, 0), iv_));
} }
TEST_F(AesCbcTest, UnsupportedIvSize) { TEST_F(AesCbcTest, VariousIvSize) {
EXPECT_FALSE(encryptor_->InitializeWithIv(key_, std::vector<uint8_t>(14, 0))); EXPECT_FALSE(encryptor_->InitializeWithIv(key_, std::vector<uint8_t>(14, 0)));
EXPECT_FALSE(decryptor_->InitializeWithIv(key_, std::vector<uint8_t>(8, 0))); EXPECT_FALSE(decryptor_->InitializeWithIv(key_, std::vector<uint8_t>(7, 0)));
EXPECT_FALSE(decryptor_->InitializeWithIv(key_, std::vector<uint8_t>(1, 0)));
EXPECT_TRUE(decryptor_->InitializeWithIv(key_, std::vector<uint8_t>(8, 0)));
} }
TEST_F(AesCbcTest, Pkcs5CipherTextNotMultipleOfBlockSize) { TEST_F(AesCbcTest, Pkcs5CipherTextNotMultipleOfBlockSize) {
@ -584,10 +595,10 @@ class AesCbcCryptorVerificationTest
public ::testing::WithParamInterface<CbcTestCase> {}; public ::testing::WithParamInterface<CbcTestCase> {};
TEST_P(AesCbcCryptorVerificationTest, EncryptDecryptTest) { TEST_P(AesCbcCryptorVerificationTest, EncryptDecryptTest) {
encryptor_.reset( encryptor_.reset(new AesCbcEncryptor(GetParam().padding_scheme,
new AesCbcEncryptor(GetParam().padding_scheme, !kChainAcrossCalls)); AesCryptor::kUseConstantIv));
decryptor_.reset( decryptor_.reset(new AesCbcDecryptor(GetParam().padding_scheme,
new AesCbcDecryptor(GetParam().padding_scheme, !kChainAcrossCalls)); AesCryptor::kUseConstantIv));
std::vector<uint8_t> plaintext; std::vector<uint8_t> plaintext;
std::string plaintext_hex(GetParam().plaintext_hex); std::string plaintext_hex(GetParam().plaintext_hex);

View File

@ -22,13 +22,16 @@ bool IsKeySizeValidForAes(size_t key_size) {
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
AesCbcDecryptor::AesCbcDecryptor(CbcPaddingScheme padding_scheme)
: AesCbcDecryptor(padding_scheme, kDontUseConstantIv) {}
AesCbcDecryptor::AesCbcDecryptor(CbcPaddingScheme padding_scheme, AesCbcDecryptor::AesCbcDecryptor(CbcPaddingScheme padding_scheme,
bool chain_across_calls) ConstantIvFlag constant_iv_flag)
: padding_scheme_(padding_scheme), : AesCryptor(constant_iv_flag), padding_scheme_(padding_scheme) {
chain_across_calls_(chain_across_calls) {
if (padding_scheme_ != kNoPadding) { if (padding_scheme_ != kNoPadding) {
CHECK(!chain_across_calls) << "cipher block chain across calls only makes " CHECK_EQ(constant_iv_flag, kUseConstantIv)
"sense if the padding_scheme is kNoPadding."; << "non-constant iv (cipher block chain across calls) only makes sense "
"if the padding_scheme is kNoPadding.";
} }
} }
@ -46,16 +49,6 @@ bool AesCbcDecryptor::InitializeWithIv(const std::vector<uint8_t>& key,
return SetIv(iv); return SetIv(iv);
} }
bool AesCbcDecryptor::SetIv(const std::vector<uint8_t>& iv) {
if (iv.size() != AES_BLOCK_SIZE) {
LOG(ERROR) << "Invalid IV size: " << iv.size();
return false;
}
set_iv(iv);
return true;
}
bool AesCbcDecryptor::CryptInternal(const uint8_t* ciphertext, bool AesCbcDecryptor::CryptInternal(const uint8_t* ciphertext,
size_t ciphertext_size, size_t ciphertext_size,
uint8_t* plaintext, uint8_t* plaintext,
@ -82,14 +75,11 @@ bool AesCbcDecryptor::CryptInternal(const uint8_t* ciphertext,
} }
DCHECK(plaintext); DCHECK(plaintext);
std::vector<uint8_t> local_iv(iv());
const size_t residual_block_size = ciphertext_size % AES_BLOCK_SIZE; const size_t residual_block_size = ciphertext_size % AES_BLOCK_SIZE;
const size_t cbc_size = ciphertext_size - residual_block_size; const size_t cbc_size = ciphertext_size - residual_block_size;
if (residual_block_size == 0) { if (residual_block_size == 0) {
AES_cbc_encrypt(ciphertext, plaintext, ciphertext_size, aes_key(), AES_cbc_encrypt(ciphertext, plaintext, ciphertext_size, aes_key(),
local_iv.data(), AES_DECRYPT); internal_iv_.data(), AES_DECRYPT);
if (chain_across_calls_)
set_iv(local_iv);
if (padding_scheme_ != kPkcs5Padding) if (padding_scheme_ != kPkcs5Padding)
return true; return true;
@ -103,10 +93,8 @@ bool AesCbcDecryptor::CryptInternal(const uint8_t* ciphertext,
*plaintext_size -= num_padding_bytes; *plaintext_size -= num_padding_bytes;
return true; return true;
} else if (padding_scheme_ == kNoPadding) { } else if (padding_scheme_ == kNoPadding) {
AES_cbc_encrypt(ciphertext, plaintext, cbc_size, aes_key(), local_iv.data(), AES_cbc_encrypt(ciphertext, plaintext, cbc_size, aes_key(),
AES_DECRYPT); internal_iv_.data(), AES_DECRYPT);
if (chain_across_calls_)
set_iv(local_iv);
// The residual block is not encrypted. // The residual block is not encrypted.
memcpy(plaintext + cbc_size, ciphertext + cbc_size, residual_block_size); memcpy(plaintext + cbc_size, ciphertext + cbc_size, residual_block_size);
@ -117,7 +105,6 @@ bool AesCbcDecryptor::CryptInternal(const uint8_t* ciphertext,
return false; return false;
} }
DCHECK(!chain_across_calls_);
DCHECK_EQ(padding_scheme_, kCtsPadding); DCHECK_EQ(padding_scheme_, kCtsPadding);
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.
@ -128,7 +115,7 @@ bool AesCbcDecryptor::CryptInternal(const uint8_t* ciphertext,
// AES-CBC decrypt everything up to the next-to-last full block. // AES-CBC decrypt everything up to the next-to-last full block.
if (cbc_size > AES_BLOCK_SIZE) { if (cbc_size > AES_BLOCK_SIZE) {
AES_cbc_encrypt(ciphertext, plaintext, cbc_size - AES_BLOCK_SIZE, aes_key(), AES_cbc_encrypt(ciphertext, plaintext, cbc_size - AES_BLOCK_SIZE, aes_key(),
local_iv.data(), AES_DECRYPT); internal_iv_.data(), AES_DECRYPT);
} }
const uint8_t* next_to_last_ciphertext_block = const uint8_t* next_to_last_ciphertext_block =
@ -162,9 +149,14 @@ bool AesCbcDecryptor::CryptInternal(const uint8_t* ciphertext,
// Decrypt the next-to-last full block. // Decrypt the next-to-last full block.
AES_cbc_encrypt(next_to_last_plaintext_block, next_to_last_plaintext_block, AES_cbc_encrypt(next_to_last_plaintext_block, next_to_last_plaintext_block,
AES_BLOCK_SIZE, aes_key(), local_iv.data(), AES_DECRYPT); AES_BLOCK_SIZE, aes_key(), internal_iv_.data(), AES_DECRYPT);
return true; return true;
} }
void AesCbcDecryptor::SetIvInternal() {
internal_iv_ = iv();
internal_iv_.resize(AES_BLOCK_SIZE, 0);
}
} // namespace media } // namespace media
} // namespace edash_packager } // namespace edash_packager

View File

@ -24,22 +24,28 @@ using AesCtrDecryptor = AesCtrEncryptor;
/// Class which implements AES-CBC (Cipher block chaining) decryption. /// Class which implements AES-CBC (Cipher block chaining) decryption.
class AesCbcDecryptor : public AesCryptor { class AesCbcDecryptor : public AesCryptor {
public: public:
/// Creates a AesCbcDecryptor with continous cipher block chain across Crypt
/// calls.
/// @param padding_scheme indicates the padding scheme used. Currently /// @param padding_scheme indicates the padding scheme used. Currently
/// supported schemes: kNoPadding, kPkcs5Padding, kCtsPadding. /// supported schemes: kNoPadding, kPkcs5Padding, kCtsPadding.
/// @param chain_across_calls indicates whether there is a continuous cipher explicit AesCbcDecryptor(CbcPaddingScheme padding_scheme);
/// block chain across calls for Decrypt function. If it is false, iv
/// is not updated across Decrypt function calls. /// @param padding_scheme indicates the padding scheme used. Currently
AesCbcDecryptor(CbcPaddingScheme padding_scheme, bool chain_across_calls); /// supported schemes: kNoPadding, kPkcs5Padding, kCtsPadding.
/// @param constant_iv_flag indicates whether a constant iv is used,
/// kUseConstantIv means that the same iv is used for all Crypt calls
/// until iv is changed via SetIv; otherwise, iv is updated internally
/// and there is a continuous cipher block chain across Crypt calls
/// util iv is changed explicitly via SetIv or UpdateIv functions.
AesCbcDecryptor(CbcPaddingScheme padding_scheme,
ConstantIvFlag constant_iv_flag);
~AesCbcDecryptor() override; ~AesCbcDecryptor() override;
/// @name AesCryptor implementation overrides. /// @name AesCryptor 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 SetIv(const std::vector<uint8_t>& iv) override;
void UpdateIv() override {
// Nop for decryptor.
}
/// @} /// @}
private: private:
@ -47,9 +53,11 @@ class AesCbcDecryptor : public AesCryptor {
size_t ciphertext_size, size_t ciphertext_size,
uint8_t* plaintext, uint8_t* plaintext,
size_t* plaintext_size) override; size_t* plaintext_size) override;
void SetIvInternal() override;
const CbcPaddingScheme padding_scheme_; const CbcPaddingScheme padding_scheme_;
const bool chain_across_calls_; // 16-byte internal iv for crypto operations.
std::vector<uint8_t> internal_iv_;
DISALLOW_COPY_AND_ASSIGN(AesCbcDecryptor); DISALLOW_COPY_AND_ASSIGN(AesCbcDecryptor);
}; };

View File

@ -15,16 +15,13 @@ namespace {
// Increment an 8-byte counter by 1. Return true if overflowed. // Increment an 8-byte counter by 1. Return true if overflowed.
bool Increment64(uint8_t* counter) { bool Increment64(uint8_t* counter) {
DCHECK(counter); DCHECK(counter);
for (int i = 7; i >= 0; --i) for (int i = 7; i >= 0; --i) {
if (++counter[i] != 0) if (++counter[i] != 0)
return false; return false;
}
return true; return true;
} }
// According to ISO/IEC FDIS 23001-7: CENC spec, IV should be either
// 64-bit (8-byte) or 128-bit (16-byte).
bool IsIvSizeValid(size_t iv_size) { return iv_size == 8 || iv_size == 16; }
// AES defines three key sizes: 128, 192 and 256 bits. // AES defines three key sizes: 128, 192 and 256 bits.
bool IsKeySizeValidForAes(size_t key_size) { 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;
@ -35,7 +32,8 @@ bool IsKeySizeValidForAes(size_t key_size) {
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
AesEncryptor::AesEncryptor() {} AesEncryptor::AesEncryptor(ConstantIvFlag constant_iv_flag)
: AesCryptor(constant_iv_flag) {}
AesEncryptor::~AesEncryptor() {} AesEncryptor::~AesEncryptor() {}
bool AesEncryptor::InitializeWithIv(const std::vector<uint8_t>& key, bool AesEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
@ -50,55 +48,15 @@ bool AesEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
return SetIv(iv); return SetIv(iv);
} }
// We don't support constant iv for counter mode, as we don't have a use case
// for that.
AesCtrEncryptor::AesCtrEncryptor() AesCtrEncryptor::AesCtrEncryptor()
: block_offset_(0), : AesEncryptor(kDontUseConstantIv),
encrypted_counter_(AES_BLOCK_SIZE, 0), block_offset_(0),
counter_overflow_(false) {} encrypted_counter_(AES_BLOCK_SIZE, 0) {}
AesCtrEncryptor::~AesCtrEncryptor() {} AesCtrEncryptor::~AesCtrEncryptor() {}
void AesCtrEncryptor::UpdateIv() {
block_offset_ = 0;
// 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) {
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())) {
LOG(ERROR) << "Invalid IV size: " << iv.size();
return false;
}
block_offset_ = 0;
set_iv(iv);
counter_ = iv;
counter_.resize(AES_BLOCK_SIZE, 0);
return true;
}
bool AesCtrEncryptor::CryptInternal(const uint8_t* plaintext, bool AesCtrEncryptor::CryptInternal(const uint8_t* plaintext,
size_t plaintext_size, size_t plaintext_size,
@ -119,13 +77,12 @@ bool AesCtrEncryptor::CryptInternal(const uint8_t* plaintext,
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()); 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 23001-7:2016 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
// subsequent block of sample data processed and is kept in network byte // subsequent block of sample data processed and is kept in network byte
// order. // order.
if (Increment64(&counter_[8])) Increment64(&counter_[8]);
counter_overflow_ = true;
} }
ciphertext[i] = plaintext[i] ^ encrypted_counter_[block_offset_]; ciphertext[i] = plaintext[i] ^ encrypted_counter_[block_offset_];
block_offset_ = (block_offset_ + 1) % AES_BLOCK_SIZE; block_offset_ = (block_offset_ + 1) % AES_BLOCK_SIZE;
@ -133,37 +90,27 @@ bool AesCtrEncryptor::CryptInternal(const uint8_t* plaintext,
return true; return true;
} }
void AesCtrEncryptor::SetIvInternal() {
block_offset_ = 0;
counter_ = iv();
counter_.resize(AES_BLOCK_SIZE, 0);
}
AesCbcEncryptor::AesCbcEncryptor(CbcPaddingScheme padding_scheme)
: AesCbcEncryptor(padding_scheme, kDontUseConstantIv) {}
AesCbcEncryptor::AesCbcEncryptor(CbcPaddingScheme padding_scheme, AesCbcEncryptor::AesCbcEncryptor(CbcPaddingScheme padding_scheme,
bool chain_across_calls) ConstantIvFlag constant_iv_flag)
: padding_scheme_(padding_scheme), : AesEncryptor(constant_iv_flag), padding_scheme_(padding_scheme) {
chain_across_calls_(chain_across_calls) {
if (padding_scheme_ != kNoPadding) { if (padding_scheme_ != kNoPadding) {
CHECK(!chain_across_calls) << "cipher block chain across calls only makes " CHECK_EQ(constant_iv_flag, kUseConstantIv)
"sense if the padding_scheme is kNoPadding."; << "non-constant iv (cipher block chain across calls) only makes sense "
"if the padding_scheme is kNoPadding.";
} }
} }
AesCbcEncryptor::~AesCbcEncryptor() {} AesCbcEncryptor::~AesCbcEncryptor() {}
void AesCbcEncryptor::UpdateIv() {
// From CENC spec: CBC mode Initialization Vectors need not be unique per
// 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
// cipher block (a continous cipher block chain across samples), or (2)
// generated by incrementing the previuos IV by the number of cipher blocks in the last
// sample or (3) by a fixed amount. We use method (1) here. No separate IV
// update is needed.
}
bool AesCbcEncryptor::SetIv(const std::vector<uint8_t>& iv) {
if (iv.size() != AES_BLOCK_SIZE) {
LOG(ERROR) << "Invalid IV size: " << iv.size();
return false;
}
set_iv(iv);
return true;
}
bool AesCbcEncryptor::CryptInternal(const uint8_t* plaintext, bool AesCbcEncryptor::CryptInternal(const uint8_t* plaintext,
size_t plaintext_size, size_t plaintext_size,
uint8_t* ciphertext, uint8_t* ciphertext,
@ -182,22 +129,18 @@ bool AesCbcEncryptor::CryptInternal(const uint8_t* plaintext,
// Encrypt everything but the residual block using CBC. // Encrypt everything but the residual block using CBC.
const size_t cbc_size = plaintext_size - residual_block_size; const size_t cbc_size = plaintext_size - residual_block_size;
std::vector<uint8_t> local_iv(iv());
if (cbc_size != 0) { if (cbc_size != 0) {
AES_cbc_encrypt(plaintext, ciphertext, cbc_size, aes_key(), local_iv.data(), AES_cbc_encrypt(plaintext, ciphertext, cbc_size, aes_key(),
AES_ENCRYPT); internal_iv_.data(), AES_ENCRYPT);
} else if (padding_scheme_ == kCtsPadding) { } else if (padding_scheme_ == kCtsPadding) {
// Don't have a full block, leave unencrypted. // Don't have a full block, leave unencrypted.
memcpy(ciphertext, plaintext, plaintext_size); memcpy(ciphertext, plaintext, plaintext_size);
return true; return true;
} }
if (residual_block_size == 0 && padding_scheme_ != kPkcs5Padding) { if (residual_block_size == 0 && padding_scheme_ != kPkcs5Padding) {
if (chain_across_calls_)
set_iv(local_iv);
// No residual block. No need to do padding. // No residual block. No need to do padding.
return true; return true;
} }
DCHECK(!chain_across_calls_);
if (padding_scheme_ == kNoPadding) { if (padding_scheme_ == kNoPadding) {
// The residual block is left unencrypted. // The residual block is left unencrypted.
@ -216,7 +159,8 @@ bool AesCbcEncryptor::CryptInternal(const uint8_t* plaintext,
// Pad residue block with PKCS5 padding. // Pad residue block with PKCS5 padding.
residual_block.resize(AES_BLOCK_SIZE, static_cast<char>(num_padding_bytes)); residual_block.resize(AES_BLOCK_SIZE, static_cast<char>(num_padding_bytes));
AES_cbc_encrypt(residual_block.data(), residual_ciphertext_block, AES_cbc_encrypt(residual_block.data(), residual_ciphertext_block,
AES_BLOCK_SIZE, aes_key(), local_iv.data(), AES_ENCRYPT); AES_BLOCK_SIZE, aes_key(), internal_iv_.data(),
AES_ENCRYPT);
} else { } else {
DCHECK_EQ(num_padding_bytes, 0u); DCHECK_EQ(num_padding_bytes, 0u);
DCHECK_EQ(padding_scheme_, kCtsPadding); DCHECK_EQ(padding_scheme_, kCtsPadding);
@ -224,7 +168,8 @@ bool AesCbcEncryptor::CryptInternal(const uint8_t* plaintext,
// Zero-pad the residual block and encrypt using CBC. // Zero-pad the residual block and encrypt using CBC.
residual_block.resize(AES_BLOCK_SIZE, 0); residual_block.resize(AES_BLOCK_SIZE, 0);
AES_cbc_encrypt(residual_block.data(), residual_block.data(), AES_cbc_encrypt(residual_block.data(), residual_block.data(),
AES_BLOCK_SIZE, aes_key(), local_iv.data(), AES_ENCRYPT); AES_BLOCK_SIZE, aes_key(), internal_iv_.data(),
AES_ENCRYPT);
// Replace the last full block with the zero-padded, encrypted residual // Replace the last full block with the zero-padded, encrypted residual
// block, and replace the residual block with the equivalent portion of the // block, and replace the residual block with the equivalent portion of the
@ -239,6 +184,11 @@ bool AesCbcEncryptor::CryptInternal(const uint8_t* plaintext,
return true; return true;
} }
void AesCbcEncryptor::SetIvInternal() {
internal_iv_ = iv();
internal_iv_.resize(AES_BLOCK_SIZE, 0);
}
size_t AesCbcEncryptor::NumPaddingBytes(size_t size) const { size_t AesCbcEncryptor::NumPaddingBytes(size_t size) const {
return (padding_scheme_ == kPkcs5Padding) return (padding_scheme_ == kPkcs5Padding)
? (AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE)) ? (AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE))

View File

@ -21,7 +21,13 @@ namespace media {
class AesEncryptor : public AesCryptor { class AesEncryptor : public AesCryptor {
public: public:
AesEncryptor(); /// @param constant_iv_flag indicates whether a constant iv is used,
/// kUseConstantIv means that the same iv is used for all Crypt calls
/// until iv is changed via SetIv; otherwise, iv can be incremented
/// (for counter mode) or chained (for cipher block chaining mode)
/// internally inside Crypt call, i.e. iv will be updated across Crypt
/// calls.
explicit AesEncryptor(ConstantIvFlag constant_iv_flag);
~AesEncryptor() override; ~AesEncryptor() override;
/// Initialize the encryptor with specified key and IV. /// Initialize the encryptor with specified key and IV.
@ -39,17 +45,6 @@ class AesCtrEncryptor : public AesEncryptor {
AesCtrEncryptor(); AesCtrEncryptor();
~AesCtrEncryptor() override; ~AesCtrEncryptor() override;
/// @name AesCryptor implementation overrides.
/// @{
/// Update IV for next sample. @a block_offset_ is reset to 0.
/// As recommended in ISO/IEC FDIS 23001-7: CENC spec,
/// For 64-bit IV size, new_iv = old_iv + 1;
/// For 128-bit IV size, new_iv = old_iv + previous_sample_block_count.
void UpdateIv() override;
bool SetIv(const std::vector<uint8_t>& iv) override;
/// @}
uint32_t block_offset() const { return block_offset_; } uint32_t block_offset() const { return block_offset_; }
private: private:
@ -57,6 +52,7 @@ class AesCtrEncryptor : public AesEncryptor {
size_t plaintext_size, size_t plaintext_size,
uint8_t* ciphertext, uint8_t* ciphertext,
size_t* ciphertext_size) override; size_t* ciphertext_size) override;
void SetIvInternal() override;
// Current block offset. // Current block offset.
uint32_t block_offset_; uint32_t block_offset_;
@ -64,8 +60,6 @@ class AesCtrEncryptor : public AesEncryptor {
std::vector<uint8_t> counter_; std::vector<uint8_t> counter_;
// Encrypted counter. // Encrypted counter.
std::vector<uint8_t> encrypted_counter_; std::vector<uint8_t> encrypted_counter_;
// Keep track of whether the counter has overflowed.
bool counter_overflow_;
DISALLOW_COPY_AND_ASSIGN(AesCtrEncryptor); DISALLOW_COPY_AND_ASSIGN(AesCtrEncryptor);
}; };
@ -80,35 +74,38 @@ enum CbcPaddingScheme {
kCtsPadding, kCtsPadding,
}; };
const bool kChainAcrossCalls = true;
// Class which implements AES-CBC (Cipher block chaining) encryption. // Class which implements AES-CBC (Cipher block chaining) encryption.
class AesCbcEncryptor : public AesEncryptor { class AesCbcEncryptor : public AesEncryptor {
public: public:
/// Creates a AesCbcEncryptor with continous cipher block chain across Crypt
/// calls, i.e. AesCbcEncryptor(padding_scheme, kDontUseConstantIv).
/// @param padding_scheme indicates the padding scheme used. Currently /// @param padding_scheme indicates the padding scheme used. Currently
/// supported schemes: kNoPadding, kPkcs5Padding, kCtsPadding. /// supported schemes: kNoPadding, kPkcs5Padding, kCtsPadding.
/// @param chain_across_calls indicates whether there is a continuous cipher explicit AesCbcEncryptor(CbcPaddingScheme padding_scheme);
/// block chain across calls for Encrypt function. If it is false, iv
/// is not updated across Encrypt function calls. /// @param padding_scheme indicates the padding scheme used. Currently
AesCbcEncryptor(CbcPaddingScheme padding_scheme, bool chain_across_calls); /// supported schemes: kNoPadding, kPkcs5Padding, kCtsPadding.
/// @param constant_iv_flag indicates whether a constant iv is used,
/// kUseConstantIv means that the same iv is used for all Crypt calls
/// until iv is changed via SetIv; otherwise, iv is updated internally
/// and there is a continuous cipher block chain across Crypt calls
/// util iv is changed explicitly via SetIv or UpdateIv functions.
AesCbcEncryptor(CbcPaddingScheme padding_scheme,
ConstantIvFlag constant_iv_flag);
~AesCbcEncryptor() override; ~AesCbcEncryptor() override;
/// @name AesCryptor implementation overrides.
/// @{
void UpdateIv() override;
bool SetIv(const std::vector<uint8_t>& iv) override;
/// @}
private: private:
bool CryptInternal(const uint8_t* plaintext, bool CryptInternal(const uint8_t* plaintext,
size_t plaintext_size, size_t plaintext_size,
uint8_t* ciphertext, uint8_t* ciphertext,
size_t* ciphertext_size) override; size_t* ciphertext_size) override;
void SetIvInternal() override;
size_t NumPaddingBytes(size_t size) const override; size_t NumPaddingBytes(size_t size) const override;
const CbcPaddingScheme padding_scheme_; const CbcPaddingScheme padding_scheme_;
const bool chain_across_calls_; // 16-byte internal iv for crypto operations.
std::vector<uint8_t> internal_iv_;
DISALLOW_COPY_AND_ASSIGN(AesCbcEncryptor); DISALLOW_COPY_AND_ASSIGN(AesCbcEncryptor);
}; };

View File

@ -17,9 +17,9 @@ AesPatternCryptor::AesPatternCryptor(uint8_t crypt_byte_block,
uint8_t skip_byte_block, uint8_t skip_byte_block,
ConstantIvFlag constant_iv_flag, ConstantIvFlag constant_iv_flag,
scoped_ptr<AesCryptor> cryptor) scoped_ptr<AesCryptor> cryptor)
: crypt_byte_block_(crypt_byte_block), : AesCryptor(constant_iv_flag),
crypt_byte_block_(crypt_byte_block),
skip_byte_block_(skip_byte_block), skip_byte_block_(skip_byte_block),
constant_iv_flag_(constant_iv_flag),
cryptor_(cryptor.Pass()) { cryptor_(cryptor.Pass()) {
DCHECK(cryptor_); DCHECK(cryptor_);
} }
@ -28,26 +28,13 @@ AesPatternCryptor::~AesPatternCryptor() {}
bool AesPatternCryptor::InitializeWithIv(const std::vector<uint8_t>& key, bool AesPatternCryptor::InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv) { const std::vector<uint8_t>& iv) {
iv_ = iv; return SetIv(iv) && cryptor_->InitializeWithIv(key, iv);
return cryptor_->InitializeWithIv(key, iv);
}
bool AesPatternCryptor::SetIv(const std::vector<uint8_t>& iv) {
iv_ = iv;
return cryptor_->SetIv(iv);
}
void AesPatternCryptor::UpdateIv() {
cryptor_->UpdateIv();
} }
bool AesPatternCryptor::CryptInternal(const uint8_t* text, bool AesPatternCryptor::CryptInternal(const uint8_t* text,
size_t text_size, size_t text_size,
uint8_t* crypt_text, uint8_t* crypt_text,
size_t* crypt_text_size) { size_t* crypt_text_size) {
if (constant_iv_flag_ == AesPatternCryptor::kUseConstantIv)
CHECK(SetIv(iv_));
// |crypt_text_size| is always the same as |text_size| for pattern encryption. // |crypt_text_size| is always the same as |text_size| for pattern encryption.
if (*crypt_text_size < text_size) { if (*crypt_text_size < text_size) {
LOG(ERROR) << "Expecting output size of at least " << text_size LOG(ERROR) << "Expecting output size of at least " << text_size
@ -80,5 +67,9 @@ bool AesPatternCryptor::CryptInternal(const uint8_t* text,
return true; return true;
} }
void AesPatternCryptor::SetIvInternal() {
CHECK(cryptor_->SetIv(iv()));
}
} // namespace media } // namespace media
} // namespace edash_packager } // namespace edash_packager

View File

@ -15,11 +15,6 @@ namespace media {
/// Implements pattern-based encryption/decryption. /// Implements pattern-based encryption/decryption.
class AesPatternCryptor : public AesCryptor { class AesPatternCryptor : public AesCryptor {
public: public:
enum ConstantIvFlag {
kUseConstantIv,
kDontUseConstantIv,
};
/// @param crypt_byte_block indicates number of encrypted blocks (16-byte) in /// @param crypt_byte_block indicates number of encrypted blocks (16-byte) in
/// pattern based encryption. /// pattern based encryption.
/// @param skip_byte_block indicates number of unencrypted blocks (16-byte) /// @param skip_byte_block indicates number of unencrypted blocks (16-byte)
@ -42,22 +37,18 @@ class AesPatternCryptor : public AesCryptor {
/// @{ /// @{
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 SetIv(const std::vector<uint8_t>& iv) override;
void UpdateIv() override;
/// @} /// @}
protected: private:
bool CryptInternal(const uint8_t* text, bool CryptInternal(const uint8_t* text,
size_t text_size, size_t text_size,
uint8_t* crypt_text, uint8_t* crypt_text,
size_t* crypt_text_size) override; size_t* crypt_text_size) override;
void SetIvInternal() override;
private:
const uint8_t crypt_byte_block_; const uint8_t crypt_byte_block_;
const uint8_t skip_byte_block_; const uint8_t skip_byte_block_;
const ConstantIvFlag constant_iv_flag_;
scoped_ptr<AesCryptor> cryptor_; scoped_ptr<AesCryptor> cryptor_;
std::vector<uint8_t> iv_;
DISALLOW_COPY_AND_ASSIGN(AesPatternCryptor); DISALLOW_COPY_AND_ASSIGN(AesPatternCryptor);
}; };

View File

@ -24,16 +24,17 @@ namespace media {
class MockAesCryptor : public AesCryptor { class MockAesCryptor : public AesCryptor {
public: public:
MockAesCryptor() : AesCryptor(kDontUseConstantIv) {}
MOCK_METHOD2(InitializeWithIv, MOCK_METHOD2(InitializeWithIv,
bool(const std::vector<uint8_t>& key, bool(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv)); const std::vector<uint8_t>& iv));
MOCK_METHOD1(SetIv, bool(const std::vector<uint8_t>& iv));
MOCK_METHOD0(UpdateIv, void());
MOCK_METHOD4(CryptInternal, MOCK_METHOD4(CryptInternal,
bool(const uint8_t* text, bool(const uint8_t* text,
size_t text_size, size_t text_size,
uint8_t* crypt_text, uint8_t* crypt_text,
size_t* crypt_text_size)); size_t* crypt_text_size));
MOCK_METHOD0(SetIvInternal, void());
}; };
class AesPatternCryptorTest : public ::testing::Test { class AesPatternCryptorTest : public ::testing::Test {
@ -42,7 +43,7 @@ class AesPatternCryptorTest : public ::testing::Test {
: mock_cryptor_(new MockAesCryptor), : mock_cryptor_(new MockAesCryptor),
pattern_cryptor_(kCryptByteBlock, pattern_cryptor_(kCryptByteBlock,
kSkipByteBlock, kSkipByteBlock,
AesPatternCryptor::kDontUseConstantIv, AesCryptor::kDontUseConstantIv,
scoped_ptr<MockAesCryptor>(mock_cryptor_)) {} scoped_ptr<MockAesCryptor>(mock_cryptor_)) {}
protected: protected:
@ -55,11 +56,7 @@ TEST_F(AesPatternCryptorTest, InitializeWithIv) {
std::vector<uint8_t> iv(8, 'i'); std::vector<uint8_t> iv(8, 'i');
EXPECT_CALL(*mock_cryptor_, InitializeWithIv(key, iv)).WillOnce(Return(true)); EXPECT_CALL(*mock_cryptor_, InitializeWithIv(key, iv)).WillOnce(Return(true));
EXPECT_TRUE(pattern_cryptor_.InitializeWithIv(key, iv)); EXPECT_TRUE(pattern_cryptor_.InitializeWithIv(key, iv));
} EXPECT_EQ(iv, pattern_cryptor_.iv());
TEST_F(AesPatternCryptorTest, UpdateIv) {
EXPECT_CALL(*mock_cryptor_, UpdateIv());
pattern_cryptor_.UpdateIv();
} }
namespace { namespace {
@ -152,7 +149,7 @@ TEST(AesPatternCryptorConstIvTest, UseConstantIv) {
// SetIv will be called twice: // SetIv will be called twice:
// once by AesPatternCryptor::SetIv, // once by AesPatternCryptor::SetIv,
// once by AesPatternCryptor::Crypt, to make sure the same iv is used. // once by AesPatternCryptor::Crypt, to make sure the same iv is used.
EXPECT_CALL(*mock_cryptor, SetIv(iv)).Times(2).WillRepeatedly(Return(true)); EXPECT_CALL(*mock_cryptor, SetIvInternal()).Times(2);
EXPECT_TRUE(pattern_cryptor.SetIv(iv)); EXPECT_TRUE(pattern_cryptor.SetIv(iv));
std::string crypt_text; std::string crypt_text;
@ -167,7 +164,7 @@ TEST(AesPatternCryptorConstIvTest, DontUseConstantIv) {
std::vector<uint8_t> iv(8, 'i'); std::vector<uint8_t> iv(8, 'i');
// SetIv will be called only once by AesPatternCryptor::SetIv. // SetIv will be called only once by AesPatternCryptor::SetIv.
EXPECT_CALL(*mock_cryptor, SetIv(iv)).WillOnce(Return(true)); EXPECT_CALL(*mock_cryptor, SetIvInternal());
EXPECT_TRUE(pattern_cryptor.SetIv(iv)); EXPECT_TRUE(pattern_cryptor.SetIv(iv));
std::string crypt_text; std::string crypt_text;

View File

@ -46,22 +46,19 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config,
aes_decryptor.reset(new AesCtrDecryptor); aes_decryptor.reset(new AesCtrDecryptor);
break; break;
case FOURCC_cbc1: case FOURCC_cbc1:
aes_decryptor.reset(new AesCbcDecryptor(kNoPadding, kChainAcrossCalls)); aes_decryptor.reset(new AesCbcDecryptor(kNoPadding));
break; break;
case FOURCC_cens: case FOURCC_cens:
aes_decryptor.reset( aes_decryptor.reset(new AesPatternCryptor(
new AesPatternCryptor(decrypt_config->crypt_byte_block(), decrypt_config->crypt_byte_block(),
decrypt_config->skip_byte_block(), decrypt_config->skip_byte_block(), AesCryptor::kDontUseConstantIv,
AesPatternCryptor::kDontUseConstantIv, scoped_ptr<AesCryptor>(new AesCtrDecryptor)));
scoped_ptr<AesCryptor>(new AesCtrDecryptor)));
break; break;
case FOURCC_cbcs: case FOURCC_cbcs:
aes_decryptor.reset( aes_decryptor.reset(new AesPatternCryptor(
new AesPatternCryptor(decrypt_config->crypt_byte_block(), decrypt_config->crypt_byte_block(),
decrypt_config->skip_byte_block(), decrypt_config->skip_byte_block(), AesCryptor::kUseConstantIv,
AesPatternCryptor::kUseConstantIv, scoped_ptr<AesCryptor>(new AesCbcDecryptor(kNoPadding))));
scoped_ptr<AesCryptor>(new AesCbcDecryptor(
kNoPadding, kChainAcrossCalls))));
break; break;
default: default:
LOG(ERROR) << "Unsupported protection scheme: " LOG(ERROR) << "Unsupported protection scheme: "

View File

@ -41,7 +41,7 @@ AesRequestSigner* AesRequestSigner::CreateSigner(const std::string& signer_name,
} }
scoped_ptr<AesCbcEncryptor> encryptor( scoped_ptr<AesCbcEncryptor> encryptor(
new AesCbcEncryptor(kPkcs5Padding, !kChainAcrossCalls)); new AesCbcEncryptor(kPkcs5Padding, AesCryptor::kUseConstantIv));
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

@ -197,20 +197,17 @@ Status EncryptingFragmenter::CreateEncryptor() {
encryptor.reset(new AesCtrEncryptor); encryptor.reset(new AesCtrEncryptor);
break; break;
case FOURCC_cbc1: case FOURCC_cbc1:
encryptor.reset(new AesCbcEncryptor(kNoPadding, kChainAcrossCalls)); encryptor.reset(new AesCbcEncryptor(kNoPadding));
break; break;
case FOURCC_cens: case FOURCC_cens:
encryptor.reset( encryptor.reset(new AesPatternCryptor(
new AesPatternCryptor(crypt_byte_block(), skip_byte_block(), crypt_byte_block(), skip_byte_block(), AesCryptor::kDontUseConstantIv,
AesPatternCryptor::kDontUseConstantIv, scoped_ptr<AesCryptor>(new AesCtrEncryptor)));
scoped_ptr<AesCryptor>(new AesCtrEncryptor)));
break; break;
case FOURCC_cbcs: case FOURCC_cbcs:
encryptor.reset( encryptor.reset(new AesPatternCryptor(
new AesPatternCryptor(crypt_byte_block(), skip_byte_block(), crypt_byte_block(), skip_byte_block(), AesCryptor::kUseConstantIv,
AesPatternCryptor::kUseConstantIv, scoped_ptr<AesCryptor>(new AesCbcEncryptor(kNoPadding))));
scoped_ptr<AesCryptor>(new AesCbcEncryptor(
kNoPadding, kChainAcrossCalls))));
break; break;
default: default:
return Status(error::MUXER_FAILURE, "Unsupported protection scheme."); return Status(error::MUXER_FAILURE, "Unsupported protection scheme.");

View File

@ -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);
AesCbcDecryptor asset_decryptor(kCtsPadding, !kChainAcrossCalls); AesCbcDecryptor asset_decryptor(kCtsPadding, AesCryptor::kUseConstantIv);
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;
@ -1131,7 +1131,7 @@ bool WvmMediaParser::ProcessEcm() {
content_key_buffer.begin() + 4, content_key_buffer.begin() + 4,
content_key_buffer.begin() + 20); content_key_buffer.begin() + 20);
scoped_ptr<AesCbcDecryptor> content_decryptor( scoped_ptr<AesCbcDecryptor> content_decryptor(
new AesCbcDecryptor(kCtsPadding, !kChainAcrossCalls)); new AesCbcDecryptor(kCtsPadding, AesCryptor::kUseConstantIv));
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;