summaryrefslogtreecommitdiffstats
path: root/media/cdm/aes_decryptor_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'media/cdm/aes_decryptor_unittest.cc')
-rw-r--r--media/cdm/aes_decryptor_unittest.cc664
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