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