Commit 5287c3c5 authored by xhwang's avatar xhwang Committed by Commit bot

Encrypted Media: Check "alg":"A128KW" in AesDecryptor.

BUG=444713
TEST=Add tests to cover this case.
R=jrummell@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#309543}
parent fd59a531
...@@ -293,21 +293,21 @@ void AesDecryptor::UpdateSession(const std::string& web_session_id, ...@@ -293,21 +293,21 @@ void AesDecryptor::UpdateSession(const std::string& web_session_id,
SessionType session_type = MediaKeys::TEMPORARY_SESSION; SessionType session_type = MediaKeys::TEMPORARY_SESSION;
if (!ExtractKeysFromJWKSet(key_string, &keys, &session_type)) { if (!ExtractKeysFromJWKSet(key_string, &keys, &session_type)) {
promise->reject( promise->reject(
INVALID_ACCESS_ERROR, 0, "response is not a valid JSON Web Key Set."); INVALID_ACCESS_ERROR, 0, "Response is not a valid JSON Web Key Set.");
return; return;
} }
// Make sure that at least one key was extracted. // Make sure that at least one key was extracted.
if (keys.empty()) { if (keys.empty()) {
promise->reject( promise->reject(
INVALID_ACCESS_ERROR, 0, "response does not contain any keys."); INVALID_ACCESS_ERROR, 0, "Response does not contain any keys.");
return; return;
} }
for (KeyIdAndKeyPairs::iterator it = keys.begin(); it != keys.end(); ++it) { for (KeyIdAndKeyPairs::iterator it = keys.begin(); it != keys.end(); ++it) {
if (it->second.length() != if (it->second.length() !=
static_cast<size_t>(DecryptConfig::kDecryptionKeySize)) { static_cast<size_t>(DecryptConfig::kDecryptionKeySize)) {
DVLOG(1) << "Invalid key length: " << key_string.length(); DVLOG(1) << "Invalid key length: " << it->second.length();
promise->reject(INVALID_ACCESS_ERROR, 0, "Invalid key length."); promise->reject(INVALID_ACCESS_ERROR, 0, "Invalid key length.");
return; return;
} }
......
...@@ -55,6 +55,7 @@ const char kKeyAsJWK[] = ...@@ -55,6 +55,7 @@ const char kKeyAsJWK[] =
" \"keys\": [" " \"keys\": ["
" {" " {"
" \"kty\": \"oct\"," " \"kty\": \"oct\","
" \"alg\": \"A128KW\","
" \"kid\": \"AAECAw\"," " \"kid\": \"AAECAw\","
" \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\"" " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
" }" " }"
...@@ -68,6 +69,7 @@ const char kKeyAlternateAsJWK[] = ...@@ -68,6 +69,7 @@ const char kKeyAlternateAsJWK[] =
" \"keys\": [" " \"keys\": ["
" {" " {"
" \"kty\": \"oct\"," " \"kty\": \"oct\","
" \"alg\": \"A128KW\","
" \"kid\": \"AAECAw\"," " \"kid\": \"AAECAw\","
" \"k\": \"FBUWFxgZGhscHR4fICEiIw\"" " \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
" }" " }"
...@@ -79,6 +81,7 @@ const char kWrongKeyAsJWK[] = ...@@ -79,6 +81,7 @@ const char kWrongKeyAsJWK[] =
" \"keys\": [" " \"keys\": ["
" {" " {"
" \"kty\": \"oct\"," " \"kty\": \"oct\","
" \"alg\": \"A128KW\","
" \"kid\": \"AAECAw\"," " \"kid\": \"AAECAw\","
" \"k\": \"7u7u7u7u7u7u7u7u7u7u7g\"" " \"k\": \"7u7u7u7u7u7u7u7u7u7u7g\""
" }" " }"
...@@ -90,6 +93,7 @@ const char kWrongSizedKeyAsJWK[] = ...@@ -90,6 +93,7 @@ const char kWrongSizedKeyAsJWK[] =
" \"keys\": [" " \"keys\": ["
" {" " {"
" \"kty\": \"oct\"," " \"kty\": \"oct\","
" \"alg\": \"A128KW\","
" \"kid\": \"AAECAw\"," " \"kid\": \"AAECAw\","
" \"k\": \"AAECAw\"" " \"k\": \"AAECAw\""
" }" " }"
...@@ -136,6 +140,7 @@ const char kKey2AsJWK[] = ...@@ -136,6 +140,7 @@ const char kKey2AsJWK[] =
" \"keys\": [" " \"keys\": ["
" {" " {"
" \"kty\": \"oct\"," " \"kty\": \"oct\","
" \"alg\": \"A128KW\","
" \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\"," " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
" \"k\": \"FBUWFxgZGhscHR4fICEiIw\"" " \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
" }" " }"
...@@ -713,6 +718,7 @@ TEST_F(AesDecryptorTest, JWKKey) { ...@@ -713,6 +718,7 @@ TEST_F(AesDecryptorTest, JWKKey) {
const std::string kJwkSimple = const std::string kJwkSimple =
"{" "{"
" \"kty\": \"oct\"," " \"kty\": \"oct\","
" \"alg\": \"A128KW\","
" \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\"," " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
" \"k\": \"FBUWFxgZGhscHR4fICEiIw\"" " \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
"}"; "}";
...@@ -724,11 +730,13 @@ TEST_F(AesDecryptorTest, JWKKey) { ...@@ -724,11 +730,13 @@ TEST_F(AesDecryptorTest, JWKKey) {
" \"keys\": [" " \"keys\": ["
" {" " {"
" \"kty\": \"oct\"," " \"kty\": \"oct\","
" \"alg\": \"A128KW\","
" \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\"," " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
" \"k\": \"FBUWFxgZGhscHR4fICEiIw\"" " \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
" }," " },"
" {" " {"
" \"kty\": \"oct\"," " \"kty\": \"oct\","
" \"alg\": \"A128KW\","
" \"kid\": \"JCUmJygpKissLS4vMA\"," " \"kid\": \"JCUmJygpKissLS4vMA\","
" \"k\":\"MTIzNDU2Nzg5Ojs8PT4/QA\"" " \"k\":\"MTIzNDU2Nzg5Ojs8PT4/QA\""
" }" " }"
...@@ -773,6 +781,7 @@ TEST_F(AesDecryptorTest, JWKKey) { ...@@ -773,6 +781,7 @@ TEST_F(AesDecryptorTest, JWKKey) {
" \"keys\": [" " \"keys\": ["
" {" " {"
" \"kty\": \"oct\"," " \"kty\": \"oct\","
" \"alg\": \"A128KW\","
" \"kid\": \"AAECAw\"," " \"kid\": \"AAECAw\","
" \"k\": \"BAUGBwgJCgsMDQ4PEBESEw==\"" " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw==\""
" }" " }"
...@@ -786,6 +795,7 @@ TEST_F(AesDecryptorTest, JWKKey) { ...@@ -786,6 +795,7 @@ TEST_F(AesDecryptorTest, JWKKey) {
" \"keys\": [" " \"keys\": ["
" {" " {"
" \"kty\": \"oct\"," " \"kty\": \"oct\","
" \"alg\": \"A128KW\","
" \"kid\": \"AAECAw==\"," " \"kid\": \"AAECAw==\","
" \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\"" " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
" }" " }"
...@@ -799,6 +809,7 @@ TEST_F(AesDecryptorTest, JWKKey) { ...@@ -799,6 +809,7 @@ TEST_F(AesDecryptorTest, JWKKey) {
" \"keys\": [" " \"keys\": ["
" {" " {"
" \"kty\": \"oct\"," " \"kty\": \"oct\","
" \"alg\": \"A128KW\","
" \"kid\": \"!@#$%^&*()\"," " \"kid\": \"!@#$%^&*()\","
" \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\"" " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
" }" " }"
...@@ -814,6 +825,7 @@ TEST_F(AesDecryptorTest, JWKKey) { ...@@ -814,6 +825,7 @@ TEST_F(AesDecryptorTest, JWKKey) {
" \"keys\": [" " \"keys\": ["
" {" " {"
" \"kty\": \"oct\"," " \"kty\": \"oct\","
" \"alg\": \"A128KW\","
" \"kid\": \"Kiss\"," " \"kid\": \"Kiss\","
" \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\"" " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
" }" " }"
...@@ -827,6 +839,7 @@ TEST_F(AesDecryptorTest, JWKKey) { ...@@ -827,6 +839,7 @@ TEST_F(AesDecryptorTest, JWKKey) {
" \"keys\": [" " \"keys\": ["
" {" " {"
" \"kty\": \"oct\"," " \"kty\": \"oct\","
" \"alg\": \"A128KW\","
" \"kid\": \"\"," " \"kid\": \"\","
" \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\"" " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
" }" " }"
......
...@@ -17,7 +17,9 @@ namespace media { ...@@ -17,7 +17,9 @@ namespace media {
const char kKeysTag[] = "keys"; const char kKeysTag[] = "keys";
const char kKeyTypeTag[] = "kty"; const char kKeyTypeTag[] = "kty";
const char kSymmetricKeyValue[] = "oct"; const char kKeyTypeOct[] = "oct"; // Octet sequence.
const char kAlgTag[] = "alg";
const char kAlgA128KW[] = "A128KW"; // AES key wrap using a 128-bit key.
const char kKeyTag[] = "k"; const char kKeyTag[] = "k";
const char kKeyIdTag[] = "kid"; const char kKeyIdTag[] = "kid";
const char kKeyIdsTag[] = "kids"; const char kKeyIdsTag[] = "kids";
...@@ -44,8 +46,10 @@ static std::string EncodeBase64(const uint8* input, int input_length) { ...@@ -44,8 +46,10 @@ static std::string EncodeBase64(const uint8* input, int input_length) {
// Decodes an unpadded base64 string. Returns empty string on error. // Decodes an unpadded base64 string. Returns empty string on error.
static std::string DecodeBase64(const std::string& encoded_text) { static std::string DecodeBase64(const std::string& encoded_text) {
// EME spec doesn't allow padding characters. // EME spec doesn't allow padding characters.
if (encoded_text.find_first_of(kBase64Padding) != std::string::npos) if (encoded_text.find_first_of(kBase64Padding) != std::string::npos) {
DVLOG(1) << "Padding characters not allowed: " << encoded_text;
return std::string(); return std::string();
}
// Since base::Base64Decode() requires padding characters, add them so length // Since base::Base64Decode() requires padding characters, add them so length
// of |encoded_text| is exactly a multiple of 4. // of |encoded_text| is exactly a multiple of 4.
...@@ -55,8 +59,10 @@ static std::string DecodeBase64(const std::string& encoded_text) { ...@@ -55,8 +59,10 @@ static std::string DecodeBase64(const std::string& encoded_text) {
modified_text.append(4 - num_last_grouping_chars, kBase64Padding); modified_text.append(4 - num_last_grouping_chars, kBase64Padding);
std::string decoded_text; std::string decoded_text;
if (!base::Base64Decode(modified_text, &decoded_text)) if (!base::Base64Decode(modified_text, &decoded_text)) {
DVLOG(1) << "Base64 decoding failed on: " << modified_text;
return std::string(); return std::string();
}
return decoded_text; return decoded_text;
} }
...@@ -69,7 +75,8 @@ std::string GenerateJWKSet(const uint8* key, int key_length, ...@@ -69,7 +75,8 @@ std::string GenerateJWKSet(const uint8* key, int key_length,
// Create the JWK, and wrap it into a JWK Set. // Create the JWK, and wrap it into a JWK Set.
scoped_ptr<base::DictionaryValue> jwk(new base::DictionaryValue()); scoped_ptr<base::DictionaryValue> jwk(new base::DictionaryValue());
jwk->SetString(kKeyTypeTag, kSymmetricKeyValue); jwk->SetString(kKeyTypeTag, kKeyTypeOct);
jwk->SetString(kAlgTag, kAlgA128KW);
jwk->SetString(kKeyTag, key_base64); jwk->SetString(kKeyTag, key_base64);
jwk->SetString(kKeyIdTag, key_id_base64); jwk->SetString(kKeyIdTag, key_id_base64);
scoped_ptr<base::ListValue> list(new base::ListValue()); scoped_ptr<base::ListValue> list(new base::ListValue());
...@@ -88,10 +95,15 @@ std::string GenerateJWKSet(const uint8* key, int key_length, ...@@ -88,10 +95,15 @@ std::string GenerateJWKSet(const uint8* key, int key_length,
// to the id/value pair and returns true on success. // to the id/value pair and returns true on success.
static bool ConvertJwkToKeyPair(const base::DictionaryValue& jwk, static bool ConvertJwkToKeyPair(const base::DictionaryValue& jwk,
KeyIdAndKeyPair* jwk_key) { KeyIdAndKeyPair* jwk_key) {
// Have found a JWK, start by checking that it is a symmetric key.
std::string type; std::string type;
if (!jwk.GetString(kKeyTypeTag, &type) || type != kSymmetricKeyValue) { if (!jwk.GetString(kKeyTypeTag, &type) || type != kKeyTypeOct) {
DVLOG(1) << "JWK is not a symmetric key"; DVLOG(1) << "Missing or invalid '" << kKeyTypeTag << "': " << type;
return false;
}
std::string alg;
if (!jwk.GetString(kAlgTag, &alg) || alg != kAlgA128KW) {
DVLOG(1) << "Missing or invalid '" << kAlgTag << "': " << alg;
return false; return false;
} }
...@@ -128,12 +140,16 @@ static bool ConvertJwkToKeyPair(const base::DictionaryValue& jwk, ...@@ -128,12 +140,16 @@ static bool ConvertJwkToKeyPair(const base::DictionaryValue& jwk,
bool ExtractKeysFromJWKSet(const std::string& jwk_set, bool ExtractKeysFromJWKSet(const std::string& jwk_set,
KeyIdAndKeyPairs* keys, KeyIdAndKeyPairs* keys,
MediaKeys::SessionType* session_type) { MediaKeys::SessionType* session_type) {
if (!base::IsStringASCII(jwk_set)) if (!base::IsStringASCII(jwk_set)) {
DVLOG(1) << "Non ASCII JWK Set: " << jwk_set;
return false; return false;
}
scoped_ptr<base::Value> root(base::JSONReader().ReadToValue(jwk_set)); scoped_ptr<base::Value> root(base::JSONReader().ReadToValue(jwk_set));
if (!root.get() || root->GetType() != base::Value::TYPE_DICTIONARY) if (!root.get() || root->GetType() != base::Value::TYPE_DICTIONARY) {
DVLOG(1) << "Not valid JSON: " << jwk_set << ", root: " << root.get();
return false; return false;
}
// Locate the set from the dictionary. // Locate the set from the dictionary.
base::DictionaryValue* dictionary = base::DictionaryValue* dictionary =
...@@ -221,12 +237,16 @@ bool ExtractFirstKeyIdFromLicenseRequest(const std::vector<uint8>& license, ...@@ -221,12 +237,16 @@ bool ExtractFirstKeyIdFromLicenseRequest(const std::vector<uint8>& license,
const std::string license_as_str( const std::string license_as_str(
reinterpret_cast<const char*>(!license.empty() ? &license[0] : NULL), reinterpret_cast<const char*>(!license.empty() ? &license[0] : NULL),
license.size()); license.size());
if (!base::IsStringASCII(license_as_str)) if (!base::IsStringASCII(license_as_str)) {
DVLOG(1) << "Non ASCII license: " << license_as_str;
return false; return false;
}
scoped_ptr<base::Value> root(base::JSONReader().ReadToValue(license_as_str)); scoped_ptr<base::Value> root(base::JSONReader().ReadToValue(license_as_str));
if (!root.get() || root->GetType() != base::Value::TYPE_DICTIONARY) if (!root.get() || root->GetType() != base::Value::TYPE_DICTIONARY) {
DVLOG(1) << "Not valid JSON: " << license_as_str;
return false; return false;
}
// Locate the set from the dictionary. // Locate the set from the dictionary.
base::DictionaryValue* dictionary = base::DictionaryValue* dictionary =
......
This diff is collapsed.
...@@ -56,7 +56,7 @@ Utils.createJWKData = function(keyId, key) { ...@@ -56,7 +56,7 @@ Utils.createJWKData = function(keyId, key) {
// Creates a JWK from raw key ID and key. // Creates a JWK from raw key ID and key.
function createJWK(keyId, key) { function createJWK(keyId, key) {
var jwk = '{"kty":"oct","kid":"'; var jwk = '{"kty":"oct","alg":"A128KW","kid":"';
jwk += base64Encode(keyId); jwk += base64Encode(keyId);
jwk += '","k":"'; jwk += '","k":"';
jwk += base64Encode(key); jwk += base64Encode(key);
......
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