Home | History | Annotate | Download | only in webcrypto
      1 // Copyright 2014 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 "jwk.h"
      6 
      7 #include <algorithm>
      8 #include <functional>
      9 #include <map>
     10 
     11 #include "base/json/json_reader.h"
     12 #include "base/json/json_writer.h"
     13 #include "base/lazy_instance.h"
     14 #include "base/strings/string_piece.h"
     15 #include "base/strings/stringprintf.h"
     16 #include "content/child/webcrypto/crypto_data.h"
     17 #include "content/child/webcrypto/platform_crypto.h"
     18 #include "content/child/webcrypto/shared_crypto.h"
     19 #include "content/child/webcrypto/status.h"
     20 #include "content/child/webcrypto/webcrypto_util.h"
     21 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
     22 
     23 // JSON Web Key Format (JWK)
     24 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21
     25 //
     26 // A JWK is a simple JSON dictionary with the following entries
     27 // - "kty" (Key Type) Parameter, REQUIRED
     28 // - <kty-specific parameters, see below>, REQUIRED
     29 // - "use" (Key Use) Parameter, OPTIONAL
     30 // - "key_ops" (Key Operations) Parameter, OPTIONAL
     31 // - "alg" (Algorithm) Parameter, OPTIONAL
     32 // - "ext" (Key Exportability), OPTIONAL
     33 // (all other entries are ignored)
     34 //
     35 // OPTIONAL here means that this code does not require the entry to be present
     36 // in the incoming JWK, because the method input parameters contain similar
     37 // information. If the optional JWK entry is present, it will be validated
     38 // against the corresponding input parameter for consistency and combined with
     39 // it according to rules defined below.
     40 //
     41 // Input 'key_data' contains the JWK. To build a Web Crypto Key, the JWK
     42 // values are parsed out and combined with the method input parameters to
     43 // build a Web Crypto Key:
     44 // Web Crypto Key type            <-- (deduced)
     45 // Web Crypto Key extractable     <-- JWK ext + input extractable
     46 // Web Crypto Key algorithm       <-- JWK alg + input algorithm
     47 // Web Crypto Key keyUsage        <-- JWK use, key_ops + input usage_mask
     48 // Web Crypto Key keying material <-- kty-specific parameters
     49 //
     50 // Values for each JWK entry are case-sensitive and defined in
     51 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18.
     52 // Note that not all values specified by JOSE are handled by this code. Only
     53 // handled values are listed.
     54 // - kty (Key Type)
     55 //   +-------+--------------------------------------------------------------+
     56 //   | "RSA" | RSA [RFC3447]                                                |
     57 //   | "oct" | Octet sequence (used to represent symmetric keys)            |
     58 //   +-------+--------------------------------------------------------------+
     59 //
     60 // - key_ops (Key Use Details)
     61 //   The key_ops field is an array that contains one or more strings from
     62 //   the table below, and describes the operations for which this key may be
     63 //   used.
     64 //   +-------+--------------------------------------------------------------+
     65 //   | "encrypt"    | encrypt operations                                    |
     66 //   | "decrypt"    | decrypt operations                                    |
     67 //   | "sign"       | sign (MAC) operations                                 |
     68 //   | "verify"     | verify (MAC) operations                               |
     69 //   | "wrapKey"    | key wrap                                              |
     70 //   | "unwrapKey"  | key unwrap                                            |
     71 //   | "deriveKey"  | key derivation                                        |
     72 //   | "deriveBits" | key derivation                                        |
     73 //   +-------+--------------------------------------------------------------+
     74 //
     75 // - use (Key Use)
     76 //   The use field contains a single entry from the table below.
     77 //   +-------+--------------------------------------------------------------+
     78 //   | "sig"     | equivalent to key_ops of [sign, verify]                  |
     79 //   | "enc"     | equivalent to key_ops of [encrypt, decrypt, wrapKey,     |
     80 //   |           | unwrapKey, deriveKey, deriveBits]                        |
     81 //   +-------+--------------------------------------------------------------+
     82 //
     83 //   NOTE: If both "use" and "key_ops" JWK members are present, the usages
     84 //   specified by them MUST be consistent.  In particular, the "use" value
     85 //   "sig" corresponds to "sign" and/or "verify".  The "use" value "enc"
     86 //   corresponds to all other values defined above.  If "key_ops" values
     87 //   corresponding to both "sig" and "enc" "use" values are present, the "use"
     88 //   member SHOULD NOT be present, and if present, its value MUST NOT be
     89 //   either "sig" or "enc".
     90 //
     91 // - ext (Key Exportability)
     92 //   +-------+--------------------------------------------------------------+
     93 //   | true  | Key may be exported from the trusted environment             |
     94 //   | false | Key cannot exit the trusted environment                      |
     95 //   +-------+--------------------------------------------------------------+
     96 //
     97 // - alg (Algorithm)
     98 //   See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18
     99 //   +--------------+-------------------------------------------------------+
    100 //   | Digital Signature or MAC Algorithm                                   |
    101 //   +--------------+-------------------------------------------------------+
    102 //   | "HS1"        | HMAC using SHA-1 hash algorithm                       |
    103 //   | "HS256"      | HMAC using SHA-256 hash algorithm                     |
    104 //   | "HS384"      | HMAC using SHA-384 hash algorithm                     |
    105 //   | "HS512"      | HMAC using SHA-512 hash algorithm                     |
    106 //   | "RS1"        | RSASSA using SHA-1 hash algorithm
    107 //   | "RS256"      | RSASSA using SHA-256 hash algorithm                   |
    108 //   | "RS384"      | RSASSA using SHA-384 hash algorithm                   |
    109 //   | "RS512"      | RSASSA using SHA-512 hash algorithm                   |
    110 //   +--------------+-------------------------------------------------------|
    111 //   | Key Management Algorithm                                             |
    112 //   +--------------+-------------------------------------------------------+
    113 //   | "RSA-OAEP"   | RSAES using Optimal Asymmetric Encryption Padding     |
    114 //   |              | (OAEP) [RFC3447], with the default parameters         |
    115 //   |              | specified by RFC3447 in Section A.2.1                 |
    116 //   | "A128KW"     | Advanced Encryption Standard (AES) Key Wrap Algorithm |
    117 //   |              | [RFC3394] using 128 bit keys                          |
    118 //   | "A192KW"     | AES Key Wrap Algorithm using 192 bit keys             |
    119 //   | "A256KW"     | AES Key Wrap Algorithm using 256 bit keys             |
    120 //   | "A128GCM"    | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using |
    121 //   |              | 128 bit keys                                          |
    122 //   | "A192GCM"    | AES GCM using 192 bit keys                            |
    123 //   | "A256GCM"    | AES GCM using 256 bit keys                            |
    124 //   | "A128CBC"    | AES in Cipher Block Chaining Mode (CBC) with PKCS #5  |
    125 //   |              | padding [NIST.800-38A]                                |
    126 //   | "A192CBC"    | AES CBC using 192 bit keys                            |
    127 //   | "A256CBC"    | AES CBC using 256 bit keys                            |
    128 //   +--------------+-------------------------------------------------------+
    129 //
    130 // kty-specific parameters
    131 // The value of kty determines the type and content of the keying material
    132 // carried in the JWK to be imported.
    133 // // - kty == "oct" (symmetric or other raw key)
    134 //   +-------+--------------------------------------------------------------+
    135 //   | "k"   | Contains the value of the symmetric (or other single-valued) |
    136 //   |       | key.  It is represented as the base64url encoding of the     |
    137 //   |       | octet sequence containing the key value.                     |
    138 //   +-------+--------------------------------------------------------------+
    139 // - kty == "RSA" (RSA public key)
    140 //   +-------+--------------------------------------------------------------+
    141 //   | "n"   | Contains the modulus value for the RSA public key.  It is    |
    142 //   |       | represented as the base64url encoding of the value's         |
    143 //   |       | unsigned big endian representation as an octet sequence.     |
    144 //   +-------+--------------------------------------------------------------+
    145 //   | "e"   | Contains the exponent value for the RSA public key.  It is   |
    146 //   |       | represented as the base64url encoding of the value's         |
    147 //   |       | unsigned big endian representation as an octet sequence.     |
    148 //   +-------+--------------------------------------------------------------+
    149 // - If key == "RSA" and the "d" parameter is present then it is a private key.
    150 //   All the parameters above for public keys apply, as well as the following.
    151 //   (Note that except for "d", all of these are optional):
    152 //   +-------+--------------------------------------------------------------+
    153 //   | "d"   | Contains the private exponent value for the RSA private key. |
    154 //   |       | It is represented as the base64url encoding of the value's   |
    155 //   |       | unsigned big endian representation as an octet sequence.     |
    156 //   +-------+--------------------------------------------------------------+
    157 //   | "p"   | Contains the first prime factor value for the RSA private    |
    158 //   |       | key.  It is represented as the base64url encoding of the     |
    159 //   |       | value's                                                      |
    160 //   |       | unsigned big endian representation as an octet sequence.     |
    161 //   +-------+--------------------------------------------------------------+
    162 //   | "q"   | Contains the second prime factor value for the RSA private   |
    163 //   |       | key.  It is represented as the base64url encoding of the     |
    164 //   |       | value's unsigned big endian representation as an octet       |
    165 //   |       | sequence.                                                    |
    166 //   +-------+--------------------------------------------------------------+
    167 //   | "dp"  | Contains the first factor CRT exponent value for the RSA     |
    168 //   |       | private key.  It is represented as the base64url encoding of |
    169 //   |       | the value's unsigned big endian representation as an octet   |
    170 //   |       | sequence.                                                    |
    171 //   +-------+--------------------------------------------------------------+
    172 //   | "dq"  | Contains the second factor CRT exponent value for the RSA    |
    173 //   |       | private key.  It is represented as the base64url encoding of |
    174 //   |       | the value's unsigned big endian representation as an octet   |
    175 //   |       | sequence.                                                    |
    176 //   +-------+--------------------------------------------------------------+
    177 //   | "dq"  | Contains the first CRT coefficient value for the RSA private |
    178 //   |       | key.  It is represented as the base64url encoding of the     |
    179 //   |       | value's unsigned big endian representation as an octet       |
    180 //   |       | sequence.                                                    |
    181 //   +-------+--------------------------------------------------------------+
    182 //
    183 // Consistency and conflict resolution
    184 // The 'algorithm', 'extractable', and 'usage_mask' input parameters
    185 // may be different than the corresponding values inside the JWK. The Web
    186 // Crypto spec says that if a JWK value is present but is inconsistent with
    187 // the input value, it is an error and the operation must fail. If no
    188 // inconsistency is found then the input parameters are used.
    189 //
    190 // algorithm
    191 //   If the JWK algorithm is provided, it must match the web crypto input
    192 //   algorithm (both the algorithm ID and inner hash if applicable).
    193 //
    194 // extractable
    195 //   If the JWK ext field is true but the input parameter is false, make the
    196 //   Web Crypto Key non-extractable. Conversely, if the JWK ext field is
    197 //   false but the input parameter is true, it is an inconsistency. If both
    198 //   are true or both are false, use that value.
    199 //
    200 // usage_mask
    201 //   The input usage_mask must be a strict subset of the interpreted JWK use
    202 //   value, else it is judged inconsistent. In all cases the input usage_mask
    203 //   is used as the final usage_mask.
    204 //
    205 
    206 namespace content {
    207 
    208 namespace webcrypto {
    209 
    210 namespace {
    211 
    212 // Creates an RSASSA-PKCS1-v1_5 algorithm. It is an error to call this with a
    213 // hash_id that is not a SHA*.
    214 blink::WebCryptoAlgorithm CreateRsaSsaImportAlgorithm(
    215     blink::WebCryptoAlgorithmId hash_id) {
    216   return CreateRsaHashedImportAlgorithm(
    217       blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, hash_id);
    218 }
    219 
    220 // Creates an RSA-OAEP algorithm. It is an error to call this with a hash_id
    221 // that is not a SHA*.
    222 blink::WebCryptoAlgorithm CreateRsaOaepImportAlgorithm(
    223     blink::WebCryptoAlgorithmId hash_id) {
    224   return CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep,
    225                                         hash_id);
    226 }
    227 
    228 // Web Crypto equivalent usage mask for JWK 'use' = 'enc'.
    229 const blink::WebCryptoKeyUsageMask kJwkEncUsage =
    230     blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt |
    231     blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey |
    232     blink::WebCryptoKeyUsageDeriveKey | blink::WebCryptoKeyUsageDeriveBits;
    233 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'.
    234 const blink::WebCryptoKeyUsageMask kJwkSigUsage =
    235     blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;
    236 
    237 typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)();
    238 
    239 class JwkAlgorithmInfo {
    240  public:
    241   JwkAlgorithmInfo()
    242       : creation_func_(NULL),
    243         required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {}
    244 
    245   explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func)
    246       : creation_func_(algorithm_creation_func),
    247         required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {}
    248 
    249   JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func,
    250                    unsigned int required_key_length_bits)
    251       : creation_func_(algorithm_creation_func),
    252         required_key_length_bytes_(required_key_length_bits / 8) {
    253     DCHECK_EQ(0u, required_key_length_bits % 8);
    254   }
    255 
    256   bool CreateImportAlgorithm(blink::WebCryptoAlgorithm* algorithm) const {
    257     *algorithm = creation_func_();
    258     return !algorithm->isNull();
    259   }
    260 
    261   bool IsInvalidKeyByteLength(size_t byte_length) const {
    262     if (required_key_length_bytes_ == NO_KEY_SIZE_REQUIREMENT)
    263       return false;
    264     return required_key_length_bytes_ != byte_length;
    265   }
    266 
    267  private:
    268   enum { NO_KEY_SIZE_REQUIREMENT = UINT_MAX };
    269 
    270   AlgorithmCreationFunc creation_func_;
    271 
    272   // The expected key size for the algorithm or NO_KEY_SIZE_REQUIREMENT.
    273   unsigned int required_key_length_bytes_;
    274 };
    275 
    276 typedef std::map<std::string, JwkAlgorithmInfo> JwkAlgorithmInfoMap;
    277 
    278 class JwkAlgorithmRegistry {
    279  public:
    280   JwkAlgorithmRegistry() {
    281     // TODO(eroman):
    282     // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-20
    283     // says HMAC with SHA-2 should have a key size at least as large as the
    284     // hash output.
    285     alg_to_info_["HS1"] =
    286         JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm,
    287                                           blink::WebCryptoAlgorithmIdSha1>);
    288     alg_to_info_["HS256"] =
    289         JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm,
    290                                           blink::WebCryptoAlgorithmIdSha256>);
    291     alg_to_info_["HS384"] =
    292         JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm,
    293                                           blink::WebCryptoAlgorithmIdSha384>);
    294     alg_to_info_["HS512"] =
    295         JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm,
    296                                           blink::WebCryptoAlgorithmIdSha512>);
    297     alg_to_info_["RS1"] =
    298         JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm,
    299                                           blink::WebCryptoAlgorithmIdSha1>);
    300     alg_to_info_["RS256"] =
    301         JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm,
    302                                           blink::WebCryptoAlgorithmIdSha256>);
    303     alg_to_info_["RS384"] =
    304         JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm,
    305                                           blink::WebCryptoAlgorithmIdSha384>);
    306     alg_to_info_["RS512"] =
    307         JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm,
    308                                           blink::WebCryptoAlgorithmIdSha512>);
    309     alg_to_info_["RSA-OAEP"] =
    310         JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm,
    311                                           blink::WebCryptoAlgorithmIdSha1>);
    312     alg_to_info_["RSA-OAEP-256"] =
    313         JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm,
    314                                           blink::WebCryptoAlgorithmIdSha256>);
    315     alg_to_info_["RSA-OAEP-384"] =
    316         JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm,
    317                                           blink::WebCryptoAlgorithmIdSha384>);
    318     alg_to_info_["RSA-OAEP-512"] =
    319         JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm,
    320                                           blink::WebCryptoAlgorithmIdSha512>);
    321     alg_to_info_["A128KW"] = JwkAlgorithmInfo(
    322         &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>,
    323         128);
    324     alg_to_info_["A192KW"] = JwkAlgorithmInfo(
    325         &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>,
    326         192);
    327     alg_to_info_["A256KW"] = JwkAlgorithmInfo(
    328         &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>,
    329         256);
    330     alg_to_info_["A128GCM"] = JwkAlgorithmInfo(
    331         &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>,
    332         128);
    333     alg_to_info_["A192GCM"] = JwkAlgorithmInfo(
    334         &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>,
    335         192);
    336     alg_to_info_["A256GCM"] = JwkAlgorithmInfo(
    337         &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>,
    338         256);
    339     alg_to_info_["A128CBC"] = JwkAlgorithmInfo(
    340         &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>,
    341         128);
    342     alg_to_info_["A192CBC"] = JwkAlgorithmInfo(
    343         &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>,
    344         192);
    345     alg_to_info_["A256CBC"] = JwkAlgorithmInfo(
    346         &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>,
    347         256);
    348   }
    349 
    350   // Returns NULL if the algorithm name was not registered.
    351   const JwkAlgorithmInfo* GetAlgorithmInfo(const std::string& jwk_alg) const {
    352     const JwkAlgorithmInfoMap::const_iterator pos = alg_to_info_.find(jwk_alg);
    353     if (pos == alg_to_info_.end())
    354       return NULL;
    355     return &pos->second;
    356   }
    357 
    358  private:
    359   // Binds a WebCryptoAlgorithmId value to a compatible factory function.
    360   typedef blink::WebCryptoAlgorithm (*FuncWithWebCryptoAlgIdArg)(
    361       blink::WebCryptoAlgorithmId);
    362   template <FuncWithWebCryptoAlgIdArg func,
    363             blink::WebCryptoAlgorithmId algorithm_id>
    364   static blink::WebCryptoAlgorithm BindAlgorithmId() {
    365     return func(algorithm_id);
    366   }
    367 
    368   JwkAlgorithmInfoMap alg_to_info_;
    369 };
    370 
    371 base::LazyInstance<JwkAlgorithmRegistry> jwk_alg_registry =
    372     LAZY_INSTANCE_INITIALIZER;
    373 
    374 bool ImportAlgorithmsConsistent(const blink::WebCryptoAlgorithm& alg1,
    375                                 const blink::WebCryptoAlgorithm& alg2) {
    376   DCHECK(!alg1.isNull());
    377   DCHECK(!alg2.isNull());
    378   if (alg1.id() != alg2.id())
    379     return false;
    380   if (alg1.paramsType() != alg2.paramsType())
    381     return false;
    382   switch (alg1.paramsType()) {
    383     case blink::WebCryptoAlgorithmParamsTypeNone:
    384       return true;
    385     case blink::WebCryptoAlgorithmParamsTypeRsaHashedImportParams:
    386       return ImportAlgorithmsConsistent(alg1.rsaHashedImportParams()->hash(),
    387                                         alg2.rsaHashedImportParams()->hash());
    388     case blink::WebCryptoAlgorithmParamsTypeHmacImportParams:
    389       return ImportAlgorithmsConsistent(alg1.hmacImportParams()->hash(),
    390                                         alg2.hmacImportParams()->hash());
    391     default:
    392       return false;
    393   }
    394 }
    395 
    396 // Extracts the required string property with key |path| from |dict| and saves
    397 // the result to |*result|. If the property does not exist or is not a string,
    398 // returns an error.
    399 Status GetJwkString(base::DictionaryValue* dict,
    400                     const std::string& path,
    401                     std::string* result) {
    402   base::Value* value = NULL;
    403   if (!dict->Get(path, &value))
    404     return Status::ErrorJwkPropertyMissing(path);
    405   if (!value->GetAsString(result))
    406     return Status::ErrorJwkPropertyWrongType(path, "string");
    407   return Status::Success();
    408 }
    409 
    410 // Extracts the optional string property with key |path| from |dict| and saves
    411 // the result to |*result| if it was found. If the property exists and is not a
    412 // string, returns an error. Otherwise returns success, and sets
    413 // |*property_exists| if it was found.
    414 Status GetOptionalJwkString(base::DictionaryValue* dict,
    415                             const std::string& path,
    416                             std::string* result,
    417                             bool* property_exists) {
    418   *property_exists = false;
    419   base::Value* value = NULL;
    420   if (!dict->Get(path, &value))
    421     return Status::Success();
    422 
    423   if (!value->GetAsString(result))
    424     return Status::ErrorJwkPropertyWrongType(path, "string");
    425 
    426   *property_exists = true;
    427   return Status::Success();
    428 }
    429 
    430 // Extracts the optional array property with key |path| from |dict| and saves
    431 // the result to |*result| if it was found. If the property exists and is not an
    432 // array, returns an error. Otherwise returns success, and sets
    433 // |*property_exists| if it was found. Note that |*result| is owned by |dict|.
    434 Status GetOptionalJwkList(base::DictionaryValue* dict,
    435                           const std::string& path,
    436                           base::ListValue** result,
    437                           bool* property_exists) {
    438   *property_exists = false;
    439   base::Value* value = NULL;
    440   if (!dict->Get(path, &value))
    441     return Status::Success();
    442 
    443   if (!value->GetAsList(result))
    444     return Status::ErrorJwkPropertyWrongType(path, "list");
    445 
    446   *property_exists = true;
    447   return Status::Success();
    448 }
    449 
    450 // Extracts the required string property with key |path| from |dict| and saves
    451 // the base64url-decoded bytes to |*result|. If the property does not exist or
    452 // is not a string, or could not be base64url-decoded, returns an error.
    453 Status GetJwkBytes(base::DictionaryValue* dict,
    454                    const std::string& path,
    455                    std::string* result) {
    456   std::string base64_string;
    457   Status status = GetJwkString(dict, path, &base64_string);
    458   if (status.IsError())
    459     return status;
    460 
    461   if (!Base64DecodeUrlSafe(base64_string, result))
    462     return Status::ErrorJwkBase64Decode(path);
    463 
    464   return Status::Success();
    465 }
    466 
    467 // Extracts the optional string property with key |path| from |dict| and saves
    468 // the base64url-decoded bytes to |*result|. If the property exist and is not a
    469 // string, or could not be base64url-decoded, returns an error. In the case
    470 // where the property does not exist, |result| is guaranteed to be empty.
    471 Status GetOptionalJwkBytes(base::DictionaryValue* dict,
    472                            const std::string& path,
    473                            std::string* result,
    474                            bool* property_exists) {
    475   std::string base64_string;
    476   Status status =
    477       GetOptionalJwkString(dict, path, &base64_string, property_exists);
    478   if (status.IsError())
    479     return status;
    480 
    481   if (!*property_exists) {
    482     result->clear();
    483     return Status::Success();
    484   }
    485 
    486   if (!Base64DecodeUrlSafe(base64_string, result))
    487     return Status::ErrorJwkBase64Decode(path);
    488 
    489   return Status::Success();
    490 }
    491 
    492 // Extracts the optional boolean property with key |path| from |dict| and saves
    493 // the result to |*result| if it was found. If the property exists and is not a
    494 // boolean, returns an error. Otherwise returns success, and sets
    495 // |*property_exists| if it was found.
    496 Status GetOptionalJwkBool(base::DictionaryValue* dict,
    497                           const std::string& path,
    498                           bool* result,
    499                           bool* property_exists) {
    500   *property_exists = false;
    501   base::Value* value = NULL;
    502   if (!dict->Get(path, &value))
    503     return Status::Success();
    504 
    505   if (!value->GetAsBoolean(result))
    506     return Status::ErrorJwkPropertyWrongType(path, "boolean");
    507 
    508   *property_exists = true;
    509   return Status::Success();
    510 }
    511 
    512 // Writes a secret/symmetric key to a JWK dictionary.
    513 void WriteSecretKey(const std::vector<uint8>& raw_key,
    514                     base::DictionaryValue* jwk_dict) {
    515   DCHECK(jwk_dict);
    516   jwk_dict->SetString("kty", "oct");
    517   // For a secret/symmetric key, the only extra JWK field is 'k', containing the
    518   // base64url encoding of the raw key.
    519   const base::StringPiece key_str(
    520       reinterpret_cast<const char*>(Uint8VectorStart(raw_key)), raw_key.size());
    521   jwk_dict->SetString("k", Base64EncodeUrlSafe(key_str));
    522 }
    523 
    524 // Writes an RSA public key to a JWK dictionary
    525 void WriteRsaPublicKey(const std::vector<uint8>& modulus,
    526                        const std::vector<uint8>& public_exponent,
    527                        base::DictionaryValue* jwk_dict) {
    528   DCHECK(jwk_dict);
    529   DCHECK(modulus.size());
    530   DCHECK(public_exponent.size());
    531   jwk_dict->SetString("kty", "RSA");
    532   jwk_dict->SetString("n", Base64EncodeUrlSafe(modulus));
    533   jwk_dict->SetString("e", Base64EncodeUrlSafe(public_exponent));
    534 }
    535 
    536 // Writes an RSA private key to a JWK dictionary
    537 Status ExportRsaPrivateKeyJwk(const blink::WebCryptoKey& key,
    538                               base::DictionaryValue* jwk_dict) {
    539   platform::PrivateKey* private_key;
    540   Status status = ToPlatformPrivateKey(key, &private_key);
    541   if (status.IsError())
    542     return status;
    543 
    544   // TODO(eroman): Copying the key properties to temporary vectors is
    545   // inefficient. Once there aren't two implementations of platform_crypto this
    546   // and other code will be easier to streamline.
    547   std::vector<uint8> modulus;
    548   std::vector<uint8> public_exponent;
    549   std::vector<uint8> private_exponent;
    550   std::vector<uint8> prime1;
    551   std::vector<uint8> prime2;
    552   std::vector<uint8> exponent1;
    553   std::vector<uint8> exponent2;
    554   std::vector<uint8> coefficient;
    555 
    556   status = platform::ExportRsaPrivateKey(private_key,
    557                                          &modulus,
    558                                          &public_exponent,
    559                                          &private_exponent,
    560                                          &prime1,
    561                                          &prime2,
    562                                          &exponent1,
    563                                          &exponent2,
    564                                          &coefficient);
    565   if (status.IsError())
    566     return status;
    567 
    568   jwk_dict->SetString("kty", "RSA");
    569   jwk_dict->SetString("n", Base64EncodeUrlSafe(modulus));
    570   jwk_dict->SetString("e", Base64EncodeUrlSafe(public_exponent));
    571   jwk_dict->SetString("d", Base64EncodeUrlSafe(private_exponent));
    572   // Although these are "optional" in the JWA, WebCrypto spec requires them to
    573   // be emitted.
    574   jwk_dict->SetString("p", Base64EncodeUrlSafe(prime1));
    575   jwk_dict->SetString("q", Base64EncodeUrlSafe(prime2));
    576   jwk_dict->SetString("dp", Base64EncodeUrlSafe(exponent1));
    577   jwk_dict->SetString("dq", Base64EncodeUrlSafe(exponent2));
    578   jwk_dict->SetString("qi", Base64EncodeUrlSafe(coefficient));
    579 
    580   return Status::Success();
    581 }
    582 
    583 // Writes a Web Crypto usage mask to a JWK dictionary.
    584 void WriteKeyOps(blink::WebCryptoKeyUsageMask key_usages,
    585                  base::DictionaryValue* jwk_dict) {
    586   jwk_dict->Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(key_usages));
    587 }
    588 
    589 // Writes a Web Crypto extractable value to a JWK dictionary.
    590 void WriteExt(bool extractable, base::DictionaryValue* jwk_dict) {
    591   jwk_dict->SetBoolean("ext", extractable);
    592 }
    593 
    594 // Writes a Web Crypto algorithm to a JWK dictionary.
    595 Status WriteAlg(const blink::WebCryptoKeyAlgorithm& algorithm,
    596                 base::DictionaryValue* jwk_dict) {
    597   switch (algorithm.paramsType()) {
    598     case blink::WebCryptoKeyAlgorithmParamsTypeAes: {
    599       DCHECK(algorithm.aesParams());
    600       const char* aes_prefix = "";
    601       switch (algorithm.aesParams()->lengthBits()) {
    602         case 128:
    603           aes_prefix = "A128";
    604           break;
    605         case 192:
    606           aes_prefix = "A192";
    607           break;
    608         case 256:
    609           aes_prefix = "A256";
    610           break;
    611         default:
    612           NOTREACHED();  // bad key length means algorithm was built improperly
    613           return Status::ErrorUnexpected();
    614       }
    615       const char* aes_suffix = "";
    616       switch (algorithm.id()) {
    617         case blink::WebCryptoAlgorithmIdAesCbc:
    618           aes_suffix = "CBC";
    619           break;
    620         case blink::WebCryptoAlgorithmIdAesCtr:
    621           aes_suffix = "CTR";
    622           break;
    623         case blink::WebCryptoAlgorithmIdAesGcm:
    624           aes_suffix = "GCM";
    625           break;
    626         case blink::WebCryptoAlgorithmIdAesKw:
    627           aes_suffix = "KW";
    628           break;
    629         default:
    630           return Status::ErrorUnsupported();
    631       }
    632       jwk_dict->SetString("alg",
    633                           base::StringPrintf("%s%s", aes_prefix, aes_suffix));
    634       break;
    635     }
    636     case blink::WebCryptoKeyAlgorithmParamsTypeHmac: {
    637       DCHECK(algorithm.hmacParams());
    638       switch (algorithm.hmacParams()->hash().id()) {
    639         case blink::WebCryptoAlgorithmIdSha1:
    640           jwk_dict->SetString("alg", "HS1");
    641           break;
    642         case blink::WebCryptoAlgorithmIdSha256:
    643           jwk_dict->SetString("alg", "HS256");
    644           break;
    645         case blink::WebCryptoAlgorithmIdSha384:
    646           jwk_dict->SetString("alg", "HS384");
    647           break;
    648         case blink::WebCryptoAlgorithmIdSha512:
    649           jwk_dict->SetString("alg", "HS512");
    650           break;
    651         default:
    652           NOTREACHED();
    653           return Status::ErrorUnexpected();
    654       }
    655       break;
    656     }
    657     case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed:
    658       switch (algorithm.id()) {
    659         case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: {
    660           switch (algorithm.rsaHashedParams()->hash().id()) {
    661             case blink::WebCryptoAlgorithmIdSha1:
    662               jwk_dict->SetString("alg", "RS1");
    663               break;
    664             case blink::WebCryptoAlgorithmIdSha256:
    665               jwk_dict->SetString("alg", "RS256");
    666               break;
    667             case blink::WebCryptoAlgorithmIdSha384:
    668               jwk_dict->SetString("alg", "RS384");
    669               break;
    670             case blink::WebCryptoAlgorithmIdSha512:
    671               jwk_dict->SetString("alg", "RS512");
    672               break;
    673             default:
    674               NOTREACHED();
    675               return Status::ErrorUnexpected();
    676           }
    677           break;
    678         }
    679         case blink::WebCryptoAlgorithmIdRsaOaep: {
    680           switch (algorithm.rsaHashedParams()->hash().id()) {
    681             case blink::WebCryptoAlgorithmIdSha1:
    682               jwk_dict->SetString("alg", "RSA-OAEP");
    683               break;
    684             case blink::WebCryptoAlgorithmIdSha256:
    685               jwk_dict->SetString("alg", "RSA-OAEP-256");
    686               break;
    687             case blink::WebCryptoAlgorithmIdSha384:
    688               jwk_dict->SetString("alg", "RSA-OAEP-384");
    689               break;
    690             case blink::WebCryptoAlgorithmIdSha512:
    691               jwk_dict->SetString("alg", "RSA-OAEP-512");
    692               break;
    693             default:
    694               NOTREACHED();
    695               return Status::ErrorUnexpected();
    696           }
    697           break;
    698         }
    699         default:
    700           NOTREACHED();
    701           return Status::ErrorUnexpected();
    702       }
    703       break;
    704     default:
    705       return Status::ErrorUnsupported();
    706   }
    707   return Status::Success();
    708 }
    709 
    710 bool IsRsaKey(const blink::WebCryptoKey& key) {
    711   return IsAlgorithmRsa(key.algorithm().id());
    712 }
    713 
    714 Status ImportRsaKey(base::DictionaryValue* dict,
    715                     const blink::WebCryptoAlgorithm& algorithm,
    716                     bool extractable,
    717                     blink::WebCryptoKeyUsageMask usage_mask,
    718                     blink::WebCryptoKey* key) {
    719   // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry
    720   // in the JWK, while an RSA private key must have those, plus at least a "d"
    721   // (private exponent) entry.
    722   // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18,
    723   // section 6.3.
    724   std::string jwk_n_value;
    725   Status status = GetJwkBytes(dict, "n", &jwk_n_value);
    726   if (status.IsError())
    727     return status;
    728   std::string jwk_e_value;
    729   status = GetJwkBytes(dict, "e", &jwk_e_value);
    730   if (status.IsError())
    731     return status;
    732 
    733   bool is_public_key = !dict->HasKey("d");
    734 
    735   // Now that the key type is known, do an additional check on the usages to
    736   // make sure they are all applicable for this algorithm + key type.
    737   status = CheckKeyUsages(algorithm.id(),
    738                           is_public_key ? blink::WebCryptoKeyTypePublic
    739                                         : blink::WebCryptoKeyTypePrivate,
    740                           usage_mask);
    741 
    742   if (status.IsError())
    743     return status;
    744 
    745   if (is_public_key) {
    746     return platform::ImportRsaPublicKey(algorithm,
    747                                         extractable,
    748                                         usage_mask,
    749                                         CryptoData(jwk_n_value),
    750                                         CryptoData(jwk_e_value),
    751                                         key);
    752   }
    753 
    754   std::string jwk_d_value;
    755   status = GetJwkBytes(dict, "d", &jwk_d_value);
    756   if (status.IsError())
    757     return status;
    758 
    759   // The "p", "q", "dp", "dq", and "qi" properties are optional. Treat these
    760   // properties the same if they are unspecified, as if they were specified-but
    761   // empty, since ImportRsaPrivateKey() doesn't do validation checks anyway.
    762 
    763   std::string jwk_p_value;
    764   bool has_p;
    765   status = GetOptionalJwkBytes(dict, "p", &jwk_p_value, &has_p);
    766   if (status.IsError())
    767     return status;
    768 
    769   std::string jwk_q_value;
    770   bool has_q;
    771   status = GetOptionalJwkBytes(dict, "q", &jwk_q_value, &has_q);
    772   if (status.IsError())
    773     return status;
    774 
    775   std::string jwk_dp_value;
    776   bool has_dp;
    777   status = GetOptionalJwkBytes(dict, "dp", &jwk_dp_value, &has_dp);
    778   if (status.IsError())
    779     return status;
    780 
    781   std::string jwk_dq_value;
    782   bool has_dq;
    783   status = GetOptionalJwkBytes(dict, "dq", &jwk_dq_value, &has_dq);
    784   if (status.IsError())
    785     return status;
    786 
    787   std::string jwk_qi_value;
    788   bool has_qi;
    789   status = GetOptionalJwkBytes(dict, "qi", &jwk_qi_value, &has_qi);
    790   if (status.IsError())
    791     return status;
    792 
    793   int num_optional_properties = has_p + has_q + has_dp + has_dq + has_qi;
    794   if (num_optional_properties != 0 && num_optional_properties != 5)
    795     return Status::ErrorJwkIncompleteOptionalRsaPrivateKey();
    796 
    797   return platform::ImportRsaPrivateKey(
    798       algorithm,
    799       extractable,
    800       usage_mask,
    801       CryptoData(jwk_n_value),   // modulus
    802       CryptoData(jwk_e_value),   // public_exponent
    803       CryptoData(jwk_d_value),   // private_exponent
    804       CryptoData(jwk_p_value),   // prime1
    805       CryptoData(jwk_q_value),   // prime2
    806       CryptoData(jwk_dp_value),  // exponent1
    807       CryptoData(jwk_dq_value),  // exponent2
    808       CryptoData(jwk_qi_value),  // coefficient
    809       key);
    810 }
    811 
    812 }  // namespace
    813 
    814 // TODO(eroman): Split this up into smaller functions.
    815 Status ImportKeyJwk(const CryptoData& key_data,
    816                     const blink::WebCryptoAlgorithm& algorithm,
    817                     bool extractable,
    818                     blink::WebCryptoKeyUsageMask usage_mask,
    819                     blink::WebCryptoKey* key) {
    820   if (!key_data.byte_length())
    821     return Status::ErrorImportEmptyKeyData();
    822   DCHECK(key);
    823 
    824   // Parse the incoming JWK JSON.
    825   base::StringPiece json_string(reinterpret_cast<const char*>(key_data.bytes()),
    826                                 key_data.byte_length());
    827   scoped_ptr<base::Value> value(base::JSONReader::Read(json_string));
    828   // Note, bare pointer dict_value is ok since it points into scoped value.
    829   base::DictionaryValue* dict_value = NULL;
    830   if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value)
    831     return Status::ErrorJwkNotDictionary();
    832 
    833   // JWK "kty". Exit early if this required JWK parameter is missing.
    834   std::string jwk_kty_value;
    835   Status status = GetJwkString(dict_value, "kty", &jwk_kty_value);
    836   if (status.IsError())
    837     return status;
    838 
    839   // JWK "ext" (optional) --> extractable parameter
    840   {
    841     bool jwk_ext_value = false;
    842     bool has_jwk_ext;
    843     status =
    844         GetOptionalJwkBool(dict_value, "ext", &jwk_ext_value, &has_jwk_ext);
    845     if (status.IsError())
    846       return status;
    847     if (has_jwk_ext && !jwk_ext_value && extractable)
    848       return Status::ErrorJwkExtInconsistent();
    849   }
    850 
    851   // JWK "alg" --> algorithm parameter
    852   // 1. JWK alg present but unrecognized: error
    853   // 2. JWK alg valid and inconsistent with input algorithm: error
    854   // 3. JWK alg valid and consistent with input algorithm: use input value
    855   // 4. JWK alg is missing: use input value
    856   const JwkAlgorithmInfo* algorithm_info = NULL;
    857   std::string jwk_alg_value;
    858   bool has_jwk_alg;
    859   status =
    860       GetOptionalJwkString(dict_value, "alg", &jwk_alg_value, &has_jwk_alg);
    861   if (status.IsError())
    862     return status;
    863 
    864   if (has_jwk_alg) {
    865     // JWK alg present
    866 
    867     // TODO(padolph): Validate alg vs kty. For example kty="RSA" implies alg can
    868     // only be from the RSA family.
    869 
    870     blink::WebCryptoAlgorithm jwk_algorithm =
    871         blink::WebCryptoAlgorithm::createNull();
    872     algorithm_info = jwk_alg_registry.Get().GetAlgorithmInfo(jwk_alg_value);
    873     if (!algorithm_info ||
    874         !algorithm_info->CreateImportAlgorithm(&jwk_algorithm))
    875       return Status::ErrorJwkUnrecognizedAlgorithm();
    876 
    877     if (!ImportAlgorithmsConsistent(jwk_algorithm, algorithm))
    878       return Status::ErrorJwkAlgorithmInconsistent();
    879   }
    880   DCHECK(!algorithm.isNull());
    881 
    882   // JWK "key_ops" (optional) --> usage_mask parameter
    883   base::ListValue* jwk_key_ops_value = NULL;
    884   bool has_jwk_key_ops;
    885   status = GetOptionalJwkList(
    886       dict_value, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops);
    887   if (status.IsError())
    888     return status;
    889   blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0;
    890   if (has_jwk_key_ops) {
    891     status =
    892         GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask);
    893     if (status.IsError())
    894       return status;
    895     // The input usage_mask must be a subset of jwk_key_ops_mask.
    896     if (!ContainsKeyUsages(jwk_key_ops_mask, usage_mask))
    897       return Status::ErrorJwkKeyopsInconsistent();
    898   }
    899 
    900   // JWK "use" (optional) --> usage_mask parameter
    901   std::string jwk_use_value;
    902   bool has_jwk_use;
    903   status =
    904       GetOptionalJwkString(dict_value, "use", &jwk_use_value, &has_jwk_use);
    905   if (status.IsError())
    906     return status;
    907   blink::WebCryptoKeyUsageMask jwk_use_mask = 0;
    908   if (has_jwk_use) {
    909     if (jwk_use_value == "enc")
    910       jwk_use_mask = kJwkEncUsage;
    911     else if (jwk_use_value == "sig")
    912       jwk_use_mask = kJwkSigUsage;
    913     else
    914       return Status::ErrorJwkUnrecognizedUse();
    915     // The input usage_mask must be a subset of jwk_use_mask.
    916     if (!ContainsKeyUsages(jwk_use_mask, usage_mask))
    917       return Status::ErrorJwkUseInconsistent();
    918   }
    919 
    920   // If both 'key_ops' and 'use' are present, ensure they are consistent.
    921   if (has_jwk_key_ops && has_jwk_use &&
    922       !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask))
    923     return Status::ErrorJwkUseAndKeyopsInconsistent();
    924 
    925   // JWK keying material --> ImportKeyInternal()
    926   if (jwk_kty_value == "oct") {
    927     std::string jwk_k_value;
    928     status = GetJwkBytes(dict_value, "k", &jwk_k_value);
    929     if (status.IsError())
    930       return status;
    931 
    932     // Some JWK alg ID's embed information about the key length in the alg ID
    933     // string. For example "A128CBC" implies the JWK carries 128 bits
    934     // of key material. For such keys validate that enough bytes were provided.
    935     // If this validation is not done, then it would be possible to select a
    936     // different algorithm by passing a different lengthed key, since that is
    937     // how WebCrypto interprets things.
    938     if (algorithm_info &&
    939         algorithm_info->IsInvalidKeyByteLength(jwk_k_value.size())) {
    940       return Status::ErrorJwkIncorrectKeyLength();
    941     }
    942 
    943     return ImportKey(blink::WebCryptoKeyFormatRaw,
    944                      CryptoData(jwk_k_value),
    945                      algorithm,
    946                      extractable,
    947                      usage_mask,
    948                      key);
    949   }
    950 
    951   if (jwk_kty_value == "RSA")
    952     return ImportRsaKey(dict_value, algorithm, extractable, usage_mask, key);
    953 
    954   return Status::ErrorJwkUnrecognizedKty();
    955 }
    956 
    957 Status ExportKeyJwk(const blink::WebCryptoKey& key,
    958                     std::vector<uint8>* buffer) {
    959   DCHECK(key.extractable());
    960   base::DictionaryValue jwk_dict;
    961   Status status = Status::OperationError();
    962 
    963   switch (key.type()) {
    964     case blink::WebCryptoKeyTypeSecret: {
    965       std::vector<uint8> exported_key;
    966       status = ExportKey(blink::WebCryptoKeyFormatRaw, key, &exported_key);
    967       if (status.IsError())
    968         return status;
    969       WriteSecretKey(exported_key, &jwk_dict);
    970       break;
    971     }
    972     case blink::WebCryptoKeyTypePublic: {
    973       // TODO(eroman): Update when there are asymmetric keys other than RSA.
    974       if (!IsRsaKey(key))
    975         return Status::ErrorUnsupported();
    976       platform::PublicKey* public_key;
    977       status = ToPlatformPublicKey(key, &public_key);
    978       if (status.IsError())
    979         return status;
    980       std::vector<uint8> modulus;
    981       std::vector<uint8> public_exponent;
    982       status =
    983           platform::ExportRsaPublicKey(public_key, &modulus, &public_exponent);
    984       if (status.IsError())
    985         return status;
    986       WriteRsaPublicKey(modulus, public_exponent, &jwk_dict);
    987       break;
    988     }
    989     case blink::WebCryptoKeyTypePrivate: {
    990       // TODO(eroman): Update when there are asymmetric keys other than RSA.
    991       if (!IsRsaKey(key))
    992         return Status::ErrorUnsupported();
    993 
    994       status = ExportRsaPrivateKeyJwk(key, &jwk_dict);
    995       if (status.IsError())
    996         return status;
    997       break;
    998     }
    999 
   1000     default:
   1001       return Status::ErrorUnsupported();
   1002   }
   1003 
   1004   WriteKeyOps(key.usages(), &jwk_dict);
   1005   WriteExt(key.extractable(), &jwk_dict);
   1006   status = WriteAlg(key.algorithm(), &jwk_dict);
   1007   if (status.IsError())
   1008     return status;
   1009 
   1010   std::string json;
   1011   base::JSONWriter::Write(&jwk_dict, &json);
   1012   buffer->assign(json.data(), json.data() + json.size());
   1013   return Status::Success();
   1014 }
   1015 
   1016 }  // namespace webcrypto
   1017 
   1018 }  // namespace content
   1019