Home | History | Annotate | Download | only in cdm
      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 base::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 (!base::IsStringASCII(jwk_set))
    125     return false;
    126 
    127   scoped_ptr<base::Value> root(base::JSONReader().ReadToValue(jwk_set));
    128   if (!root.get() || root->GetType() != base::Value::TYPE_DICTIONARY)
    129     return false;
    130 
    131   // Locate the set from the dictionary.
    132   base::DictionaryValue* dictionary =
    133       static_cast<base::DictionaryValue*>(root.get());
    134   base::ListValue* list_val = NULL;
    135   if (!dictionary->GetList(kKeysTag, &list_val)) {
    136     DVLOG(1) << "Missing '" << kKeysTag
    137              << "' parameter or not a list in JWK Set";
    138     return false;
    139   }
    140 
    141   // Create a local list of keys, so that |jwk_keys| only gets updated on
    142   // success.
    143   KeyIdAndKeyPairs local_keys;
    144   for (size_t i = 0; i < list_val->GetSize(); ++i) {
    145     base::DictionaryValue* jwk = NULL;
    146     if (!list_val->GetDictionary(i, &jwk)) {
    147       DVLOG(1) << "Unable to access '" << kKeysTag << "'[" << i
    148                << "] in JWK Set";
    149       return false;
    150     }
    151     KeyIdAndKeyPair key_pair;
    152     if (!ConvertJwkToKeyPair(*jwk, &key_pair)) {
    153       DVLOG(1) << "Error from '" << kKeysTag << "'[" << i << "]";
    154       return false;
    155     }
    156     local_keys.push_back(key_pair);
    157   }
    158 
    159   // Successfully processed all JWKs in the set.
    160   keys->swap(local_keys);
    161   return true;
    162 }
    163 
    164 }  // namespace media
    165