diff options
Diffstat (limited to 'media/cdm/aes_decryptor_unittest.cc')
-rw-r--r-- | media/cdm/aes_decryptor_unittest.cc | 664 |
1 files changed, 664 insertions, 0 deletions
diff --git a/media/cdm/aes_decryptor_unittest.cc b/media/cdm/aes_decryptor_unittest.cc new file mode 100644 index 0000000..1edb8e8 --- /dev/null +++ b/media/cdm/aes_decryptor_unittest.cc @@ -0,0 +1,664 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "media/base/decoder_buffer.h" +#include "media/base/decrypt_config.h" +#include "media/base/mock_filters.h" +#include "media/cdm/aes_decryptor.h" +#include "media/webm/webm_constants.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::ElementsAreArray; +using ::testing::Gt; +using ::testing::IsNull; +using ::testing::NotNull; +using ::testing::SaveArg; +using ::testing::StrEq; +using ::testing::StrNe; + +MATCHER(IsEmpty, "") { return arg.empty(); } + +namespace media { + +// |encrypted_data| is encrypted from |plain_text| using |key|. |key_id| is +// used to distinguish |key|. +struct WebmEncryptedData { + uint8 plain_text[32]; + int plain_text_size; + uint8 key_id[32]; + int key_id_size; + uint8 key[32]; + int key_size; + uint8 encrypted_data[64]; + int encrypted_data_size; +}; + +static const char kClearKeySystem[] = "org.w3.clearkey"; + +// Frames 0 & 1 are encrypted with the same key. Frame 2 is encrypted with a +// different key. Frame 3 is unencrypted. +const WebmEncryptedData kWebmEncryptedFrames[] = { + { + // plaintext + "Original data.", 14, + // key_id + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13 + }, 20, + // key + { 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23 + }, 16, + // encrypted_data + { 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf0, 0xd1, 0x12, 0xd5, 0x24, 0x81, 0x96, + 0x55, 0x1b, 0x68, 0x9f, 0x38, 0x91, 0x85 + }, 23 + }, { + // plaintext + "Changed Original data.", 22, + // key_id + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13 + }, 20, + // key + { 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23 + }, 16, + // encrypted_data + { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x57, 0x66, 0xf4, 0x12, 0x1a, 0xed, 0xb5, + 0x79, 0x1c, 0x8e, 0x25, 0xd7, 0x17, 0xe7, 0x5e, + 0x16, 0xe3, 0x40, 0x08, 0x27, 0x11, 0xe9 + }, 31 + }, { + // plaintext + "Original data.", 14, + // key_id + { 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30 + }, 13, + // key + { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40 + }, 16, + // encrypted_data + { 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9c, 0x71, 0x26, 0x57, 0x3e, 0x25, 0x37, + 0xf7, 0x31, 0x81, 0x19, 0x64, 0xce, 0xbc + }, 23 + }, { + // plaintext + "Changed Original data.", 22, + // key_id + { 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30 + }, 13, + // key + { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40 + }, 16, + // encrypted_data + { 0x00, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, + 0x20, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, + 0x6c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e + }, 23 + } +}; + +static const uint8 kWebmWrongSizedKey[] = { 0x20, 0x20 }; + +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 +}; + +// kSubsampleOriginalData encrypted with kSubsampleKey and kSubsampleIv using +// kSubsampleEntriesNormal. +static const uint8 kSubsampleEncryptedData[] = { + 0x4f, 0x72, 0x09, 0x16, 0x09, 0xe6, 0x79, 0xad, + 0x70, 0x73, 0x75, 0x62, 0x09, 0xbb, 0x83, 0x1d, + 0x4d, 0x08, 0xd7, 0x78, 0xa4, 0xa7, 0xf1, 0x2e +}; + +// kSubsampleEncryptedData with 8 bytes padding at the beginning. +static const uint8 kPaddedSubsampleEncryptedData[] = { + 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 +}; + +// kSubsampleOriginalData encrypted with kSubsampleKey and kSubsampleIv but +// without any subsamples (or equivalently using kSubsampleEntriesCypherOnly). +static const uint8 kEncryptedData[] = { + 0x2f, 0x03, 0x09, 0xef, 0x71, 0xaf, 0x31, 0x16, + 0xfa, 0x9d, 0x18, 0x43, 0x1e, 0x96, 0x71, 0xb5, + 0xbf, 0xf5, 0x30, 0x53, 0x9a, 0x20, 0xdf, 0x95 +}; + +// Subsample entries for testing. The sum of |cypher_bytes| and |clear_bytes| of +// all entries must be equal to kSubsampleOriginalDataSize to make the subsample +// entries valid. + +static const SubsampleEntry kSubsampleEntriesNormal[] = { + { 2, 7 }, + { 3, 11 }, + { 1, 0 } +}; + +static const SubsampleEntry kSubsampleEntriesClearOnly[] = { + { 7, 0 }, + { 8, 0 }, + { 9, 0 } +}; + +static const SubsampleEntry kSubsampleEntriesCypherOnly[] = { + { 0, 6 }, + { 0, 8 }, + { 0, 10 } +}; + +// Generates a 16 byte CTR counter block. The CTR counter block format is a +// CTR IV appended with a CTR block counter. |iv| is an 8 byte CTR IV. +// |iv_size| is the size of |iv| in bytes. Returns a string of +// kDecryptionKeySize bytes. +static std::string GenerateCounterBlock(const uint8* iv, int iv_size) { + CHECK_GT(iv_size, 0); + CHECK_LE(iv_size, DecryptConfig::kDecryptionKeySize); + + std::string counter_block(reinterpret_cast<const char*>(iv), iv_size); + counter_block.append(DecryptConfig::kDecryptionKeySize - iv_size, 0); + return counter_block; +} + +// Creates a WebM encrypted buffer that the demuxer would pass to the +// decryptor. |data| is the payload of a WebM encrypted Block. |key_id| is +// initialization data from the WebM file. Every encrypted Block has +// a signal byte prepended to a frame. If the frame is encrypted then an IV is +// prepended to the Block. Current encrypted WebM request for comments +// specification is here +// http://wiki.webmproject.org/encryption/webm-encryption-rfc +static 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, data_size); + CHECK(encrypted_buffer.get()); + DCHECK_EQ(kWebMSignalByteSize, 1); + + uint8 signal_byte = data[0]; + int data_offset = kWebMSignalByteSize; + + // Setting the DecryptConfig object of the buffer while leaving the + // initialization vector empty will tell the decryptor that the frame is + // unencrypted. + std::string counter_block_str; + + if (signal_byte & kWebMFlagEncryptedFrame) { + counter_block_str = GenerateCounterBlock(data + data_offset, kWebMIvSize); + data_offset += kWebMIvSize; + } + + encrypted_buffer->set_decrypt_config( + scoped_ptr<DecryptConfig>(new DecryptConfig( + std::string(reinterpret_cast<const char*>(key_id), key_id_size), + counter_block_str, + data_offset, + std::vector<SubsampleEntry>()))); + return encrypted_buffer; +} + +// TODO(xhwang): Refactor this function to encapsulate more details about +// creating an encrypted DecoderBuffer with subsamples so we don't have so much +// boilerplate code in each test before calling this function. +static 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.get()); + encrypted_buffer->set_decrypt_config( + 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), + data_offset, + subsample_entries))); + return encrypted_buffer; +} + +class AesDecryptorTest : public testing::Test { + public: + AesDecryptorTest() + : decryptor_( + base::Bind(&AesDecryptorTest::KeyAdded, base::Unretained(this)), + base::Bind(&AesDecryptorTest::KeyError, base::Unretained(this)), + base::Bind(&AesDecryptorTest::KeyMessage, base::Unretained(this))), + decrypt_cb_(base::Bind(&AesDecryptorTest::BufferDecrypted, + base::Unretained(this))), + subsample_entries_normal_( + kSubsampleEntriesNormal, + kSubsampleEntriesNormal + arraysize(kSubsampleEntriesNormal)) { + } + + protected: + void GenerateKeyRequest(const uint8* key_id, int key_id_size) { + EXPECT_CALL(*this, KeyMessage( + StrNe(std::string()), ElementsAreArray(key_id, key_id_size), "")) + .WillOnce(SaveArg<0>(&session_id_string_)); + EXPECT_TRUE(decryptor_.GenerateKeyRequest( + std::string(), key_id, key_id_size)); + } + + void AddKeyAndExpectToSucceed(const uint8* key_id, int key_id_size, + const uint8* key, int key_size) { + EXPECT_CALL(*this, KeyAdded(session_id_string_)); + decryptor_.AddKey(key, key_size, key_id, key_id_size, + session_id_string_); + } + + void AddKeyAndExpectToFail(const uint8* key_id, int key_id_size, + const uint8* key, int key_size) { + EXPECT_CALL(*this, KeyError(session_id_string_, + MediaKeys::kUnknownError, 0)); + decryptor_.AddKey(key, key_size, key_id, key_id_size, session_id_string_); + } + + MOCK_METHOD2(BufferDecrypted, void(Decryptor::Status, + const scoped_refptr<DecoderBuffer>&)); + + 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(Decryptor::kVideo, encrypted, decrypt_cb_); + ASSERT_TRUE(decrypted.get()); + ASSERT_EQ(plain_text_size, decrypted->data_size()); + EXPECT_EQ(0, memcmp(plain_text, decrypted->data(), plain_text_size)); + } + + void DecryptAndExpectDataMismatch( + 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(Decryptor::kVideo, encrypted, decrypt_cb_); + ASSERT_TRUE(decrypted.get()); + ASSERT_EQ(plain_text_size, decrypted->data_size()); + EXPECT_NE(0, memcmp(plain_text, decrypted->data(), plain_text_size)); + } + + void DecryptAndExpectSizeDataMismatch( + 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(Decryptor::kVideo, encrypted, decrypt_cb_); + ASSERT_TRUE(decrypted.get()); + EXPECT_NE(plain_text_size, decrypted->data_size()); + EXPECT_NE(0, memcmp(plain_text, decrypted->data(), plain_text_size)); + } + + void DecryptAndExpectToFail(const scoped_refptr<DecoderBuffer>& encrypted) { + EXPECT_CALL(*this, BufferDecrypted(AesDecryptor::kError, IsNull())); + decryptor_.Decrypt(Decryptor::kVideo, encrypted, decrypt_cb_); + } + + MOCK_METHOD1(KeyAdded, void(const std::string&)); + MOCK_METHOD3(KeyError, void(const std::string&, + MediaKeys::KeyError, int)); + MOCK_METHOD3(KeyMessage, void(const std::string& session_id, + const std::vector<uint8>& message, + const std::string& default_url)); + + AesDecryptor decryptor_; + std::string session_id_string_; + AesDecryptor::DecryptCB decrypt_cb_; + std::vector<SubsampleEntry> subsample_entries_normal_; +}; + +TEST_F(AesDecryptorTest, GenerateKeyRequestWithNullInitData) { + EXPECT_CALL(*this, KeyMessage(StrNe(std::string()), IsEmpty(), "")); + EXPECT_TRUE(decryptor_.GenerateKeyRequest(std::string(), NULL, 0)); +} + +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); + 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)); +} + +TEST_F(AesDecryptorTest, UnencryptedFrameWebMDecryption) { + const WebmEncryptedData& frame = kWebmEncryptedFrames[3]; + GenerateKeyRequest(frame.key_id, frame.key_id_size); + AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, + frame.key, frame.key_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)); +} + +TEST_F(AesDecryptorTest, WrongKey) { + const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; + GenerateKeyRequest(frame.key_id, frame.key_id_size); + + // Change the first byte of the key. + std::vector<uint8> wrong_key(frame.key, frame.key + frame.key_size); + wrong_key[0]++; + + AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, + &wrong_key[0], frame.key_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(DecryptAndExpectDataMismatch(encrypted_data, + frame.plain_text, + frame.plain_text_size)); +} + +TEST_F(AesDecryptorTest, NoKey) { + const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; + GenerateKeyRequest(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); + EXPECT_CALL(*this, BufferDecrypted(AesDecryptor::kNoKey, IsNull())); + decryptor_.Decrypt(Decryptor::kVideo, encrypted_data, decrypt_cb_); +} + +TEST_F(AesDecryptorTest, KeyReplacement) { + const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; + GenerateKeyRequest(frame.key_id, frame.key_id_size); + + // Change the first byte of the key. + std::vector<uint8> wrong_key(frame.key, frame.key + frame.key_size); + wrong_key[0]++; + + AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, + &wrong_key[0], frame.key_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(DecryptAndExpectDataMismatch(encrypted_data, + frame.plain_text, + frame.plain_text_size)); + AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, + frame.key, frame.key_size); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data, + frame.plain_text, + frame.plain_text_size)); +} + +TEST_F(AesDecryptorTest, WrongSizedKey) { + const WebmEncryptedData& frame = kWebmEncryptedFrames[0]; + GenerateKeyRequest(frame.key_id, frame.key_id_size); + AddKeyAndExpectToFail(frame.key_id, frame.key_id_size, + kWebmWrongSizedKey, arraysize(kWebmWrongSizedKey)); +} + +TEST_F(AesDecryptorTest, MultipleKeysAndFrames) { + 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); + 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)); + + const WebmEncryptedData& frame2 = kWebmEncryptedFrames[2]; + GenerateKeyRequest(frame2.key_id, frame2.key_id_size); + AddKeyAndExpectToSucceed(frame2.key_id, frame2.key_id_size, + frame2.key, frame2.key_size); + + const WebmEncryptedData& frame1 = kWebmEncryptedFrames[1]; + 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)); + + 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)); +} + +TEST_F(AesDecryptorTest, CorruptedIv) { + 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); + + // Change byte 13 to modify the IV. Bytes 13-20 of WebM encrypted data + // contains the IV. + std::vector<uint8> frame_with_bad_iv( + frame.encrypted_data, frame.encrypted_data + frame.encrypted_data_size); + frame_with_bad_iv[1]++; + + scoped_refptr<DecoderBuffer> encrypted_data = + CreateWebMEncryptedBuffer(&frame_with_bad_iv[0], + frame.encrypted_data_size, + frame.key_id, frame.key_id_size); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectDataMismatch(encrypted_data, + frame.plain_text, + frame.plain_text_size)); +} + +TEST_F(AesDecryptorTest, CorruptedData) { + 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); + + // Change last byte to modify the data. Bytes 21+ of WebM encrypted data + // contains the encrypted frame. + std::vector<uint8> frame_with_bad_vp8_data( + frame.encrypted_data, frame.encrypted_data + frame.encrypted_data_size); + frame_with_bad_vp8_data[frame.encrypted_data_size - 1]++; + + scoped_refptr<DecoderBuffer> encrypted_data = + CreateWebMEncryptedBuffer(&frame_with_bad_vp8_data[0], + frame.encrypted_data_size, + frame.key_id, frame.key_id_size); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectDataMismatch(encrypted_data, + frame.plain_text, + frame.plain_text_size)); +} + +TEST_F(AesDecryptorTest, EncryptedAsUnencryptedFailure) { + 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); + + // Change signal byte from an encrypted frame to an unencrypted frame. Byte + // 12 of WebM encrypted data contains the signal byte. + std::vector<uint8> frame_with_wrong_signal_byte( + frame.encrypted_data, frame.encrypted_data + frame.encrypted_data_size); + frame_with_wrong_signal_byte[0] = 0; + + scoped_refptr<DecoderBuffer> encrypted_data = + CreateWebMEncryptedBuffer(&frame_with_wrong_signal_byte[0], + frame.encrypted_data_size, + frame.key_id, frame.key_id_size); + ASSERT_NO_FATAL_FAILURE( + DecryptAndExpectSizeDataMismatch(encrypted_data, + frame.plain_text, + frame.plain_text_size)); +} + +TEST_F(AesDecryptorTest, UnencryptedAsEncryptedFailure) { + const WebmEncryptedData& frame = kWebmEncryptedFrames[3]; + GenerateKeyRequest(frame.key_id, frame.key_id_size); + AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, + frame.key, frame.key_size); + + // Change signal byte from an unencrypted frame to an encrypted frame. Byte + // 0 of WebM encrypted data contains the signal byte. + std::vector<uint8> frame_with_wrong_signal_byte( + frame.encrypted_data, frame.encrypted_data + frame.encrypted_data_size); + frame_with_wrong_signal_byte[0] = kWebMFlagEncryptedFrame; + + scoped_refptr<DecoderBuffer> encrypted_data = + CreateWebMEncryptedBuffer(&frame_with_wrong_signal_byte[0], + frame.encrypted_data_size, + frame.key_id, frame.key_id_size); + ASSERT_NO_FATAL_FAILURE( + DecryptAndExpectSizeDataMismatch(encrypted_data, + frame.plain_text, + frame.plain_text_size)); +} + +TEST_F(AesDecryptorTest, SubsampleDecryption) { + GenerateKeyRequest(kSubsampleKeyId, arraysize(kSubsampleKeyId)); + AddKeyAndExpectToSucceed(kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleKey, arraysize(kSubsampleKey)); + scoped_refptr<DecoderBuffer> encrypted_data = CreateSubsampleEncryptedBuffer( + kSubsampleEncryptedData, arraysize(kSubsampleEncryptedData), + kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleIv, arraysize(kSubsampleIv), + 0, + subsample_entries_normal_); + 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( + kPaddedSubsampleEncryptedData, arraysize(kPaddedSubsampleEncryptedData), + kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleIv, arraysize(kSubsampleIv), + arraysize(kPaddedSubsampleEncryptedData) + - arraysize(kSubsampleEncryptedData), + subsample_entries_normal_); + 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( + kEncryptedData, arraysize(kEncryptedData), + 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 = subsample_entries_normal_; + entries[2].cypher_bytes += 1; + + scoped_refptr<DecoderBuffer> encrypted_data = CreateSubsampleEncryptedBuffer( + kSubsampleEncryptedData, arraysize(kSubsampleEncryptedData), + kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleIv, arraysize(kSubsampleIv), + 0, + entries); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToFail(encrypted_data)); +} + +// No cypher bytes in any of the subsamples. +TEST_F(AesDecryptorTest, SubsampleClearBytesOnly) { + GenerateKeyRequest(kSubsampleKeyId, arraysize(kSubsampleKeyId)); + AddKeyAndExpectToSucceed(kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleKey, arraysize(kSubsampleKey)); + std::vector<SubsampleEntry> subsample_entries_clear_only( + kSubsampleEntriesClearOnly, + kSubsampleEntriesClearOnly + arraysize(kSubsampleEntriesClearOnly)); + scoped_refptr<DecoderBuffer> encrypted_data = CreateSubsampleEncryptedBuffer( + kSubsampleOriginalData, kSubsampleOriginalDataSize, + kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleIv, arraysize(kSubsampleIv), + 0, + subsample_entries_clear_only); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data, + kSubsampleOriginalData, kSubsampleOriginalDataSize)); +} + +// No clear bytes in any of the subsamples. +TEST_F(AesDecryptorTest, SubsampleCypherBytesOnly) { + GenerateKeyRequest(kSubsampleKeyId, arraysize(kSubsampleKeyId)); + AddKeyAndExpectToSucceed(kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleKey, arraysize(kSubsampleKey)); + std::vector<SubsampleEntry> subsample_entries_cypher_only( + kSubsampleEntriesCypherOnly, + kSubsampleEntriesCypherOnly + arraysize(kSubsampleEntriesCypherOnly)); + scoped_refptr<DecoderBuffer> encrypted_data = CreateSubsampleEncryptedBuffer( + kEncryptedData, arraysize(kEncryptedData), + kSubsampleKeyId, arraysize(kSubsampleKeyId), + kSubsampleIv, arraysize(kSubsampleIv), + 0, + subsample_entries_cypher_only); + ASSERT_NO_FATAL_FAILURE(DecryptAndExpectToSucceed(encrypted_data, + kSubsampleOriginalData, kSubsampleOriginalDataSize)); +} + +} // namespace media |