1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "media/cdm/json_web_key.h" 6 7 #include "base/base64.h" 8 #include "base/json/json_reader.h" 9 #include "base/json/json_string_value_serializer.h" 10 #include "base/logging.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/strings/string_util.h" 13 #include "base/values.h" 14 15 namespace media { 16 17 const char kKeysTag[] = "keys"; 18 const char kKeyTypeTag[] = "kty"; 19 const char kSymmetricKeyValue[] = "oct"; 20 const char kKeyTag[] = "k"; 21 const char kKeyIdTag[] = "kid"; 22 const char kBase64Padding = '='; 23 24 // Encodes |input| into a base64 string without padding. 25 static std::string EncodeBase64(const uint8* input, int input_length) { 26 std::string encoded_text; 27 base::Base64Encode( 28 std::string(reinterpret_cast<const char*>(input), input_length), 29 &encoded_text); 30 31 // Remove any padding characters added by Base64Encode(). 32 size_t found = encoded_text.find_last_not_of(kBase64Padding); 33 if (found != std::string::npos) 34 encoded_text.erase(found + 1); 35 36 return encoded_text; 37 } 38 39 // Decodes an unpadded base64 string. Returns empty string on error. 40 static std::string DecodeBase64(const std::string& encoded_text) { 41 // EME spec doesn't allow padding characters. 42 if (encoded_text.find_first_of(kBase64Padding) != std::string::npos) 43 return std::string(); 44 45 // Since base::Base64Decode() requires padding characters, add them so length 46 // of |encoded_text| is exactly a multiple of 4. 47 size_t num_last_grouping_chars = encoded_text.length() % 4; 48 std::string modified_text = encoded_text; 49 if (num_last_grouping_chars > 0) 50 modified_text.append(4 - num_last_grouping_chars, kBase64Padding); 51 52 std::string decoded_text; 53 if (!base::Base64Decode(modified_text, &decoded_text)) 54 return std::string(); 55 56 return decoded_text; 57 } 58 59 std::string GenerateJWKSet(const uint8* key, int key_length, 60 const uint8* key_id, int key_id_length) { 61 // Both |key| and |key_id| need to be base64 encoded strings in the JWK. 62 std::string key_base64 = EncodeBase64(key, key_length); 63 std::string key_id_base64 = EncodeBase64(key_id, key_id_length); 64 65 // Create the JWK, and wrap it into a JWK Set. 66 scoped_ptr<base::DictionaryValue> jwk(new base::DictionaryValue()); 67 jwk->SetString(kKeyTypeTag, kSymmetricKeyValue); 68 jwk->SetString(kKeyTag, key_base64); 69 jwk->SetString(kKeyIdTag, key_id_base64); 70 scoped_ptr<base::ListValue> list(new base::ListValue()); 71 list->Append(jwk.release()); 72 base::DictionaryValue jwk_set; 73 jwk_set.Set(kKeysTag, list.release()); 74 75 // Finally serialize |jwk_set| into a string and return it. 76 std::string serialized_jwk; 77 JSONStringValueSerializer serializer(&serialized_jwk); 78 serializer.Serialize(jwk_set); 79 return serialized_jwk; 80 } 81 82 // Processes a JSON Web Key to extract the key id and key value. Sets |jwk_key| 83 // to the id/value pair and returns true on success. 84 static bool ConvertJwkToKeyPair(const DictionaryValue& jwk, 85 KeyIdAndKeyPair* jwk_key) { 86 // Have found a JWK, start by checking that it is a symmetric key. 87 std::string type; 88 if (!jwk.GetString(kKeyTypeTag, &type) || type != kSymmetricKeyValue) { 89 DVLOG(1) << "JWK is not a symmetric key"; 90 return false; 91 } 92 93 // Get the key id and actual key parameters. 94 std::string encoded_key_id; 95 std::string encoded_key; 96 if (!jwk.GetString(kKeyIdTag, &encoded_key_id)) { 97 DVLOG(1) << "Missing '" << kKeyIdTag << "' parameter"; 98 return false; 99 } 100 if (!jwk.GetString(kKeyTag, &encoded_key)) { 101 DVLOG(1) << "Missing '" << kKeyTag << "' parameter"; 102 return false; 103 } 104 105 // Key ID and key are base64-encoded strings, so decode them. 106 std::string raw_key_id = DecodeBase64(encoded_key_id); 107 if (raw_key_id.empty()) { 108 DVLOG(1) << "Invalid '" << kKeyIdTag << "' value: " << encoded_key_id; 109 return false; 110 } 111 112 std::string raw_key = DecodeBase64(encoded_key); 113 if (raw_key.empty()) { 114 DVLOG(1) << "Invalid '" << kKeyTag << "' value: " << encoded_key; 115 return false; 116 } 117 118 // Add the decoded key ID and the decoded key to the list. 119 *jwk_key = std::make_pair(raw_key_id, raw_key); 120 return true; 121 } 122 123 bool ExtractKeysFromJWKSet(const std::string& jwk_set, KeyIdAndKeyPairs* keys) { 124 if (!IsStringASCII(jwk_set)) 125 return false; 126 127 scoped_ptr<Value> root(base::JSONReader().ReadToValue(jwk_set)); 128 if (!root.get() || root->GetType() != Value::TYPE_DICTIONARY) 129 return false; 130 131 // Locate the set from the dictionary. 132 DictionaryValue* dictionary = static_cast<DictionaryValue*>(root.get()); 133 ListValue* list_val = NULL; 134 if (!dictionary->GetList(kKeysTag, &list_val)) { 135 DVLOG(1) << "Missing '" << kKeysTag 136 << "' parameter or not a list in JWK Set"; 137 return false; 138 } 139 140 // Create a local list of keys, so that |jwk_keys| only gets updated on 141 // success. 142 KeyIdAndKeyPairs local_keys; 143 for (size_t i = 0; i < list_val->GetSize(); ++i) { 144 DictionaryValue* jwk = NULL; 145 if (!list_val->GetDictionary(i, &jwk)) { 146 DVLOG(1) << "Unable to access '" << kKeysTag << "'[" << i 147 << "] in JWK Set"; 148 return false; 149 } 150 KeyIdAndKeyPair key_pair; 151 if (!ConvertJwkToKeyPair(*jwk, &key_pair)) { 152 DVLOG(1) << "Error from '" << kKeysTag << "'[" << i << "]"; 153 return false; 154 } 155 local_keys.push_back(key_pair); 156 } 157 158 // Successfully processed all JWKs in the set. 159 keys->swap(local_keys); 160 return true; 161 } 162 163 } // namespace media 164