diff --git a/packager/media/base/aes_cryptor.cc b/packager/media/base/aes_cryptor.cc new file mode 100644 index 0000000000..e85914434f --- /dev/null +++ b/packager/media/base/aes_cryptor.cc @@ -0,0 +1,59 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "packager/media/base/aes_cryptor.h" + +#include + +#include "packager/base/logging.h" +#include "packager/base/stl_util.h" + +namespace edash_packager { +namespace media { + +AesCryptor::AesCryptor() : aes_key_(new AES_KEY) {} +AesCryptor::~AesCryptor() {} + +bool AesCryptor::Crypt(const std::vector& text, + std::vector* crypt_text) { + // Save text size to make it work for in-place conversion, since the + // next statement will update the text size. + const size_t text_size = text.size(); + crypt_text->resize(text_size + NumPaddingBytes(text_size)); + size_t crypt_text_size = crypt_text->size(); + if (!CryptInternal(text.data(), text_size, crypt_text->data(), + &crypt_text_size)) { + return false; + } + DCHECK_LE(crypt_text_size, crypt_text->size()); + crypt_text->resize(crypt_text_size); + return true; +} + +bool AesCryptor::Crypt(const std::string& text, std::string* crypt_text) { + // Save text size to make it work for in-place conversion, since the + // next statement will update the text size. + const size_t text_size = text.size(); + crypt_text->resize(text_size + NumPaddingBytes(text_size)); + size_t crypt_text_size = crypt_text->size(); + if (!CryptInternal(reinterpret_cast(text.data()), text_size, + reinterpret_cast(string_as_array(crypt_text)), + &crypt_text_size)) + return false; + DCHECK_LE(crypt_text_size, crypt_text->size()); + crypt_text->resize(crypt_text_size); + return true; +} + +size_t AesCryptor::NumPaddingBytes(size_t size) const { + // No padding by default. + return 0; +} + +} // namespace media +} // namespace edash_packager + + diff --git a/packager/media/base/aes_cryptor.h b/packager/media/base/aes_cryptor.h new file mode 100644 index 0000000000..5e4dc0be52 --- /dev/null +++ b/packager/media/base/aes_cryptor.h @@ -0,0 +1,100 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#ifndef PACKAGER_MEDIA_BASE_AES_CRYPTOR_H_ +#define PACKAGER_MEDIA_BASE_AES_CRYPTOR_H_ + +#include +#include + +#include "packager/base/macros.h" +#include "packager/base/memory/scoped_ptr.h" + +struct aes_key_st; +typedef struct aes_key_st AES_KEY; + +namespace edash_packager { +namespace media { + +// AES cryptor interface. Inherited by various AES encryptor and decryptor +// implementations. +class AesCryptor { + public: + AesCryptor(); + virtual ~AesCryptor(); + + /// Initialize the cryptor with specified key and IV. + /// @return true on successful initialization, false otherwise. + virtual bool InitializeWithIv(const std::vector& key, + const std::vector& iv) = 0; + + /// @name Various forms of crypt (Encrypt/Decrypt) calls. + /// It is an Encrypt function for encryptor and a Decrypt function for + /// decryptor. The text and crypt_text pointers can be the same address for + /// in place encryption/decryption. + /// @{ + bool Crypt(const std::vector& text, + std::vector* crypt_text); + bool Crypt(const std::string& text, std::string* crypt_text); + /// @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) { + size_t crypt_text_size = text_size; + return CryptInternal(text, text_size, crypt_text, &crypt_text_size); + } + /// @} + + /// Set IV. + /// @return true if successful, false if the input is invalid. + virtual bool SetIv(const std::vector& iv) = 0; + + /// Update IV for next sample. As recommended in ISO/IEC 23001-7: IV need to + /// be updated per sample for CENC. + /// This is used by encryptors only. + virtual void UpdateIv() = 0; + + /// @return The current iv. + const std::vector& iv() const { return iv_; } + + protected: + void set_iv(const std::vector& iv) { iv_ = iv; } + const AES_KEY* aes_key() const { return aes_key_.get(); } + AES_KEY* mutable_aes_key() { return aes_key_.get(); } + + private: + // Internal implementation of crypt function. + // |text| points to the input text. + // |text_size| is the size of input text. + // |crypt_text| points to the output encrypted or decrypted text, depends on + // whether it is an encryption or decryption. |text| and |crypt_text| can + // point to the same address for in place encryption/decryption. + // |crypt_text_size| contains the size of |crypt_text| and it will be updated + // to contain the actual encrypted/decrypted size for |crypt_text| on success. + // Return false if the input |crypt_text_size| is not large enough to hold the + // output |crypt_text| or if there is any error in encryption/decryption. + virtual bool CryptInternal(const uint8_t* text, + size_t text_size, + uint8_t* crypt_text, + size_t* crypt_text_size) = 0; + + // |size| specifies the input text size. + // Return the number of padding bytes needed. + // Note: No paddings should be needed except for pkcs5-cbc encryptor. + virtual size_t NumPaddingBytes(size_t size) const; + + // Initialization vector, with size 8 or 16. + std::vector iv_; + // Openssl AES_KEY. + scoped_ptr aes_key_; + + DISALLOW_COPY_AND_ASSIGN(AesCryptor); +}; + +} // namespace media +} // namespace edash_packager + +#endif // PACKAGER_MEDIA_BASE_AES_CRYPTOR_H_ + + diff --git a/packager/media/base/aes_encryptor_unittest.cc b/packager/media/base/aes_cryptor_unittest.cc similarity index 90% rename from packager/media/base/aes_encryptor_unittest.cc rename to packager/media/base/aes_cryptor_unittest.cc index 435c7ca272..8df13f9213 100644 --- a/packager/media/base/aes_encryptor_unittest.cc +++ b/packager/media/base/aes_cryptor_unittest.cc @@ -159,22 +159,22 @@ class AesCtrEncryptorTest : public testing::Test { TEST_F(AesCtrEncryptorTest, NistTestCase) { std::vector encrypted; - EXPECT_TRUE(encryptor_.Encrypt(plaintext_, &encrypted)); + ASSERT_TRUE(encryptor_.Crypt(plaintext_, &encrypted)); EXPECT_EQ(ciphertext_, encrypted); - EXPECT_TRUE(decryptor_.SetIv(iv_)); + ASSERT_TRUE(decryptor_.SetIv(iv_)); std::vector decrypted; - EXPECT_TRUE(decryptor_.Decrypt(encrypted, &decrypted)); + ASSERT_TRUE(decryptor_.Crypt(encrypted, &decrypted)); EXPECT_EQ(plaintext_, decrypted); } TEST_F(AesCtrEncryptorTest, NistTestCaseInplaceEncryptionDecryption) { std::vector buffer = plaintext_; - EXPECT_TRUE(encryptor_.Encrypt(&buffer[0], buffer.size(), &buffer[0])); + ASSERT_TRUE(encryptor_.Crypt(&buffer[0], buffer.size(), &buffer[0])); EXPECT_EQ(ciphertext_, buffer); - EXPECT_TRUE(decryptor_.SetIv(iv_)); - EXPECT_TRUE(decryptor_.Decrypt(&buffer[0], buffer.size(), &buffer[0])); + ASSERT_TRUE(decryptor_.SetIv(iv_)); + ASSERT_TRUE(decryptor_.Crypt(&buffer[0], buffer.size(), &buffer[0])); EXPECT_EQ(plaintext_, buffer); } @@ -184,13 +184,13 @@ TEST_F(AesCtrEncryptorTest, EncryptDecryptString) { "82E3AD1EF90C5CC09EB37F1B9EFBD99016441A1C15123F0777CD57BB993E14DA02"; std::string ciphertext; - EXPECT_TRUE(encryptor_.Encrypt(kPlaintext, &ciphertext)); + ASSERT_TRUE(encryptor_.Crypt(kPlaintext, &ciphertext)); EXPECT_EQ(kExpectedCiphertextInHex, base::HexEncode(ciphertext.data(), ciphertext.size())); std::string decrypted; - EXPECT_TRUE(decryptor_.SetIv(iv_)); - EXPECT_TRUE(decryptor_.Decrypt(ciphertext, &decrypted)); + ASSERT_TRUE(decryptor_.SetIv(iv_)); + ASSERT_TRUE(decryptor_.Crypt(ciphertext, &decrypted)); EXPECT_EQ(kPlaintext, decrypted); } @@ -202,7 +202,7 @@ TEST_F(AesCtrEncryptorTest, 128BitIVBoundaryCaseEncryption) { kIv128Max64 + arraysize(kIv128Max64)); ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_max64)); std::vector encrypted; - EXPECT_TRUE(encryptor_.Encrypt(plaintext_, &encrypted)); + ASSERT_TRUE(encryptor_.Crypt(plaintext_, &encrypted)); std::vector iv_one_and_three( kIv128OneAndThree, kIv128OneAndThree + arraysize(kIv128OneAndThree)); @@ -211,12 +211,12 @@ TEST_F(AesCtrEncryptorTest, 128BitIVBoundaryCaseEncryption) { ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_max64)); std::vector encrypted_verify(plaintext_.size(), 0); - EXPECT_TRUE( - encryptor_.Encrypt(&plaintext_[0], kAesBlockSize, &encrypted_verify[0])); + ASSERT_TRUE( + encryptor_.Crypt(&plaintext_[0], kAesBlockSize, &encrypted_verify[0])); std::vector iv_zero(kIv128Zero, kIv128Zero + arraysize(kIv128Zero)); ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_zero)); - EXPECT_TRUE(encryptor_.Encrypt(&plaintext_[kAesBlockSize], kAesBlockSize * 3, - &encrypted_verify[kAesBlockSize])); + ASSERT_TRUE(encryptor_.Crypt(&plaintext_[kAesBlockSize], kAesBlockSize * 3, + &encrypted_verify[kAesBlockSize])); EXPECT_EQ(encrypted, encrypted_verify); } @@ -224,8 +224,8 @@ TEST_F(AesCtrEncryptorTest, InitWithRandomIv) { const uint8_t kIvSize = 8; ASSERT_TRUE(encryptor_.InitializeWithRandomIv(key_, kIvSize)); ASSERT_EQ(kIvSize, encryptor_.iv().size()); - LOG(INFO) << "Random IV: " << base::HexEncode(&encryptor_.iv()[0], - encryptor_.iv().size()); + LOG(INFO) << "Random IV: " + << base::HexEncode(&encryptor_.iv()[0], encryptor_.iv().size()); } TEST_F(AesCtrEncryptorTest, UnsupportedKeySize) { @@ -252,19 +252,17 @@ TEST_P(AesCtrEncryptorSubsampleTest, NistTestCaseSubsamples) { std::vector encrypted(plaintext_.size(), 0); for (uint32_t i = 0, offset = 0; i < test_case->subsample_count; ++i) { uint32_t len = test_case->subsample_sizes[i]; - EXPECT_TRUE( - encryptor_.Encrypt(&plaintext_[offset], len, &encrypted[offset])); + ASSERT_TRUE(encryptor_.Crypt(&plaintext_[offset], len, &encrypted[offset])); offset += len; EXPECT_EQ(offset % kAesBlockSize, encryptor_.block_offset()); } EXPECT_EQ(ciphertext_, encrypted); - EXPECT_TRUE(decryptor_.SetIv(iv_)); + ASSERT_TRUE(decryptor_.SetIv(iv_)); std::vector decrypted(encrypted.size(), 0); for (uint32_t i = 0, offset = 0; i < test_case->subsample_count; ++i) { uint32_t len = test_case->subsample_sizes[i]; - EXPECT_TRUE( - decryptor_.Decrypt(&encrypted[offset], len, &decrypted[offset])); + ASSERT_TRUE(decryptor_.Crypt(&encrypted[offset], len, &decrypted[offset])); offset += len; EXPECT_EQ(offset % kAesBlockSize, decryptor_.block_offset()); } @@ -291,7 +289,7 @@ TEST_P(AesCtrEncryptorIvTest, IvTest) { ASSERT_TRUE(encryptor.InitializeWithIv(key, iv_test)); std::vector encrypted; - EXPECT_TRUE(encryptor.Encrypt(plaintext, &encrypted)); + ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted)); encryptor.UpdateIv(); EXPECT_EQ(iv_expected, encryptor.iv()); } @@ -300,9 +298,9 @@ INSTANTIATE_TEST_CASE_P(IvTestCases, AesCtrEncryptorIvTest, ::testing::ValuesIn(kIvTestCases)); -class AesCbcEncryptDecryptTest { +class AesCbcTest : public ::testing::Test { public: - AesCbcEncryptDecryptTest() + AesCbcTest() : encryptor_(new AesCbcEncryptor(kPkcs5Padding, !kChainAcrossCalls)), decryptor_(new AesCbcDecryptor(kPkcs5Padding, !kChainAcrossCalls)), key_(kAesKey, kAesKey + arraysize(kAesKey)), @@ -330,11 +328,11 @@ class AesCbcEncryptDecryptTest { ASSERT_TRUE(decryptor_->InitializeWithIv(key_, iv_)); T encrypted; - EXPECT_TRUE(encryptor_->Encrypt(plaintext, &encrypted)); + ASSERT_TRUE(encryptor_->Crypt(plaintext, &encrypted)); EXPECT_EQ(expected_ciphertext, encrypted); T decrypted; - EXPECT_TRUE(decryptor_->Decrypt(encrypted, &decrypted)); + ASSERT_TRUE(decryptor_->Crypt(encrypted, &decrypted)); EXPECT_EQ(plaintext, decrypted); } @@ -345,9 +343,9 @@ class AesCbcEncryptDecryptTest { ASSERT_TRUE(decryptor_->InitializeWithIv(key_, iv_)); T buffer(plaintext); - EXPECT_TRUE(encryptor_->Encrypt(buffer, &buffer)); + ASSERT_TRUE(encryptor_->Crypt(buffer, &buffer)); EXPECT_EQ(expected_ciphertext, buffer); - EXPECT_TRUE(decryptor_->Decrypt(buffer, &buffer)); + ASSERT_TRUE(decryptor_->Crypt(buffer, &buffer)); EXPECT_EQ(plaintext, buffer); } @@ -357,8 +355,6 @@ class AesCbcEncryptDecryptTest { std::vector iv_; }; -class AesCbcTest : public AesCbcEncryptDecryptTest, public testing::Test {}; - TEST_F(AesCbcTest, Aes256CbcPkcs5) { // NIST SP 800-38A test vector F.2.5 CBC-AES256.Encrypt. static const uint8_t kAesCbcKey[] = { @@ -462,22 +458,22 @@ TEST_F(AesCbcTest, NoPaddingNoChainAcrossCalls) { ASSERT_TRUE(encryptor.InitializeWithIv(key_, iv_)); std::vector encrypted; - ASSERT_TRUE(encryptor.Encrypt(plaintext, &encrypted)); + ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted)); EXPECT_EQ(ciphertext, encrypted); // Iv should not have been updated. EXPECT_EQ(iv_, encryptor.iv()); - ASSERT_TRUE(encryptor.Encrypt(plaintext, &encrypted)); + ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted)); EXPECT_EQ(ciphertext, encrypted); AesCbcDecryptor decryptor(kNoPadding, !kChainAcrossCalls); ASSERT_TRUE(decryptor.InitializeWithIv(key_, iv_)); std::vector decrypted; - ASSERT_TRUE(decryptor.Decrypt(ciphertext, &decrypted)); + ASSERT_TRUE(decryptor.Crypt(ciphertext, &decrypted)); EXPECT_EQ(plaintext, decrypted); // Iv should not have been updated. EXPECT_EQ(iv_, encryptor.iv()); - ASSERT_TRUE(decryptor.Decrypt(ciphertext, &decrypted)); + ASSERT_TRUE(decryptor.Crypt(ciphertext, &decrypted)); EXPECT_EQ(plaintext, decrypted); } @@ -506,24 +502,24 @@ TEST_F(AesCbcTest, NoPaddingChainAcrossCalls) { ASSERT_TRUE(encryptor.InitializeWithIv(key_, iv_)); std::vector encrypted; - ASSERT_TRUE(encryptor.Encrypt(plaintext, &encrypted)); + ASSERT_TRUE(encryptor.Crypt(plaintext, &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)); + ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted)); EXPECT_EQ(ciphertext2, encrypted); AesCbcDecryptor decryptor(kNoPadding, kChainAcrossCalls); ASSERT_TRUE(decryptor.InitializeWithIv(key_, iv_)); std::vector decrypted; - ASSERT_TRUE(decryptor.Decrypt(ciphertext, &decrypted)); + ASSERT_TRUE(decryptor.Crypt(ciphertext, &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)); + ASSERT_TRUE(decryptor.Crypt(ciphertext2, &decrypted)); EXPECT_EQ(plaintext, decrypted); } @@ -540,13 +536,13 @@ TEST_F(AesCbcTest, UnsupportedIvSize) { TEST_F(AesCbcTest, Pkcs5CipherTextNotMultipleOfBlockSize) { std::string plaintext; ASSERT_TRUE(decryptor_->InitializeWithIv(key_, iv_)); - EXPECT_FALSE(decryptor_->Decrypt("1", &plaintext)); + EXPECT_FALSE(decryptor_->Crypt("1", &plaintext)); } TEST_F(AesCbcTest, Pkcs5CipherTextEmpty) { std::string plaintext; ASSERT_TRUE(decryptor_->InitializeWithIv(key_, iv_)); - EXPECT_FALSE(decryptor_->Decrypt("", &plaintext)); + EXPECT_FALSE(decryptor_->Crypt("", &plaintext)); } struct CbcTestCase { @@ -585,8 +581,8 @@ const CbcTestCase kCbcTestCases[] = { }; class AesCbcCryptorVerificationTest - : public AesCbcEncryptDecryptTest, - public ::testing::TestWithParam {}; + : public AesCbcTest, + public ::testing::WithParamInterface {}; TEST_P(AesCbcCryptorVerificationTest, EncryptDecryptTest) { encryptor_.reset( diff --git a/packager/media/base/aes_decryptor.cc b/packager/media/base/aes_decryptor.cc index b62f47983d..58a8945c78 100644 --- a/packager/media/base/aes_decryptor.cc +++ b/packager/media/base/aes_decryptor.cc @@ -7,8 +7,6 @@ #include "packager/media/base/aes_decryptor.h" #include -#include -#include #include "packager/base/logging.h" @@ -24,60 +22,6 @@ bool IsKeySizeValidForAes(size_t key_size) { namespace edash_packager { namespace media { -AesDecryptor::AesDecryptor() {} -AesDecryptor::~AesDecryptor() {} - -bool AesDecryptor::Decrypt(const std::vector& ciphertext, - std::vector* 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(ciphertext.data()), - ciphertext.size(), - reinterpret_cast(string_as_array(plaintext)), - &plaintext_size)) - return false; - plaintext->resize(plaintext_size); - return true; -} - -AesCtrDecryptor::AesCtrDecryptor() {} - -AesCtrDecryptor::~AesCtrDecryptor() {} - -bool AesCtrDecryptor::InitializeWithIv(const std::vector& key, - const std::vector& iv) { - encryptor_.reset(new AesCtrEncryptor); - return encryptor_->InitializeWithIv(key, iv); -} - -bool AesCtrDecryptor::SetIv(const std::vector& iv) { - DCHECK(encryptor_); - return encryptor_->SetIv(iv); -} - -bool AesCtrDecryptor::DecryptInternal(const uint8_t* ciphertext, - 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); -} - AesCbcDecryptor::AesCbcDecryptor(CbcPaddingScheme padding_scheme, bool chain_across_calls) : padding_scheme_(padding_scheme), @@ -87,6 +31,7 @@ AesCbcDecryptor::AesCbcDecryptor(CbcPaddingScheme padding_scheme, "sense if the padding_scheme is kNoPadding."; } } + AesCbcDecryptor::~AesCbcDecryptor() {} bool AesCbcDecryptor::InitializeWithIv(const std::vector& key, @@ -96,9 +41,8 @@ bool AesCbcDecryptor::InitializeWithIv(const std::vector& key, return false; } - aes_key_.reset(new AES_KEY()); - CHECK_EQ(AES_set_decrypt_key(key.data(), key.size() * 8, aes_key_.get()), 0); - + CHECK_EQ(AES_set_decrypt_key(key.data(), key.size() * 8, mutable_aes_key()), + 0); return SetIv(iv); } @@ -108,36 +52,43 @@ bool AesCbcDecryptor::SetIv(const std::vector& iv) { return false; } - iv_ = iv; + set_iv(iv); return true; } -bool AesCbcDecryptor::DecryptInternal(const uint8_t* ciphertext, - size_t ciphertext_size, - uint8_t* plaintext, - size_t* plaintext_size) { +bool AesCbcDecryptor::CryptInternal(const uint8_t* ciphertext, + size_t ciphertext_size, + uint8_t* plaintext, + size_t* plaintext_size) { DCHECK(plaintext_size); - DCHECK(aes_key_); + DCHECK(aes_key()); // Plaintext size is the same as ciphertext size except for pkcs5 padding. - // Will update later if using pkcs5 padding. + // Will update later if using pkcs5 padding. For pkcs5 padding, we still + // need at least |ciphertext_size| bytes for intermediate operation. + if (*plaintext_size < ciphertext_size) { + LOG(ERROR) << "Expecting output size of at least " << ciphertext_size + << " bytes."; + return false; + } *plaintext_size = ciphertext_size; + if (ciphertext_size == 0) { if (padding_scheme_ == kPkcs5Padding) { LOG(ERROR) << "Expected ciphertext to be at least " << AES_BLOCK_SIZE - << " bytes with Pkcs5 padding"; + << " bytes with Pkcs5 padding."; return false; } return true; } DCHECK(plaintext); - std::vector local_iv(iv_); + std::vector local_iv(iv()); const size_t residual_block_size = ciphertext_size % AES_BLOCK_SIZE; if (residual_block_size == 0) { - AES_cbc_encrypt(ciphertext, plaintext, ciphertext_size, aes_key_.get(), + AES_cbc_encrypt(ciphertext, plaintext, ciphertext_size, aes_key(), local_iv.data(), AES_DECRYPT); if (chain_across_calls_) - iv_ = local_iv; + set_iv(local_iv); if (padding_scheme_ != kPkcs5Padding) return true; @@ -167,8 +118,8 @@ bool AesCbcDecryptor::DecryptInternal(const uint8_t* ciphertext, // AES-CBC decrypt everything up to the next-to-last full block. const size_t cbc_size = ciphertext_size - residual_block_size; if (cbc_size > AES_BLOCK_SIZE) { - AES_cbc_encrypt(ciphertext, plaintext, cbc_size - AES_BLOCK_SIZE, - aes_key_.get(), local_iv.data(), AES_DECRYPT); + AES_cbc_encrypt(ciphertext, plaintext, cbc_size - AES_BLOCK_SIZE, aes_key(), + local_iv.data(), AES_DECRYPT); } const uint8_t* next_to_last_ciphertext_block = @@ -186,7 +137,7 @@ bool AesCbcDecryptor::DecryptInternal(const uint8_t* ciphertext, // Decrypt the next-to-last block using the IV determined above. This decrypts // the residual block bits. AES_cbc_encrypt(next_to_last_ciphertext_block, next_to_last_plaintext_block, - AES_BLOCK_SIZE, aes_key_.get(), last_iv.data(), AES_DECRYPT); + AES_BLOCK_SIZE, aes_key(), last_iv.data(), AES_DECRYPT); // Swap back the residual block bits and the next-to-last block. if (plaintext == ciphertext) { @@ -202,7 +153,7 @@ bool AesCbcDecryptor::DecryptInternal(const uint8_t* ciphertext, // Decrypt the next-to-last full block. AES_cbc_encrypt(next_to_last_plaintext_block, next_to_last_plaintext_block, - AES_BLOCK_SIZE, aes_key_.get(), local_iv.data(), AES_DECRYPT); + AES_BLOCK_SIZE, aes_key(), local_iv.data(), AES_DECRYPT); return true; } diff --git a/packager/media/base/aes_decryptor.h b/packager/media/base/aes_decryptor.h index 5545224b40..3dfcb3cff3 100644 --- a/packager/media/base/aes_decryptor.h +++ b/packager/media/base/aes_decryptor.h @@ -6,98 +6,23 @@ // // AES Decryptor implementation using openssl. -#ifndef MEDIA_BASE_AES_DECRYPTOR_H_ -#define MEDIA_BASE_AES_DECRYPTOR_H_ +#ifndef PACKAGER_MEDIA_BASE_AES_DECRYPTOR_H_ +#define PACKAGER_MEDIA_BASE_AES_DECRYPTOR_H_ -#include #include -#include "packager/base/memory/scoped_ptr.h" -#include "packager/base/stl_util.h" +#include "packager/base/macros.h" +#include "packager/media/base/aes_cryptor.h" #include "packager/media/base/aes_encryptor.h" -struct aes_key_st; -typedef struct aes_key_st AES_KEY; - namespace edash_packager { namespace media { -class AesDecryptor { - public: - AesDecryptor(); - virtual ~AesDecryptor(); +/// For AES-CTR, encryption and decryption are identical. +using AesCtrDecryptor = AesCtrEncryptor; - /// Initialize the decryptor with specified key and IV. - /// @return true on successful initialization, false otherwise. - virtual bool InitializeWithIv(const std::vector& key, - const std::vector& iv) = 0; - - /// @name Various forms of decrypt calls. - /// The plaintext and ciphertext pointers can be the same address. - /// @{ - bool Decrypt(const std::vector& ciphertext, - std::vector* plaintext); - bool Decrypt(const std::string& ciphertext, std::string* plaintext); - bool Decrypt(const uint8_t* ciphertext, - size_t ciphertext_size, - uint8_t* plaintext) { - size_t plaintext_size; - return DecryptInternal(ciphertext, ciphertext_size, plaintext, - &plaintext_size); - } - /// @} - - /// Set IV. - /// @return true if successful, false if the input is invalid. - virtual bool SetIv(const std::vector& iv) = 0; - - protected: - /// Internal implementation of decrypt function. - /// @param ciphertext points to the input ciphertext. - /// @param ciphertext_size is the input ciphertext size. - /// @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: - DISALLOW_COPY_AND_ASSIGN(AesDecryptor); -}; - -// Class which implements AES-CTR counter-mode decryption. -class AesCtrDecryptor : public AesDecryptor { - public: - AesCtrDecryptor(); - ~AesCtrDecryptor() override; - - /// @name AesDecryptor implementation overrides. - /// @{ - bool InitializeWithIv(const std::vector& key, - const std::vector& iv) override; - - bool SetIv(const std::vector& iv) override; - /// @} - - 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: - scoped_ptr encryptor_; - - DISALLOW_COPY_AND_ASSIGN(AesCtrDecryptor); -}; - -// Class which implements AES-CBC (Cipher block chaining) decryption. -class AesCbcDecryptor : public AesDecryptor { +/// Class which implements AES-CBC (Cipher block chaining) decryption. +class AesCbcDecryptor : public AesCryptor { public: /// @param padding_scheme indicates the padding scheme used. Currently /// supported schemes: kNoPadding, kPkcs5Padding, kCtsPadding. @@ -107,25 +32,22 @@ class AesCbcDecryptor : public AesDecryptor { AesCbcDecryptor(CbcPaddingScheme padding_scheme, bool chain_across_calls); ~AesCbcDecryptor() override; - /// @name AesDecryptor implementation overrides. + /// @name AesCryptor implementation overrides. /// @{ bool InitializeWithIv(const std::vector& key, const std::vector& iv) override; - bool SetIv(const std::vector& iv) override; + void UpdateIv() override { + // Nop for decryptor. + } /// @} - protected: - bool DecryptInternal(const uint8_t* ciphertext, - size_t ciphertext_size, - uint8_t* plaintext, - size_t* plaintext_size) override; - private: - // Openssl AES_KEY. - scoped_ptr aes_key_; - // Initialization vector, must be 16 for CBC. - std::vector iv_; + bool CryptInternal(const uint8_t* ciphertext, + size_t ciphertext_size, + uint8_t* plaintext, + size_t* plaintext_size) override; + const CbcPaddingScheme padding_scheme_; const bool chain_across_calls_; @@ -135,4 +57,4 @@ class AesCbcDecryptor : public AesDecryptor { } // namespace media } // namespace edash_packager -#endif // MEDIA_BASE_AES_DECRYPTOR_H_ +#endif // PACKAGER_MEDIA_BASE_AES_DECRYPTOR_H_ diff --git a/packager/media/base/aes_encryptor.cc b/packager/media/base/aes_encryptor.cc index 36728d22f7..54ccf2e848 100644 --- a/packager/media/base/aes_encryptor.cc +++ b/packager/media/base/aes_encryptor.cc @@ -40,9 +40,8 @@ namespace media { AesEncryptor::AesEncryptor() {} AesEncryptor::~AesEncryptor() {} -bool AesEncryptor::InitializeWithRandomIv( - const std::vector& key, - uint8_t iv_size) { +bool AesEncryptor::InitializeWithRandomIv(const std::vector& key, + uint8_t iv_size) { std::vector iv(iv_size, 0); if (RAND_bytes(iv.data(), iv_size) != 1) { LOG(ERROR) << "RAND_bytes failed with error: " @@ -59,31 +58,11 @@ bool AesEncryptor::InitializeWithIv(const std::vector& key, return false; } - aes_key_.reset(new AES_KEY()); - CHECK_EQ(AES_set_encrypt_key(key.data(), key.size() * 8, aes_key_.get()), 0); + CHECK_EQ(AES_set_encrypt_key(key.data(), key.size() * 8, mutable_aes_key()), + 0); return SetIv(iv); } -bool AesEncryptor::Encrypt(const std::vector& plaintext, - std::vector* ciphertext) { - // Save plaintext size to make it work for in-place conversion, since the - // next statement will update the plaintext size. - const size_t plaintext_size = plaintext.size(); - ciphertext->resize(plaintext_size + NumPaddingBytes(plaintext.size())); - return EncryptInternal(plaintext.data(), plaintext_size, ciphertext->data()); -} - -bool AesEncryptor::Encrypt(const std::string& plaintext, - std::string* ciphertext) { - // Save plaintext size to make it work for in-place conversion, since the - // next statement will update the plaintext size. - const size_t plaintext_size = plaintext.size(); - ciphertext->resize(plaintext_size + NumPaddingBytes(plaintext.size())); - return EncryptInternal( - reinterpret_cast(plaintext.data()), plaintext_size, - reinterpret_cast(string_as_array(ciphertext))); -} - AesCtrEncryptor::AesCtrEncryptor() : block_offset_(0), encrypted_counter_(AES_BLOCK_SIZE, 0), @@ -134,13 +113,22 @@ bool AesCtrEncryptor::SetIv(const std::vector& iv) { return true; } -bool AesCtrEncryptor::EncryptInternal(const uint8_t* plaintext, - size_t plaintext_size, - uint8_t* ciphertext) { +bool AesCtrEncryptor::CryptInternal(const uint8_t* plaintext, + size_t plaintext_size, + uint8_t* ciphertext, + size_t* ciphertext_size) { DCHECK(plaintext); DCHECK(ciphertext); DCHECK(aes_key()); + // |ciphertext_size| is always the same as |plaintext_size| for counter mode. + if (*ciphertext_size < plaintext_size) { + LOG(ERROR) << "Expecting output size of at least " << plaintext_size + << " bytes."; + return false; + } + *ciphertext_size = plaintext_size; + for (size_t i = 0; i < plaintext_size; ++i) { if (block_offset_ == 0) { AES_encrypt(&counter_[0], &encrypted_counter_[0], aes_key()); @@ -158,11 +146,6 @@ bool AesCtrEncryptor::EncryptInternal(const uint8_t* plaintext, return true; } -size_t AesCtrEncryptor::NumPaddingBytes(size_t size) const { - // No padding needed for CTR. - return 0; -} - AesCbcEncryptor::AesCbcEncryptor(CbcPaddingScheme padding_scheme, bool chain_across_calls) : padding_scheme_(padding_scheme), @@ -194,9 +177,10 @@ bool AesCbcEncryptor::SetIv(const std::vector& iv) { return true; } -bool AesCbcEncryptor::EncryptInternal(const uint8_t* plaintext, - size_t plaintext_size, - uint8_t* ciphertext) { +bool AesCbcEncryptor::CryptInternal(const uint8_t* plaintext, + size_t plaintext_size, + uint8_t* ciphertext, + size_t* ciphertext_size) { DCHECK(aes_key()); const size_t residual_block_size = plaintext_size % AES_BLOCK_SIZE; @@ -206,6 +190,15 @@ bool AesCbcEncryptor::EncryptInternal(const uint8_t* plaintext, return false; } + const size_t num_padding_bytes = NumPaddingBytes(plaintext_size); + const size_t required_ciphertext_size = plaintext_size + num_padding_bytes; + if (*ciphertext_size < required_ciphertext_size) { + LOG(ERROR) << "Expecting output size of at least " + << required_ciphertext_size << " bytes."; + return false; + } + *ciphertext_size = required_ciphertext_size; + // Encrypt everything but the residual block using CBC. const size_t cbc_size = plaintext_size - residual_block_size; std::vector local_iv(iv()); @@ -231,13 +224,14 @@ bool AesCbcEncryptor::EncryptInternal(const uint8_t* plaintext, 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)); + DCHECK_EQ(num_padding_bytes, AES_BLOCK_SIZE - residual_block_size); + // Pad residue block with PKCS5 padding. residual_block.resize(AES_BLOCK_SIZE, static_cast(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(num_padding_bytes, 0u); DCHECK_EQ(padding_scheme_, kCtsPadding); // Zero-pad the residual block and encrypt using CBC. diff --git a/packager/media/base/aes_encryptor.h b/packager/media/base/aes_encryptor.h index 72896d20fc..2d84ae9448 100644 --- a/packager/media/base/aes_encryptor.h +++ b/packager/media/base/aes_encryptor.h @@ -12,77 +12,29 @@ #include #include +#include "packager/base/macros.h" #include "packager/base/memory/scoped_ptr.h" -#include "packager/base/stl_util.h" - -struct aes_key_st; -typedef struct aes_key_st AES_KEY; +#include "packager/media/base/aes_cryptor.h" namespace edash_packager { namespace media { -class AesEncryptor { +class AesEncryptor : public AesCryptor { public: AesEncryptor(); - virtual ~AesEncryptor(); + ~AesEncryptor() override; /// Initialize the encryptor with specified key and a random generated IV /// of the specified size. /// @return true on successful initialization, false otherwise. - bool InitializeWithRandomIv(const std::vector& key, - uint8_t iv_size); + bool InitializeWithRandomIv(const std::vector& key, uint8_t iv_size); /// Initialize the encryptor with specified key and IV. /// @return true on successful initialization, false otherwise. bool InitializeWithIv(const std::vector& key, - const std::vector& iv); - - /// @name Various forms of encrypt calls. - /// The plaintext and ciphertext pointers can be the same address. - bool Encrypt(const std::vector& plaintext, - std::vector* 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. - /// As recommended in ISO/IEC FDIS 23001-7: - /// IV need to be updated per sample for CENC. - virtual void UpdateIv() = 0; - - /// Set IV. - /// @return true if successful, false if the input is invalid. - virtual bool SetIv(const std::vector& iv) = 0; - - /// @return The current iv. - const std::vector& iv() const { return iv_; } - - 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& iv) { iv_ = iv; } - AES_KEY* aes_key() const { return aes_key_.get(); } + const std::vector& iv) override; private: - // Initialization vector, with size 8 or 16. - std::vector iv_; - // Openssl AES_KEY. - scoped_ptr aes_key_; - DISALLOW_COPY_AND_ASSIGN(AesEncryptor); }; @@ -92,7 +44,7 @@ class AesCtrEncryptor : public AesEncryptor { AesCtrEncryptor(); ~AesCtrEncryptor() override; - /// @name AesEncryptor implementation overrides. + /// @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, @@ -105,13 +57,12 @@ class AesCtrEncryptor : public AesEncryptor { 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: + bool CryptInternal(const uint8_t* plaintext, + size_t plaintext_size, + uint8_t* ciphertext, + size_t* ciphertext_size) override; + // Current block offset. uint32_t block_offset_; // Current AES-CTR counter. @@ -143,20 +94,20 @@ class AesCbcEncryptor : public AesEncryptor { AesCbcEncryptor(CbcPaddingScheme padding_scheme, bool chain_across_calls); ~AesCbcEncryptor() override; - /// @name AesEncryptor implementation overrides. + /// @name AesCryptor implementation overrides. /// @{ void UpdateIv() override; bool SetIv(const std::vector& iv) override; /// @} - protected: - bool EncryptInternal(const uint8_t* plaintext, - size_t plaintext_size, - uint8_t* ciphertext) override; + private: + bool CryptInternal(const uint8_t* plaintext, + size_t plaintext_size, + uint8_t* ciphertext, + size_t* ciphertext_size) override; size_t NumPaddingBytes(size_t size) const override; - private: const CbcPaddingScheme padding_scheme_; const bool chain_across_calls_; diff --git a/packager/media/base/decryptor_source.cc b/packager/media/base/decryptor_source.cc index 099e715809..4375b87cc1 100644 --- a/packager/media/base/decryptor_source.cc +++ b/packager/media/base/decryptor_source.cc @@ -27,7 +27,7 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config, DCHECK(buffer); // Get the decryptor object. - AesDecryptor* decryptor; + AesCryptor* decryptor; auto found = decryptor_map_.find(decrypt_config->key_id()); if (found == decryptor_map_.end()) { // Create new AesDecryptor based on decryption mode. @@ -38,7 +38,7 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config, return false; } - scoped_ptr aes_decryptor; + scoped_ptr aes_decryptor; switch (decrypt_config->decryption_mode()) { case kEncryptionModeAesCtr: aes_decryptor.reset(new AesCtrDecryptor); @@ -68,7 +68,7 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config, if (decrypt_config->subsamples().empty()) { // Sample not encrypted using subsample encryption. Decrypt whole. - if (!decryptor->Decrypt(buffer, buffer_size, buffer)) { + if (!decryptor->Crypt(buffer, buffer_size, buffer)) { LOG(ERROR) << "Error during bulk sample decryption."; return false; } @@ -86,7 +86,7 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config, return false; } current_ptr += subsample.clear_bytes; - if (!decryptor->Decrypt(current_ptr, subsample.cipher_bytes, current_ptr)) { + if (!decryptor->Crypt(current_ptr, subsample.cipher_bytes, current_ptr)) { LOG(ERROR) << "Error decrypting subsample buffer."; return false; } diff --git a/packager/media/base/decryptor_source.h b/packager/media/base/decryptor_source.h index 60598c2e01..4bde1355b5 100644 --- a/packager/media/base/decryptor_source.h +++ b/packager/media/base/decryptor_source.h @@ -29,7 +29,7 @@ class DecryptorSource { private: KeySource* key_source_; - std::map, AesDecryptor*> decryptor_map_; + std::map, AesCryptor*> decryptor_map_; DISALLOW_COPY_AND_ASSIGN(DecryptorSource); }; diff --git a/packager/media/base/media_base.gyp b/packager/media/base/media_base.gyp index 8f40c0c92f..521b3d6b6b 100644 --- a/packager/media/base/media_base.gyp +++ b/packager/media/base/media_base.gyp @@ -13,6 +13,8 @@ 'target_name': 'media_base', 'type': '<(component)', 'sources': [ + 'aes_cryptor.cc', + 'aes_cryptor.h', 'aes_decryptor.cc', 'aes_decryptor.h', 'aes_encryptor.cc', @@ -113,7 +115,7 @@ 'target_name': 'media_base_unittest', 'type': '<(gtest_target_type)', 'sources': [ - 'aes_encryptor_unittest.cc', + 'aes_cryptor_unittest.cc', 'audio_timestamp_helper_unittest.cc', 'bit_reader_unittest.cc', 'buffer_writer_unittest.cc', diff --git a/packager/media/base/request_signer.cc b/packager/media/base/request_signer.cc index 0ca0153705..d8314185b0 100644 --- a/packager/media/base/request_signer.cc +++ b/packager/media/base/request_signer.cc @@ -6,6 +6,7 @@ #include "packager/media/base/request_signer.h" +#include "packager/base/logging.h" #include "packager/base/sha1.h" #include "packager/base/strings/string_number_conversions.h" #include "packager/media/base/aes_encryptor.h" @@ -48,7 +49,7 @@ AesRequestSigner* AesRequestSigner::CreateSigner(const std::string& signer_name, bool AesRequestSigner::GenerateSignature(const std::string& message, std::string* signature) { - aes_cbc_encryptor_->Encrypt(base::SHA1HashString(message), signature); + aes_cbc_encryptor_->Crypt(base::SHA1HashString(message), signature); return true; } diff --git a/packager/media/formats/mp4/encrypting_fragmenter.cc b/packager/media/formats/mp4/encrypting_fragmenter.cc index b878ca4023..6fe21f9ad9 100644 --- a/packager/media/formats/mp4/encrypting_fragmenter.cc +++ b/packager/media/formats/mp4/encrypting_fragmenter.cc @@ -199,7 +199,7 @@ Status EncryptingFragmenter::CreateEncryptor() { void EncryptingFragmenter::EncryptBytes(uint8_t* data, uint32_t size) { DCHECK(encryptor_); - CHECK(encryptor_->Encrypt(data, size, data)); + CHECK(encryptor_->Crypt(data, size, data)); } Status EncryptingFragmenter::EncryptSample(scoped_refptr sample) { diff --git a/packager/media/formats/webm/encryptor.cc b/packager/media/formats/webm/encryptor.cc index 554c1f9ff0..1fcecbc443 100644 --- a/packager/media/formats/webm/encryptor.cc +++ b/packager/media/formats/webm/encryptor.cc @@ -78,7 +78,7 @@ Status Encryptor::EncryptFrame(scoped_refptr sample, uint8_t* sample_data = sample->writable_data(); // Encrypt the data in-place. - if (!encryptor_->Encrypt(sample_data, sample_size, sample_data)) { + if (!encryptor_->Crypt(sample_data, sample_size, sample_data)) { return Status(error::MUXER_FAILURE, "Failed to encrypt the frame."); } diff --git a/packager/media/formats/wvm/wvm_media_parser.cc b/packager/media/formats/wvm/wvm_media_parser.cc index 1e81093c1e..32d9ea7aeb 100644 --- a/packager/media/formats/wvm/wvm_media_parser.cc +++ b/packager/media/formats/wvm/wvm_media_parser.cc @@ -779,9 +779,9 @@ bool WvmMediaParser::DemuxNextPes(bool is_program_end) { if (!content_decryptor_) { output_encrypted_sample = true; } else { - content_decryptor_->Decrypt(&sample_data_[crypto_unit_start_pos_], - sample_data_.size() - crypto_unit_start_pos_, - &sample_data_[crypto_unit_start_pos_]); + content_decryptor_->Crypt(&sample_data_[crypto_unit_start_pos_], + sample_data_.size() - crypto_unit_start_pos_, + &sample_data_[crypto_unit_start_pos_]); } } // Demux media sample if we are at program end or if we are not at a @@ -1124,8 +1124,8 @@ bool WvmMediaParser::ProcessEcm() { kEcmFlagsSizeBytes + kEcmContentKeySizeBytes + kEcmPaddingSizeBytes; // flags + contentKey + padding. std::vector content_key_buffer(content_key_buffer_size); - CHECK(asset_decryptor.Decrypt(ecm_data, content_key_buffer_size, - content_key_buffer.data())); + CHECK(asset_decryptor.Crypt(ecm_data, content_key_buffer_size, + content_key_buffer.data())); std::vector decrypted_content_key_vec( content_key_buffer.begin() + 4,