diff --git a/packager/app/packager_main.cc b/packager/app/packager_main.cc index 144efac5f6..e450e5d334 100644 --- a/packager/app/packager_main.cc +++ b/packager/app/packager_main.cc @@ -25,7 +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/fourccs.h" #include "packager/media/base/key_source.h" #include "packager/media/base/muxer_options.h" #include "packager/media/base/muxer_util.h" @@ -105,15 +105,19 @@ std::string DetermineTextFileFormat(const std::string& file) { return ""; } -edash_packager::media::EncryptionMode GetEncryptionMode( +edash_packager::media::FourCC GetProtectionScheme( const std::string& protection_scheme) { if (protection_scheme == "cenc") { - return edash_packager::media::kEncryptionModeAesCtr; + return edash_packager::media::FOURCC_cenc; + } else if (protection_scheme == "cens") { + return edash_packager::media::FOURCC_cens; } else if (protection_scheme == "cbc1") { - return edash_packager::media::kEncryptionModeAesCbc; + return edash_packager::media::FOURCC_cbc1; + } else if (protection_scheme == "cbcs") { + return edash_packager::media::FOURCC_cbcs; } else { LOG(ERROR) << "Unknown protection scheme: " << protection_scheme; - return edash_packager::media::kEncryptionModeUnknown; + return edash_packager::media::FOURCC_NULL; } } @@ -310,7 +314,7 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors, FLAGS_max_sd_pixels, FLAGS_clear_lead, FLAGS_crypto_period_duration, - GetEncryptionMode(FLAGS_protection_scheme)); + GetProtectionScheme(FLAGS_protection_scheme)); } scoped_ptr muxer_listener; @@ -374,11 +378,11 @@ Status RunRemuxJobs(const std::vector& remux_jobs) { } bool RunPackager(const StreamDescriptorList& stream_descriptors) { - EncryptionMode encryption_mode = GetEncryptionMode(FLAGS_protection_scheme); - if (encryption_mode == kEncryptionModeUnknown) + const FourCC protection_scheme = GetProtectionScheme(FLAGS_protection_scheme); + if (protection_scheme == FOURCC_NULL) return false; - if (encryption_mode == kEncryptionModeAesCbc && !FLAGS_iv.empty()) { - if (FLAGS_iv.size() != 16) { + if (protection_scheme == FOURCC_cbc1 || protection_scheme == FOURCC_cbcs) { + if (!FLAGS_iv.empty() && FLAGS_iv.size() != 16) { LOG(ERROR) << "Iv size should be 16 bytes for CBC encryption mode."; return false; } diff --git a/packager/media/base/aes_cryptor.cc b/packager/media/base/aes_cryptor.cc index e85914434f..dee1d3e909 100644 --- a/packager/media/base/aes_cryptor.cc +++ b/packager/media/base/aes_cryptor.cc @@ -7,6 +7,8 @@ #include "packager/media/base/aes_cryptor.h" #include +#include +#include #include "packager/base/logging.h" #include "packager/base/stl_util.h" @@ -53,6 +55,25 @@ size_t AesCryptor::NumPaddingBytes(size_t size) const { return 0; } +bool AesCryptor::GenerateRandomIv(FourCC protection_scheme, + std::vector* iv) { + // ISO/IEC 23001-7:2016 10.1 and 10.3 For 'cenc' and 'cens' + // default_Per_Sample_IV_Size and Per_Sample_IV_Size SHOULD be 8-bytes. + // There is no official guideline on the iv size for 'cbc1' and 'cbcs', + // but 16-byte provides better security. + const size_t iv_size = + (protection_scheme == FOURCC_cenc || protection_scheme == FOURCC_cens) + ? 8 + : 16; + iv->resize(iv_size); + if (RAND_bytes(iv->data(), iv_size) != 1) { + LOG(ERROR) << "RAND_bytes failed with error: " + << ERR_error_string(ERR_get_error(), NULL); + return false; + } + return true; +} + } // namespace media } // namespace edash_packager diff --git a/packager/media/base/aes_cryptor.h b/packager/media/base/aes_cryptor.h index 5e4dc0be52..c17fbd307f 100644 --- a/packager/media/base/aes_cryptor.h +++ b/packager/media/base/aes_cryptor.h @@ -12,6 +12,7 @@ #include "packager/base/macros.h" #include "packager/base/memory/scoped_ptr.h" +#include "packager/media/base/fourccs.h" struct aes_key_st; typedef struct aes_key_st AES_KEY; @@ -58,6 +59,13 @@ class AesCryptor { /// @return The current iv. const std::vector& iv() const { return iv_; } + /// @param protection_scheme specifies the protection scheme: 'cenc', 'cens', + /// 'cbc1', 'cbcs', which is useful to determine the random iv size. + /// @param iv points to generated initialization vector. + /// @return true on success, false otherwise. + static bool GenerateRandomIv(FourCC protection_scheme, + std::vector* iv); + protected: void set_iv(const std::vector& iv) { iv_ = iv; } const AES_KEY* aes_key() const { return aes_key_.get(); } diff --git a/packager/media/base/aes_cryptor_unittest.cc b/packager/media/base/aes_cryptor_unittest.cc index 8df13f9213..5328f3f9c7 100644 --- a/packager/media/base/aes_cryptor_unittest.cc +++ b/packager/media/base/aes_cryptor_unittest.cc @@ -220,12 +220,12 @@ TEST_F(AesCtrEncryptorTest, 128BitIVBoundaryCaseEncryption) { EXPECT_EQ(encrypted, encrypted_verify); } -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()); +TEST_F(AesCtrEncryptorTest, GenerateRandomIv) { + const uint8_t kCencIvSize = 8; + std::vector iv; + ASSERT_TRUE(AesCryptor::GenerateRandomIv(FOURCC_cenc, &iv)); + ASSERT_EQ(kCencIvSize, iv.size()); + LOG(INFO) << "Random IV: " << base::HexEncode(iv.data(), iv.size()); } TEST_F(AesCtrEncryptorTest, UnsupportedKeySize) { @@ -238,10 +238,6 @@ TEST_F(AesCtrEncryptorTest, UnsupportedIV) { ASSERT_FALSE(encryptor_.InitializeWithIv(key_, iv)); } -TEST_F(AesCtrEncryptorTest, IncorrectIvSize) { - ASSERT_FALSE(encryptor_.InitializeWithRandomIv(key_, 15)); -} - class AesCtrEncryptorSubsampleTest : public AesCtrEncryptorTest, public ::testing::WithParamInterface {}; diff --git a/packager/media/base/aes_encryptor.cc b/packager/media/base/aes_encryptor.cc index 54ccf2e848..53b20f4bc4 100644 --- a/packager/media/base/aes_encryptor.cc +++ b/packager/media/base/aes_encryptor.cc @@ -7,8 +7,6 @@ #include "packager/media/base/aes_encryptor.h" #include -#include -#include #include "packager/base/logging.h" @@ -40,17 +38,6 @@ 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.data(), 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::InitializeWithIv(const std::vector& key, const std::vector& iv) { if (!IsKeySizeValidForAes(key.size())) { diff --git a/packager/media/base/aes_encryptor.h b/packager/media/base/aes_encryptor.h index 2d84ae9448..ceea48a4c0 100644 --- a/packager/media/base/aes_encryptor.h +++ b/packager/media/base/aes_encryptor.h @@ -24,11 +24,6 @@ class AesEncryptor : public AesCryptor { 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); - /// Initialize the encryptor with specified key and IV. /// @return true on successful initialization, false otherwise. bool InitializeWithIv(const std::vector& key, diff --git a/packager/media/base/decrypt_config.cc b/packager/media/base/decrypt_config.cc index d8c73946be..882d5ed5c2 100644 --- a/packager/media/base/decrypt_config.cc +++ b/packager/media/base/decrypt_config.cc @@ -9,14 +9,19 @@ namespace edash_packager { namespace media { +DecryptConfig::DecryptConfig(const std::vector& key_id, + const std::vector& iv, + const std::vector& subsamples) + : DecryptConfig(key_id, iv, subsamples, FOURCC_cenc) {} + DecryptConfig::DecryptConfig(const std::vector& key_id, const std::vector& iv, const std::vector& subsamples, - EncryptionMode decryption_mode) + FourCC protection_scheme) : key_id_(key_id), iv_(iv), subsamples_(subsamples), - decryption_mode_(decryption_mode) { + protection_scheme_(protection_scheme) { CHECK_GT(key_id.size(), 0u); } diff --git a/packager/media/base/decrypt_config.h b/packager/media/base/decrypt_config.h index 82a86e34ef..73194d1afa 100644 --- a/packager/media/base/decrypt_config.h +++ b/packager/media/base/decrypt_config.h @@ -11,7 +11,7 @@ #include #include "packager/base/memory/scoped_ptr.h" -#include "packager/media/base/encryption_modes.h" +#include "packager/media/base/fourccs.h" namespace edash_packager { namespace media { @@ -42,23 +42,35 @@ class DecryptConfig { /// Keys are always 128 bits. static const size_t kDecryptionKeySize = 16; + /// Create a 'cenc' decrypt config. /// @param key_id is the ID that references the decryption key. /// @param iv is the initialization vector defined by the encryptor. /// @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); + + /// Create a general decrypt config with possible pattern-based encryption. + /// @param key_id is the ID that references the decryption key. + /// @param iv is the initialization vector defined by the encryptor. + /// @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 protection_scheme specifies the protection scheme: 'cenc', 'cens', + /// 'cbc1', 'cbcs'. DecryptConfig(const std::vector& key_id, const std::vector& iv, const std::vector& subsamples, - EncryptionMode decryption_mode); + FourCC protection_scheme); + ~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_; } + FourCC protection_scheme() const { return protection_scheme_; } private: const std::vector key_id_; @@ -70,7 +82,7 @@ class DecryptConfig { // (less data ignored by data_offset_) is encrypted. const std::vector subsamples_; - EncryptionMode decryption_mode_; + const FourCC protection_scheme_; DISALLOW_COPY_AND_ASSIGN(DecryptConfig); }; diff --git a/packager/media/base/decryptor_source.cc b/packager/media/base/decryptor_source.cc index 4375b87cc1..c23177545a 100644 --- a/packager/media/base/decryptor_source.cc +++ b/packager/media/base/decryptor_source.cc @@ -8,6 +8,7 @@ #include "packager/base/logging.h" #include "packager/base/stl_util.h" +#include "packager/media/base/aes_decryptor.h" namespace edash_packager { namespace media { @@ -39,16 +40,16 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config, } scoped_ptr aes_decryptor; - switch (decrypt_config->decryption_mode()) { - case kEncryptionModeAesCtr: + switch (decrypt_config->protection_scheme()) { + case FOURCC_cenc: aes_decryptor.reset(new AesCtrDecryptor); break; - case kEncryptionModeAesCbc: + case FOURCC_cbc1: aes_decryptor.reset(new AesCbcDecryptor(kNoPadding, kChainAcrossCalls)); break; default: - LOG(ERROR) << "Unsupported Decryption Mode: " - << decrypt_config->decryption_mode(); + LOG(ERROR) << "Unsupported protection scheme: " + << decrypt_config->protection_scheme(); return false; } diff --git a/packager/media/base/decryptor_source_unittest.cc b/packager/media/base/decryptor_source_unittest.cc index be1f99d959..a5fe29b627 100644 --- a/packager/media/base/decryptor_source_unittest.cc +++ b/packager/media/base/decryptor_source_unittest.cc @@ -81,10 +81,9 @@ TEST_F(DecryptorSourceTest, FullSampleDecryption) { DecryptConfig decrypt_config(key_id_, std::vector(kIv, kIv + arraysize(kIv)), - std::vector(), - kEncryptionModeAesCtr); - ASSERT_TRUE(decryptor_source_.DecryptSampleBuffer(&decrypt_config, &buffer_[0], - buffer_.size())); + std::vector()); + ASSERT_TRUE(decryptor_source_.DecryptSampleBuffer( + &decrypt_config, &buffer_[0], buffer_.size())); EXPECT_EQ(std::vector( kExpectedDecryptedBuffer, kExpectedDecryptedBuffer + arraysize(kExpectedDecryptedBuffer)), @@ -95,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(), kEncryptionModeAesCtr); + std::vector()); ASSERT_TRUE(decryptor_source_.DecryptSampleBuffer( &decrypt_config2, &buffer_[0], buffer_.size())); EXPECT_EQ(std::vector(kExpectedDecryptedBuffer2, @@ -130,8 +129,7 @@ TEST_F(DecryptorSourceTest, SubsampleDecryption) { DecryptConfig decrypt_config( key_id_, std::vector(kIv, kIv + arraysize(kIv)), std::vector(kSubsamples, - kSubsamples + arraysize(kSubsamples)), - kEncryptionModeAesCtr); + kSubsamples + arraysize(kSubsamples))); ASSERT_TRUE(decryptor_source_.DecryptSampleBuffer( &decrypt_config, &buffer_[0], buffer_.size())); EXPECT_EQ(std::vector( @@ -155,8 +153,7 @@ TEST_F(DecryptorSourceTest, SubsampleDecryptionSizeValidation) { DecryptConfig decrypt_config( key_id_, std::vector(kIv, kIv + arraysize(kIv)), std::vector(kSubsamples, - kSubsamples + arraysize(kSubsamples)), - kEncryptionModeAesCtr); + kSubsamples + arraysize(kSubsamples))); ASSERT_FALSE(decryptor_source_.DecryptSampleBuffer( &decrypt_config, &buffer_[0], buffer_.size())); } @@ -167,8 +164,7 @@ TEST_F(DecryptorSourceTest, DecryptFailedIfGetKeyFailed) { DecryptConfig decrypt_config(key_id_, std::vector(kIv, kIv + arraysize(kIv)), - std::vector(), - kEncryptionModeAesCtr); + std::vector()); 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 deleted file mode 100644 index c99c344b6f..0000000000 --- a/packager/media/base/encryption_modes.h +++ /dev/null @@ -1,23 +0,0 @@ -// 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/fourccs.h b/packager/media/base/fourccs.h index 2253b6f3f1..fd0e6f9c55 100644 --- a/packager/media/base/fourccs.h +++ b/packager/media/base/fourccs.h @@ -21,7 +21,9 @@ enum FourCC { FOURCC_avcC = 0x61766343, FOURCC_bloc = 0x626C6F63, FOURCC_cbc1 = 0x63626331, + FOURCC_cbcs = 0x63626373, FOURCC_cenc = 0x63656e63, + FOURCC_cens = 0x63656e73, FOURCC_co64 = 0x636f3634, FOURCC_ctim = 0x6374696d, FOURCC_ctts = 0x63747473, diff --git a/packager/media/base/muxer.cc b/packager/media/base/muxer.cc index ba921cd19e..e765f7e143 100644 --- a/packager/media/base/muxer.cc +++ b/packager/media/base/muxer.cc @@ -6,6 +6,7 @@ #include "packager/media/base/muxer.h" +#include "packager/media/base/fourccs.h" #include "packager/media/base/media_sample.h" #include "packager/media/base/media_stream.h" @@ -19,7 +20,7 @@ Muxer::Muxer(const MuxerOptions& options) max_sd_pixels_(0), clear_lead_in_seconds_(0), crypto_period_duration_in_seconds_(0), - encryption_mode_(kEncryptionModeUnknown), + protection_scheme_(FOURCC_NULL), cancelled_(false), clock_(NULL) {} @@ -29,13 +30,13 @@ void Muxer::SetKeySource(KeySource* encryption_key_source, uint32_t max_sd_pixels, double clear_lead_in_seconds, double crypto_period_duration_in_seconds, - EncryptionMode encryption_mode) { + FourCC protection_scheme) { 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; + protection_scheme_ = protection_scheme; } void Muxer::AddStream(MediaStream* stream) { diff --git a/packager/media/base/muxer.h b/packager/media/base/muxer.h index 234c87eea4..9f34201260 100644 --- a/packager/media/base/muxer.h +++ b/packager/media/base/muxer.h @@ -14,7 +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/fourccs.h" #include "packager/media/base/muxer_options.h" #include "packager/media/base/status.h" #include "packager/media/event/muxer_listener.h" @@ -45,11 +45,13 @@ class Muxer { /// @param crypto_period_duration_in_seconds specifies crypto period duration /// in seconds. A positive value means key rotation is enabled, the /// key source must support key rotation in this case. + /// @param protection_scheme specifies the protection scheme: 'cenc', 'cens', + /// 'cbc1', 'cbcs'. void SetKeySource(KeySource* encryption_key_source, uint32_t max_sd_pixels, double clear_lead_in_seconds, double crypto_period_duration_in_seconds, - EncryptionMode encryption_mode); + FourCC protection_scheme); /// Add video/audio stream. void AddStream(MediaStream* stream); @@ -94,7 +96,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_; } + FourCC protection_scheme() const { return protection_scheme_; } private: friend class MediaStream; // Needed to access AddSample. @@ -120,7 +122,7 @@ class Muxer { uint32_t max_sd_pixels_; double clear_lead_in_seconds_; double crypto_period_duration_in_seconds_; - EncryptionMode encryption_mode_; + FourCC protection_scheme_; bool cancelled_; scoped_ptr muxer_listener_; diff --git a/packager/media/formats/mp4/encrypting_fragmenter.cc b/packager/media/formats/mp4/encrypting_fragmenter.cc index 6fe21f9ad9..7de34f9f04 100644 --- a/packager/media/formats/mp4/encrypting_fragmenter.cc +++ b/packager/media/formats/mp4/encrypting_fragmenter.cc @@ -22,9 +22,6 @@ namespace media { namespace mp4 { namespace { -// Generate 64bit IV by default. -const size_t kDefaultIvSizeForCtr = 8u; -const size_t kDefaultIvSizeForCbc = 16u; const size_t kCencBlockSize = 16u; // Adds one or more subsamples to |*subsamples|. This may add more than one @@ -66,14 +63,14 @@ EncryptingFragmenter::EncryptingFragmenter( TrackFragment* traf, scoped_ptr encryption_key, int64_t clear_time, - EncryptionMode encryption_mode) + FourCC protection_scheme) : Fragmenter(traf), info_(info), encryption_key_(encryption_key.Pass()), nalu_length_size_(GetNaluLengthSize(*info)), video_codec_(GetVideoCodec(*info)), clear_time_(clear_time), - encryption_mode_(encryption_mode) { + protection_scheme_(protection_scheme) { DCHECK(encryption_key_); if (video_codec_ == kCodecVP8) { vpx_parser_.reset(new VP8Parser); @@ -175,22 +172,21 @@ void EncryptingFragmenter::FinalizeFragmentForEncryption() { Status EncryptingFragmenter::CreateEncryptor() { DCHECK(encryption_key_); - scoped_ptr encryptor; - size_t default_iv_size = 0; - if (encryption_mode_ == kEncryptionModeAesCtr) { - encryptor.reset(new AesCtrEncryptor); - default_iv_size = kDefaultIvSizeForCtr; - } else if (encryption_mode_ == kEncryptionModeAesCbc) { - encryptor.reset(new AesCbcEncryptor(kNoPadding, kChainAcrossCalls)); - default_iv_size = kDefaultIvSizeForCbc; - } else { - return Status(error::MUXER_FAILURE, "Unsupported encryption mode."); + scoped_ptr encryptor; + switch (protection_scheme_) { + case FOURCC_cenc: + encryptor.reset(new AesCtrEncryptor); + break; + case FOURCC_cbc1: + encryptor.reset(new AesCbcEncryptor(kNoPadding, kChainAcrossCalls)); + break; + default: + return Status(error::MUXER_FAILURE, "Unsupported protection scheme."); } - const bool initialized = encryption_key_->iv.empty() - ? encryptor->InitializeWithRandomIv( - encryption_key_->key, default_iv_size) - : encryptor->InitializeWithIv( - encryption_key_->key, encryption_key_->iv); + + DCHECK(!encryption_key_->iv.empty()); + const bool initialized = + encryptor->InitializeWithIv(encryption_key_->key, encryption_key_->iv); if (!initialized) return Status(error::MUXER_FAILURE, "Failed to create the encryptor."); encryptor_ = encryptor.Pass(); @@ -229,7 +225,7 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr sample) { // within the superframe. // For AES-CBC mode 'cbc1' scheme, clear data is sized appropriately so // that the cipher data is block aligned. - if (is_superframe || encryption_mode_ == kEncryptionModeAesCbc) { + if (is_superframe || protection_scheme_ == FOURCC_cbc1) { const uint16_t misalign_bytes = subsample.cipher_bytes % kCencBlockSize; subsample.clear_bytes += misalign_bytes; @@ -271,7 +267,7 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr sample) { // For AES-CBC mode 'cbc1' scheme, clear data is sized appropriately // so that the cipher data is block aligned. - if (encryption_mode_ == kEncryptionModeAesCbc) { + if (protection_scheme_ == FOURCC_cbc1) { const uint16_t misalign_bytes = cipher_bytes % kCencBlockSize; current_clear_bytes += misalign_bytes; cipher_bytes -= misalign_bytes; @@ -303,7 +299,7 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr sample) { uint64_t encryption_data_size = sample->data_size(); // AES-CBC mode requires all encrypted cipher blocks to be 16 bytes. The // partial blocks are left unencrypted. - if (encryption_mode_ == kEncryptionModeAesCbc) + if (protection_scheme_ == FOURCC_cbc1) encryption_data_size -= encryption_data_size % kCencBlockSize; EncryptBytes(data, encryption_data_size); } diff --git a/packager/media/formats/mp4/encrypting_fragmenter.h b/packager/media/formats/mp4/encrypting_fragmenter.h index bb2cc22a05..e960d5bd52 100644 --- a/packager/media/formats/mp4/encrypting_fragmenter.h +++ b/packager/media/formats/mp4/encrypting_fragmenter.h @@ -9,7 +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/base/fourccs.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" @@ -17,7 +17,7 @@ namespace edash_packager { namespace media { -class AesEncryptor; +class AesCryptor; class StreamInfo; struct EncryptionKey; @@ -30,11 +30,13 @@ class EncryptingFragmenter : public Fragmenter { /// @param encryption_key contains the encryption parameters. /// @param clear_time specifies clear lead duration in units of the current /// track's timescale. + /// @param protection_scheme specifies the protection scheme: 'cenc', 'cens', + /// 'cbc1', 'cbcs'. EncryptingFragmenter(scoped_refptr info, TrackFragment* traf, scoped_ptr encryption_key, int64_t clear_time, - EncryptionMode encryption_mode); + FourCC protection_scheme); ~EncryptingFragmenter() override; @@ -57,8 +59,9 @@ class EncryptingFragmenter : public Fragmenter { /// @return OK on success, an error status otherwise. Status CreateEncryptor(); - EncryptionKey* encryption_key() { return encryption_key_.get(); } - AesEncryptor* encryptor() { return encryptor_.get(); } + const EncryptionKey* encryption_key() const { return encryption_key_.get(); } + AesCryptor* encryptor() { return encryptor_.get(); } + FourCC protection_scheme() const { return protection_scheme_; } void set_encryption_key(scoped_ptr encryption_key) { encryption_key_ = encryption_key.Pass(); @@ -73,14 +76,14 @@ 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_; const VideoCodec video_codec_; int64_t clear_time_; - EncryptionMode encryption_mode_; + FourCC protection_scheme_; scoped_ptr vpx_parser_; scoped_ptr header_parser_; diff --git a/packager/media/formats/mp4/key_rotation_fragmenter.cc b/packager/media/formats/mp4/key_rotation_fragmenter.cc index 14c7f6c551..2d25d677f3 100644 --- a/packager/media/formats/mp4/key_rotation_fragmenter.cc +++ b/packager/media/formats/mp4/key_rotation_fragmenter.cc @@ -24,13 +24,13 @@ KeyRotationFragmenter::KeyRotationFragmenter(MovieFragment* moof, KeySource::TrackType track_type, int64_t crypto_period_duration, int64_t clear_time, - MuxerListener* muxer_listener, - EncryptionMode encryption_mode) + FourCC protection_scheme, + MuxerListener* muxer_listener) : EncryptingFragmenter(info, traf, scoped_ptr(new EncryptionKey()), clear_time, - encryption_mode), + protection_scheme), moof_(moof), encryption_key_source_(encryption_key_source), track_type_(track_type), @@ -55,6 +55,12 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption( current_crypto_period_index, track_type_, encryption_key.get()); if (!status.ok()) return status; + if (encryption_key->iv.empty()) { + if (!AesCryptor::GenerateRandomIv(protection_scheme(), + &encryption_key->iv)) { + return Status(error::INTERNAL_ERROR, "Failed to generate random iv."); + } + } set_encryption_key(encryption_key.Pass()); prev_crypto_period_index_ = current_crypto_period_index; need_to_refresh_encryptor = true; diff --git a/packager/media/formats/mp4/key_rotation_fragmenter.h b/packager/media/formats/mp4/key_rotation_fragmenter.h index f798aaa0fd..821d140d4f 100644 --- a/packager/media/formats/mp4/key_rotation_fragmenter.h +++ b/packager/media/formats/mp4/key_rotation_fragmenter.h @@ -7,7 +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/fourccs.h" #include "packager/media/base/key_source.h" #include "packager/media/event/muxer_listener.h" #include "packager/media/formats/mp4/encrypting_fragmenter.h" @@ -32,6 +32,8 @@ class KeyRotationFragmenter : public EncryptingFragmenter { /// of the current track's timescale. /// @param clear_time specifies clear lead duration in units of the current /// track's timescale. + /// @param protection_scheme specifies the protection scheme: 'cenc', 'cens', + /// 'cbc1', 'cbcs'. /// @param muxer_listener is a pointer to MuxerListener for notifying /// muxer related events. This may be null. KeyRotationFragmenter(MovieFragment* moof, @@ -41,8 +43,8 @@ class KeyRotationFragmenter : public EncryptingFragmenter { KeySource::TrackType track_type, int64_t crypto_period_duration, int64_t clear_time, - MuxerListener* muxer_listener, - EncryptionMode encryption_mode); + FourCC protection_scheme, + MuxerListener* muxer_listener); ~KeyRotationFragmenter() override; protected: diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index 445f02d2e6..723884e2f7 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -147,15 +147,10 @@ Status MP4Muxer::Initialize() { new MultiSegmentSegmenter(options(), ftyp.Pass(), moov.Pass())); } - Status segmenter_initialized = - segmenter_->Initialize(streams(), - muxer_listener(), - progress_listener(), - encryption_key_source(), - max_sd_pixels(), - clear_lead_in_seconds(), - crypto_period_duration_in_seconds(), - encryption_mode()); + const Status segmenter_initialized = segmenter_->Initialize( + streams(), muxer_listener(), progress_listener(), encryption_key_source(), + max_sd_pixels(), clear_lead_in_seconds(), + crypto_period_duration_in_seconds(), protection_scheme()); if (!segmenter_initialized.ok()) return segmenter_initialized; diff --git a/packager/media/formats/mp4/segmenter.cc b/packager/media/formats/mp4/segmenter.cc index 55572955aa..761801b206 100644 --- a/packager/media/formats/mp4/segmenter.cc +++ b/packager/media/formats/mp4/segmenter.cc @@ -9,6 +9,7 @@ #include #include "packager/base/stl_util.h" +#include "packager/media/base/aes_cryptor.h" #include "packager/media/base/buffer_writer.h" #include "packager/media/base/key_source.h" #include "packager/media/base/media_sample.h" @@ -26,8 +27,6 @@ namespace mp4 { namespace { -// Generate 64bit IV by default. -const size_t kDefaultIvSize = 8u; const size_t kCencKeyIdSize = 16u; // The version of cenc implemented here. CENC 4. @@ -50,26 +49,24 @@ uint64_t Rescale(uint64_t time_in_old_scale, void GenerateSinf(const EncryptionKey& encryption_key, FourCC old_type, - EncryptionMode encryption_mode, + FourCC protection_scheme, ProtectionSchemeInfo* sinf) { sinf->format.format = old_type; - if (encryption_mode == kEncryptionModeAesCtr){ - sinf->type.type = FOURCC_cenc; - } else if (encryption_mode == kEncryptionModeAesCbc) { - sinf->type.type = FOURCC_cbc1; - } - + DCHECK_NE(protection_scheme, FOURCC_NULL); + sinf->type.type = protection_scheme; sinf->type.version = kCencSchemeVersion; - sinf->info.track_encryption.is_encrypted = true; - sinf->info.track_encryption.default_iv_size = - encryption_key.iv.empty() ? kDefaultIvSize : encryption_key.iv.size(); - sinf->info.track_encryption.default_kid = encryption_key.key_id; + + auto& track_encryption = sinf->info.track_encryption; + track_encryption.is_encrypted = true; + DCHECK(!encryption_key.iv.empty()); + track_encryption.default_iv_size = encryption_key.iv.size(); + track_encryption.default_kid = encryption_key.key_id; } void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key, double clear_lead_in_seconds, - EncryptionMode encryption_mode, + FourCC protection_scheme, SampleDescription* description) { DCHECK(description); if (description->type == kVideo) { @@ -81,7 +78,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, encryption_mode, &entry.sinf); + GenerateSinf(encryption_key, entry.format, protection_scheme, &entry.sinf); entry.format = FOURCC_encv; } else { DCHECK_EQ(kAudio, description->type); @@ -93,7 +90,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, encryption_mode, &entry.sinf); + GenerateSinf(encryption_key, entry.format, protection_scheme, &entry.sinf); entry.format = FOURCC_enca; } } @@ -137,7 +134,7 @@ Status Segmenter::Initialize(const std::vector& streams, uint32_t max_sd_pixels, double clear_lead_in_seconds, double crypto_period_duration_in_seconds, - EncryptionMode encryption_mode) { + FourCC protection_scheme) { DCHECK_LT(0u, streams.size()); muxer_listener_ = muxer_listener; progress_listener_ = progress_listener; @@ -173,8 +170,10 @@ Status Segmenter::Initialize(const std::vector& streams, encryption_key.key_id.assign( kKeyRotationDefaultKeyId, kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId)); + if (!AesCryptor::GenerateRandomIv(protection_scheme, &encryption_key.iv)) + return Status(error::INTERNAL_ERROR, "Failed to generate random iv."); GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds, - encryption_mode, &description); + protection_scheme, &description); if (muxer_listener_) { muxer_listener_->OnEncryptionInfoReady( kInitialEncryptionInfo, encryption_key.key_id, @@ -186,7 +185,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_, encryption_mode); + protection_scheme, muxer_listener_); continue; } @@ -195,9 +194,13 @@ Status Segmenter::Initialize(const std::vector& streams, encryption_key_source->GetKey(track_type, encryption_key.get()); if (!status.ok()) return status; + if (encryption_key->iv.empty()) { + if (!AesCryptor::GenerateRandomIv(protection_scheme, &encryption_key->iv)) + return Status(error::INTERNAL_ERROR, "Failed to generate random iv."); + } GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds, - encryption_mode, &description); + protection_scheme, &description); if (moov_->pssh.empty()) { moov_->pssh.resize(encryption_key->key_system_info.size()); @@ -215,7 +218,7 @@ 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(), - encryption_mode); + protection_scheme); } // 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 b0972839eb..8adda270ce 100644 --- a/packager/media/formats/mp4/segmenter.h +++ b/packager/media/formats/mp4/segmenter.h @@ -12,7 +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/fourccs.h" #include "packager/media/base/status.h" #include "packager/media/formats/mp4/box_definitions.h" @@ -59,6 +59,8 @@ class Segmenter { /// pixels per frame than max_sd_pixels, it is HD, SD otherwise. /// @param clear_time specifies clear lead duration in seconds. /// @param crypto_period_duration specifies crypto period duration in seconds. + /// @param protection_scheme specifies the protection scheme: 'senc', 'sens', + /// 'cbc1', 'cbcs'. /// @return OK on success, an error status otherwise. Status Initialize(const std::vector& streams, MuxerListener* muxer_listener, @@ -67,7 +69,7 @@ class Segmenter { uint32_t max_sd_pixels, double clear_lead_in_seconds, double crypto_period_duration_in_seconds, - EncryptionMode encryption_mode); + FourCC protection_scheme); /// 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 1ee3916e12..240d8b7b60 100644 --- a/packager/media/formats/mp4/track_run_iterator.cc +++ b/packager/media/formats/mp4/track_run_iterator.cc @@ -8,7 +8,7 @@ #include #include "packager/media/base/buffer_reader.h" -#include "packager/media/base/encryption_modes.h" +#include "packager/media/base/fourccs.h" #include "packager/media/base/rcheck.h" #include "packager/media/formats/mp4/chunk_info_iterator.h" #include "packager/media/formats/mp4/composition_offset_iterator.h" @@ -600,24 +600,10 @@ scoped_ptr TrackRunIterator::GetDecryptConfig() { 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, - decryption_mode)); + return scoped_ptr( + new DecryptConfig(track_encryption().default_kid, + sample_encryption_entry.initialization_vector, + sample_encryption_entry.subsamples, protection_scheme)); } } // namespace mp4 diff --git a/packager/media/formats/webm/encryptor.cc b/packager/media/formats/webm/encryptor.cc index 1fcecbc443..d029fc0dfd 100644 --- a/packager/media/formats/webm/encryptor.cc +++ b/packager/media/formats/webm/encryptor.cc @@ -7,6 +7,7 @@ #include "packager/media/formats/webm/encryptor.h" #include "packager/media/base/aes_encryptor.h" +#include "packager/media/base/fourccs.h" #include "packager/media/base/media_sample.h" namespace edash_packager { @@ -14,9 +15,6 @@ namespace media { namespace webm { namespace { -// Generate 64bit IV by default. -const size_t kDefaultIvSize = 8u; - Status CreateContentEncryption(mkvmuxer::Track* track, EncryptionKey* key) { if (!track->AddContentEncoding()) { return Status(error::INTERNAL_ERROR, @@ -107,13 +105,14 @@ Status Encryptor::CreateEncryptor(MuxerListener* muxer_listener, Status status = key_source->GetKey(track_type, encryption_key.get()); if (!status.ok()) return status; + if (encryption_key->iv.empty()) { + if (!AesCryptor::GenerateRandomIv(FOURCC_cenc, &encryption_key->iv)) + return Status(error::INTERNAL_ERROR, "Failed to generate random iv."); + } scoped_ptr encryptor(new AesCtrEncryptor()); - const bool initialized = encryption_key->iv.empty() - ? encryptor->InitializeWithRandomIv( - encryption_key->key, kDefaultIvSize) - : encryptor->InitializeWithIv( - encryption_key->key, encryption_key->iv); + const bool initialized = + encryptor->InitializeWithIv(encryption_key->key, encryption_key->iv); if (!initialized) return Status(error::INTERNAL_ERROR, "Failed to create the encryptor."); diff --git a/packager/media/formats/webm/webm_crypto_helpers.cc b/packager/media/formats/webm/webm_crypto_helpers.cc index e350f7bc5c..338d13c087 100644 --- a/packager/media/formats/webm/webm_crypto_helpers.cc +++ b/packager/media/formats/webm/webm_crypto_helpers.cc @@ -6,7 +6,6 @@ #include "packager/base/logging.h" #include "packager/base/sys_byteorder.h" -#include "packager/media/base/encryption_modes.h" #include "packager/media/formats/webm/webm_constants.h" namespace edash_packager { @@ -56,8 +55,7 @@ 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(), - kEncryptionModeAesCtr)); + std::vector())); *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 af342f6ddd..9091af3a91 100644 --- a/packager/media/formats/webm/webm_muxer.cc +++ b/packager/media/formats/webm/webm_muxer.cc @@ -6,6 +6,7 @@ #include "packager/media/formats/webm/webm_muxer.h" +#include "packager/media/base/fourccs.h" #include "packager/media/base/media_sample.h" #include "packager/media/base/media_stream.h" #include "packager/media/base/stream_info.h" @@ -30,12 +31,11 @@ Status WebMMuxer::Initialize() { "Key rotation is not implemented for WebM"); } - if (encryption_key_source() && (encryption_mode() != kEncryptionModeAesCtr)) { + if (encryption_key_source() && (protection_scheme() != FOURCC_cenc)) { 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."); + << "WebM does not support protection scheme other than 'cenc'."; + return Status(error::UNIMPLEMENTED, + "WebM does not support protection scheme other than 'cenc'."); } scoped_ptr writer(new MkvWriter); diff --git a/packager/media/test/packager_test.cc b/packager/media/test/packager_test.cc index 7b84182660..8db096ccc5 100644 --- a/packager/media/test/packager_test.cc +++ b/packager/media/test/packager_test.cc @@ -11,8 +11,8 @@ #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/fixed_key_source.h" +#include "packager/media/base/fourccs.h" #include "packager/media/base/media_stream.h" #include "packager/media/base/muxer.h" #include "packager/media/base/stream_info.h" @@ -174,10 +174,8 @@ void PackagerTestBasic::Remux(const std::string& input, if (enable_encryption) { muxer_video->SetKeySource(encryption_key_source.get(), - KeySource::TRACK_TYPE_SD, - kClearLeadInSeconds, - kCryptoDurationInSeconds, - kEncryptionModeAesCtr); + KeySource::TRACK_TYPE_SD, kClearLeadInSeconds, + kCryptoDurationInSeconds, FOURCC_cenc); } } @@ -196,10 +194,8 @@ void PackagerTestBasic::Remux(const std::string& input, if (enable_encryption) { muxer_audio->SetKeySource(encryption_key_source.get(), - KeySource::TRACK_TYPE_SD, - kClearLeadInSeconds, - kCryptoDurationInSeconds, - kEncryptionModeAesCtr); + KeySource::TRACK_TYPE_SD, kClearLeadInSeconds, + kCryptoDurationInSeconds, FOURCC_cenc); } }