diff options
author | strobe@google.com <strobe@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-26 00:49:59 +0000 |
---|---|---|
committer | strobe@google.com <strobe@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-26 00:49:59 +0000 |
commit | 9746f9132e55a91d2ec3d866711277b874574743 (patch) | |
tree | b0f6d236afc3515855403363b6e9fe455a6c801d /media/crypto | |
parent | 7db8893ab741949612cebfed89e11d267daacbf9 (diff) | |
download | chromium_src-9746f9132e55a91d2ec3d866711277b874574743.zip chromium_src-9746f9132e55a91d2ec3d866711277b874574743.tar.gz chromium_src-9746f9132e55a91d2ec3d866711277b874574743.tar.bz2 |
Add Common Encryption support to BMFF, including subsample decryption.
BUG=132351
TEST=AesDecryptorTest, plus manual playback in browser
Review URL: https://chromiumcodereview.appspot.com/10651006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@148453 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/crypto')
-rw-r--r-- | media/crypto/aes_decryptor.cc | 180 | ||||
-rw-r--r-- | media/crypto/aes_decryptor.h | 11 | ||||
-rw-r--r-- | media/crypto/aes_decryptor_unittest.cc | 270 |
3 files changed, 313 insertions, 148 deletions
diff --git a/media/crypto/aes_decryptor.cc b/media/crypto/aes_decryptor.cc index 730a9b8..552197b 100644 --- a/media/crypto/aes_decryptor.cc +++ b/media/crypto/aes_decryptor.cc @@ -59,7 +59,7 @@ static bool CheckData(const DecoderBuffer& input, const base::StringPiece& hmac_key) { CHECK(input.GetDataSize()); CHECK(input.GetDecryptConfig()); - CHECK_GT(input.GetDecryptConfig()->checksum_size(), 0); + CHECK_GT(input.GetDecryptConfig()->checksum().size(), 0u); CHECK(!hmac_key.empty()); crypto::HMAC hmac(crypto::HMAC::SHA1); @@ -72,65 +72,117 @@ static bool CheckData(const DecoderBuffer& input, // Here, check that checksum size is not greater than the hash // algorithm's digest length. - DCHECK_LE(input.GetDecryptConfig()->checksum_size(), - static_cast<int>(hmac.DigestLength())); + DCHECK_LE(input.GetDecryptConfig()->checksum().size(), + hmac.DigestLength()); base::StringPiece data_to_check( reinterpret_cast<const char*>(input.GetData()), input.GetDataSize()); - base::StringPiece digest( - reinterpret_cast<const char*>(input.GetDecryptConfig()->checksum()), - input.GetDecryptConfig()->checksum_size()); - return hmac.VerifyTruncated(data_to_check, digest); + return hmac.VerifyTruncated(data_to_check, + input.GetDecryptConfig()->checksum()); } -// Decrypts |input| using |key|. |encrypted_data_offset| is the number of bytes -// into |input| that the encrypted data starts. -// Returns a DecoderBuffer with the decrypted data if decryption succeeded or -// NULL if decryption failed. +enum ClearBytesBufferSel { + kSrcContainsClearBytes, + kDstContainsClearBytes, +}; + +static void CopySubsamples(const std::vector<SubsampleEntry>& subsamples, + const ClearBytesBufferSel sel, + const uint8* src, + uint8* dst) { + for (size_t i = 0; i < subsamples.size(); i++) { + const SubsampleEntry& subsample = subsamples[i]; + if (sel == kSrcContainsClearBytes) { + src += subsample.clear_bytes; + } else { + dst += subsample.clear_bytes; + } + memcpy(dst, src, subsample.cypher_bytes); + src += subsample.cypher_bytes; + dst += subsample.cypher_bytes; + } +} + +// Decrypts |input| using |key|. Returns a DecoderBuffer with the decrypted +// data if decryption succeeded or NULL if decryption failed. static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input, - crypto::SymmetricKey* key, - int encrypted_data_offset) { + crypto::SymmetricKey* key) { CHECK(input.GetDataSize()); CHECK(input.GetDecryptConfig()); CHECK(key); - // Initialize decryptor. crypto::Encryptor encryptor; if (!encryptor.Init(key, crypto::Encryptor::CTR, "")) { DVLOG(1) << "Could not initialize decryptor."; return NULL; } - DCHECK_EQ(input.GetDecryptConfig()->iv_size(), - DecryptConfig::kDecryptionKeySize); - // Set the counter block. - base::StringPiece counter_block( - reinterpret_cast<const char*>(input.GetDecryptConfig()->iv()), - input.GetDecryptConfig()->iv_size()); - if (counter_block.empty()) { - DVLOG(1) << "Could not generate counter block."; + DCHECK_EQ(input.GetDecryptConfig()->iv().size(), + static_cast<size_t>(DecryptConfig::kDecryptionKeySize)); + if (!encryptor.SetCounter(input.GetDecryptConfig()->iv())) { + DVLOG(1) << "Could not set counter block."; return NULL; } - if (!encryptor.SetCounter(counter_block)) { - DVLOG(1) << "Could not set counter block."; + + const int data_offset = input.GetDecryptConfig()->data_offset(); + const char* sample = + reinterpret_cast<const char*>(input.GetData() + data_offset); + int sample_size = input.GetDataSize() - data_offset; + + if (input.GetDecryptConfig()->subsamples().empty()) { + std::string decrypted_text; + base::StringPiece encrypted_text(sample, sample_size); + if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) { + DVLOG(1) << "Could not decrypt data."; + return NULL; + } + + // TODO(xhwang): Find a way to avoid this data copy. + return DecoderBuffer::CopyFrom( + reinterpret_cast<const uint8*>(decrypted_text.data()), + decrypted_text.size()); + } + + const std::vector<SubsampleEntry>& subsamples = + input.GetDecryptConfig()->subsamples(); + + int total_clear_size = 0; + int total_encrypted_size = 0; + for (size_t i = 0; i < subsamples.size(); i++) { + total_clear_size += subsamples[i].clear_bytes; + total_encrypted_size += subsamples[i].cypher_bytes; + } + if (total_clear_size + total_encrypted_size != sample_size) { + DVLOG(1) << "Subsample sizes do not equal input size"; return NULL; } + // The encrypted portions of all subsamples must form a contiguous block, + // such that an encrypted subsample that ends away from a block boundary is + // immediately followed by the start of the next encrypted subsample. We + // copy all encrypted subsamples to a contiguous buffer, decrypt them, then + // copy the decrypted bytes over the encrypted bytes in the output. + // TODO(strobe): attempt to reduce number of memory copies + scoped_array<uint8> encrypted_bytes(new uint8[total_encrypted_size]); + CopySubsamples(subsamples, kSrcContainsClearBytes, + reinterpret_cast<const uint8*>(sample), encrypted_bytes.get()); + + base::StringPiece encrypted_text( + reinterpret_cast<const char*>(encrypted_bytes.get()), + total_encrypted_size); std::string decrypted_text; - const char* frame = - reinterpret_cast<const char*>(input.GetData() + encrypted_data_offset); - int frame_size = input.GetDataSize() - encrypted_data_offset; - base::StringPiece encrypted_text(frame, frame_size); if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) { DVLOG(1) << "Could not decrypt data."; return NULL; } - // TODO(xhwang): Find a way to avoid this data copy. - return DecoderBuffer::CopyFrom( - reinterpret_cast<const uint8*>(decrypted_text.data()), - decrypted_text.size()); + scoped_refptr<DecoderBuffer> output = DecoderBuffer::CopyFrom( + reinterpret_cast<const uint8*>(sample), sample_size); + CopySubsamples(subsamples, kDstContainsClearBytes, + reinterpret_cast<const uint8*>(decrypted_text.data()), + output->GetWritableData()); + return output; } AesDecryptor::AesDecryptor(DecryptorClient* client) @@ -192,9 +244,7 @@ void AesDecryptor::AddKey(const std::string& key_system, return; } - // TODO(fgalligan): When ISO is added we will need to figure out how to - // detect if the encrypted data will contain an HMAC. - if (!decryption_key->Init(true)) { + if (!decryption_key->Init()) { DVLOG(1) << "Could not initialize decryption key."; client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0); return; @@ -220,16 +270,12 @@ void AesDecryptor::CancelKeyRequest(const std::string& key_system, void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, const DecryptCB& decrypt_cb) { CHECK(encrypted->GetDecryptConfig()); - const uint8* key_id = encrypted->GetDecryptConfig()->key_id(); - const int key_id_size = encrypted->GetDecryptConfig()->key_id_size(); - - // TODO(xhwang): Avoid always constructing a string with StringPiece? - std::string key_id_string(reinterpret_cast<const char*>(key_id), key_id_size); + const std::string& key_id = encrypted->GetDecryptConfig()->key_id(); DecryptionKey* key = NULL; { base::AutoLock auto_lock(key_map_lock_); - KeyMap::const_iterator found = key_map_.find(key_id_string); + KeyMap::const_iterator found = key_map_.find(key_id); if (found != key_map_.end()) key = found->second; } @@ -241,7 +287,7 @@ void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, return; } - int checksum_size = encrypted->GetDecryptConfig()->checksum_size(); + int checksum_size = encrypted->GetDecryptConfig()->checksum().size(); // According to the WebM encrypted specification, it is an open question // what should happen when a frame fails the integrity check. // http://wiki.webmproject.org/encryption/webm-encryption-rfc @@ -253,10 +299,13 @@ void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, return; } + // TODO(strobe): Currently, presence of checksum is used to indicate the use + // of normal or WebM decryption keys. Consider a more explicit signaling + // mechanism and the removal of the webm_decryption_key member. + crypto::SymmetricKey* decryption_key = (checksum_size > 0) ? + key->webm_decryption_key() : key->decryption_key(); scoped_refptr<DecoderBuffer> decrypted = - DecryptData(*encrypted, - key->decryption_key(), - encrypted->GetDecryptConfig()->encrypted_frame_offset()); + DecryptData(*encrypted, decryption_key); if (!decrypted) { DVLOG(1) << "Decryption failed."; decrypt_cb.Run(kError, NULL); @@ -275,34 +324,31 @@ AesDecryptor::DecryptionKey::DecryptionKey( AesDecryptor::DecryptionKey::~DecryptionKey() {} -bool AesDecryptor::DecryptionKey::Init(bool derive_webm_keys) { +bool AesDecryptor::DecryptionKey::Init() { CHECK(!secret_.empty()); - - if (derive_webm_keys) { - std::string raw_key = DeriveKey(secret_, - kWebmEncryptionSeed, - secret_.length()); - if (raw_key.empty()) { - return false; - } - decryption_key_.reset( - crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key)); - if (!decryption_key_.get()) { - return false; - } - - hmac_key_ = DeriveKey(secret_, kWebmHmacSeed, kWebmSha1DigestSize); - if (hmac_key_.empty()) { - return false; - } - return true; - } - decryption_key_.reset( crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, secret_)); if (!decryption_key_.get()) { return false; } + + std::string raw_key = DeriveKey(secret_, + kWebmEncryptionSeed, + secret_.length()); + if (raw_key.empty()) { + return false; + } + webm_decryption_key_.reset( + crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key)); + if (!webm_decryption_key_.get()) { + return false; + } + + hmac_key_ = DeriveKey(secret_, kWebmHmacSeed, kWebmSha1DigestSize); + if (hmac_key_.empty()) { + return false; + } + return true; } diff --git a/media/crypto/aes_decryptor.h b/media/crypto/aes_decryptor.h index 224035c..86b258d 100644 --- a/media/crypto/aes_decryptor.h +++ b/media/crypto/aes_decryptor.h @@ -61,12 +61,12 @@ class MEDIA_EXPORT AesDecryptor : public Decryptor { explicit DecryptionKey(const std::string& secret); ~DecryptionKey(); - // Creates the encryption key and HMAC. If |derive_webm_keys| is true then - // the object will derive the decryption key and the HMAC key from - // |secret_|. - bool Init(bool derive_webm_keys); + // Creates the encryption key, and derives the WebM decryption key and HMAC. + bool Init(); crypto::SymmetricKey* decryption_key() { return decryption_key_.get(); } + crypto::SymmetricKey* webm_decryption_key() + { return webm_decryption_key_.get(); } base::StringPiece hmac_key() { return base::StringPiece(hmac_key_); } private: @@ -77,6 +77,9 @@ class MEDIA_EXPORT AesDecryptor : public Decryptor { // The key used to decrypt the data. scoped_ptr<crypto::SymmetricKey> decryption_key_; + // The key used for decryption of WebM media, derived from the secret. + scoped_ptr<crypto::SymmetricKey> webm_decryption_key_; + // The key used to perform the integrity check. Currently the HMAC key is // defined by the WebM encrypted specification. Current encrypted WebM // request for comments specification is here diff --git a/media/crypto/aes_decryptor_unittest.cc b/media/crypto/aes_decryptor_unittest.cc index 34354a4..7877f6e 100644 --- a/media/crypto/aes_decryptor_unittest.cc +++ b/media/crypto/aes_decryptor_unittest.cc @@ -143,6 +143,47 @@ static const unsigned char kWebmFrame0FrameDataChanged[] = { 0x64, 0xf8 }; +static const uint8 kSubsampleOriginalData[] = "Original subsample data."; +static const int kSubsampleOriginalDataSize = 24; + +static const uint8 kSubsampleKeyId[] = { 0x00, 0x01, 0x02, 0x03 }; + +static const uint8 kSubsampleKey[] = { + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13 +}; + +static const uint8 kSubsampleIv[] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8 kSubsampleData[] = { + 0x4f, 0x72, 0x09, 0x16, 0x09, 0xe6, 0x79, 0xad, + 0x70, 0x73, 0x75, 0x62, 0x09, 0xbb, 0x83, 0x1d, + 0x4d, 0x08, 0xd7, 0x78, 0xa4, 0xa7, 0xf1, 0x2e +}; + +static const uint8 kPaddedSubsampleData[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x4f, 0x72, 0x09, 0x16, 0x09, 0xe6, 0x79, 0xad, + 0x70, 0x73, 0x75, 0x62, 0x09, 0xbb, 0x83, 0x1d, + 0x4d, 0x08, 0xd7, 0x78, 0xa4, 0xa7, 0xf1, 0x2e +}; + +// Encrypted with kSubsampleKey and kSubsampleIv but without subsamples. +static const uint8 kNoSubsampleData[] = { + 0x2f, 0x03, 0x09, 0xef, 0x71, 0xaf, 0x31, 0x16, + 0xfa, 0x9d, 0x18, 0x43, 0x1e, 0x96, 0x71, 0xb5, + 0xbf, 0xf5, 0x30, 0x53, 0x9a, 0x20, 0xdf, 0x95 +}; + +static const SubsampleEntry kSubsampleEntries[] = { + { 2, 7 }, + { 3, 11 }, + { 1, 0 }, +}; + class AesDecryptorTest : public testing::Test { public: AesDecryptorTest() @@ -176,10 +217,9 @@ class AesDecryptorTest : public testing::Test { // an HMAC and IV prepended to an encrypted frame. Current encrypted WebM // request for comments specification is here // http://wiki.webmproject.org/encryption/webm-encryption-rfc - scoped_refptr<DecoderBuffer> CreateWebMEncryptedBuffer(const uint8* data, - int data_size, - const uint8* key_id, - int key_id_size) { + scoped_refptr<DecoderBuffer> CreateWebMEncryptedBuffer( + const uint8* data, int data_size, + const uint8* key_id, int key_id_size) { scoped_refptr<DecoderBuffer> encrypted_buffer = DecoderBuffer::CopyFrom( data + kWebMHmacSize, data_size - kWebMHmacSize); CHECK(encrypted_buffer); @@ -191,10 +231,30 @@ class AesDecryptorTest : public testing::Test { GenerateCounterBlock(reinterpret_cast<const uint8*>(&iv), sizeof(iv)); encrypted_buffer->SetDecryptConfig( scoped_ptr<DecryptConfig>(new DecryptConfig( - key_id, key_id_size, - reinterpret_cast<const uint8*>(webm_iv.data()), webm_iv.size(), - data, kWebMHmacSize, - sizeof(iv)))); + std::string(reinterpret_cast<const char*>(key_id), key_id_size), + webm_iv, + std::string(reinterpret_cast<const char*>(data), kWebMHmacSize), + sizeof(iv), + std::vector<SubsampleEntry>()))); + return encrypted_buffer; + } + + scoped_refptr<DecoderBuffer> CreateSubsampleEncryptedBuffer( + const uint8* data, int data_size, + const uint8* key_id, int key_id_size, + const uint8* iv, int iv_size, + int data_offset, + const std::vector<SubsampleEntry>& subsample_entries) { + scoped_refptr<DecoderBuffer> encrypted_buffer = + DecoderBuffer::CopyFrom(data, data_size); + CHECK(encrypted_buffer); + encrypted_buffer->SetDecryptConfig( + scoped_ptr<DecryptConfig>(new DecryptConfig( + std::string(reinterpret_cast<const char*>(key_id), key_id_size), + std::string(reinterpret_cast<const char*>(iv), iv_size), + std::string(), + data_offset, + subsample_entries))); return encrypted_buffer; } @@ -223,29 +283,21 @@ class AesDecryptorTest : public testing::Test { MOCK_METHOD2(BufferDecrypted, void(Decryptor::DecryptStatus, const scoped_refptr<DecoderBuffer>&)); - void DecryptAndExpectToSucceed(const uint8* data, int data_size, - const uint8* plain_text, - int plain_text_size, - const uint8* key_id, int key_id_size) { - scoped_refptr<DecoderBuffer> encrypted_data = - CreateWebMEncryptedBuffer(data, data_size, key_id, key_id_size); + void DecryptAndExpectToSucceed(const scoped_refptr<DecoderBuffer>& encrypted, + const uint8* plain_text, int plain_text_size) { scoped_refptr<DecoderBuffer> decrypted; EXPECT_CALL(*this, BufferDecrypted(AesDecryptor::kSuccess, NotNull())) .WillOnce(SaveArg<1>(&decrypted)); - decryptor_.Decrypt(encrypted_data, decrypt_cb_); + decryptor_.Decrypt(encrypted, decrypt_cb_); ASSERT_TRUE(decrypted); ASSERT_EQ(plain_text_size, decrypted->GetDataSize()); EXPECT_EQ(0, memcmp(plain_text, decrypted->GetData(), plain_text_size)); } - void DecryptAndExpectToFail(const uint8* data, int data_size, - const uint8* plain_text, int plain_text_size, - const uint8* key_id, int key_id_size) { - scoped_refptr<DecoderBuffer> encrypted_data = - CreateWebMEncryptedBuffer(data, data_size, key_id, key_id_size); + void DecryptAndExpectToFail(const scoped_refptr<DecoderBuffer>& encrypted) { EXPECT_CALL(*this, BufferDecrypted(AesDecryptor::kError, IsNull())); - decryptor_.Decrypt(encrypted_data, decrypt_cb_); + decryptor_.Decrypt(encrypted, decrypt_cb_); } scoped_refptr<DecoderBuffer> encrypted_data_; @@ -255,17 +307,18 @@ class AesDecryptorTest : public testing::Test { AesDecryptor::DecryptCB decrypt_cb_; }; -TEST_F(AesDecryptorTest, NormalDecryption) { +TEST_F(AesDecryptorTest, NormalWebMDecryption) { const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; GenerateKeyRequest(frame.key_id, frame.key_id_size); AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, frame.key, frame.key_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(frame.encrypted_data, - frame.encrypted_data_size, + scoped_refptr<DecoderBuffer> encrypted_data = + CreateWebMEncryptedBuffer(frame.encrypted_data, + frame.encrypted_data_size, + frame.key_id, frame.key_id_size); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data, frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); + frame.plain_text_size)); } TEST_F(AesDecryptorTest, WrongKey) { @@ -273,12 +326,11 @@ TEST_F(AesDecryptorTest, WrongKey) { GenerateKeyRequest(frame.key_id, frame.key_id_size); AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, kWebmWrongKey, arraysize(kWebmWrongKey)); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(frame.encrypted_data, - frame.encrypted_data_size, - frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); + scoped_refptr<DecoderBuffer> encrypted_data = + CreateWebMEncryptedBuffer(frame.encrypted_data, + frame.encrypted_data_size, + frame.key_id, frame.key_id_size); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(encrypted_data)); } TEST_F(AesDecryptorTest, KeyReplacement) { @@ -286,20 +338,16 @@ TEST_F(AesDecryptorTest, KeyReplacement) { GenerateKeyRequest(frame.key_id, frame.key_id_size); AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, kWebmWrongKey, arraysize(kWebmWrongKey)); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(frame.encrypted_data, - frame.encrypted_data_size, - frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); + scoped_refptr<DecoderBuffer> encrypted_data = + CreateWebMEncryptedBuffer(frame.encrypted_data, + frame.encrypted_data_size, + frame.key_id, frame.key_id_size); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(encrypted_data)); AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, frame.key, frame.key_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(frame.encrypted_data, - frame.encrypted_data_size, + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data, frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); + frame.plain_text_size)); } TEST_F(AesDecryptorTest, WrongSizedKey) { @@ -314,12 +362,13 @@ TEST_F(AesDecryptorTest, MultipleKeysAndFrames) { GenerateKeyRequest(frame.key_id, frame.key_id_size); AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, frame.key, frame.key_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(frame.encrypted_data, - frame.encrypted_data_size, + scoped_refptr<DecoderBuffer> encrypted_data = + CreateWebMEncryptedBuffer(frame.encrypted_data, + frame.encrypted_data_size, + frame.key_id, frame.key_id_size); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data, frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); + frame.plain_text_size)); const WebmEncryptedData& frame2 = kWebmEncryptedFrames[2]; GenerateKeyRequest(frame2.key_id, frame2.key_id_size); @@ -327,19 +376,21 @@ TEST_F(AesDecryptorTest, MultipleKeysAndFrames) { frame2.key, frame2.key_size); const WebmEncryptedData& frame1 = kWebmEncryptedFrames[1]; - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(frame1.encrypted_data, - frame1.encrypted_data_size, + scoped_refptr<DecoderBuffer> encrypted_data1 = + CreateWebMEncryptedBuffer(frame1.encrypted_data, + frame1.encrypted_data_size, + frame1.key_id, frame1.key_id_size); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data1, frame1.plain_text, - frame1.plain_text_size, - frame1.key_id, - frame1.key_id_size)); + frame1.plain_text_size)); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(frame2.encrypted_data, - frame2.encrypted_data_size, + scoped_refptr<DecoderBuffer> encrypted_data2 = + CreateWebMEncryptedBuffer(frame2.encrypted_data, + frame2.encrypted_data_size, + frame2.key_id, frame2.key_id_size); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data2, frame2.plain_text, - frame2.plain_text_size, - frame2.key_id, - frame2.key_id_size)); + frame2.plain_text_size)); } TEST_F(AesDecryptorTest, HmacCheckFailure) { @@ -347,12 +398,11 @@ TEST_F(AesDecryptorTest, HmacCheckFailure) { GenerateKeyRequest(frame.key_id, frame.key_id_size); AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, frame.key, frame.key_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(kWebmFrame0HmacDataChanged, - frame.encrypted_data_size, - frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); + scoped_refptr<DecoderBuffer> encrypted_data = + CreateWebMEncryptedBuffer(kWebmFrame0HmacDataChanged, + frame.encrypted_data_size, + frame.key_id, frame.key_id_size); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(encrypted_data)); } TEST_F(AesDecryptorTest, IvCheckFailure) { @@ -360,12 +410,11 @@ TEST_F(AesDecryptorTest, IvCheckFailure) { GenerateKeyRequest(frame.key_id, frame.key_id_size); AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, frame.key, frame.key_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(kWebmFrame0IvDataChanged, - frame.encrypted_data_size, - frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); + scoped_refptr<DecoderBuffer> encrypted_data = + CreateWebMEncryptedBuffer(kWebmFrame0IvDataChanged, + frame.encrypted_data_size, + frame.key_id, frame.key_id_size); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(encrypted_data)); } TEST_F(AesDecryptorTest, DataCheckFailure) { @@ -373,12 +422,79 @@ TEST_F(AesDecryptorTest, DataCheckFailure) { GenerateKeyRequest(frame.key_id, frame.key_id_size); AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, frame.key, frame.key_size); - ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(kWebmFrame0FrameDataChanged, - frame.encrypted_data_size, - frame.plain_text, - frame.plain_text_size, - frame.key_id, - frame.key_id_size)); + scoped_refptr<DecoderBuffer> encrypted_data = + CreateWebMEncryptedBuffer(kWebmFrame0FrameDataChanged, + frame.encrypted_data_size, + frame.key_id, frame.key_id_size); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(encrypted_data)); +} + +TEST_F(AesDecryptorTest, SubsampleDecryption) { + GenerateKeyRequest(kSubsampleKeyId, arraysize(kSubsampleKeyId)); + AddKeyAndExpectToSucceed(kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleKey, arraysize(kSubsampleKey)); + scoped_refptr<DecoderBuffer> encrypted_data = CreateSubsampleEncryptedBuffer( + kSubsampleData, arraysize(kSubsampleData), + kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleIv, arraysize(kSubsampleIv), + 0, + std::vector<SubsampleEntry>( + kSubsampleEntries, + kSubsampleEntries + arraysize(kSubsampleEntries))); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed( + encrypted_data, kSubsampleOriginalData, kSubsampleOriginalDataSize)); +} + +// Ensures noninterference of data offset and subsample mechanisms. We never +// expect to encounter this in the wild, but since the DecryptConfig doesn't +// disallow such a configuration, it should be covered. +TEST_F(AesDecryptorTest, SubsampleDecryptionWithOffset) { + GenerateKeyRequest(kSubsampleKeyId, arraysize(kSubsampleKeyId)); + AddKeyAndExpectToSucceed(kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleKey, arraysize(kSubsampleKey)); + scoped_refptr<DecoderBuffer> encrypted_data = CreateSubsampleEncryptedBuffer( + kPaddedSubsampleData, arraysize(kPaddedSubsampleData), + kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleIv, arraysize(kSubsampleIv), + arraysize(kPaddedSubsampleData) - arraysize(kSubsampleData), + std::vector<SubsampleEntry>( + kSubsampleEntries, + kSubsampleEntries + arraysize(kSubsampleEntries))); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed( + encrypted_data, kSubsampleOriginalData, kSubsampleOriginalDataSize)); +} + +// No subsample or offset. +TEST_F(AesDecryptorTest, NormalDecryption) { + GenerateKeyRequest(kSubsampleKeyId, arraysize(kSubsampleKeyId)); + AddKeyAndExpectToSucceed(kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleKey, arraysize(kSubsampleKey)); + scoped_refptr<DecoderBuffer> encrypted_data = CreateSubsampleEncryptedBuffer( + kNoSubsampleData, arraysize(kNoSubsampleData), + kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleIv, arraysize(kSubsampleIv), + 0, + std::vector<SubsampleEntry>()); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed( + encrypted_data, kSubsampleOriginalData, kSubsampleOriginalDataSize)); +} + +TEST_F(AesDecryptorTest, IncorrectSubsampleSize) { + GenerateKeyRequest(kSubsampleKeyId, arraysize(kSubsampleKeyId)); + AddKeyAndExpectToSucceed(kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleKey, arraysize(kSubsampleKey)); + std::vector<SubsampleEntry> entries( + kSubsampleEntries, + kSubsampleEntries + arraysize(kSubsampleEntries)); + entries[2].cypher_bytes += 1; + + scoped_refptr<DecoderBuffer> encrypted_data = CreateSubsampleEncryptedBuffer( + kSubsampleData, arraysize(kSubsampleData), + kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleIv, arraysize(kSubsampleIv), + 0, + entries); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(encrypted_data)); } } // namespace media |