From e39c3572af3777167a12b5870e73b496d6e34059 Mon Sep 17 00:00:00 2001 From: Bei Li Date: Thu, 17 Mar 2016 10:03:19 -0700 Subject: [PATCH] Add support for "AES 128-bit Cipher Block Chaining (CBC-128) Encryption" - Part 1. - Add packager command line argument "protection_scheme" to specify protection scheme. Plumb through packager code to enable CBC encryption/decryption. - Add scheme type "cbc1" to sinf. - Refactor AES encryptor and decryptor. - Need more work in the subsample handling. Issue #77 Change-Id: I3a9304d89adf5efbfb226b6e805a3077b6cb8c68 --- packager/app/packager_main.cc | 20 +- packager/app/widevine_encryption_flags.cc | 4 + packager/app/widevine_encryption_flags.h | 1 + packager/media/base/aes_decryptor.cc | 268 +++++++++++++++ packager/media/base/aes_decryptor.h | 153 +++++++++ packager/media/base/aes_encryptor.cc | 309 +++++------------- packager/media/base/aes_encryptor.h | 265 ++++++--------- packager/media/base/aes_encryptor_unittest.cc | 35 +- packager/media/base/decrypt_config.cc | 8 +- packager/media/base/decrypt_config.h | 9 +- packager/media/base/decryptor_source.cc | 26 +- packager/media/base/decryptor_source.h | 4 +- .../media/base/decryptor_source_unittest.cc | 14 +- packager/media/base/encryption_modes.h | 23 ++ packager/media/base/media_base.gyp | 3 + packager/media/base/muxer.cc | 5 +- packager/media/base/muxer.h | 6 +- packager/media/formats/mp4/box_definitions.cc | 6 +- .../formats/mp4/encrypting_fragmenter.cc | 18 +- .../media/formats/mp4/encrypting_fragmenter.h | 11 +- packager/media/formats/mp4/fourccs.h | 1 + .../formats/mp4/key_rotation_fragmenter.cc | 6 +- .../formats/mp4/key_rotation_fragmenter.h | 4 +- packager/media/formats/mp4/mp4_muxer.cc | 3 +- packager/media/formats/mp4/segmenter.cc | 28 +- packager/media/formats/mp4/segmenter.h | 4 +- .../media/formats/mp4/track_run_iterator.cc | 19 +- packager/media/formats/webm/encryptor.cc | 2 +- .../media/formats/webm/webm_crypto_helpers.cc | 5 +- packager/media/formats/webm/webm_muxer.cc | 8 + .../media/formats/wvm/wvm_media_parser.cc | 2 +- packager/media/test/packager_test.cc | 7 +- 32 files changed, 801 insertions(+), 476 deletions(-) create mode 100644 packager/media/base/aes_decryptor.cc create mode 100644 packager/media/base/aes_decryptor.h create mode 100644 packager/media/base/encryption_modes.h diff --git a/packager/app/packager_main.cc b/packager/app/packager_main.cc index c7935b13aa..0f28b06528 100644 --- a/packager/app/packager_main.cc +++ b/packager/app/packager_main.cc @@ -25,6 +25,7 @@ #include "packager/base/time/clock.h" #include "packager/media/base/container_names.h" #include "packager/media/base/demuxer.h" +#include "packager/media/base/encryption_modes.h" #include "packager/media/base/key_source.h" #include "packager/media/base/muxer_options.h" #include "packager/media/base/muxer_util.h" @@ -104,6 +105,18 @@ std::string DetermineTextFileFormat(const std::string& file) { return ""; } +edash_packager::media::EncryptionMode GetEncryptionMode( + const std::string& protection_scheme) { + if (protection_scheme == "cenc") { + return edash_packager::media::kEncryptionModeAesCtr; + } else if (protection_scheme == "cbc1") { + return edash_packager::media::kEncryptionModeAesCbc; + } else { + LOG(ERROR) << "Protection scheme is unknown."; + return edash_packager::media::kEncryptionModeUnknown; + } +} + } // namespace namespace edash_packager { @@ -296,7 +309,8 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors, muxer->SetKeySource(key_source, FLAGS_max_sd_pixels, FLAGS_clear_lead, - FLAGS_crypto_period_duration); + FLAGS_crypto_period_duration, + GetEncryptionMode(FLAGS_protection_scheme)); } scoped_ptr muxer_listener; @@ -360,6 +374,10 @@ Status RunRemuxJobs(const std::vector& remux_jobs) { } bool RunPackager(const StreamDescriptorList& stream_descriptors) { + EncryptionMode encryption_mode = GetEncryptionMode(FLAGS_protection_scheme); + if (encryption_mode == kEncryptionModeUnknown) + return false; + if (!AssignFlagsFromProfile()) return false; diff --git a/packager/app/widevine_encryption_flags.cc b/packager/app/widevine_encryption_flags.cc index 1ddf8f8f2c..c65c22b38c 100644 --- a/packager/app/widevine_encryption_flags.cc +++ b/packager/app/widevine_encryption_flags.cc @@ -50,6 +50,10 @@ DEFINE_int32(crypto_period_duration, 0, "Crypto period duration in seconds. If it is non-zero, key " "rotation is enabled."); +DEFINE_string(protection_scheme, + "cenc", + "Choose protection scheme. Currently support cenc and cbc1. " + "Default is cenc."); namespace edash_packager { diff --git a/packager/app/widevine_encryption_flags.h b/packager/app/widevine_encryption_flags.h index 8eeb9927bf..f6a9b45db3 100644 --- a/packager/app/widevine_encryption_flags.h +++ b/packager/app/widevine_encryption_flags.h @@ -22,6 +22,7 @@ DECLARE_string(aes_signing_key); DECLARE_string(aes_signing_iv); DECLARE_string(rsa_signing_key_path); DECLARE_int32(crypto_period_duration); +DECLARE_string(protection_scheme); namespace edash_packager { diff --git a/packager/media/base/aes_decryptor.cc b/packager/media/base/aes_decryptor.cc new file mode 100644 index 0000000000..9841e6a3c0 --- /dev/null +++ b/packager/media/base/aes_decryptor.cc @@ -0,0 +1,268 @@ +// 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_decryptor.h" + +#include +#include +#include + +#include "packager/base/logging.h" + +namespace { + +// AES defines three key sizes: 128, 192 and 256 bits. +bool IsKeySizeValidForAes(size_t key_size) { + return key_size == 16 || key_size == 24 || key_size == 32; +} + +} // namespace + +namespace edash_packager { +namespace media { + +AesDecryptor::AesDecryptor() {} +AesDecryptor::~AesDecryptor() {} + +AesCtrDecryptor::AesCtrDecryptor() {} + +AesCtrDecryptor::~AesCtrDecryptor() {} + +bool AesCtrDecryptor::InitializeWithIv(const std::vector& key, + const std::vector& iv) { + encryptor_.reset(new AesCtrEncryptor); + return encryptor_->InitializeWithIv(key, iv); +} + +// For AES CTR, encryption and decryption are identical. +bool AesCtrDecryptor::Decrypt(const uint8_t* ciphertext, + size_t ciphertext_size, + uint8_t* plaintext) { + DCHECK(encryptor_); + return encryptor_->EncryptData(ciphertext, ciphertext_size, plaintext); +} + +bool AesCtrDecryptor::Decrypt(const std::vector& ciphertext, + std::vector* plaintext) { + DCHECK(encryptor_); + return encryptor_->Encrypt(ciphertext, plaintext); +} + +bool AesCtrDecryptor::Decrypt(const std::string& ciphertext, + std::string* plaintext) { + DCHECK(encryptor_); + return encryptor_->Encrypt(ciphertext, plaintext); +} + +bool AesCtrDecryptor::SetIv(const std::vector& iv) { + DCHECK(encryptor_); + return encryptor_->SetIv(iv); +} + +AesCbcPkcs5Decryptor::AesCbcPkcs5Decryptor() {} +AesCbcPkcs5Decryptor::~AesCbcPkcs5Decryptor() {} + +bool AesCbcPkcs5Decryptor::InitializeWithIv(const std::vector& key, + const std::vector& iv) { + if (!IsKeySizeValidForAes(key.size())) { + LOG(ERROR) << "Invalid AES key size: " << key.size(); + return false; + } + if (iv.size() != AES_BLOCK_SIZE) { + LOG(ERROR) << "Invalid IV size: " << iv.size(); + return false; + } + + aes_key_.reset(new AES_KEY()); + CHECK_EQ(AES_set_decrypt_key(&key[0], key.size() * 8, aes_key_.get()), 0); + + iv_ = iv; + return true; +} + +bool AesCbcPkcs5Decryptor::Decrypt(const uint8_t* ciphertext, + size_t ciphertext_size, + uint8_t* plaintext) { + NOTIMPLEMENTED(); + return false; +} + +bool AesCbcPkcs5Decryptor::Decrypt(const std::vector& ciphertext, + std::vector* plaintext) { + NOTIMPLEMENTED(); + return false; +} + +bool AesCbcPkcs5Decryptor::Decrypt(const std::string& ciphertext, + std::string* plaintext) { + if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) { + LOG(ERROR) << "Expecting cipher text size to be multiple of " + << AES_BLOCK_SIZE << ", got " << ciphertext.size(); + return false; + } + + DCHECK(plaintext); + DCHECK(aes_key_); + + plaintext->resize(ciphertext.size()); + AES_cbc_encrypt(reinterpret_cast(ciphertext.data()), + reinterpret_cast(string_as_array(plaintext)), + ciphertext.size(), + aes_key_.get(), + &iv_[0], + AES_DECRYPT); + + // Strip off PKCS5 padding bytes. + const uint8_t num_padding_bytes = (*plaintext)[plaintext->size() - 1]; + if (num_padding_bytes > AES_BLOCK_SIZE) { + LOG(ERROR) << "Padding length is too large : " + << static_cast(num_padding_bytes); + return false; + } + plaintext->resize(plaintext->size() - num_padding_bytes); + return true; +} + +bool AesCbcPkcs5Decryptor::SetIv(const std::vector& iv) { + if (iv.size() != AES_BLOCK_SIZE) { + LOG(ERROR) << "Invalid IV size: " << iv.size(); + return false; + } + + iv_ = iv; + return true; +} + +AesCbcCtsDecryptor::AesCbcCtsDecryptor() {} +AesCbcCtsDecryptor::~AesCbcCtsDecryptor() {} + +bool AesCbcCtsDecryptor::InitializeWithIv(const std::vector& key, + const std::vector& iv) { + if (!IsKeySizeValidForAes(key.size())) { + LOG(ERROR) << "Invalid AES key size: " << key.size(); + return false; + } + if (iv.size() != AES_BLOCK_SIZE) { + LOG(ERROR) << "Invalid IV size: " << iv.size(); + return false; + } + + aes_key_.reset(new AES_KEY()); + CHECK_EQ(AES_set_decrypt_key(&key[0], key.size() * 8, aes_key_.get()), 0); + + iv_ = iv; + return true; +} + +bool AesCbcCtsDecryptor::Decrypt(const uint8_t* ciphertext, + size_t ciphertext_size, + uint8_t* plaintext) { + DCHECK(ciphertext); + DCHECK(plaintext); + + if (ciphertext_size < AES_BLOCK_SIZE) { + // Don't have a full block, leave unencrypted. + memcpy(plaintext, ciphertext, ciphertext_size); + return true; + } + + std::vector iv(iv_); + size_t residual_block_size = ciphertext_size % AES_BLOCK_SIZE; + + if (residual_block_size == 0) { + // No residual block. No need to do ciphertext stealing. + AES_cbc_encrypt(ciphertext, + plaintext, + ciphertext_size, + aes_key_.get(), + &iv[0], + AES_DECRYPT); + return true; + } + + // AES-CBC decrypt everything up to the next-to-last full block. + 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(), + &iv[0], + AES_DECRYPT); + } + + // Determine what the last IV should be so that we can "skip ahead" in the + // CBC decryption. + std::vector last_iv( + ciphertext + ciphertext_size - residual_block_size, + ciphertext + ciphertext_size); + last_iv.resize(AES_BLOCK_SIZE, 0); + + // Decrypt the next-to-last block using the IV determined above. This decrypts + // the residual block bits. + AES_cbc_encrypt( + ciphertext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE, + plaintext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE, + AES_BLOCK_SIZE, aes_key_.get(), &last_iv[0], AES_DECRYPT); + + // Swap back the residual block bits and the next-to-last full block. + if (plaintext == ciphertext) { + uint8_t* ptr1 = plaintext + ciphertext_size - residual_block_size; + uint8_t* ptr2 = plaintext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE; + for (size_t i = 0; i < residual_block_size; ++i) { + uint8_t temp = *ptr1; + *ptr1 = *ptr2; + *ptr2 = temp; + ++ptr1; + ++ptr2; + } + } else { + uint8_t* residual_plaintext_block = + plaintext + ciphertext_size - residual_block_size; + memcpy(residual_plaintext_block, residual_plaintext_block - AES_BLOCK_SIZE, + residual_block_size); + memcpy(residual_plaintext_block - AES_BLOCK_SIZE, + ciphertext + ciphertext_size - residual_block_size, + residual_block_size); + } + + // Decrypt the last full block. + AES_cbc_encrypt( + plaintext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE, + plaintext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE, + AES_BLOCK_SIZE, aes_key_.get(), &iv[0], AES_DECRYPT); + return true; +} + +bool AesCbcCtsDecryptor::Decrypt(const std::vector& ciphertext, + std::vector* plaintext) { + DCHECK(plaintext); + + plaintext->resize(ciphertext.size(), 0); + if (ciphertext.empty()) + return true; + + return Decrypt(ciphertext.data(), ciphertext.size(), &(*plaintext)[0]); +} + +bool AesCbcCtsDecryptor::Decrypt(const std::string& ciphertext, + std::string* plaintext) { + NOTIMPLEMENTED(); + return false; +} + +bool AesCbcCtsDecryptor::SetIv(const std::vector& iv) { + if (iv.size() != AES_BLOCK_SIZE) { + LOG(ERROR) << "Invalid IV size: " << iv.size(); + return false; + } + + iv_ = iv; + return true; +} + +} // namespace media +} // namespace edash_packager diff --git a/packager/media/base/aes_decryptor.h b/packager/media/base/aes_decryptor.h new file mode 100644 index 0000000000..27baacf001 --- /dev/null +++ b/packager/media/base/aes_decryptor.h @@ -0,0 +1,153 @@ +// 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 +// +// AES Decryptor implementation using openssl. + +#ifndef MEDIA_BASE_AES_DECRYPTOR_H_ +#define MEDIA_BASE_AES_DECRYPTOR_H_ + +#include +#include + +#include "packager/base/memory/scoped_ptr.h" +#include "packager/base/stl_util.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(); + + 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. + /// @{ + virtual bool Decrypt(const uint8_t* ciphertext, + size_t ciphertext_size, + uint8_t* plaintext) = 0; + + virtual bool Decrypt(const std::vector& ciphertext, + std::vector* plaintext) = 0; + + virtual bool Decrypt(const std::string& ciphertext, + std::string* plaintext) = 0; + /// @} + + /// Set IV. @a block_offset_ is reset to 0 on success. + /// @return true if successful, false if the input is invalid. + virtual bool SetIv(const std::vector& iv) = 0; + + const std::vector& iv() const { return iv_; } + + protected: + // Initialization vector, with size 8 or 16. + std::vector iv_; + // Openssl AES_KEY. + scoped_ptr aes_key_; + + 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 Decrypt(const uint8_t* ciphertext, + size_t ciphertext_size, + uint8_t* plaintext) override; + + bool Decrypt(const std::vector& ciphertext, + std::vector* plaintext) override; + + bool Decrypt(const std::string& ciphertext, std::string* plaintext) override; + + bool SetIv(const std::vector& iv) override; + /// @} + + uint32_t block_offset() const { return encryptor_->block_offset(); } + + private: + scoped_ptr encryptor_; + + DISALLOW_COPY_AND_ASSIGN(AesCtrDecryptor); +}; + +// Class which implements AES-CBC (Cipher block chaining) decryption with +// PKCS#5 padding. +class AesCbcPkcs5Decryptor : public AesDecryptor { + public: + AesCbcPkcs5Decryptor(); + ~AesCbcPkcs5Decryptor() override; + + /// @name AesDecryptor implementation overrides. + /// @{ + bool InitializeWithIv(const std::vector& key, + const std::vector& iv) override; + + bool Decrypt(const uint8_t* ciphertext, + size_t ciphertext_size, + uint8_t* plaintext) override; + + bool Decrypt(const std::vector& ciphertext, + std::vector* plaintext) override; + + bool Decrypt(const std::string& ciphertext, std::string* plaintext) override; + + bool SetIv(const std::vector& iv) override; + /// @} + + private: + DISALLOW_COPY_AND_ASSIGN(AesCbcPkcs5Decryptor); +}; + +// Class which implements AES-CBC (Cipher block chaining) decryption with +// Ciphertext stealing. +class AesCbcCtsDecryptor : public AesDecryptor { + public: + AesCbcCtsDecryptor(); + ~AesCbcCtsDecryptor() override; + + /// @name AesDecryptor implementation overrides. + /// @{ + bool InitializeWithIv(const std::vector& key, + const std::vector& iv) override; + + bool Decrypt(const uint8_t* ciphertext, + size_t ciphertext_size, + uint8_t* plaintext) override; + + bool Decrypt(const std::vector& ciphertext, + std::vector* plaintext) override; + + bool Decrypt(const std::string& ciphertext, std::string* plaintext) override; + + bool SetIv(const std::vector& iv) override; + /// @} + + private: + DISALLOW_COPY_AND_ASSIGN(AesCbcCtsDecryptor); +}; + +} // namespace media +} // namespace edash_packager + +#endif // MEDIA_BASE_AES_DECRYPTOR_H_ diff --git a/packager/media/base/aes_encryptor.cc b/packager/media/base/aes_encryptor.cc index 564ad3e36d..63f71bb9cc 100644 --- a/packager/media/base/aes_encryptor.cc +++ b/packager/media/base/aes_encryptor.cc @@ -40,6 +40,37 @@ const uint32_t kCencKeySize = 16; namespace edash_packager { namespace media { +AesEncryptor::AesEncryptor() {} +AesEncryptor::~AesEncryptor() {} + +bool AesEncryptor::InitializeWithRandomIv( + const std::vector& key, + uint8_t iv_size) { + std::vector iv(iv_size, 0); + if (RAND_bytes(&iv[0], iv_size) != 1) { + LOG(ERROR) << "RAND_bytes failed with error: " + << ERR_error_string(ERR_get_error(), NULL); + return false; + } + return InitializeWithIv(key, iv); +} + +bool AesEncryptor::Encrypt(const std::vector& plaintext, + std::vector* ciphertext) { + if (plaintext.empty()) + return true; + ciphertext->resize(plaintext.size() + NumPaddingBytes(plaintext.size())); + return EncryptData(plaintext.data(), plaintext.size(), ciphertext->data()); +} + +bool AesEncryptor::Encrypt(const std::string& plaintext, + std::string* ciphertext) { + ciphertext->resize(plaintext.size() + NumPaddingBytes(plaintext.size())); + return EncryptData(reinterpret_cast(plaintext.data()), + plaintext.size(), + reinterpret_cast(string_as_array(ciphertext))); +} + AesCtrEncryptor::AesCtrEncryptor() : block_offset_(0), encrypted_counter_(AES_BLOCK_SIZE, 0), @@ -50,17 +81,6 @@ AesCtrEncryptor::AesCtrEncryptor() AesCtrEncryptor::~AesCtrEncryptor() {} -bool AesCtrEncryptor::InitializeWithRandomIv(const std::vector& key, - uint8_t iv_size) { - std::vector iv(iv_size, 0); - if (RAND_bytes(&iv[0], iv_size) != 1) { - LOG(ERROR) << "RAND_bytes failed with error: " - << ERR_error_string(ERR_get_error(), NULL); - return false; - } - return InitializeWithIv(key, iv); -} - bool AesCtrEncryptor::InitializeWithIv(const std::vector& key, const std::vector& iv) { if (key.size() != kCencKeySize) { @@ -77,9 +97,13 @@ bool AesCtrEncryptor::InitializeWithIv(const std::vector& key, return SetIv(iv); } -bool AesCtrEncryptor::Encrypt(const uint8_t* plaintext, - size_t plaintext_size, - uint8_t* ciphertext) { +size_t AesCtrEncryptor::NumPaddingBytes(size_t size) { + return 0; +} + +bool AesCtrEncryptor::EncryptData(const uint8_t* plaintext, + size_t plaintext_size, + uint8_t* ciphertext) { DCHECK(plaintext); DCHECK(ciphertext); DCHECK(aes_key_); @@ -156,34 +180,40 @@ bool AesCbcPkcs5Encryptor::InitializeWithIv(const std::vector& key, return false; } - encrypt_key_.reset(new AES_KEY()); - CHECK_EQ(AES_set_encrypt_key(&key[0], key.size() * 8, encrypt_key_.get()), 0); + aes_key_.reset(new AES_KEY()); + CHECK_EQ(AES_set_encrypt_key(&key[0], key.size() * 8, aes_key_.get()), 0); iv_ = iv; return true; } -void AesCbcPkcs5Encryptor::Encrypt(const std::string& plaintext, - std::string* ciphertext) { +size_t AesCbcPkcs5Encryptor::NumPaddingBytes(size_t size) { + return AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE); +} + +bool AesCbcPkcs5Encryptor::EncryptData(const uint8_t* plaintext, + size_t plaintext_size, + uint8_t* ciphertext) { DCHECK(ciphertext); - DCHECK(encrypt_key_); + DCHECK(aes_key_); // Pad the input with PKCS5 padding. - const size_t num_padding_bytes = - AES_BLOCK_SIZE - (plaintext.size() % AES_BLOCK_SIZE); - std::string padded_text = plaintext; - padded_text.append(num_padding_bytes, static_cast(num_padding_bytes)); + // TODO(kqyang): Consider more efficient implementation. + memcpy(ciphertext, plaintext, plaintext_size); + for (size_t i = plaintext_size; + i < plaintext_size + NumPaddingBytes(plaintext_size); ++i) { + ciphertext[i] = NumPaddingBytes(plaintext_size); + } - ciphertext->resize(padded_text.size()); std::vector iv(iv_); - AES_cbc_encrypt(reinterpret_cast(padded_text.data()), - reinterpret_cast(string_as_array(ciphertext)), - padded_text.size(), - encrypt_key_.get(), - &iv[0], - AES_ENCRYPT); + AES_cbc_encrypt(ciphertext, ciphertext, + plaintext_size + NumPaddingBytes(plaintext_size), + aes_key_.get(), &iv[0], AES_ENCRYPT); + return true; } +void AesCbcPkcs5Encryptor::UpdateIv() {} + bool AesCbcPkcs5Encryptor::SetIv(const std::vector& iv) { if (iv.size() != AES_BLOCK_SIZE) { LOG(ERROR) << "Invalid IV size: " << iv.size(); @@ -194,67 +224,6 @@ bool AesCbcPkcs5Encryptor::SetIv(const std::vector& iv) { return true; } -AesCbcPkcs5Decryptor::AesCbcPkcs5Decryptor() {} -AesCbcPkcs5Decryptor::~AesCbcPkcs5Decryptor() {} - -bool AesCbcPkcs5Decryptor::InitializeWithIv(const std::vector& key, - const std::vector& iv) { - if (!IsKeySizeValidForAes(key.size())) { - LOG(ERROR) << "Invalid AES key size: " << key.size(); - return false; - } - if (iv.size() != AES_BLOCK_SIZE) { - LOG(ERROR) << "Invalid IV size: " << iv.size(); - return false; - } - - decrypt_key_.reset(new AES_KEY()); - CHECK_EQ(AES_set_decrypt_key(&key[0], key.size() * 8, decrypt_key_.get()), 0); - - iv_ = iv; - return true; -} - -bool AesCbcPkcs5Decryptor::Decrypt(const std::string& ciphertext, - std::string* plaintext) { - if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) { - LOG(ERROR) << "Expecting cipher text size to be multiple of " - << AES_BLOCK_SIZE << ", got " << ciphertext.size(); - return false; - } - - DCHECK(plaintext); - DCHECK(decrypt_key_); - - plaintext->resize(ciphertext.size()); - AES_cbc_encrypt(reinterpret_cast(ciphertext.data()), - reinterpret_cast(string_as_array(plaintext)), - ciphertext.size(), - decrypt_key_.get(), - &iv_[0], - AES_DECRYPT); - - // Strip off PKCS5 padding bytes. - const uint8_t num_padding_bytes = (*plaintext)[plaintext->size() - 1]; - if (num_padding_bytes > AES_BLOCK_SIZE) { - LOG(ERROR) << "Padding length is too large : " - << static_cast(num_padding_bytes); - return false; - } - plaintext->resize(plaintext->size() - num_padding_bytes); - return true; -} - -bool AesCbcPkcs5Decryptor::SetIv(const std::vector& iv) { - if (iv.size() != AES_BLOCK_SIZE) { - LOG(ERROR) << "Invalid IV size: " << iv.size(); - return false; - } - - iv_ = iv; - return true; -} - AesCbcCtsEncryptor::AesCbcCtsEncryptor() {} AesCbcCtsEncryptor::~AesCbcCtsEncryptor() {} @@ -269,23 +238,27 @@ bool AesCbcCtsEncryptor::InitializeWithIv(const std::vector& key, return false; } - encrypt_key_.reset(new AES_KEY()); - CHECK_EQ(AES_set_encrypt_key(&key[0], key.size() * 8, encrypt_key_.get()), 0); + aes_key_.reset(new AES_KEY()); + CHECK_EQ(AES_set_encrypt_key(&key[0], key.size() * 8, aes_key_.get()), 0); iv_ = iv; return true; } -void AesCbcCtsEncryptor::Encrypt(const uint8_t* plaintext, - size_t size, - uint8_t* ciphertext) { +size_t AesCbcCtsEncryptor::NumPaddingBytes(size_t size) { + return 0; +} + +bool AesCbcCtsEncryptor::EncryptData(const uint8_t* plaintext, + size_t size, + uint8_t* ciphertext) { DCHECK(plaintext); DCHECK(ciphertext); if (size < AES_BLOCK_SIZE) { // Don't have a full block, leave unencrypted. memcpy(ciphertext, plaintext, size); - return; + return true; } std::vector iv(iv_); @@ -296,12 +269,12 @@ void AesCbcCtsEncryptor::Encrypt(const uint8_t* plaintext, AES_cbc_encrypt(plaintext, ciphertext, cbc_size, - encrypt_key_.get(), + aes_key_.get(), &iv[0], AES_ENCRYPT); if (residual_block_size == 0) { // No residual block. No need to do ciphertext stealing. - return; + return true; } // Zero-pad the residual block and encrypt using CBC. @@ -311,7 +284,7 @@ void AesCbcCtsEncryptor::Encrypt(const uint8_t* plaintext, AES_cbc_encrypt(&residual_block[0], &residual_block[0], AES_BLOCK_SIZE, - encrypt_key_.get(), + aes_key_.get(), &iv[0], AES_ENCRYPT); @@ -327,18 +300,10 @@ void AesCbcCtsEncryptor::Encrypt(const uint8_t* plaintext, memcpy(residual_ciphertext_block - AES_BLOCK_SIZE, residual_block.data(), AES_BLOCK_SIZE); + return true; } -void AesCbcCtsEncryptor::Encrypt(const std::vector& plaintext, - std::vector* ciphertext) { - DCHECK(ciphertext); - - ciphertext->resize(plaintext.size(), 0); - if (plaintext.empty()) - return; - - Encrypt(plaintext.data(), plaintext.size(), &(*ciphertext)[0]); -} +void AesCbcCtsEncryptor::UpdateIv() {} bool AesCbcCtsEncryptor::SetIv(const std::vector& iv) { if (iv.size() != AES_BLOCK_SIZE) { @@ -350,129 +315,5 @@ bool AesCbcCtsEncryptor::SetIv(const std::vector& iv) { return true; } -AesCbcCtsDecryptor::AesCbcCtsDecryptor() {} -AesCbcCtsDecryptor::~AesCbcCtsDecryptor() {} - -bool AesCbcCtsDecryptor::InitializeWithIv(const std::vector& key, - const std::vector& iv) { - if (!IsKeySizeValidForAes(key.size())) { - LOG(ERROR) << "Invalid AES key size: " << key.size(); - return false; - } - if (iv.size() != AES_BLOCK_SIZE) { - LOG(ERROR) << "Invalid IV size: " << iv.size(); - return false; - } - - decrypt_key_.reset(new AES_KEY()); - CHECK_EQ(AES_set_decrypt_key(&key[0], key.size() * 8, decrypt_key_.get()), 0); - - iv_ = iv; - return true; -} - -void AesCbcCtsDecryptor::Decrypt(const uint8_t* ciphertext, - size_t size, - uint8_t* plaintext) { - DCHECK(ciphertext); - DCHECK(plaintext); - - if (size < AES_BLOCK_SIZE) { - // Don't have a full block, leave unencrypted. - memcpy(plaintext, ciphertext, size); - return; - } - - std::vector iv(iv_); - size_t residual_block_size = size % AES_BLOCK_SIZE; - - if (residual_block_size == 0) { - // No residual block. No need to do ciphertext stealing. - AES_cbc_encrypt(ciphertext, - plaintext, - size, - decrypt_key_.get(), - &iv[0], - AES_DECRYPT); - return; - } - - // AES-CBC decrypt everything up to the next-to-last full block. - size_t cbc_size = size - residual_block_size; - if (cbc_size > AES_BLOCK_SIZE) { - AES_cbc_encrypt(ciphertext, - plaintext, - cbc_size - AES_BLOCK_SIZE, - decrypt_key_.get(), - &iv[0], - AES_DECRYPT); - } - - // Determine what the last IV should be so that we can "skip ahead" in the - // CBC decryption. - std::vector last_iv(ciphertext + size - residual_block_size, - ciphertext + size); - last_iv.resize(AES_BLOCK_SIZE, 0); - - // Decrypt the next-to-last block using the IV determined above. This decrypts - // the residual block bits. - AES_cbc_encrypt(ciphertext + size - residual_block_size - AES_BLOCK_SIZE, - plaintext + size - residual_block_size - AES_BLOCK_SIZE, - AES_BLOCK_SIZE, - decrypt_key_.get(), - &last_iv[0], - AES_DECRYPT); - - // Swap back the residual block bits and the next-to-last full block. - if (plaintext == ciphertext) { - uint8_t* ptr1 = plaintext + size - residual_block_size; - uint8_t* ptr2 = plaintext + size - residual_block_size - AES_BLOCK_SIZE; - for (size_t i = 0; i < residual_block_size; ++i) { - uint8_t temp = *ptr1; - *ptr1 = *ptr2; - *ptr2 = temp; - ++ptr1; - ++ptr2; - } - } else { - uint8_t* residual_plaintext_block = plaintext + size - residual_block_size; - memcpy(residual_plaintext_block, - residual_plaintext_block - AES_BLOCK_SIZE, - residual_block_size); - memcpy(residual_plaintext_block - AES_BLOCK_SIZE, - ciphertext + size - residual_block_size, - residual_block_size); - } - - // Decrypt the last full block. - AES_cbc_encrypt(plaintext + size - residual_block_size - AES_BLOCK_SIZE, - plaintext + size - residual_block_size - AES_BLOCK_SIZE, - AES_BLOCK_SIZE, - decrypt_key_.get(), - &iv[0], - AES_DECRYPT); -} - -void AesCbcCtsDecryptor::Decrypt(const std::vector& ciphertext, - std::vector* plaintext) { - DCHECK(plaintext); - - plaintext->resize(ciphertext.size(), 0); - if (ciphertext.empty()) - return; - - Decrypt(ciphertext.data(), ciphertext.size(), &(*plaintext)[0]); -} - -bool AesCbcCtsDecryptor::SetIv(const std::vector& iv) { - if (iv.size() != AES_BLOCK_SIZE) { - LOG(ERROR) << "Invalid IV size: " << iv.size(); - return false; - } - - iv_ = iv; - return true; -} - } // namespace media } // namespace edash_packager diff --git a/packager/media/base/aes_encryptor.h b/packager/media/base/aes_encryptor.h index edb8a5b437..f584727def 100644 --- a/packager/media/base/aes_encryptor.h +++ b/packager/media/base/aes_encryptor.h @@ -21,86 +21,93 @@ typedef struct aes_key_st AES_KEY; namespace edash_packager { namespace media { -// Class which implements AES-CTR counter-mode encryption/decryption. -class AesCtrEncryptor { +class AesEncryptor { public: - AesCtrEncryptor(); - ~AesCtrEncryptor(); + AesEncryptor(); + virtual ~AesEncryptor(); /// Initialize the encryptor with specified key and a random generated IV - /// of the specified size. block_offset() is reset to 0 on success. + /// of the specified size. + /// @return true on successful initialization, false otherwise. + virtual 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. + virtual bool InitializeWithIv(const std::vector& key, + const std::vector& iv) = 0; + + virtual size_t NumPaddingBytes(size_t size) = 0; + + /// @name Various forms of encrypt and decrypt calls. + /// The plaintext and ciphertext pointers can be the same address. + /// @{ + virtual bool EncryptData(const uint8_t* plaintext, + size_t plaintext_size, + uint8_t* ciphertext) = 0; + + bool Encrypt(const std::vector& plaintext, + std::vector* ciphertext); + + bool Encrypt(const std::string& plaintext, std::string* ciphertext); + /// @} + + /// Update IV for next sample. + /// As recommended in ISO/IEC FDIS 23001-7: + /// IV need to be updated per sample for CENC. + /// IV need not be unique per sample for CBC mode. + virtual void UpdateIv() = 0; + + /// Set IV. + /// @return true if successful, false if the input is invalid. + virtual bool SetIv(const std::vector& iv) = 0; + + const std::vector& iv() const { return iv_; } + + protected: + // Initialization vector, with size 8 or 16. + std::vector iv_; + // Openssl AES_KEY. + scoped_ptr aes_key_; + + private: + DISALLOW_COPY_AND_ASSIGN(AesEncryptor); +}; + +// Class which implements AES-CTR counter-mode encryption/decryption. +class AesCtrEncryptor : public AesEncryptor { + public: + AesCtrEncryptor(); + ~AesCtrEncryptor() override; + + /// @name AesEncryptor implementation overrides. + /// @{ /// @param key should be 16 bytes in size as specified in CENC spec. /// @param iv_size should be either 8 or 16 as specified in CENC spec. /// @return true on successful initialization, false otherwise. - bool InitializeWithRandomIv(const std::vector& key, uint8_t iv_size); - - /// Initialize the encryptor with specified key and IV. block_offset() is - /// reset to 0 on success. - /// @param key should be 16 bytes in size as specified in CENC spec. - /// @param iv should be 8 bytes or 16 bytes in size as specified in CENC spec. - /// @return true on successful initialization, false otherwise. bool InitializeWithIv(const std::vector& key, - const std::vector& iv); + const std::vector& iv) override; - /// @name Various forms of encrypt calls. - /// block_offset() will be updated according to input plaintext size. - /// The plaintext and ciphertext pointers can be the same address. - /// @{ - bool Encrypt(const uint8_t* plaintext, - size_t plaintext_size, - uint8_t* ciphertext); + size_t NumPaddingBytes(size_t size) override; - bool Encrypt(const std::vector& plaintext, - std::vector* ciphertext) { - ciphertext->resize(plaintext.size()); - return Encrypt(&plaintext[0], plaintext.size(), &(*ciphertext)[0]); - } - - bool Encrypt(const std::string& plaintext, std::string* ciphertext) { - ciphertext->resize(plaintext.size()); - return Encrypt(reinterpret_cast(plaintext.data()), - plaintext.size(), - reinterpret_cast(string_as_array(ciphertext))); - } - /// @} - - // For AES CTR, encryption and decryption are identical. - bool Decrypt(const uint8_t* ciphertext, - size_t ciphertext_size, - uint8_t* plaintext) { - return Encrypt(ciphertext, ciphertext_size, plaintext); - } - - bool Decrypt(const std::vector& ciphertext, - std::vector* plaintext) { - return Encrypt(ciphertext, plaintext); - } - - bool Decrypt(const std::string& ciphertext, std::string* plaintext) { - return Encrypt(ciphertext, plaintext); - } + bool EncryptData(const uint8_t* plaintext, + size_t plaintext_size, + uint8_t* ciphertext) override; /// Update IV for next sample. @a block_offset_ is reset to 0. /// 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(); + void UpdateIv() override; - /// Set IV. @a block_offset_ is reset to 0 on success. - /// @return true if successful, false if the input is invalid. - bool SetIv(const std::vector& iv); - - const std::vector& iv() const { return iv_; } + bool SetIv(const std::vector& iv) override; + /// @} uint32_t block_offset() const { return block_offset_; } private: - // Initialization vector, with size 8 or 16. - std::vector iv_; // Current block offset. uint32_t block_offset_; - // Openssl AES_KEY. - scoped_ptr aes_key_; // Current AES-CTR counter. std::vector counter_; // Encrypted counter. @@ -113,148 +120,58 @@ class AesCtrEncryptor { // Class which implements AES-CBC (Cipher block chaining) encryption with // PKCS#5 padding. -class AesCbcPkcs5Encryptor { +class AesCbcPkcs5Encryptor : public AesEncryptor { public: AesCbcPkcs5Encryptor(); - ~AesCbcPkcs5Encryptor(); + ~AesCbcPkcs5Encryptor() override; - /// Initialize the encryptor with specified key and IV. - /// @param key should be 128 bits or 192 bits or 256 bits in size as defined - /// in AES spec. - /// @param iv should be 16 bytes in size. - /// @return true on successful initialization, false otherwise. + /// @name AesEncryptor implementation overrides. + /// @{ bool InitializeWithIv(const std::vector& key, - const std::vector& iv); + const std::vector& iv) override; - /// @param plaintext will be PKCS5 padded before being encrypted. - /// @param ciphertext should not be NULL. - void Encrypt(const std::string& plaintext, std::string* ciphertext); + size_t NumPaddingBytes(size_t size) override; - /// @return true if successful, false if the input is invalid. - bool SetIv(const std::vector& iv); + bool EncryptData(const uint8_t* plaintext, + size_t plaintext_size, + uint8_t* ciphertext) override; - const std::vector& iv() const { return iv_; } + void UpdateIv() override; + + bool SetIv(const std::vector& iv) override; + /// @} private: - std::vector iv_; - scoped_ptr encrypt_key_; - DISALLOW_COPY_AND_ASSIGN(AesCbcPkcs5Encryptor); }; -// Class which implements AES-CBC (Cipher block chaining) decryption with -// PKCS#5 padding. -class AesCbcPkcs5Decryptor { - public: - AesCbcPkcs5Decryptor(); - ~AesCbcPkcs5Decryptor(); - - /// Initialize the decryptor with specified key and IV. - /// @param key should be 128 bits or 192 bits or 256 bits in size as defined - /// in AES spec. - /// @param iv should be 16 bytes in size. - /// @return true on successful initialization, false otherwise. - bool InitializeWithIv(const std::vector& key, - const std::vector& iv); - - /// @param ciphertext is expected to be padded with PKCS5 padding. - /// @param plaintext should not be NULL. - /// @return true on success, false otherwise. - bool Decrypt(const std::string& ciphertext, std::string* plaintext); - - /// @return true if successful, false if the input is invalid. - bool SetIv(const std::vector& iv); - - const std::vector& iv() const { return iv_; } - - private: - std::vector iv_; - scoped_ptr decrypt_key_; - - DISALLOW_COPY_AND_ASSIGN(AesCbcPkcs5Decryptor); -}; - // Class which implements AES-CBC (Cipher block chaining) encryption with // Ciphertext stealing. -class AesCbcCtsEncryptor { +class AesCbcCtsEncryptor : public AesEncryptor { public: AesCbcCtsEncryptor(); - ~AesCbcCtsEncryptor(); + ~AesCbcCtsEncryptor() override; - /// Initialize the encryptor with specified key and IV. - /// @param key should be 128 bits or 192 bits or 256 bits in size as defined - /// in AES spec. - /// @param iv should be 16 bytes in size. - /// @return true on successful initialization, false otherwise. + /// @name AesEncryptor implementation overrides. + /// @{ bool InitializeWithIv(const std::vector& key, - const std::vector& iv); + const std::vector& iv) override; - /// @param plaintext points to the data to be encrypted. - /// @param size is the number of bytes to be encrypted. If less than 16 - /// bytes, it will be copied in the clear. - /// @param ciphertext should not be NULL. The buffer should be at least - /// @a size bytes in length. - void Encrypt(const uint8_t* plaintext, size_t size, uint8_t* ciphertext); + size_t NumPaddingBytes(size_t size) override; - /// @param plaintext contains the data to be encrypted. If less than 16 - /// bytes in size, it will be copied in the clear. - /// @param ciphertext should not be NULL. Caller retains ownership. - void Encrypt(const std::vector& plaintext, - std::vector* ciphertext); + bool EncryptData(const uint8_t* plaintext, + size_t plaintext_size, + uint8_t* ciphertext) override; - /// @param iv is the initialization vector. Should be 16 bytes in size. - /// @return true if successful, false if the input is invalid. - bool SetIv(const std::vector& iv); + void UpdateIv() override; - const std::vector& iv() const { return iv_; } + bool SetIv(const std::vector& iv) override; + /// @} private: - std::vector iv_; - scoped_ptr encrypt_key_; - DISALLOW_COPY_AND_ASSIGN(AesCbcCtsEncryptor); }; -// Class which implements AES-CBC (Cipher block chaining) decryption with -// Ciphertext stealing. -class AesCbcCtsDecryptor { - public: - AesCbcCtsDecryptor(); - ~AesCbcCtsDecryptor(); - - /// Initialize the decryptor with specified key and IV. - /// @param key should be 128 bits or 192 bits or 256 bits in size as defined - /// in AES spec. - /// @param iv should be 16 bytes in size. - /// @return true on successful initialization, false otherwise. - bool InitializeWithIv(const std::vector& key, - const std::vector& iv); - - /// @param ciphertext points to the data to be decrypted. - /// @param size is the number of bytes to be decrypted. If less than 16 - /// bytes, it will be copied in the clear. - /// @param plaintext should not be NULL. The buffer should be at least - /// @a size bytes in length. - void Decrypt(const uint8_t* ciphertext, size_t size, uint8_t* plaintext); - - /// @param ciphertext contains the data to be decrypted. If less than 16 - /// bytes in size, it will be copied in the clear. - /// @param plaintext should not be NULL. Caller retains ownership. - void Decrypt(const std::vector& ciphertext, - std::vector* plaintext); - - /// @return true if successful, false if the input is invalid. - bool SetIv(const std::vector& iv); - - const std::vector& iv() const { return iv_; } - - private: - std::vector iv_; - scoped_ptr decrypt_key_; - - DISALLOW_COPY_AND_ASSIGN(AesCbcCtsDecryptor); -}; - } // namespace media } // namespace edash_packager diff --git a/packager/media/base/aes_encryptor_unittest.cc b/packager/media/base/aes_encryptor_unittest.cc index 4e6f638f71..71d2455587 100644 --- a/packager/media/base/aes_encryptor_unittest.cc +++ b/packager/media/base/aes_encryptor_unittest.cc @@ -10,6 +10,7 @@ #include "packager/base/memory/scoped_ptr.h" #include "packager/base/strings/string_number_conversions.h" #include "packager/media/base/aes_encryptor.h" +#include "packager/media/base/aes_decryptor.h" namespace { @@ -144,6 +145,7 @@ class AesCtrEncryptorTest : public testing::Test { kAesCtrCiphertext + arraysize(kAesCtrCiphertext)); ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_)); + ASSERT_TRUE(decryptor_.InitializeWithIv(key_, iv_)); } protected: @@ -152,6 +154,7 @@ class AesCtrEncryptorTest : public testing::Test { std::vector plaintext_; std::vector ciphertext_; AesCtrEncryptor encryptor_; + AesCtrDecryptor decryptor_; }; TEST_F(AesCtrEncryptorTest, NistTestCase) { @@ -159,19 +162,19 @@ TEST_F(AesCtrEncryptorTest, NistTestCase) { EXPECT_TRUE(encryptor_.Encrypt(plaintext_, &encrypted)); EXPECT_EQ(ciphertext_, encrypted); - EXPECT_TRUE(encryptor_.SetIv(iv_)); + EXPECT_TRUE(decryptor_.SetIv(iv_)); std::vector decrypted; - EXPECT_TRUE(encryptor_.Decrypt(encrypted, &decrypted)); + EXPECT_TRUE(decryptor_.Decrypt(encrypted, &decrypted)); EXPECT_EQ(plaintext_, decrypted); } TEST_F(AesCtrEncryptorTest, NistTestCaseInplaceEncryptionDecryption) { std::vector buffer = plaintext_; - EXPECT_TRUE(encryptor_.Encrypt(&buffer[0], buffer.size(), &buffer[0])); + EXPECT_TRUE(encryptor_.EncryptData(&buffer[0], buffer.size(), &buffer[0])); EXPECT_EQ(ciphertext_, buffer); - EXPECT_TRUE(encryptor_.SetIv(iv_)); - EXPECT_TRUE(encryptor_.Decrypt(&buffer[0], buffer.size(), &buffer[0])); + EXPECT_TRUE(decryptor_.SetIv(iv_)); + EXPECT_TRUE(decryptor_.Decrypt(&buffer[0], buffer.size(), &buffer[0])); EXPECT_EQ(plaintext_, buffer); } @@ -186,8 +189,8 @@ TEST_F(AesCtrEncryptorTest, EncryptDecryptString) { base::HexEncode(ciphertext.data(), ciphertext.size())); std::string decrypted; - EXPECT_TRUE(encryptor_.SetIv(iv_)); - EXPECT_TRUE(encryptor_.Decrypt(ciphertext, &decrypted)); + EXPECT_TRUE(decryptor_.SetIv(iv_)); + EXPECT_TRUE(decryptor_.Decrypt(ciphertext, &decrypted)); EXPECT_EQ(kPlaintext, decrypted); } @@ -208,13 +211,13 @@ 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])); + EXPECT_TRUE(encryptor_.EncryptData(&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])); + EXPECT_TRUE(encryptor_.EncryptData(&plaintext_[kAesBlockSize], + kAesBlockSize * 3, + &encrypted_verify[kAesBlockSize])); EXPECT_EQ(encrypted, encrypted_verify); } @@ -251,20 +254,20 @@ TEST_P(AesCtrEncryptorSubsampleTest, NistTestCaseSubsamples) { 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])); + encryptor_.EncryptData(&plaintext_[offset], len, &encrypted[offset])); offset += len; EXPECT_EQ(offset % kAesBlockSize, encryptor_.block_offset()); } EXPECT_EQ(ciphertext_, encrypted); - EXPECT_TRUE(encryptor_.SetIv(iv_)); + EXPECT_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( - encryptor_.Decrypt(&encrypted[offset], len, &decrypted[offset])); + decryptor_.Decrypt(&encrypted[offset], len, &decrypted[offset])); offset += len; - EXPECT_EQ(offset % kAesBlockSize, encryptor_.block_offset()); + EXPECT_EQ(offset % kAesBlockSize, decryptor_.block_offset()); } EXPECT_EQ(plaintext_, decrypted); } diff --git a/packager/media/base/decrypt_config.cc b/packager/media/base/decrypt_config.cc index bd2dc56933..d8c73946be 100644 --- a/packager/media/base/decrypt_config.cc +++ b/packager/media/base/decrypt_config.cc @@ -11,8 +11,12 @@ namespace media { DecryptConfig::DecryptConfig(const std::vector& key_id, const std::vector& iv, - const std::vector& subsamples) - : key_id_(key_id), iv_(iv), subsamples_(subsamples) { + const std::vector& subsamples, + EncryptionMode decryption_mode) + : key_id_(key_id), + iv_(iv), + subsamples_(subsamples), + decryption_mode_(decryption_mode) { CHECK_GT(key_id.size(), 0u); } diff --git a/packager/media/base/decrypt_config.h b/packager/media/base/decrypt_config.h index f0921abcb4..82a86e34ef 100644 --- a/packager/media/base/decrypt_config.h +++ b/packager/media/base/decrypt_config.h @@ -11,6 +11,7 @@ #include #include "packager/base/memory/scoped_ptr.h" +#include "packager/media/base/encryption_modes.h" namespace edash_packager { namespace media { @@ -46,14 +47,18 @@ class DecryptConfig { /// @param subsamples defines the clear and encrypted portions of the sample /// as described in SubsampleEntry. A decrypted buffer will be equal /// in size to the sum of the subsample sizes. + /// @param decryption_mode decryption_mode is to determine which decryptor to + /// use. DecryptConfig(const std::vector& key_id, const std::vector& iv, - const std::vector& subsamples); + const std::vector& subsamples, + EncryptionMode decryption_mode); ~DecryptConfig(); const std::vector& key_id() const { return key_id_; } const std::vector& iv() const { return iv_; } const std::vector& subsamples() const { return subsamples_; } + EncryptionMode decryption_mode() const { return decryption_mode_; } private: const std::vector key_id_; @@ -65,6 +70,8 @@ class DecryptConfig { // (less data ignored by data_offset_) is encrypted. const std::vector subsamples_; + EncryptionMode decryption_mode_; + DISALLOW_COPY_AND_ASSIGN(DecryptConfig); }; diff --git a/packager/media/base/decryptor_source.cc b/packager/media/base/decryptor_source.cc index a464fafe7a..64048e3f8d 100644 --- a/packager/media/base/decryptor_source.cc +++ b/packager/media/base/decryptor_source.cc @@ -27,22 +27,36 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config, DCHECK(buffer); // Get the decryptor object. - AesCtrEncryptor* decryptor; + AesDecryptor* decryptor; auto found = decryptor_map_.find(decrypt_config->key_id()); if (found == decryptor_map_.end()) { - // Create new AesCtrEncryptor + // Create new AesDecryptor based on decryption mode. EncryptionKey key; Status status(key_source_->GetKey(decrypt_config->key_id(), &key)); if (!status.ok()) { LOG(ERROR) << "Error retrieving decryption key: " << status; return false; } - scoped_ptr aes_ctr_encryptor(new AesCtrEncryptor); - if (!aes_ctr_encryptor->InitializeWithIv(key.key, decrypt_config->iv())) { - LOG(ERROR) << "Failed to initialize AesCtrEncryptor for decryption."; + + scoped_ptr aes_decryptor; + switch (decrypt_config->decryption_mode()) { + case kEncryptionModeAesCtr: + aes_decryptor.reset(new AesCtrDecryptor); + break; + case kEncryptionModeAesCbc: + aes_decryptor.reset(new AesCbcPkcs5Decryptor); + break; + default: + LOG(ERROR) << "Unsupported Decryption Mode: " + << decrypt_config->decryption_mode(); + return false; + } + + if (!aes_decryptor->InitializeWithIv(key.key, decrypt_config->iv())) { + LOG(ERROR) << "Failed to initialize AesDecryptor for decryption."; return false; } - decryptor = aes_ctr_encryptor.release(); + decryptor = aes_decryptor.release(); decryptor_map_[decrypt_config->key_id()] = decryptor; } else { decryptor = found->second; diff --git a/packager/media/base/decryptor_source.h b/packager/media/base/decryptor_source.h index bf1091b6fc..60598c2e01 100644 --- a/packager/media/base/decryptor_source.h +++ b/packager/media/base/decryptor_source.h @@ -10,7 +10,7 @@ #include #include -#include "packager/media/base/aes_encryptor.h" +#include "packager/media/base/aes_decryptor.h" #include "packager/media/base/decrypt_config.h" #include "packager/media/base/key_source.h" @@ -29,7 +29,7 @@ class DecryptorSource { private: KeySource* key_source_; - std::map, AesCtrEncryptor*> decryptor_map_; + std::map, AesDecryptor*> decryptor_map_; DISALLOW_COPY_AND_ASSIGN(DecryptorSource); }; diff --git a/packager/media/base/decryptor_source_unittest.cc b/packager/media/base/decryptor_source_unittest.cc index ae4350c6a4..a595824bd8 100644 --- a/packager/media/base/decryptor_source_unittest.cc +++ b/packager/media/base/decryptor_source_unittest.cc @@ -80,7 +80,8 @@ TEST_F(DecryptorSourceTest, FullSampleDecryption) { DecryptConfig decrypt_config(key_id_, std::vector(kIv, kIv + arraysize(kIv)), - std::vector()); + std::vector(), + kEncryptionModeAesCtr); ASSERT_TRUE(decryptor_source_.DecryptSampleBuffer(&decrypt_config, &buffer_[0], buffer_.size())); EXPECT_EQ(std::vector( @@ -93,7 +94,7 @@ TEST_F(DecryptorSourceTest, FullSampleDecryption) { buffer_.assign(kBuffer2, kBuffer2 + arraysize(kBuffer2)); DecryptConfig decrypt_config2( key_id_, std::vector(kIv2, kIv2 + arraysize(kIv2)), - std::vector()); + std::vector(), kEncryptionModeAesCtr); ASSERT_TRUE(decryptor_source_.DecryptSampleBuffer( &decrypt_config2, &buffer_[0], buffer_.size())); EXPECT_EQ(std::vector(kExpectedDecryptedBuffer2, @@ -128,7 +129,8 @@ TEST_F(DecryptorSourceTest, SubsampleDecryption) { DecryptConfig decrypt_config( key_id_, std::vector(kIv, kIv + arraysize(kIv)), std::vector(kSubsamples, - kSubsamples + arraysize(kSubsamples))); + kSubsamples + arraysize(kSubsamples)), + kEncryptionModeAesCtr); ASSERT_TRUE(decryptor_source_.DecryptSampleBuffer( &decrypt_config, &buffer_[0], buffer_.size())); EXPECT_EQ(std::vector( @@ -152,7 +154,8 @@ TEST_F(DecryptorSourceTest, SubsampleDecryptionSizeValidation) { DecryptConfig decrypt_config( key_id_, std::vector(kIv, kIv + arraysize(kIv)), std::vector(kSubsamples, - kSubsamples + arraysize(kSubsamples))); + kSubsamples + arraysize(kSubsamples)), + kEncryptionModeAesCtr); ASSERT_FALSE(decryptor_source_.DecryptSampleBuffer( &decrypt_config, &buffer_[0], buffer_.size())); } @@ -163,7 +166,8 @@ TEST_F(DecryptorSourceTest, DecryptFailedIfGetKeyFailed) { DecryptConfig decrypt_config(key_id_, std::vector(kIv, kIv + arraysize(kIv)), - std::vector()); + std::vector(), + kEncryptionModeAesCtr); ASSERT_FALSE(decryptor_source_.DecryptSampleBuffer( &decrypt_config, &buffer_[0], buffer_.size())); } diff --git a/packager/media/base/encryption_modes.h b/packager/media/base/encryption_modes.h new file mode 100644 index 0000000000..c99c344b6f --- /dev/null +++ b/packager/media/base/encryption_modes.h @@ -0,0 +1,23 @@ +// 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 MEDIA_BASE_ENCRYPTION_MODE_H_ +#define MEDIA_BASE_ENCRYPTION_MODE_H_ + +namespace edash_packager { +namespace media { + +/// Supported encryption mode. +enum EncryptionMode { + kEncryptionModeUnknown, + kEncryptionModeAesCtr, + kEncryptionModeAesCbc +}; + +} // namespace media +} // namespace edash_packager + +#endif // MEDIA_BASE_ENCRYPTION_MODE_H_ diff --git a/packager/media/base/media_base.gyp b/packager/media/base/media_base.gyp index 01ac04dc13..f1e64be7d5 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_decryptor.cc', + 'aes_decryptor.h', 'aes_encryptor.cc', 'aes_encryptor.h', 'audio_stream_info.cc', @@ -37,6 +39,7 @@ 'decrypt_config.h', 'decryptor_source.cc', 'decryptor_source.h', + 'encryption_modes.h', 'http_key_fetcher.cc', 'http_key_fetcher.h', 'key_fetcher.cc', diff --git a/packager/media/base/muxer.cc b/packager/media/base/muxer.cc index 61310d5968..ba921cd19e 100644 --- a/packager/media/base/muxer.cc +++ b/packager/media/base/muxer.cc @@ -19,6 +19,7 @@ Muxer::Muxer(const MuxerOptions& options) max_sd_pixels_(0), clear_lead_in_seconds_(0), crypto_period_duration_in_seconds_(0), + encryption_mode_(kEncryptionModeUnknown), cancelled_(false), clock_(NULL) {} @@ -27,12 +28,14 @@ Muxer::~Muxer() {} void Muxer::SetKeySource(KeySource* encryption_key_source, uint32_t max_sd_pixels, double clear_lead_in_seconds, - double crypto_period_duration_in_seconds) { + double crypto_period_duration_in_seconds, + EncryptionMode encryption_mode) { DCHECK(encryption_key_source); encryption_key_source_ = encryption_key_source; max_sd_pixels_ = max_sd_pixels; clear_lead_in_seconds_ = clear_lead_in_seconds; crypto_period_duration_in_seconds_ = crypto_period_duration_in_seconds; + encryption_mode_ = encryption_mode; } void Muxer::AddStream(MediaStream* stream) { diff --git a/packager/media/base/muxer.h b/packager/media/base/muxer.h index 27d10844e0..234c87eea4 100644 --- a/packager/media/base/muxer.h +++ b/packager/media/base/muxer.h @@ -14,6 +14,7 @@ #include "packager/base/memory/ref_counted.h" #include "packager/base/memory/scoped_ptr.h" #include "packager/base/time/clock.h" +#include "packager/media/base/encryption_modes.h" #include "packager/media/base/muxer_options.h" #include "packager/media/base/status.h" #include "packager/media/event/muxer_listener.h" @@ -47,7 +48,8 @@ class Muxer { void SetKeySource(KeySource* encryption_key_source, uint32_t max_sd_pixels, double clear_lead_in_seconds, - double crypto_period_duration_in_seconds); + double crypto_period_duration_in_seconds, + EncryptionMode encryption_mode); /// Add video/audio stream. void AddStream(MediaStream* stream); @@ -92,6 +94,7 @@ class Muxer { MuxerListener* muxer_listener() { return muxer_listener_.get(); } ProgressListener* progress_listener() { return progress_listener_.get(); } base::Clock* clock() { return clock_; } + EncryptionMode encryption_mode() const { return encryption_mode_; } private: friend class MediaStream; // Needed to access AddSample. @@ -117,6 +120,7 @@ class Muxer { uint32_t max_sd_pixels_; double clear_lead_in_seconds_; double crypto_period_duration_in_seconds_; + EncryptionMode encryption_mode_; bool cancelled_; scoped_ptr muxer_listener_; diff --git a/packager/media/formats/mp4/box_definitions.cc b/packager/media/formats/mp4/box_definitions.cc index 3afe3c4d43..7227aac03c 100644 --- a/packager/media/formats/mp4/box_definitions.cc +++ b/packager/media/formats/mp4/box_definitions.cc @@ -450,7 +450,7 @@ bool ProtectionSchemeInfo::ReadWriteInternal(BoxBuffer* buffer) { buffer->PrepareChildren() && buffer->ReadWriteChild(&format) && buffer->ReadWriteChild(&type)); - if (type.type == FOURCC_CENC) + if (type.type == FOURCC_CENC || type.type == FOURCC_CBC1) RCHECK(buffer->ReadWriteChild(&info)); // Other protection schemes are silently ignored. Since the protection scheme // type can't be determined until this box is opened, we return 'true' for @@ -1300,7 +1300,7 @@ bool VideoSampleEntry::ReadWriteInternal(BoxBuffer* buffer) { if (buffer->Reading()) { // Continue scanning until a recognized protection scheme is found, // or until we run out of protection schemes. - while (sinf.type.type != FOURCC_CENC) { + while (sinf.type.type != FOURCC_CENC && sinf.type.type != FOURCC_CBC1) { if (!buffer->ReadWriteChild(&sinf)) return false; } @@ -1485,7 +1485,7 @@ bool AudioSampleEntry::ReadWriteInternal(BoxBuffer* buffer) { if (buffer->Reading()) { // Continue scanning until a recognized protection scheme is found, // or until we run out of protection schemes. - while (sinf.type.type != FOURCC_CENC) { + while (sinf.type.type != FOURCC_CENC && sinf.type.type != FOURCC_CBC1) { if (!buffer->ReadWriteChild(&sinf)) return false; } diff --git a/packager/media/formats/mp4/encrypting_fragmenter.cc b/packager/media/formats/mp4/encrypting_fragmenter.cc index 0abfa2cde0..007d200110 100644 --- a/packager/media/formats/mp4/encrypting_fragmenter.cc +++ b/packager/media/formats/mp4/encrypting_fragmenter.cc @@ -64,12 +64,14 @@ EncryptingFragmenter::EncryptingFragmenter( scoped_refptr info, TrackFragment* traf, scoped_ptr encryption_key, - int64_t clear_time) + int64_t clear_time, + EncryptionMode encryption_mode) : Fragmenter(traf), info_(info), encryption_key_(encryption_key.Pass()), nalu_length_size_(GetNaluLengthSize(*info)), - clear_time_(clear_time) { + clear_time_(clear_time), + encryption_mode_(encryption_mode) { DCHECK(encryption_key_); VideoCodec video_codec = GetVideoCodec(*info); if (video_codec == kCodecVP8) { @@ -168,8 +170,14 @@ void EncryptingFragmenter::FinalizeFragmentForEncryption() { Status EncryptingFragmenter::CreateEncryptor() { DCHECK(encryption_key_); - - scoped_ptr encryptor(new AesCtrEncryptor()); + scoped_ptr encryptor; + if (encryption_mode_ == kEncryptionModeAesCtr) { + encryptor.reset(new AesCtrEncryptor); + } else if (encryption_mode_ == kEncryptionModeAesCbc) { + encryptor.reset(new AesCbcPkcs5Encryptor); + } else { + return Status(error::MUXER_FAILURE, "Unsupported encryption mode."); + } const bool initialized = encryption_key_->iv.empty() ? encryptor->InitializeWithRandomIv( encryption_key_->key, kDefaultIvSize) @@ -183,7 +191,7 @@ Status EncryptingFragmenter::CreateEncryptor() { void EncryptingFragmenter::EncryptBytes(uint8_t* data, uint32_t size) { DCHECK(encryptor_); - CHECK(encryptor_->Encrypt(data, size, data)); + CHECK(encryptor_->EncryptData(data, size, data)); } Status EncryptingFragmenter::EncryptSample(scoped_refptr sample) { diff --git a/packager/media/formats/mp4/encrypting_fragmenter.h b/packager/media/formats/mp4/encrypting_fragmenter.h index 0767dc298d..50291416ca 100644 --- a/packager/media/formats/mp4/encrypting_fragmenter.h +++ b/packager/media/formats/mp4/encrypting_fragmenter.h @@ -9,6 +9,7 @@ #include "packager/base/memory/ref_counted.h" #include "packager/base/memory/scoped_ptr.h" +#include "packager/media/base/encryption_modes.h" #include "packager/media/filters/vpx_parser.h" #include "packager/media/formats/mp4/fragmenter.h" #include "packager/media/formats/mp4/video_slice_header_parser.h" @@ -16,7 +17,7 @@ namespace edash_packager { namespace media { -class AesCtrEncryptor; +class AesEncryptor; class StreamInfo; struct EncryptionKey; @@ -32,7 +33,8 @@ class EncryptingFragmenter : public Fragmenter { EncryptingFragmenter(scoped_refptr info, TrackFragment* traf, scoped_ptr encryption_key, - int64_t clear_time); + int64_t clear_time, + EncryptionMode encryption_mode); ~EncryptingFragmenter() override; @@ -56,7 +58,7 @@ class EncryptingFragmenter : public Fragmenter { Status CreateEncryptor(); EncryptionKey* encryption_key() { return encryption_key_.get(); } - AesCtrEncryptor* encryptor() { return encryptor_.get(); } + AesEncryptor* encryptor() { return encryptor_.get(); } void set_encryption_key(scoped_ptr encryption_key) { encryption_key_ = encryption_key.Pass(); @@ -71,12 +73,13 @@ class EncryptingFragmenter : public Fragmenter { scoped_refptr info_; scoped_ptr encryption_key_; - scoped_ptr encryptor_; + scoped_ptr encryptor_; // If this stream contains AVC, subsample encryption specifies that the size // and type of NAL units remain unencrypted. This function returns the size of // the size field in bytes. Can be 1, 2 or 4 bytes. const uint8_t nalu_length_size_; int64_t clear_time_; + EncryptionMode encryption_mode_; scoped_ptr vpx_parser_; scoped_ptr header_parser_; diff --git a/packager/media/formats/mp4/fourccs.h b/packager/media/formats/mp4/fourccs.h index 8c92b8f59b..5350de4c9e 100644 --- a/packager/media/formats/mp4/fourccs.h +++ b/packager/media/formats/mp4/fourccs.h @@ -20,6 +20,7 @@ enum FourCC { FOURCC_AVC1 = 0x61766331, FOURCC_AVCC = 0x61766343, FOURCC_BLOC = 0x626C6F63, + FOURCC_CBC1 = 0x63626331, FOURCC_CENC = 0x63656e63, FOURCC_CO64 = 0x636f3634, FOURCC_CTTS = 0x63747473, diff --git a/packager/media/formats/mp4/key_rotation_fragmenter.cc b/packager/media/formats/mp4/key_rotation_fragmenter.cc index 0d25b38226..e5487243f9 100644 --- a/packager/media/formats/mp4/key_rotation_fragmenter.cc +++ b/packager/media/formats/mp4/key_rotation_fragmenter.cc @@ -24,11 +24,13 @@ KeyRotationFragmenter::KeyRotationFragmenter(MovieFragment* moof, KeySource::TrackType track_type, int64_t crypto_period_duration, int64_t clear_time, - MuxerListener* muxer_listener) + MuxerListener* muxer_listener, + EncryptionMode encryption_mode) : EncryptingFragmenter(info, traf, scoped_ptr(new EncryptionKey()), - clear_time), + clear_time, + encryption_mode), moof_(moof), encryption_key_source_(encryption_key_source), track_type_(track_type), diff --git a/packager/media/formats/mp4/key_rotation_fragmenter.h b/packager/media/formats/mp4/key_rotation_fragmenter.h index 5bdab76b00..f798aaa0fd 100644 --- a/packager/media/formats/mp4/key_rotation_fragmenter.h +++ b/packager/media/formats/mp4/key_rotation_fragmenter.h @@ -7,6 +7,7 @@ #ifndef MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_ #define MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_ +#include "packager/media/base/encryption_modes.h" #include "packager/media/base/key_source.h" #include "packager/media/event/muxer_listener.h" #include "packager/media/formats/mp4/encrypting_fragmenter.h" @@ -40,7 +41,8 @@ class KeyRotationFragmenter : public EncryptingFragmenter { KeySource::TrackType track_type, int64_t crypto_period_duration, int64_t clear_time, - MuxerListener* muxer_listener); + MuxerListener* muxer_listener, + EncryptionMode encryption_mode); ~KeyRotationFragmenter() override; protected: diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index cc744becc9..10c90efc03 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -154,7 +154,8 @@ Status MP4Muxer::Initialize() { encryption_key_source(), max_sd_pixels(), clear_lead_in_seconds(), - crypto_period_duration_in_seconds()); + crypto_period_duration_in_seconds(), + encryption_mode()); if (!segmenter_initialized.ok()) return segmenter_initialized; diff --git a/packager/media/formats/mp4/segmenter.cc b/packager/media/formats/mp4/segmenter.cc index ef6a320001..514d444119 100644 --- a/packager/media/formats/mp4/segmenter.cc +++ b/packager/media/formats/mp4/segmenter.cc @@ -38,6 +38,7 @@ const uint8_t kKeyRotationDefaultKeyId[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + COMPILE_ASSERT(arraysize(kKeyRotationDefaultKeyId) == kCencKeyIdSize, cenc_key_id_must_be_size_16); @@ -49,9 +50,16 @@ uint64_t Rescale(uint64_t time_in_old_scale, void GenerateSinf(const EncryptionKey& encryption_key, FourCC old_type, + EncryptionMode encryption_mode, ProtectionSchemeInfo* sinf) { sinf->format.format = old_type; - sinf->type.type = FOURCC_CENC; + + if (encryption_mode == kEncryptionModeAesCtr){ + sinf->type.type = FOURCC_CENC; + } else if (encryption_mode == kEncryptionModeAesCbc) { + sinf->type.type = FOURCC_CBC1; + } + sinf->type.version = kCencSchemeVersion; sinf->info.track_encryption.is_encrypted = true; sinf->info.track_encryption.default_iv_size = @@ -61,6 +69,7 @@ void GenerateSinf(const EncryptionKey& encryption_key, void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key, double clear_lead_in_seconds, + EncryptionMode encryption_mode, SampleDescription* description) { DCHECK(description); if (description->type == kVideo) { @@ -72,7 +81,7 @@ void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key, // Convert the first entry to an encrypted entry. VideoSampleEntry& entry = description->video_entries[0]; - GenerateSinf(encryption_key, entry.format, &entry.sinf); + GenerateSinf(encryption_key, entry.format, encryption_mode, &entry.sinf); entry.format = FOURCC_ENCV; } else { DCHECK_EQ(kAudio, description->type); @@ -84,7 +93,7 @@ void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key, // Convert the first entry to an encrypted entry. AudioSampleEntry& entry = description->audio_entries[0]; - GenerateSinf(encryption_key, entry.format, &entry.sinf); + GenerateSinf(encryption_key, entry.format, encryption_mode, &entry.sinf); entry.format = FOURCC_ENCA; } } @@ -127,7 +136,8 @@ Status Segmenter::Initialize(const std::vector& streams, KeySource* encryption_key_source, uint32_t max_sd_pixels, double clear_lead_in_seconds, - double crypto_period_duration_in_seconds) { + double crypto_period_duration_in_seconds, + EncryptionMode encryption_mode) { DCHECK_LT(0u, streams.size()); muxer_listener_ = muxer_listener; progress_listener_ = progress_listener; @@ -164,8 +174,7 @@ Status Segmenter::Initialize(const std::vector& streams, kKeyRotationDefaultKeyId, kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId)); GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds, - &description); - + encryption_mode, &description); if (muxer_listener_) { muxer_listener_->OnEncryptionInfoReady( kInitialEncryptionInfo, encryption_key.key_id, @@ -177,7 +186,7 @@ Status Segmenter::Initialize(const std::vector& streams, encryption_key_source, track_type, crypto_period_duration_in_seconds * streams[i]->info()->time_scale(), clear_lead_in_seconds * streams[i]->info()->time_scale(), - muxer_listener_); + muxer_listener_, encryption_mode); continue; } @@ -188,7 +197,7 @@ Status Segmenter::Initialize(const std::vector& streams, return status; GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds, - &description); + encryption_mode, &description); if (moov_->pssh.empty()) { moov_->pssh.resize(encryption_key->key_system_info.size()); @@ -205,7 +214,8 @@ Status Segmenter::Initialize(const std::vector& streams, fragmenters_[i] = new EncryptingFragmenter( streams[i]->info(), &moof_->tracks[i], encryption_key.Pass(), - clear_lead_in_seconds * streams[i]->info()->time_scale()); + clear_lead_in_seconds * streams[i]->info()->time_scale(), + encryption_mode); } // Choose the first stream if there is no VIDEO. diff --git a/packager/media/formats/mp4/segmenter.h b/packager/media/formats/mp4/segmenter.h index db30db4aa2..b0972839eb 100644 --- a/packager/media/formats/mp4/segmenter.h +++ b/packager/media/formats/mp4/segmenter.h @@ -12,6 +12,7 @@ #include "packager/base/memory/ref_counted.h" #include "packager/base/memory/scoped_ptr.h" +#include "packager/media/base/encryption_modes.h" #include "packager/media/base/status.h" #include "packager/media/formats/mp4/box_definitions.h" @@ -65,7 +66,8 @@ class Segmenter { KeySource* encryption_key_source, uint32_t max_sd_pixels, double clear_lead_in_seconds, - double crypto_period_duration_in_seconds); + double crypto_period_duration_in_seconds, + EncryptionMode encryption_mode); /// Finalize the segmenter. /// @return OK on success, an error status otherwise. diff --git a/packager/media/formats/mp4/track_run_iterator.cc b/packager/media/formats/mp4/track_run_iterator.cc index 389a6ace33..8eaa29ca08 100644 --- a/packager/media/formats/mp4/track_run_iterator.cc +++ b/packager/media/formats/mp4/track_run_iterator.cc @@ -8,6 +8,7 @@ #include #include "packager/media/base/buffer_reader.h" +#include "packager/media/base/encryption_modes.h" #include "packager/media/formats/mp4/chunk_info_iterator.h" #include "packager/media/formats/mp4/composition_offset_iterator.h" #include "packager/media/formats/mp4/decoding_time_iterator.h" @@ -597,10 +598,26 @@ scoped_ptr TrackRunIterator::GetDecryptConfig() { return scoped_ptr(); } + FourCC protection_scheme = is_audio() ? audio_description().sinf.type.type + : video_description().sinf.type.type; + EncryptionMode decryption_mode; + switch (protection_scheme) { + case FOURCC_CENC: + decryption_mode = kEncryptionModeAesCtr; + break; + case FOURCC_CBC1: + decryption_mode = kEncryptionModeAesCbc; + break; + default: + LOG(ERROR) << "Unsupported protection scheme."; + return scoped_ptr(); + } + return scoped_ptr(new DecryptConfig( track_encryption().default_kid, sample_encryption_entry.initialization_vector, - sample_encryption_entry.subsamples)); + sample_encryption_entry.subsamples, + decryption_mode)); } } // namespace mp4 diff --git a/packager/media/formats/webm/encryptor.cc b/packager/media/formats/webm/encryptor.cc index 554c1f9ff0..0679864a00 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_->EncryptData(sample_data, sample_size, sample_data)) { return Status(error::MUXER_FAILURE, "Failed to encrypt the frame."); } diff --git a/packager/media/formats/webm/webm_crypto_helpers.cc b/packager/media/formats/webm/webm_crypto_helpers.cc index e22a4b6423..e350f7bc5c 100644 --- a/packager/media/formats/webm/webm_crypto_helpers.cc +++ b/packager/media/formats/webm/webm_crypto_helpers.cc @@ -6,7 +6,7 @@ #include "packager/base/logging.h" #include "packager/base/sys_byteorder.h" -#include "packager/media/base/decrypt_config.h" +#include "packager/media/base/encryption_modes.h" #include "packager/media/formats/webm/webm_constants.h" namespace edash_packager { @@ -56,7 +56,8 @@ bool WebMCreateDecryptConfig(const uint8_t* data, decrypt_config->reset(new DecryptConfig( std::vector(key_id, key_id + key_id_size), std::vector(counter_block.begin(), counter_block.end()), - std::vector())); + std::vector(), + kEncryptionModeAesCtr)); *data_offset = frame_offset; return true; diff --git a/packager/media/formats/webm/webm_muxer.cc b/packager/media/formats/webm/webm_muxer.cc index 73fb319df3..af342f6ddd 100644 --- a/packager/media/formats/webm/webm_muxer.cc +++ b/packager/media/formats/webm/webm_muxer.cc @@ -30,6 +30,14 @@ Status WebMMuxer::Initialize() { "Key rotation is not implemented for WebM"); } + if (encryption_key_source() && (encryption_mode() != kEncryptionModeAesCtr)) { + NOTIMPLEMENTED() + << "WebM muxer does not support encryption mode other than AES-CTR."; + return Status( + error::UNIMPLEMENTED, + "WebM muxer does not support encryption mode other than AES-CTR."); + } + scoped_ptr writer(new MkvWriter); Status status = writer->Open(options().output_file_name); if (!status.ok()) diff --git a/packager/media/formats/wvm/wvm_media_parser.cc b/packager/media/formats/wvm/wvm_media_parser.cc index 9d4fa33cf7..65333fc6ea 100644 --- a/packager/media/formats/wvm/wvm_media_parser.cc +++ b/packager/media/formats/wvm/wvm_media_parser.cc @@ -9,7 +9,7 @@ #include #include "packager/base/strings/string_number_conversions.h" -#include "packager/media/base/aes_encryptor.h" +#include "packager/media/base/aes_decryptor.h" #include "packager/media/base/audio_stream_info.h" #include "packager/media/base/key_source.h" #include "packager/media/base/media_sample.h" diff --git a/packager/media/test/packager_test.cc b/packager/media/test/packager_test.cc index 28989e9866..183ecd0cd6 100644 --- a/packager/media/test/packager_test.cc +++ b/packager/media/test/packager_test.cc @@ -11,6 +11,7 @@ #include "packager/base/strings/stringprintf.h" #include "packager/base/time/clock.h" #include "packager/media/base/demuxer.h" +#include "packager/media/base/encryption_modes.h" #include "packager/media/base/key_source.h" #include "packager/media/base/media_stream.h" #include "packager/media/base/muxer.h" @@ -180,7 +181,8 @@ void PackagerTestBasic::Remux(const std::string& input, muxer_video->SetKeySource(encryption_key_source.get(), KeySource::TRACK_TYPE_SD, kClearLeadInSeconds, - kCryptoDurationInSeconds); + kCryptoDurationInSeconds, + kEncryptionModeAesCtr); } } @@ -201,7 +203,8 @@ void PackagerTestBasic::Remux(const std::string& input, muxer_audio->SetKeySource(encryption_key_source.get(), KeySource::TRACK_TYPE_SD, kClearLeadInSeconds, - kCryptoDurationInSeconds); + kCryptoDurationInSeconds, + kEncryptionModeAesCtr); } }