From e9b77add23f353420fc39247b9ce1df12e35ff80 Mon Sep 17 00:00:00 2001 From: Kongqun Yang Date: Tue, 12 Nov 2013 12:34:58 -0800 Subject: [PATCH] Implemented FixedEncryptorSource. FixedEncryptorSource takes hardcoded key id and content key. Implemented an AES Encryptor using OpenSSL. Change-Id: I59ba9a41fc0f40925d697045dd1b147b7351c2f9 --- media/base/aes_encryptor.cc | 128 ++++++++++++ media/base/aes_encryptor.h | 105 ++++++++++ media/base/aes_encryptor_unittest.cc | 298 +++++++++++++++++++++++++++ media/base/encryptor_source.cc | 22 ++ media/base/encryptor_source.h | 44 +++- media/base/fixed_encryptor_source.cc | 60 ++++++ media/base/fixed_encryptor_source.h | 35 ++++ 7 files changed, 687 insertions(+), 5 deletions(-) create mode 100644 media/base/aes_encryptor.cc create mode 100644 media/base/aes_encryptor.h create mode 100644 media/base/aes_encryptor_unittest.cc create mode 100644 media/base/encryptor_source.cc create mode 100644 media/base/fixed_encryptor_source.cc create mode 100644 media/base/fixed_encryptor_source.h diff --git a/media/base/aes_encryptor.cc b/media/base/aes_encryptor.cc new file mode 100644 index 0000000000..107683a134 --- /dev/null +++ b/media/base/aes_encryptor.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2013 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. + +#include "media/base/aes_encryptor.h" + +#include + +#include "base/logging.h" +#include "base/rand_util.h" + +namespace { + +// Increment an 8-byte counter by 1. Return true if overflowed. +bool Increment64(uint8* counter) { + DCHECK(counter); + for (int i = 7; i >= 0; --i) + if (++counter[i] != 0) + return false; + return true; +} + +// According to ISO/IEC FDIS 23001-7: CENC spec, IV should be either +// 64-bit (8-byte) or 128-bit (16-byte). +bool IsIvSizeValid(size_t iv_size) { return iv_size == 8 || iv_size == 16; } + +// CENC protection scheme uses 128-bit keys in counter mode. +const uint32 kCencKeySize = 16; + +} // namespace + +namespace media { + +AesCtrEncryptor::AesCtrEncryptor() + : block_offset_(0), + encrypted_counter_(AES_BLOCK_SIZE, 0), + counter_overflow_(false) { + COMPILE_ASSERT(AES_BLOCK_SIZE == kCencKeySize, + cenc_key_size_should_be_the_same_as_aes_block_size); +} + +AesCtrEncryptor::~AesCtrEncryptor() {} + +bool AesCtrEncryptor::InitializeWithRandomIv(const std::vector& key, + uint8 iv_size) { + CHECK(IsIvSizeValid(iv_size)); + + // TODO(kqyang): should we use RAND_bytes provided by openssl instead? + std::vector iv(iv_size, 0); + base::RandBytes(&iv[0], iv_size); + return InitializeWithIv(key, iv); +} + +bool AesCtrEncryptor::InitializeWithIv(const std::vector& key, + const std::vector& iv) { + CHECK_EQ(kCencKeySize, key.size()); + CHECK(IsIvSizeValid(iv.size())); + + aes_key_.reset(new AES_KEY()); + if (AES_set_encrypt_key(&key[0], AES_BLOCK_SIZE * 8, aes_key_.get()) != 0) { + aes_key_.reset(); + LOG(ERROR) << "Failed to setup encryption key."; + return false; + } + SetIv(iv); + return true; +} + +bool AesCtrEncryptor::Encrypt(const uint8* plaintext, + size_t plaintext_size, + uint8* ciphertext) { + DCHECK(plaintext != NULL && plaintext_size > 0 && ciphertext != NULL); + DCHECK(aes_key_ != NULL); + + for (size_t i = 0; i < plaintext_size; ++i) { + if (block_offset_ == 0) { + AES_encrypt(&counter_[0], &encrypted_counter_[0], aes_key_.get()); + // As mentioned in ISO/IEC FDIS 23001-7: CENC spec, of the 16 byte counter + // block, bytes 8 to 15 (i.e. the least significant bytes) are used as a + // simple 64 bit unsigned integer that is incremented by one for each + // subsequent block of sample data processed and is kept in network byte + // order. + if (Increment64(&counter_[8])) + counter_overflow_ = true; + } + ciphertext[i] = plaintext[i] ^ encrypted_counter_[block_offset_]; + block_offset_ = (block_offset_ + 1) % AES_BLOCK_SIZE; + } + return true; +} + +void AesCtrEncryptor::UpdateIv() { + block_offset_ = 0; + + // As recommended in ISO/IEC FDIS 23001-7: CENC spec, for 64-bit (8-byte) + // IV_Sizes, initialization vectors for subsequent samples can be created by + // incrementing the initialization vector of the previous sample. + // For 128-bit (16-byte) IV_Sizes, initialization vectors for subsequent + // samples should be created by adding the block count of the previous sample + // to the initialization vector of the previous sample. + if (iv_.size() == 8) { + Increment64(&iv_[0]); + counter_ = iv_; + counter_.resize(AES_BLOCK_SIZE, 0); + } else { + DCHECK_EQ(16, iv_.size()); + // Even though the block counter portion of the counter (bytes 8 to 15) is + // treated as a 64-bit number, it is recommended that the initialization + // vector is treated as a 128-bit number when calculating the next + // initialization vector from the previous one. The block counter portion + // is already incremented by number of blocks, the other 64 bits of the + // counter (bytes 0 to 7) is incremented here if the block counter portion + // has overflowed. + if (counter_overflow_) + Increment64(&counter_[0]); + iv_ = counter_; + } + counter_overflow_ = false; +} + +void AesCtrEncryptor::SetIv(const std::vector& iv) { + CHECK(IsIvSizeValid(iv.size())); + block_offset_ = 0; + counter_ = iv_ = iv; + counter_.resize(AES_BLOCK_SIZE, 0); +} + +} // namespace diff --git a/media/base/aes_encryptor.h b/media/base/aes_encryptor.h new file mode 100644 index 0000000000..838fd0dd7a --- /dev/null +++ b/media/base/aes_encryptor.h @@ -0,0 +1,105 @@ +// Copyright (c) 2013 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. +// +// AES Encryptor implementation using openssl. + +#ifndef MEDIA_BASE_AES_ENCRYPTOR_H_ +#define MEDIA_BASE_AES_ENCRYPTOR_H_ + +#include +#include + +#include "base/memory/scoped_ptr.h" + +struct aes_key_st; +typedef struct aes_key_st AES_KEY; + +namespace media { + +class AesCtrEncryptor { + public: + AesCtrEncryptor(); + ~AesCtrEncryptor(); + + // Initialize the encryptor with specified key. A random iv will be generated. + // |key| size should be 16. |iv_size| should be either 8 or 16. + // |block_offset_| is set to 0. + bool InitializeWithRandomIv(const std::vector& key, uint8 iv_size); + + // Initialize the encryptor with specified key and iv. + // |key| size should be 16. |iv| size should be either 8 or 16. + // |block_offset_| is set to 0. + bool InitializeWithIv(const std::vector& key, + const std::vector& iv); + + // Various forms of encrypt calls. |block_offset_| will be updated according + // to input plaintext size. + bool Encrypt(const uint8* plaintext, + size_t plaintext_size, + uint8* ciphertext); + + 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(&(*ciphertext)[0])); + } + + // For AES CTR, encryption and decryption are identical. + bool Decrypt(const uint8* ciphertext, + size_t ciphertext_size, + uint8* 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); + } + + // Update IV for next sample. |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(); + + // Set IV. |block_offset_| is reset to 0. + void SetIv(const std::vector& iv); + + const std::vector& iv() const { return iv_; } + + uint32 block_offset() const { return block_offset_; } + + private: + // Initialization vector, with size 8 or 16. + std::vector iv_; + // Current block offset. + uint32 block_offset_; + // Openssl AES_KEY. + scoped_ptr aes_key_; + // Current AES-CTR counter. + std::vector counter_; + // Encrypted counter. + std::vector encrypted_counter_; + // Keep track of whether the counter has overflowed. + bool counter_overflow_; + + DISALLOW_COPY_AND_ASSIGN(AesCtrEncryptor); +}; + +// TODO(kqyang): implement AesCbcEncryptor. + +} // namespace + +#endif // MEDIA_BASE_AES_ENCRYPTOR_H_ diff --git a/media/base/aes_encryptor_unittest.cc b/media/base/aes_encryptor_unittest.cc new file mode 100644 index 0000000000..6208daea5d --- /dev/null +++ b/media/base/aes_encryptor_unittest.cc @@ -0,0 +1,298 @@ +// Copyright (c) 2013 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. + +#include "media/base/aes_encryptor.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const uint32 kAesBlockSize = 16; + +// From NIST SP 800-38a test case: - F.5.1 CTR-AES128.Encrypt +// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf +const uint8 kAesCtrKey[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}; + +const uint8 kAesCtrIv[] = {0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}; + +const uint8 kAesCtrPlaintext[] = { + // Block #1 + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + // Block #2 + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + // Block #3 + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + // Block #4 + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10}; + +const uint8 kAesCtrCiphertext[] = { + // Block #1 + 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, + 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce, + // Block #2 + 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, + 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff, + // Block #3 + 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, + 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab, + // Block #4 + 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, + 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee}; + +// Subsample test cases. +struct SubsampleTestCase { + const uint8* subsample_sizes; + uint32 subsample_count; +}; + +const uint8 kSubsampleTest1[] = {64}; +const uint8 kSubsampleTest2[] = {13, 51}; +const uint8 kSubsampleTest3[] = {52, 12}; +const uint8 kSubsampleTest4[] = {16, 48}; +const uint8 kSubsampleTest5[] = {3, 16, 45}; +const uint8 kSubsampleTest6[] = {18, 16, 34}; +const uint8 kSubsampleTest7[] = {8, 16, 2, 38}; +const uint8 kSubsampleTest8[] = {10, 1, 33, 20}; +const uint8 kSubsampleTest9[] = {7, 19, 6, 32}; +const uint8 kSubsampleTest10[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9}; + +const SubsampleTestCase kSubsampleTestCases[] = { + {kSubsampleTest1, arraysize(kSubsampleTest1)}, + {kSubsampleTest2, arraysize(kSubsampleTest2)}, + {kSubsampleTest3, arraysize(kSubsampleTest3)}, + {kSubsampleTest4, arraysize(kSubsampleTest4)}, + {kSubsampleTest5, arraysize(kSubsampleTest5)}, + {kSubsampleTest6, arraysize(kSubsampleTest6)}, + {kSubsampleTest7, arraysize(kSubsampleTest7)}, + {kSubsampleTest8, arraysize(kSubsampleTest8)}, + {kSubsampleTest9, arraysize(kSubsampleTest9)}, + {kSubsampleTest10, arraysize(kSubsampleTest10)}, }; + +// IV test values. +const uint32 kTextSizeInBytes = 60; // 3 full blocks + 1 partial block. + +const uint8 kIv128Zero[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +const uint8 kIv128Two[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}; +const uint8 kIv128Four[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}; +const uint8 kIv128Max64[] = {0, 0, 0, 0, 0, 0, 0, 0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +const uint8 kIv128OneAndThree[] = {0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 3}; +const uint8 kIv128MaxMinusOne[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe}; + +const uint8 kIv64Zero[] = {0, 0, 0, 0, 0, 0, 0, 0}; +const uint8 kIv64One[] = {0, 0, 0, 0, 0, 0, 0, 1}; +const uint8 kIv64MaxMinusOne[] = {0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe}; +const uint8 kIv64Max[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +struct IvTestCase { + const uint8* iv_test; + uint32 iv_size; + const uint8* iv_expected; +}; + +// As recommended in ISO/IEC FDIS 23001-7: CENC spec, +// For 64-bit (8-byte) IV_Sizes, initialization vectors for subsequent samples +// can be created by incrementing the initialization vector of the previous +// sample. For 128-bit (16-byte) IV_Sizes, initialization vectors for subsequent +// samples should be created by adding the block count of the previous sample to +// the initialization vector of the previous sample. +const IvTestCase kIvTestCases[] = { + {kIv128Zero, arraysize(kIv128Zero), kIv128Four}, + {kIv128Max64, arraysize(kIv128Max64), kIv128OneAndThree}, + {kIv128MaxMinusOne, arraysize(kIv128MaxMinusOne), kIv128Two}, + {kIv64Zero, arraysize(kIv64Zero), kIv64One}, + {kIv64MaxMinusOne, arraysize(kIv64MaxMinusOne), kIv64Max}, + {kIv64Max, arraysize(kIv64Max), kIv64Zero}, }; + +// We support AES 128, i.e. 16 bytes key only. +const uint8 kInvalidKey[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, + 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, }; + +// We support Iv of size 8 or 16 only as defined in CENC spec. +const uint8 kInvalidIv[] = {0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, }; + +} // namespace + +namespace media { + +class AesEncryptorTest : public testing::Test { + public: + virtual void SetUp() { + key_.assign(kAesCtrKey, kAesCtrKey + arraysize(kAesCtrKey)); + iv_.assign(kAesCtrIv, kAesCtrIv + arraysize(kAesCtrIv)); + plaintext_.assign(kAesCtrPlaintext, + kAesCtrPlaintext + arraysize(kAesCtrPlaintext)); + ciphertext_.assign(kAesCtrCiphertext, + kAesCtrCiphertext + arraysize(kAesCtrCiphertext)); + + ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_)); + } + + protected: + std::vector key_; + std::vector iv_; + std::vector plaintext_; + std::vector ciphertext_; + AesCtrEncryptor encryptor_; +}; + +TEST_F(AesEncryptorTest, NistTestCase) { + std::vector encrypted; + EXPECT_TRUE(encryptor_.Encrypt(plaintext_, &encrypted)); + EXPECT_EQ(ciphertext_, encrypted); + + encryptor_.SetIv(iv_); + std::vector decrypted; + EXPECT_TRUE(encryptor_.Decrypt(encrypted, &decrypted)); + EXPECT_EQ(plaintext_, decrypted); +} + +TEST_F(AesEncryptorTest, NistTestCaseInplaceEncryptionDecryption) { + std::vector buffer = plaintext_; + EXPECT_TRUE(encryptor_.Encrypt(&buffer[0], buffer.size(), &buffer[0])); + EXPECT_EQ(ciphertext_, buffer); + + encryptor_.SetIv(iv_); + EXPECT_TRUE(encryptor_.Decrypt(&buffer[0], buffer.size(), &buffer[0])); + EXPECT_EQ(plaintext_, buffer); +} + +TEST_F(AesEncryptorTest, EncryptDecryptString) { + static const char kPlaintext[] = "normal plaintext of random length"; + static const char kExpectedCiphertextInHex[] = + "82E3AD1EF90C5CC09EB37F1B9EFBD99016441A1C15123F0777CD57BB993E14DA02"; + + std::string ciphertext; + EXPECT_TRUE(encryptor_.Encrypt(kPlaintext, &ciphertext)); + EXPECT_EQ(kExpectedCiphertextInHex, + base::HexEncode(ciphertext.data(), ciphertext.size())); + + std::string decrypted; + encryptor_.SetIv(iv_); + EXPECT_TRUE(encryptor_.Decrypt(ciphertext, &decrypted)); + EXPECT_EQ(kPlaintext, decrypted); +} + +TEST_F(AesEncryptorTest, 128BitIVBoundaryCaseEncryption) { + // There are four blocks of text in |plaintext_|. The first block should be + // encrypted with IV = kIv128Max64, the subsequent blocks should be encrypted + // with iv 0 to 3. + std::vector iv_max64(kIv128Max64, + kIv128Max64 + arraysize(kIv128Max64)); + ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_max64)); + std::vector encrypted; + EXPECT_TRUE(encryptor_.Encrypt(plaintext_, &encrypted)); + + std::vector iv_one_and_three( + kIv128OneAndThree, kIv128OneAndThree + arraysize(kIv128OneAndThree)); + encryptor_.UpdateIv(); + EXPECT_EQ(iv_one_and_three, encryptor_.iv()); + + ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_max64)); + std::vector encrypted_verify(plaintext_.size(), 0); + EXPECT_TRUE( + encryptor_.Encrypt(&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_EQ(encrypted, encrypted_verify); +} + +TEST_F(AesEncryptorTest, InitWithRandomIv) { + const uint8 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(AesEncryptorTest, UnsupportedKeySize) { + std::vector key(kInvalidKey, kInvalidKey + arraysize(kInvalidKey)); + ASSERT_DEATH(encryptor_.InitializeWithIv(key, iv_), ""); +} + +TEST_F(AesEncryptorTest, UnsupportedIV) { + std::vector iv(kInvalidIv, kInvalidIv + arraysize(kInvalidIv)); + ASSERT_DEATH(encryptor_.InitializeWithIv(key_, iv), ""); +} + +TEST_F(AesEncryptorTest, IncorrectIvSize) { + ASSERT_DEATH(encryptor_.InitializeWithRandomIv(key_, 15), ""); +} + +class AesCtrEncryptorSubsampleTest + : public AesEncryptorTest, + public ::testing::WithParamInterface {}; + +TEST_P(AesCtrEncryptorSubsampleTest, NistTestCaseSubsamples) { + const SubsampleTestCase* test_case = &GetParam(); + + std::vector encrypted(plaintext_.size(), 0); + for (uint32 i = 0, offset = 0; i < test_case->subsample_count; ++i) { + uint32 len = test_case->subsample_sizes[i]; + EXPECT_TRUE( + encryptor_.Encrypt(&plaintext_[offset], len, &encrypted[offset])); + offset += len; + EXPECT_EQ(offset % kAesBlockSize, encryptor_.block_offset()); + } + EXPECT_EQ(ciphertext_, encrypted); + + encryptor_.SetIv(iv_); + std::vector decrypted(encrypted.size(), 0); + for (uint32 i = 0, offset = 0; i < test_case->subsample_count; ++i) { + uint32 len = test_case->subsample_sizes[i]; + EXPECT_TRUE( + encryptor_.Decrypt(&encrypted[offset], len, &decrypted[offset])); + offset += len; + EXPECT_EQ(offset % kAesBlockSize, encryptor_.block_offset()); + } + EXPECT_EQ(plaintext_, decrypted); +} + +INSTANTIATE_TEST_CASE_P(SubsampleTestCases, + AesCtrEncryptorSubsampleTest, + ::testing::ValuesIn(kSubsampleTestCases)); + +class AesCtrEncryptorIvTest : public ::testing::TestWithParam {}; + +TEST_P(AesCtrEncryptorIvTest, IvTest) { + // Some dummy key and plaintext. + std::vector key(16, 1); + std::vector plaintext(kTextSizeInBytes, 3); + + std::vector iv_test(GetParam().iv_test, + GetParam().iv_test + GetParam().iv_size); + std::vector iv_expected(GetParam().iv_expected, + GetParam().iv_expected + GetParam().iv_size); + + AesCtrEncryptor encryptor; + ASSERT_TRUE(encryptor.InitializeWithIv(key, iv_test)); + + std::vector encrypted; + EXPECT_TRUE(encryptor.Encrypt(plaintext, &encrypted)); + encryptor.UpdateIv(); + EXPECT_EQ(iv_expected, encryptor.iv()); +} + +INSTANTIATE_TEST_CASE_P(IvTestCases, + AesCtrEncryptorIvTest, + ::testing::ValuesIn(kIvTestCases)); + +} // namespace media diff --git a/media/base/encryptor_source.cc b/media/base/encryptor_source.cc new file mode 100644 index 0000000000..593611fc0e --- /dev/null +++ b/media/base/encryptor_source.cc @@ -0,0 +1,22 @@ +// Copyright (c) 2013 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. + +#include "media/base/encryptor_source.h" + +namespace { +const char kWidevineSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, + 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, + 0xd5, 0x1d, 0x21, 0xed}; +} // namespace + +namespace media { + +EncryptorSource::EncryptorSource() + : key_system_id_(kWidevineSystemId, + kWidevineSystemId + arraysize(kWidevineSystemId)), + clear_milliseconds_(0) {} + +EncryptorSource::~EncryptorSource() {} + +} // namespace media diff --git a/media/base/encryptor_source.h b/media/base/encryptor_source.h index 3bccca242a..7f1623f72f 100644 --- a/media/base/encryptor_source.h +++ b/media/base/encryptor_source.h @@ -7,23 +7,57 @@ #ifndef MEDIA_BASE_ENCRYPTOR_SOURCE_H_ #define MEDIA_BASE_ENCRYPTOR_SOURCE_H_ +#include + #include "base/memory/scoped_ptr.h" -#include "media/base/container_names.h" +#include "media/base/aes_encryptor.h" #include "media/base/status.h" namespace media { class EncryptorSource { public: - EncryptorSource() {} - virtual ~EncryptorSource() {} + EncryptorSource(); + virtual ~EncryptorSource(); - virtual Status Init() = 0; + virtual Status Initialize() = 0; + + // Refreshes the encryptor. NOP except for key rotation encryptor source. + // TODO(kqyang): Do we need to pass in duration or fragment number? + virtual void RefreshEncryptor() {} + + // EncryptorSource retains the ownership of |encryptor_|. + AesCtrEncryptor* encryptor() { return encryptor_.get(); } + const std::vector& key_id() const { return key_id_; } + const std::vector& key() const { return key_; } + const std::vector& pssh() const { return pssh_; } + uint32 clear_milliseconds() const { return clear_milliseconds_; } + const std::vector& key_system_id() const { return key_system_id_; } + + protected: + // EncryptorSource takes ownership of |encryptor|. + void set_encryptor(scoped_ptr encryptor) { + encryptor_ = encryptor.Pass(); + } + void set_key_id(const std::vector& key_id) { key_id_ = key_id; } + void set_key(const std::vector& key) { key_ = key; } + void set_pssh(const std::vector& pssh) { pssh_ = pssh; } + void set_clear_milliseconds(uint32 clear_milliseconds) { + clear_milliseconds_ = clear_milliseconds; + } private: + scoped_ptr encryptor_; + std::vector key_id_; + std::vector key_; + std::vector pssh_; + // The first |clear_milliseconds_| of the result media should be in the clear + // text, i.e. should not be encrypted. + uint32 clear_milliseconds_; + const std::vector key_system_id_; + DISALLOW_COPY_AND_ASSIGN(EncryptorSource); }; - } #endif // MEDIA_BASE_ENCRYPTOR_SOURCE_H_ diff --git a/media/base/fixed_encryptor_source.cc b/media/base/fixed_encryptor_source.cc new file mode 100644 index 0000000000..265a931215 --- /dev/null +++ b/media/base/fixed_encryptor_source.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2013 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. + +#include "media/base/fixed_encryptor_source.h" + +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "media/base/aes_encryptor.h" + +namespace { +// The size of generated IV for this encryptor source. +const uint8 kIvSize = 8; +} // namespace + +namespace media { + +FixedEncryptorSource::FixedEncryptorSource(const std::string& key_id_hex, + const std::string& key_hex, + const std::string& pssh_hex, + uint32 clear_milliseconds) + : key_id_hex_(key_id_hex), + key_hex_(key_hex), + pssh_hex_(pssh_hex) { + set_clear_milliseconds(clear_milliseconds); +} + +FixedEncryptorSource::~FixedEncryptorSource() {} + +Status FixedEncryptorSource::Initialize() { + std::vector key_id; + if (!base::HexStringToBytes(key_id_hex_, &key_id)) { + LOG(ERROR) << "Cannot parse key_id_hex " << key_id_hex_; + return Status(error::INVALID_ARGUMENT, "Cannot parse input key_id_hex."); + } + + std::vector key; + if (!base::HexStringToBytes(key_hex_, &key)) { + LOG(ERROR) << "Cannot parse key_hex " << key_hex_; + return Status(error::INVALID_ARGUMENT, "Cannot parse input key_hex."); + } + + std::vector pssh; + if (!base::HexStringToBytes(pssh_hex_, &pssh)) { + LOG(ERROR) << "Cannot parse pssh_hex " << pssh_hex_; + return Status(error::INVALID_ARGUMENT, "Cannot parse input pssh_hex."); + } + + scoped_ptr encryptor(new AesCtrEncryptor()); + if (!encryptor->InitializeWithRandomIv(key, kIvSize)) + return Status(error::UNKNOWN, "Failed to initialize the encryptor."); + + set_encryptor(encryptor.Pass()); + set_key_id(key_id); + set_key(key); + set_pssh(pssh); + return Status::OK; +} + +} // namespace media diff --git a/media/base/fixed_encryptor_source.h b/media/base/fixed_encryptor_source.h new file mode 100644 index 0000000000..85ffa2d245 --- /dev/null +++ b/media/base/fixed_encryptor_source.h @@ -0,0 +1,35 @@ +// Copyright (c) 2013 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. +// +// Defines a fixed encryptor source with keys provided by user. + +#ifndef MEDIA_BASE_FIXED_ENCRYPTOR_SOURCE_H_ +#define MEDIA_BASE_FIXED_ENCRYPTOR_SOURCE_H_ + +#include "media/base/encryptor_source.h" + +namespace media { + +class FixedEncryptorSource : public EncryptorSource { + public: + FixedEncryptorSource(const std::string& key_id_hex, + const std::string& key_hex, + const std::string& pssh_hex, + uint32 clear_milliseconds); + virtual ~FixedEncryptorSource(); + + // EncryptorSource implementation. + virtual Status Initialize() OVERRIDE; + + private: + std::string key_id_hex_; + std::string key_hex_; + std::string pssh_hex_; + + DISALLOW_COPY_AND_ASSIGN(FixedEncryptorSource); +}; + +} // namespace media + +#endif // MEDIA_BASE_FIXED_ENCRYPTOR_SOURCE_H_