#include "base/basictypes.h"
#include "base/bind.h"
+#include "base/json/json_reader.h"
+#include "base/values.h"
+#include "media/base/cdm_promise.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::StrNe;
MATCHER(IsEmpty, "") { return arg.empty(); }
+MATCHER(IsNotEmpty, "") { return !arg.empty(); }
+MATCHER(IsJSONDictionary, "") {
+ std::string result(arg.begin(), arg.end());
+ scoped_ptr<base::Value> root(base::JSONReader().ReadToValue(result));
+ return (root.get() && root->GetType() == base::Value::TYPE_DICTIONARY);
+}
+
+class GURL;
namespace media {
0x00, 0x01, 0x02, 0x03
};
-const uint8 kKey[] = {
- // base64 equivalent is BAUGBwgJCgsMDQ4PEBESEw
- 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
- 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13
-};
-
+// Key is 0x0405060708090a0b0c0d0e0f10111213,
+// base64 equivalent is BAUGBwgJCgsMDQ4PEBESEw.
const char kKeyAsJWK[] =
"{"
" \"keys\": ["
" \"kid\": \"AAECAw\","
" \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
" }"
+ " ],"
+ " \"type\": \"temporary\""
+ "}";
+
+// Same kid as kKeyAsJWK, key to decrypt kEncryptedData2
+const char kKeyAlternateAsJWK[] =
+ "{"
+ " \"keys\": ["
+ " {"
+ " \"kty\": \"oct\","
+ " \"kid\": \"AAECAw\","
+ " \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
+ " }"
" ]"
"}";
const std::vector<uint8>& data,
const std::vector<uint8>& key_id,
const std::vector<uint8>& iv,
- int offset,
const std::vector<SubsampleEntry>& subsample_entries) {
DCHECK(!data.empty());
- int padded_size = offset + data.size();
- scoped_refptr<DecoderBuffer> encrypted_buffer(new DecoderBuffer(padded_size));
- memcpy(encrypted_buffer->writable_data() + offset, &data[0], data.size());
+ scoped_refptr<DecoderBuffer> encrypted_buffer(new DecoderBuffer(data.size()));
+ memcpy(encrypted_buffer->writable_data(), &data[0], data.size());
CHECK(encrypted_buffer.get());
std::string key_id_string(
reinterpret_cast<const char*>(key_id.empty() ? NULL : &key_id[0]),
std::string iv_string(
reinterpret_cast<const char*>(iv.empty() ? NULL : &iv[0]), iv.size());
encrypted_buffer->set_decrypt_config(scoped_ptr<DecryptConfig>(
- new DecryptConfig(key_id_string, iv_string, offset, subsample_entries)));
+ new DecryptConfig(key_id_string, iv_string, subsample_entries)));
return encrypted_buffer;
}
+enum PromiseResult { RESOLVED, REJECTED };
+
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))),
+ : decryptor_(base::Bind(&AesDecryptorTest::OnSessionMessage,
+ base::Unretained(this)),
+ base::Bind(&AesDecryptorTest::OnSessionClosed,
+ base::Unretained(this)),
+ base::Bind(&AesDecryptorTest::OnSessionKeysChange,
+ base::Unretained(this))),
decrypt_cb_(base::Bind(&AesDecryptorTest::BufferDecrypted,
base::Unretained(this))),
original_data_(kOriginalData, kOriginalData + kOriginalDataSize),
}
protected:
- void GenerateKeyRequest(const std::vector<uint8>& key_id) {
- DCHECK(!key_id.empty());
- EXPECT_CALL(*this, KeyMessage(StrNe(std::string()), key_id, ""))
- .WillOnce(SaveArg<0>(&session_id_string_));
- EXPECT_TRUE(decryptor_.GenerateKeyRequest(
- std::string(), &key_id[0], key_id.size()));
+ void OnResolveWithSession(PromiseResult expected_result,
+ const std::string& web_session_id) {
+ EXPECT_EQ(expected_result, RESOLVED) << "Unexpectedly resolved.";
+ EXPECT_GT(web_session_id.length(), 0ul);
+ web_session_id_ = web_session_id;
}
- enum AddKeyExpectation {
- KEY_ADDED,
- KEY_ERROR
- };
+ void OnResolve(PromiseResult expected_result) {
+ EXPECT_EQ(expected_result, RESOLVED) << "Unexpectedly resolved.";
+ }
+
+ void OnResolveWithUsableKeyIds(PromiseResult expected_result,
+ uint32 expected_count,
+ const KeyIdsVector& useable_key_ids) {
+ EXPECT_EQ(expected_result, RESOLVED) << "Unexpectedly resolved.";
+ EXPECT_EQ(expected_count, useable_key_ids.size());
+ useable_key_ids_ = useable_key_ids;
+ }
+
+ void OnReject(PromiseResult expected_result,
+ MediaKeys::Exception exception_code,
+ uint32 system_code,
+ const std::string& error_message) {
+ EXPECT_EQ(expected_result, REJECTED) << "Unexpectedly rejected.";
+ }
+
+ scoped_ptr<SimpleCdmPromise> CreatePromise(PromiseResult expected_result) {
+ scoped_ptr<SimpleCdmPromise> promise(
+ new SimpleCdmPromise(base::Bind(&AesDecryptorTest::OnResolve,
+ base::Unretained(this),
+ expected_result),
+ base::Bind(&AesDecryptorTest::OnReject,
+ base::Unretained(this),
+ expected_result)));
+ return promise.Pass();
+ }
+
+ scoped_ptr<NewSessionCdmPromise> CreateSessionPromise(
+ PromiseResult expected_result) {
+ scoped_ptr<NewSessionCdmPromise> promise(new NewSessionCdmPromise(
+ base::Bind(&AesDecryptorTest::OnResolveWithSession,
+ base::Unretained(this),
+ expected_result),
+ base::Bind(&AesDecryptorTest::OnReject,
+ base::Unretained(this),
+ expected_result)));
+ return promise.Pass();
+ }
- void AddRawKeyAndExpect(const std::vector<uint8>& key_id,
- const std::vector<uint8>& key,
- AddKeyExpectation result) {
- // TODO(jrummell): Remove once raw keys no longer supported.
+ scoped_ptr<KeyIdsPromise> CreateUsableKeyIdsPromise(
+ PromiseResult expected_result,
+ uint32 expected_count) {
+ scoped_ptr<KeyIdsPromise> promise(new KeyIdsPromise(
+ base::Bind(&AesDecryptorTest::OnResolveWithUsableKeyIds,
+ base::Unretained(this),
+ expected_result,
+ expected_count),
+ base::Bind(&AesDecryptorTest::OnReject,
+ base::Unretained(this),
+ expected_result)));
+ return promise.Pass();
+ }
+
+ // Creates a new session using |key_id|. Returns the session ID.
+ std::string CreateSession(const std::vector<uint8>& key_id) {
DCHECK(!key_id.empty());
- DCHECK(!key.empty());
+ EXPECT_CALL(*this,
+ OnSessionMessage(
+ IsNotEmpty(), IsJSONDictionary(), GURL::EmptyGURL()));
+ decryptor_.CreateSession(std::string(),
+ &key_id[0],
+ key_id.size(),
+ MediaKeys::TEMPORARY_SESSION,
+ CreateSessionPromise(RESOLVED));
+ // This expects the promise to be called synchronously, which is the case
+ // for AesDecryptor.
+ return web_session_id_;
+ }
- if (result == KEY_ADDED) {
- EXPECT_CALL(*this, KeyAdded(session_id_string_));
- } else if (result == KEY_ERROR) {
- EXPECT_CALL(*this, KeyError(session_id_string_,
- MediaKeys::kUnknownError, 0));
- } else {
- NOTREACHED();
- }
+ // Closes the session specified by |session_id|.
+ void CloseSession(const std::string& session_id) {
+ EXPECT_CALL(*this, OnSessionClosed(session_id));
+ decryptor_.CloseSession(session_id, CreatePromise(RESOLVED));
+ }
- decryptor_.AddKey(&key[0], key.size(), &key_id[0], key_id.size(),
- session_id_string_);
+ // Removes the session specified by |session_id|. This should simply do a
+ // CloseSession().
+ // TODO(jrummell): Clean this up when the prefixed API is removed.
+ // http://crbug.com/249976.
+ void RemoveSession(const std::string& session_id) {
+ EXPECT_CALL(*this, OnSessionClosed(session_id));
+ decryptor_.RemoveSession(session_id, CreatePromise(RESOLVED));
}
- void AddKeyAndExpect(const std::string& key, AddKeyExpectation result) {
+ // Updates the session specified by |session_id| with |key|. |result|
+ // tests that the update succeeds or generates an error.
+ void UpdateSessionAndExpect(std::string session_id,
+ const std::string& key,
+ PromiseResult expected_result) {
DCHECK(!key.empty());
- if (result == KEY_ADDED) {
- EXPECT_CALL(*this, KeyAdded(session_id_string_));
- } else if (result == KEY_ERROR) {
- EXPECT_CALL(*this,
- KeyError(session_id_string_, MediaKeys::kUnknownError, 0));
+ if (expected_result == RESOLVED) {
+ EXPECT_CALL(*this, OnSessionKeysChange(session_id, true));
} else {
- NOTREACHED();
+ EXPECT_CALL(*this, OnSessionKeysChange(_, _)).Times(0);
}
- decryptor_.AddKey(reinterpret_cast<const uint8*>(key.c_str()), key.length(),
- NULL, 0,
- session_id_string_);
+ decryptor_.UpdateSession(session_id,
+ reinterpret_cast<const uint8*>(key.c_str()),
+ key.length(),
+ CreatePromise(expected_result));
+ }
+
+ void GetUsableKeyIdsAndExpect(const std::string& session_id,
+ PromiseResult expected_result,
+ uint32 expected_count) {
+ decryptor_.GetUsableKeyIds(
+ session_id, CreateUsableKeyIdsPromise(expected_result, expected_count));
+ }
+
+ bool UsableKeyIdsContains(std::vector<uint8> expected) {
+ for (KeyIdsVector::iterator it = useable_key_ids_.begin();
+ it != useable_key_ids_.end();
+ ++it) {
+ if (*it == expected)
+ return true;
+ }
+ return false;
}
MOCK_METHOD2(BufferDecrypted, void(Decryptor::Status,
SUCCESS,
DATA_MISMATCH,
DATA_AND_SIZE_MISMATCH,
- DECRYPT_ERROR
+ DECRYPT_ERROR,
+ NO_KEY
};
void DecryptAndExpect(const scoped_refptr<DecoderBuffer>& encrypted,
DecryptExpectation result) {
scoped_refptr<DecoderBuffer> decrypted;
- if (result != DECRYPT_ERROR) {
- EXPECT_CALL(*this, BufferDecrypted(Decryptor::kSuccess, NotNull()))
- .WillOnce(SaveArg<1>(&decrypted));
- } else {
- EXPECT_CALL(*this, BufferDecrypted(Decryptor::kError, IsNull()))
- .WillOnce(SaveArg<1>(&decrypted));
+ switch (result) {
+ case SUCCESS:
+ case DATA_MISMATCH:
+ case DATA_AND_SIZE_MISMATCH:
+ EXPECT_CALL(*this, BufferDecrypted(Decryptor::kSuccess, NotNull()))
+ .WillOnce(SaveArg<1>(&decrypted));
+ break;
+ case DECRYPT_ERROR:
+ EXPECT_CALL(*this, BufferDecrypted(Decryptor::kError, IsNull()))
+ .WillOnce(SaveArg<1>(&decrypted));
+ break;
+ case NO_KEY:
+ EXPECT_CALL(*this, BufferDecrypted(Decryptor::kNoKey, IsNull()))
+ .WillOnce(SaveArg<1>(&decrypted));
+ break;
}
decryptor_.Decrypt(Decryptor::kVideo, encrypted, decrypt_cb_);
std::vector<uint8> decrypted_text;
- if (decrypted && decrypted->data_size()) {
+ if (decrypted.get() && decrypted->data_size()) {
decrypted_text.assign(
decrypted->data(), decrypted->data() + decrypted->data_size());
}
EXPECT_NE(plain_text.size(), decrypted_text.size());
break;
case DECRYPT_ERROR:
+ case NO_KEY:
EXPECT_TRUE(decrypted_text.empty());
break;
}
}
- 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));
+ MOCK_METHOD3(OnSessionMessage,
+ void(const std::string& web_session_id,
+ const std::vector<uint8>& message,
+ const GURL& destination_url));
+ MOCK_METHOD2(OnSessionKeysChange,
+ void(const std::string& web_session_id,
+ bool has_additional_usable_key));
+ MOCK_METHOD1(OnSessionClosed, void(const std::string& web_session_id));
AesDecryptor decryptor_;
- std::string session_id_string_;
AesDecryptor::DecryptCB decrypt_cb_;
+ std::string web_session_id_;
+
+ // Copy of the vector from the last successful call to
+ // OnResolveWithUsableKeyIds().
+ KeyIdsVector useable_key_ids_;
// Constants for testing.
const std::vector<uint8> original_data_;
const std::vector<SubsampleEntry> no_subsample_entries_;
};
-TEST_F(AesDecryptorTest, GenerateKeyRequestWithNullInitData) {
- EXPECT_CALL(*this, KeyMessage(StrNe(std::string()), IsEmpty(), ""));
- EXPECT_TRUE(decryptor_.GenerateKeyRequest(std::string(), NULL, 0));
+TEST_F(AesDecryptorTest, CreateSessionWithNullInitData) {
+ EXPECT_CALL(*this,
+ OnSessionMessage(IsNotEmpty(), IsEmpty(), GURL::EmptyGURL()));
+ decryptor_.CreateSession(std::string(),
+ NULL,
+ 0,
+ MediaKeys::TEMPORARY_SESSION,
+ CreateSessionPromise(RESOLVED));
}
-TEST_F(AesDecryptorTest, NormalDecryption) {
- GenerateKeyRequest(key_id_);
- AddKeyAndExpect(kKeyAsJWK, KEY_ADDED);
- scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- encrypted_data_, key_id_, iv_, 0, no_subsample_entries_);
- DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS);
+TEST_F(AesDecryptorTest, MultipleCreateSession) {
+ EXPECT_CALL(*this,
+ OnSessionMessage(IsNotEmpty(), IsEmpty(), GURL::EmptyGURL()));
+ decryptor_.CreateSession(std::string(),
+ NULL,
+ 0,
+ MediaKeys::TEMPORARY_SESSION,
+ CreateSessionPromise(RESOLVED));
+
+ EXPECT_CALL(*this,
+ OnSessionMessage(IsNotEmpty(), IsEmpty(), GURL::EmptyGURL()));
+ decryptor_.CreateSession(std::string(),
+ NULL,
+ 0,
+ MediaKeys::TEMPORARY_SESSION,
+ CreateSessionPromise(RESOLVED));
+
+ EXPECT_CALL(*this,
+ OnSessionMessage(IsNotEmpty(), IsEmpty(), GURL::EmptyGURL()));
+ decryptor_.CreateSession(std::string(),
+ NULL,
+ 0,
+ MediaKeys::TEMPORARY_SESSION,
+ CreateSessionPromise(RESOLVED));
}
-TEST_F(AesDecryptorTest, DecryptionWithOffset) {
- GenerateKeyRequest(key_id_);
- AddKeyAndExpect(kKeyAsJWK, KEY_ADDED);
+TEST_F(AesDecryptorTest, NormalDecryption) {
+ std::string session_id = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- encrypted_data_, key_id_, iv_, 23, no_subsample_entries_);
+ encrypted_data_, key_id_, iv_, no_subsample_entries_);
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS);
}
TEST_F(AesDecryptorTest, UnencryptedFrame) {
// An empty iv string signals that the frame is unencrypted.
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- original_data_, key_id_, std::vector<uint8>(), 0, no_subsample_entries_);
+ original_data_, key_id_, std::vector<uint8>(), no_subsample_entries_);
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS);
}
TEST_F(AesDecryptorTest, WrongKey) {
- GenerateKeyRequest(key_id_);
- AddKeyAndExpect(kWrongKeyAsJWK, KEY_ADDED);
+ std::string session_id = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id, kWrongKeyAsJWK, RESOLVED);
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- encrypted_data_, key_id_, iv_, 0, no_subsample_entries_);
+ encrypted_data_, key_id_, iv_, no_subsample_entries_);
DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH);
}
TEST_F(AesDecryptorTest, NoKey) {
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- encrypted_data_, key_id_, iv_, 0, no_subsample_entries_);
+ encrypted_data_, key_id_, iv_, no_subsample_entries_);
EXPECT_CALL(*this, BufferDecrypted(AesDecryptor::kNoKey, IsNull()));
decryptor_.Decrypt(Decryptor::kVideo, encrypted_buffer, decrypt_cb_);
}
TEST_F(AesDecryptorTest, KeyReplacement) {
- GenerateKeyRequest(key_id_);
+ std::string session_id = CreateSession(key_id_);
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- encrypted_data_, key_id_, iv_, 0, no_subsample_entries_);
+ encrypted_data_, key_id_, iv_, no_subsample_entries_);
- AddKeyAndExpect(kWrongKeyAsJWK, KEY_ADDED);
+ UpdateSessionAndExpect(session_id, kWrongKeyAsJWK, RESOLVED);
ASSERT_NO_FATAL_FAILURE(DecryptAndExpect(
encrypted_buffer, original_data_, DATA_MISMATCH));
- AddKeyAndExpect(kKeyAsJWK, KEY_ADDED);
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
ASSERT_NO_FATAL_FAILURE(
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS));
}
TEST_F(AesDecryptorTest, WrongSizedKey) {
- GenerateKeyRequest(key_id_);
- AddKeyAndExpect(kWrongSizedKeyAsJWK, KEY_ERROR);
-
- // Repeat for a raw key. Use "-1" to create a wrong sized key.
- std::vector<uint8> wrong_sized_key(kKey, kKey + arraysize(kKey) - 1);
- AddRawKeyAndExpect(key_id_, wrong_sized_key, KEY_ERROR);
+ std::string session_id = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id, kWrongSizedKeyAsJWK, REJECTED);
}
TEST_F(AesDecryptorTest, MultipleKeysAndFrames) {
- GenerateKeyRequest(key_id_);
- AddKeyAndExpect(kKeyAsJWK, KEY_ADDED);
+ std::string session_id = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- encrypted_data_, key_id_, iv_, 10, no_subsample_entries_);
+ encrypted_data_, key_id_, iv_, no_subsample_entries_);
ASSERT_NO_FATAL_FAILURE(
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS));
- AddKeyAndExpect(kKey2AsJWK, KEY_ADDED);
+ UpdateSessionAndExpect(session_id, kKey2AsJWK, RESOLVED);
// The first key is still available after we added a second key.
ASSERT_NO_FATAL_FAILURE(
kEncryptedData2 + arraysize(kEncryptedData2)),
std::vector<uint8>(kKeyId2, kKeyId2 + arraysize(kKeyId2)),
std::vector<uint8>(kIv2, kIv2 + arraysize(kIv2)),
- 30,
no_subsample_entries_);
ASSERT_NO_FATAL_FAILURE(DecryptAndExpect(
encrypted_buffer,
}
TEST_F(AesDecryptorTest, CorruptedIv) {
- GenerateKeyRequest(key_id_);
- AddKeyAndExpect(kKeyAsJWK, KEY_ADDED);
+ std::string session_id = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
std::vector<uint8> bad_iv = iv_;
bad_iv[1]++;
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- encrypted_data_, key_id_, bad_iv, 0, no_subsample_entries_);
+ encrypted_data_, key_id_, bad_iv, no_subsample_entries_);
DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH);
}
TEST_F(AesDecryptorTest, CorruptedData) {
- GenerateKeyRequest(key_id_);
- AddKeyAndExpect(kKeyAsJWK, KEY_ADDED);
+ std::string session_id = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
std::vector<uint8> bad_data = encrypted_data_;
bad_data[1]++;
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- bad_data, key_id_, iv_, 0, no_subsample_entries_);
+ bad_data, key_id_, iv_, no_subsample_entries_);
DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH);
}
TEST_F(AesDecryptorTest, EncryptedAsUnencryptedFailure) {
- GenerateKeyRequest(key_id_);
- AddKeyAndExpect(kKeyAsJWK, KEY_ADDED);
+ std::string session_id = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- encrypted_data_, key_id_, std::vector<uint8>(), 0, no_subsample_entries_);
+ encrypted_data_, key_id_, std::vector<uint8>(), no_subsample_entries_);
DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH);
}
TEST_F(AesDecryptorTest, SubsampleDecryption) {
- GenerateKeyRequest(key_id_);
- AddKeyAndExpect(kKeyAsJWK, KEY_ADDED);
+ std::string session_id = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- subsample_encrypted_data_, key_id_, iv_, 0, normal_subsample_entries_);
+ subsample_encrypted_data_, key_id_, iv_, normal_subsample_entries_);
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS);
}
// 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(key_id_);
- AddKeyAndExpect(kKeyAsJWK, KEY_ADDED);
+ std::string session_id = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- subsample_encrypted_data_, key_id_, iv_, 23, normal_subsample_entries_);
+ subsample_encrypted_data_, key_id_, iv_, normal_subsample_entries_);
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS);
}
TEST_F(AesDecryptorTest, SubsampleWrongSize) {
- GenerateKeyRequest(key_id_);
- AddKeyAndExpect(kKeyAsJWK, KEY_ADDED);
+ std::string session_id = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
std::vector<SubsampleEntry> subsample_entries_wrong_size(
kSubsampleEntriesWrongSize,
kSubsampleEntriesWrongSize + arraysize(kSubsampleEntriesWrongSize));
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- subsample_encrypted_data_, key_id_, iv_, 0, subsample_entries_wrong_size);
+ subsample_encrypted_data_, key_id_, iv_, subsample_entries_wrong_size);
DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH);
}
TEST_F(AesDecryptorTest, SubsampleInvalidTotalSize) {
- GenerateKeyRequest(key_id_);
- AddKeyAndExpect(kKeyAsJWK, KEY_ADDED);
+ std::string session_id = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
std::vector<SubsampleEntry> subsample_entries_invalid_total_size(
kSubsampleEntriesInvalidTotalSize,
arraysize(kSubsampleEntriesInvalidTotalSize));
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- subsample_encrypted_data_, key_id_, iv_, 0,
+ subsample_encrypted_data_, key_id_, iv_,
subsample_entries_invalid_total_size);
DecryptAndExpect(encrypted_buffer, original_data_, DECRYPT_ERROR);
}
// No cypher bytes in any of the subsamples.
TEST_F(AesDecryptorTest, SubsampleClearBytesOnly) {
- GenerateKeyRequest(key_id_);
- AddKeyAndExpect(kKeyAsJWK, KEY_ADDED);
+ std::string session_id = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
std::vector<SubsampleEntry> clear_only_subsample_entries(
kSubsampleEntriesClearOnly,
kSubsampleEntriesClearOnly + arraysize(kSubsampleEntriesClearOnly));
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- original_data_, key_id_, iv_, 0, clear_only_subsample_entries);
+ original_data_, key_id_, iv_, clear_only_subsample_entries);
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS);
}
// No clear bytes in any of the subsamples.
TEST_F(AesDecryptorTest, SubsampleCypherBytesOnly) {
- GenerateKeyRequest(key_id_);
- AddKeyAndExpect(kKeyAsJWK, KEY_ADDED);
+ std::string session_id = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
std::vector<SubsampleEntry> cypher_only_subsample_entries(
kSubsampleEntriesCypherOnly,
kSubsampleEntriesCypherOnly + arraysize(kSubsampleEntriesCypherOnly));
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- encrypted_data_, key_id_, iv_, 0, cypher_only_subsample_entries);
+ encrypted_data_, key_id_, iv_, cypher_only_subsample_entries);
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS);
}
+TEST_F(AesDecryptorTest, CloseSession) {
+ std::string session_id = CreateSession(key_id_);
+ scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
+ encrypted_data_, key_id_, iv_, no_subsample_entries_);
+
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
+ ASSERT_NO_FATAL_FAILURE(
+ DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS));
+
+ CloseSession(session_id);
+}
+
+TEST_F(AesDecryptorTest, RemoveSession) {
+ // TODO(jrummell): Clean this up when the prefixed API is removed.
+ // http://crbug.com/249976.
+ std::string session_id = CreateSession(key_id_);
+ scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
+ encrypted_data_, key_id_, iv_, no_subsample_entries_);
+
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
+ ASSERT_NO_FATAL_FAILURE(
+ DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS));
+
+ RemoveSession(session_id);
+}
+
+TEST_F(AesDecryptorTest, NoKeyAfterCloseSession) {
+ std::string session_id = CreateSession(key_id_);
+ scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
+ encrypted_data_, key_id_, iv_, no_subsample_entries_);
+
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
+ ASSERT_NO_FATAL_FAILURE(
+ DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS));
+
+ CloseSession(session_id);
+ ASSERT_NO_FATAL_FAILURE(
+ DecryptAndExpect(encrypted_buffer, original_data_, NO_KEY));
+}
+
+TEST_F(AesDecryptorTest, LatestKeyUsed) {
+ std::string session_id1 = CreateSession(key_id_);
+ scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
+ encrypted_data_, key_id_, iv_, no_subsample_entries_);
+
+ // Add alternate key, buffer should not be decoded properly.
+ UpdateSessionAndExpect(session_id1, kKeyAlternateAsJWK, RESOLVED);
+ ASSERT_NO_FATAL_FAILURE(
+ DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH));
+
+ // Create a second session with a correct key value for key_id_.
+ std::string session_id2 = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id2, kKeyAsJWK, RESOLVED);
+
+ // Should be able to decode with latest key.
+ ASSERT_NO_FATAL_FAILURE(
+ DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS));
+}
+
+TEST_F(AesDecryptorTest, LatestKeyUsedAfterCloseSession) {
+ std::string session_id1 = CreateSession(key_id_);
+ scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
+ encrypted_data_, key_id_, iv_, no_subsample_entries_);
+ UpdateSessionAndExpect(session_id1, kKeyAsJWK, RESOLVED);
+ ASSERT_NO_FATAL_FAILURE(
+ DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS));
+
+ // Create a second session with a different key value for key_id_.
+ std::string session_id2 = CreateSession(key_id_);
+ UpdateSessionAndExpect(session_id2, kKeyAlternateAsJWK, RESOLVED);
+
+ // Should not be able to decode with new key.
+ ASSERT_NO_FATAL_FAILURE(
+ DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH));
+
+ // Close second session, should revert to original key.
+ CloseSession(session_id2);
+ ASSERT_NO_FATAL_FAILURE(
+ DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS));
+}
+
TEST_F(AesDecryptorTest, JWKKey) {
+ std::string session_id = CreateSession(key_id_);
+
// Try a simple JWK key (i.e. not in a set)
- const std::string key1 =
+ const std::string kJwkSimple =
"{"
" \"kty\": \"oct\","
" \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
" \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
"}";
- AddKeyAndExpect(key1, KEY_ERROR);
+ UpdateSessionAndExpect(session_id, kJwkSimple, REJECTED);
// Try a key list with multiple entries.
- const std::string key2 =
+ const std::string kJwksMultipleEntries =
"{"
" \"keys\": ["
" {"
" }"
" ]"
"}";
- AddKeyAndExpect(key2, KEY_ADDED);
+ UpdateSessionAndExpect(session_id, kJwksMultipleEntries, RESOLVED);
// Try a key with no spaces and some \n plus additional fields.
- const std::string key3 =
+ const std::string kJwksNoSpaces =
"\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\",\"alg\":\"A128KW\","
"\"kid\":\"AAECAwQFBgcICQoLDA0ODxAREhM\",\"k\":\"GawgguFyGrWKav7AX4VKUg"
"\",\"foo\":\"bar\"}]}\n\n";
- AddKeyAndExpect(key3, KEY_ADDED);
+ UpdateSessionAndExpect(session_id, kJwksNoSpaces, RESOLVED);
// Try some non-ASCII characters.
- AddKeyAndExpect("This is not ASCII due to \xff\xfe\xfd in it.", KEY_ERROR);
+ UpdateSessionAndExpect(
+ session_id, "This is not ASCII due to \xff\xfe\xfd in it.", REJECTED);
// Try a badly formatted key. Assume that the JSON parser is fully tested,
// so we won't try a lot of combinations. However, need a test to ensure
// that the code doesn't crash if invalid JSON received.
- AddKeyAndExpect("This is not a JSON key.", KEY_ERROR);
+ UpdateSessionAndExpect(session_id, "This is not a JSON key.", REJECTED);
// Try passing some valid JSON that is not a dictionary at the top level.
- AddKeyAndExpect("40", KEY_ERROR);
+ UpdateSessionAndExpect(session_id, "40", REJECTED);
// Try an empty dictionary.
- AddKeyAndExpect("{ }", KEY_ERROR);
+ UpdateSessionAndExpect(session_id, "{ }", REJECTED);
// Try an empty 'keys' dictionary.
- AddKeyAndExpect("{ \"keys\": [] }", KEY_ERROR);
+ UpdateSessionAndExpect(session_id, "{ \"keys\": [] }", REJECTED);
// Try with 'keys' not a dictionary.
- AddKeyAndExpect("{ \"keys\":\"1\" }", KEY_ERROR);
+ UpdateSessionAndExpect(session_id, "{ \"keys\":\"1\" }", REJECTED);
// Try with 'keys' a list of integers.
- AddKeyAndExpect("{ \"keys\": [ 1, 2, 3 ] }", KEY_ERROR);
+ UpdateSessionAndExpect(session_id, "{ \"keys\": [ 1, 2, 3 ] }", REJECTED);
- // TODO(jrummell): The next 2 tests should fail once checking for padding
- // characters is enabled.
- // Try a key with padding(=) at end of base64 string.
- const std::string key4 =
+ // Try padding(=) at end of 'k' base64 string.
+ const std::string kJwksWithPaddedKey =
"{"
" \"keys\": ["
" {"
" }"
" ]"
"}";
- AddKeyAndExpect(key4, KEY_ADDED);
+ UpdateSessionAndExpect(session_id, kJwksWithPaddedKey, REJECTED);
- // Try a key ID with padding(=) at end of base64 string.
- const std::string key5 =
+ // Try padding(=) at end of 'kid' base64 string.
+ const std::string kJwksWithPaddedKeyId =
"{"
" \"keys\": ["
" {"
" }"
" ]"
"}";
- AddKeyAndExpect(key5, KEY_ADDED);
+ UpdateSessionAndExpect(session_id, kJwksWithPaddedKeyId, REJECTED);
// Try a key with invalid base64 encoding.
- const std::string key6 =
+ const std::string kJwksWithInvalidBase64 =
"{"
" \"keys\": ["
" {"
" }"
" ]"
"}";
- AddKeyAndExpect(key6, KEY_ERROR);
+ UpdateSessionAndExpect(session_id, kJwksWithInvalidBase64, REJECTED);
- // Try a key where no padding is required. 'k' has to be 16 bytes, so it
- // will always require padding. (Test above using |key2| has 2 'kid's that
- // require 1 and 2 padding bytes).
- const std::string key7 =
+ // Try a 3-byte 'kid' where no base64 padding is required.
+ // |kJwksMultipleEntries| above has 2 'kid's that require 1 and 2 padding
+ // bytes. Note that 'k' has to be 16 bytes, so it will always require padding.
+ const std::string kJwksWithNoPadding =
"{"
" \"keys\": ["
" {"
" }"
" ]"
"}";
- AddKeyAndExpect(key7, KEY_ADDED);
+ UpdateSessionAndExpect(session_id, kJwksWithNoPadding, RESOLVED);
// Empty key id.
- const std::string key8 =
+ const std::string kJwksWithEmptyKeyId =
"{"
" \"keys\": ["
" {"
" }"
" ]"
"}";
- AddKeyAndExpect(key8, KEY_ERROR);
+ UpdateSessionAndExpect(session_id, kJwksWithEmptyKeyId, REJECTED);
+ CloseSession(session_id);
}
-TEST_F(AesDecryptorTest, RawKey) {
- // Verify that v0.1b keys (raw key) is still supported. Raw keys are
- // 16 bytes long. Use the undecoded value of |kKey|.
- GenerateKeyRequest(key_id_);
- AddRawKeyAndExpect(
- key_id_, std::vector<uint8>(kKey, kKey + arraysize(kKey)), KEY_ADDED);
- scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
- encrypted_data_, key_id_, iv_, 0, no_subsample_entries_);
- DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS);
+TEST_F(AesDecryptorTest, GetKeyIds) {
+ std::vector<uint8> key_id1(kKeyId, kKeyId + arraysize(kKeyId));
+ std::vector<uint8> key_id2(kKeyId2, kKeyId2 + arraysize(kKeyId2));
+
+ std::string session_id = CreateSession(key_id_);
+ GetUsableKeyIdsAndExpect(session_id, RESOLVED, 0);
+ EXPECT_FALSE(UsableKeyIdsContains(key_id1));
+ EXPECT_FALSE(UsableKeyIdsContains(key_id2));
+
+ // Add 1 key, verify ID is returned.
+ UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
+ GetUsableKeyIdsAndExpect(session_id, RESOLVED, 1);
+ EXPECT_TRUE(UsableKeyIdsContains(key_id1));
+ EXPECT_FALSE(UsableKeyIdsContains(key_id2));
+
+ // Add second key, verify both IDs returned.
+ UpdateSessionAndExpect(session_id, kKey2AsJWK, RESOLVED);
+ GetUsableKeyIdsAndExpect(session_id, RESOLVED, 2);
+ EXPECT_TRUE(UsableKeyIdsContains(key_id1));
+ EXPECT_TRUE(UsableKeyIdsContains(key_id2));
}
} // namespace media