Commit 4d83add3 authored by jrummell's avatar jrummell Committed by Commit bot

Properly determine if a new key was added in AesDecryptor::Update()

BUG=448219
TEST=new test passes

Review URL: https://codereview.chromium.org/1002193005

Cr-Commit-Position: refs/heads/master@{#320958}
parent 1edacadd
......@@ -334,6 +334,7 @@ void AesDecryptor::UpdateSession(const std::string& session_id,
return;
}
bool key_added = false;
for (KeyIdAndKeyPairs::iterator it = keys.begin(); it != keys.end(); ++it) {
if (it->second.length() !=
static_cast<size_t>(DecryptConfig::kDecryptionKeySize)) {
......@@ -341,6 +342,12 @@ void AesDecryptor::UpdateSession(const std::string& session_id,
promise->reject(INVALID_ACCESS_ERROR, 0, "Invalid key length.");
return;
}
// If this key_id doesn't currently exist in this session,
// a new key is added.
if (!HasKey(session_id, it->first))
key_added = true;
if (!AddDecryptionKey(session_id, it->first, it->second)) {
promise->reject(INVALID_ACCESS_ERROR, 0, "Unable to add key.");
return;
......@@ -374,9 +381,7 @@ void AesDecryptor::UpdateSession(const std::string& session_id,
}
}
// Assume that at least 1 new key has been successfully added and thus
// sending true for |has_additional_usable_key|. http://crbug.com/448219.
session_keys_change_cb_.Run(session_id, true, keys_info.Pass());
session_keys_change_cb_.Run(session_id, key_added, keys_info.Pass());
}
void AesDecryptor::CloseSession(const std::string& session_id,
......@@ -546,6 +551,16 @@ AesDecryptor::DecryptionKey* AesDecryptor::GetKey(
return key_id_found->second->LatestDecryptionKey();
}
bool AesDecryptor::HasKey(const std::string& session_id,
const std::string& key_id) {
base::AutoLock auto_lock(key_map_lock_);
KeyIdToSessionKeysMap::const_iterator key_id_found = key_map_.find(key_id);
if (key_id_found == key_map_.end())
return false;
return key_id_found->second->Contains(session_id);
}
void AesDecryptor::DeleteKeysForSession(const std::string& session_id) {
base::AutoLock auto_lock(key_map_lock_);
......
......@@ -126,6 +126,9 @@ class MEDIA_EXPORT AesDecryptor : public MediaKeys,
// the key. Returns NULL if no key is associated with |key_id|.
DecryptionKey* GetKey(const std::string& key_id) const;
// Determines if |key_id| is already specified for |session_id|.
bool HasKey(const std::string& session_id, const std::string& key_id);
// Deletes all keys associated with |session_id|.
void DeleteKeysForSession(const std::string& session_id);
......
......@@ -323,11 +323,13 @@ class AesDecryptorTest : public testing::Test {
// tests that the update succeeds or generates an error.
void UpdateSessionAndExpect(std::string session_id,
const std::string& key,
PromiseResult expected_result) {
PromiseResult expected_result,
bool new_key_expected) {
DCHECK(!key.empty());
if (expected_result == RESOLVED) {
EXPECT_CALL(*this, OnSessionKeysChangeCalled(session_id, true));
EXPECT_CALL(*this,
OnSessionKeysChangeCalled(session_id, new_key_expected));
} else {
EXPECT_CALL(*this, OnSessionKeysChangeCalled(_, _)).Times(0);
}
......@@ -491,7 +493,7 @@ TEST_F(AesDecryptorTest, CreateSessionWithKeyIdsInitData) {
TEST_F(AesDecryptorTest, NormalDecryption) {
std::string session_id = CreateSession(key_id_);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
encrypted_data_, key_id_, iv_, no_subsample_entries_);
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS);
......@@ -506,7 +508,7 @@ TEST_F(AesDecryptorTest, UnencryptedFrame) {
TEST_F(AesDecryptorTest, WrongKey) {
std::string session_id = CreateSession(key_id_);
UpdateSessionAndExpect(session_id, kWrongKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kWrongKeyAsJWK, RESOLVED, true);
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
encrypted_data_, key_id_, iv_, no_subsample_entries_);
DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH);
......@@ -524,29 +526,29 @@ TEST_F(AesDecryptorTest, KeyReplacement) {
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
encrypted_data_, key_id_, iv_, no_subsample_entries_);
UpdateSessionAndExpect(session_id, kWrongKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kWrongKeyAsJWK, RESOLVED, true);
ASSERT_NO_FATAL_FAILURE(DecryptAndExpect(
encrypted_buffer, original_data_, DATA_MISMATCH));
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, false);
ASSERT_NO_FATAL_FAILURE(
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS));
}
TEST_F(AesDecryptorTest, WrongSizedKey) {
std::string session_id = CreateSession(key_id_);
UpdateSessionAndExpect(session_id, kWrongSizedKeyAsJWK, REJECTED);
UpdateSessionAndExpect(session_id, kWrongSizedKeyAsJWK, REJECTED, true);
}
TEST_F(AesDecryptorTest, MultipleKeysAndFrames) {
std::string session_id = CreateSession(key_id_);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
encrypted_data_, key_id_, iv_, no_subsample_entries_);
ASSERT_NO_FATAL_FAILURE(
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS));
UpdateSessionAndExpect(session_id, kKey2AsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKey2AsJWK, RESOLVED, true);
// The first key is still available after we added a second key.
ASSERT_NO_FATAL_FAILURE(
......@@ -568,7 +570,7 @@ TEST_F(AesDecryptorTest, MultipleKeysAndFrames) {
TEST_F(AesDecryptorTest, CorruptedIv) {
std::string session_id = CreateSession(key_id_);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
std::vector<uint8> bad_iv = iv_;
bad_iv[1]++;
......@@ -581,7 +583,7 @@ TEST_F(AesDecryptorTest, CorruptedIv) {
TEST_F(AesDecryptorTest, CorruptedData) {
std::string session_id = CreateSession(key_id_);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
std::vector<uint8> bad_data = encrypted_data_;
bad_data[1]++;
......@@ -593,7 +595,7 @@ TEST_F(AesDecryptorTest, CorruptedData) {
TEST_F(AesDecryptorTest, EncryptedAsUnencryptedFailure) {
std::string session_id = CreateSession(key_id_);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
encrypted_data_, key_id_, std::vector<uint8>(), no_subsample_entries_);
DecryptAndExpect(encrypted_buffer, original_data_, DATA_MISMATCH);
......@@ -601,7 +603,7 @@ TEST_F(AesDecryptorTest, EncryptedAsUnencryptedFailure) {
TEST_F(AesDecryptorTest, SubsampleDecryption) {
std::string session_id = CreateSession(key_id_);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
subsample_encrypted_data_, key_id_, iv_, normal_subsample_entries_);
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS);
......@@ -612,7 +614,7 @@ TEST_F(AesDecryptorTest, SubsampleDecryption) {
// disallow such a configuration, it should be covered.
TEST_F(AesDecryptorTest, SubsampleDecryptionWithOffset) {
std::string session_id = CreateSession(key_id_);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
subsample_encrypted_data_, key_id_, iv_, normal_subsample_entries_);
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS);
......@@ -620,7 +622,7 @@ TEST_F(AesDecryptorTest, SubsampleDecryptionWithOffset) {
TEST_F(AesDecryptorTest, SubsampleWrongSize) {
std::string session_id = CreateSession(key_id_);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
std::vector<SubsampleEntry> subsample_entries_wrong_size(
kSubsampleEntriesWrongSize,
......@@ -633,7 +635,7 @@ TEST_F(AesDecryptorTest, SubsampleWrongSize) {
TEST_F(AesDecryptorTest, SubsampleInvalidTotalSize) {
std::string session_id = CreateSession(key_id_);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
std::vector<SubsampleEntry> subsample_entries_invalid_total_size(
kSubsampleEntriesInvalidTotalSize,
......@@ -649,7 +651,7 @@ TEST_F(AesDecryptorTest, SubsampleInvalidTotalSize) {
// No cypher bytes in any of the subsamples.
TEST_F(AesDecryptorTest, SubsampleClearBytesOnly) {
std::string session_id = CreateSession(key_id_);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
std::vector<SubsampleEntry> clear_only_subsample_entries(
kSubsampleEntriesClearOnly,
......@@ -663,7 +665,7 @@ TEST_F(AesDecryptorTest, SubsampleClearBytesOnly) {
// No clear bytes in any of the subsamples.
TEST_F(AesDecryptorTest, SubsampleCypherBytesOnly) {
std::string session_id = CreateSession(key_id_);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
std::vector<SubsampleEntry> cypher_only_subsample_entries(
kSubsampleEntriesCypherOnly,
......@@ -679,7 +681,7 @@ TEST_F(AesDecryptorTest, CloseSession) {
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
encrypted_data_, key_id_, iv_, no_subsample_entries_);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
ASSERT_NO_FATAL_FAILURE(
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS));
......@@ -693,7 +695,7 @@ TEST_F(AesDecryptorTest, RemoveSession) {
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
encrypted_data_, key_id_, iv_, no_subsample_entries_);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
ASSERT_NO_FATAL_FAILURE(
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS));
......@@ -705,7 +707,7 @@ TEST_F(AesDecryptorTest, NoKeyAfterCloseSession) {
scoped_refptr<DecoderBuffer> encrypted_buffer = CreateEncryptedBuffer(
encrypted_data_, key_id_, iv_, no_subsample_entries_);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
ASSERT_NO_FATAL_FAILURE(
DecryptAndExpect(encrypted_buffer, original_data_, SUCCESS));
......@@ -720,13 +722,13 @@ TEST_F(AesDecryptorTest, LatestKeyUsed) {
encrypted_data_, key_id_, iv_, no_subsample_entries_);
// Add alternate key, buffer should not be decoded properly.
UpdateSessionAndExpect(session_id1, kKeyAlternateAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id1, kKeyAlternateAsJWK, RESOLVED, true);
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);
UpdateSessionAndExpect(session_id2, kKeyAsJWK, RESOLVED, true);
// Should be able to decode with latest key.
ASSERT_NO_FATAL_FAILURE(
......@@ -737,13 +739,13 @@ 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);
UpdateSessionAndExpect(session_id1, kKeyAsJWK, RESOLVED, true);
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);
UpdateSessionAndExpect(session_id2, kKeyAlternateAsJWK, RESOLVED, true);
// Should not be able to decode with new key.
ASSERT_NO_FATAL_FAILURE(
......@@ -766,7 +768,7 @@ TEST_F(AesDecryptorTest, JWKKey) {
" \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
" \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
"}";
UpdateSessionAndExpect(session_id, kJwkSimple, REJECTED);
UpdateSessionAndExpect(session_id, kJwkSimple, REJECTED, true);
// Try a key list with multiple entries.
const std::string kJwksMultipleEntries =
......@@ -786,38 +788,40 @@ TEST_F(AesDecryptorTest, JWKKey) {
" }"
" ]"
"}";
UpdateSessionAndExpect(session_id, kJwksMultipleEntries, RESOLVED);
UpdateSessionAndExpect(session_id, kJwksMultipleEntries, RESOLVED, true);
// Try a key with no spaces and some \n plus additional fields.
const std::string kJwksNoSpaces =
"\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\",\"alg\":\"A128KW\","
"\"kid\":\"AAECAwQFBgcICQoLDA0ODxAREhM\",\"k\":\"GawgguFyGrWKav7AX4VKUg"
"\"kid\":\"AQIDBAUGBwgJCgsMCg4PAA\",\"k\":\"GawgguFyGrWKav7AX4VKUg"
"\",\"foo\":\"bar\"}]}\n\n";
UpdateSessionAndExpect(session_id, kJwksNoSpaces, RESOLVED);
UpdateSessionAndExpect(session_id, kJwksNoSpaces, RESOLVED, true);
// Try some non-ASCII characters.
UpdateSessionAndExpect(
session_id, "This is not ASCII due to \xff\xfe\xfd in it.", REJECTED);
UpdateSessionAndExpect(session_id,
"This is not ASCII due to \xff\xfe\xfd in it.",
REJECTED, true);
// 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.
UpdateSessionAndExpect(session_id, "This is not a JSON key.", REJECTED);
UpdateSessionAndExpect(session_id, "This is not a JSON key.", REJECTED, true);
// Try passing some valid JSON that is not a dictionary at the top level.
UpdateSessionAndExpect(session_id, "40", REJECTED);
UpdateSessionAndExpect(session_id, "40", REJECTED, true);
// Try an empty dictionary.
UpdateSessionAndExpect(session_id, "{ }", REJECTED);
UpdateSessionAndExpect(session_id, "{ }", REJECTED, true);
// Try an empty 'keys' dictionary.
UpdateSessionAndExpect(session_id, "{ \"keys\": [] }", REJECTED);
UpdateSessionAndExpect(session_id, "{ \"keys\": [] }", REJECTED, true);
// Try with 'keys' not a dictionary.
UpdateSessionAndExpect(session_id, "{ \"keys\":\"1\" }", REJECTED);
UpdateSessionAndExpect(session_id, "{ \"keys\":\"1\" }", REJECTED, true);
// Try with 'keys' a list of integers.
UpdateSessionAndExpect(session_id, "{ \"keys\": [ 1, 2, 3 ] }", REJECTED);
UpdateSessionAndExpect(session_id, "{ \"keys\": [ 1, 2, 3 ] }", REJECTED,
true);
// Try padding(=) at end of 'k' base64 string.
const std::string kJwksWithPaddedKey =
......@@ -831,7 +835,7 @@ TEST_F(AesDecryptorTest, JWKKey) {
" }"
" ]"
"}";
UpdateSessionAndExpect(session_id, kJwksWithPaddedKey, REJECTED);
UpdateSessionAndExpect(session_id, kJwksWithPaddedKey, REJECTED, true);
// Try padding(=) at end of 'kid' base64 string.
const std::string kJwksWithPaddedKeyId =
......@@ -845,7 +849,7 @@ TEST_F(AesDecryptorTest, JWKKey) {
" }"
" ]"
"}";
UpdateSessionAndExpect(session_id, kJwksWithPaddedKeyId, REJECTED);
UpdateSessionAndExpect(session_id, kJwksWithPaddedKeyId, REJECTED, true);
// Try a key with invalid base64 encoding.
const std::string kJwksWithInvalidBase64 =
......@@ -859,7 +863,7 @@ TEST_F(AesDecryptorTest, JWKKey) {
" }"
" ]"
"}";
UpdateSessionAndExpect(session_id, kJwksWithInvalidBase64, REJECTED);
UpdateSessionAndExpect(session_id, kJwksWithInvalidBase64, REJECTED, true);
// Try a 3-byte 'kid' where no base64 padding is required.
// |kJwksMultipleEntries| above has 2 'kid's that require 1 and 2 padding
......@@ -875,7 +879,7 @@ TEST_F(AesDecryptorTest, JWKKey) {
" }"
" ]"
"}";
UpdateSessionAndExpect(session_id, kJwksWithNoPadding, RESOLVED);
UpdateSessionAndExpect(session_id, kJwksWithNoPadding, RESOLVED, true);
// Empty key id.
const std::string kJwksWithEmptyKeyId =
......@@ -889,7 +893,7 @@ TEST_F(AesDecryptorTest, JWKKey) {
" }"
" ]"
"}";
UpdateSessionAndExpect(session_id, kJwksWithEmptyKeyId, REJECTED);
UpdateSessionAndExpect(session_id, kJwksWithEmptyKeyId, REJECTED, true);
CloseSession(session_id);
}
......@@ -902,14 +906,33 @@ TEST_F(AesDecryptorTest, GetKeyIds) {
EXPECT_FALSE(KeysInfoContains(key_id2));
// Add 1 key, verify it is returned.
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
EXPECT_TRUE(KeysInfoContains(key_id1));
EXPECT_FALSE(KeysInfoContains(key_id2));
// Add second key, verify both IDs returned.
UpdateSessionAndExpect(session_id, kKey2AsJWK, RESOLVED);
UpdateSessionAndExpect(session_id, kKey2AsJWK, RESOLVED, true);
EXPECT_TRUE(KeysInfoContains(key_id1));
EXPECT_TRUE(KeysInfoContains(key_id2));
}
TEST_F(AesDecryptorTest, NoKeysChangeForSameKey) {
std::vector<uint8> key_id(kKeyId, kKeyId + arraysize(kKeyId));
std::string session_id = CreateSession(key_id_);
EXPECT_FALSE(KeysInfoContains(key_id));
// Add key, verify it is returned.
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, true);
EXPECT_TRUE(KeysInfoContains(key_id));
// Add key a second time.
UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED, false);
EXPECT_TRUE(KeysInfoContains(key_id));
// Create a new session. Add key, should indicate key added for this session.
std::string session_id2 = CreateSession(key_id_);
UpdateSessionAndExpect(session_id2, kKeyAsJWK, RESOLVED, true);
}
} // namespace media
......@@ -302,6 +302,12 @@ class KeyProvidingApp : public FakeEncryptedMedia::AppBase {
void OnEncryptedMediaInitData(const std::string& init_data_type,
const std::vector<uint8>& init_data,
AesDecryptor* decryptor) override {
// Since only 1 session is created, skip the request if the |init_data|
// has been seen before (no need to add the same key again).
if (init_data == prev_init_data_)
return;
prev_init_data_ = init_data;
if (current_session_id_.empty()) {
if (init_data_type == kCencInitDataType) {
// Since the 'cenc' files are not created with proper 'pssh' boxes,
......@@ -340,6 +346,7 @@ class KeyProvidingApp : public FakeEncryptedMedia::AppBase {
}
std::string current_session_id_;
std::vector<uint8> prev_init_data_;
};
class RotatingKeyProvidingApp : public KeyProvidingApp {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment