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