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