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/base64.h" 12 #include "base/json/json_reader.h" 13 #include "base/json/json_writer.h" 14 #include "base/stl_util.h" 15 #include "base/strings/string_piece.h" 16 #include "content/child/webcrypto/crypto_data.h" 17 #include "content/child/webcrypto/status.h" 18 #include "content/child/webcrypto/webcrypto_util.h" 19 20 // TODO(eroman): The algorithm-specific logic in this file for AES and RSA 21 // should be moved into the corresponding AlgorithmImplementation file. It 22 // exists in this file to avoid duplication between OpenSSL and NSS 23 // implementations. 24 25 // JSON Web Key Format (JWK) 26 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21 27 // 28 // A JWK is a simple JSON dictionary with the following entries 29 // - "kty" (Key Type) Parameter, REQUIRED 30 // - <kty-specific parameters, see below>, REQUIRED 31 // - "use" (Key Use) Parameter, OPTIONAL 32 // - "key_ops" (Key Operations) Parameter, OPTIONAL 33 // - "alg" (Algorithm) Parameter, OPTIONAL 34 // - "ext" (Key Exportability), OPTIONAL 35 // (all other entries are ignored) 36 // 37 // OPTIONAL here means that this code does not require the entry to be present 38 // in the incoming JWK, because the method input parameters contain similar 39 // information. If the optional JWK entry is present, it will be validated 40 // against the corresponding input parameter for consistency and combined with 41 // it according to rules defined below. 42 // 43 // Input 'key_data' contains the JWK. To build a Web Crypto Key, the JWK 44 // values are parsed out and combined with the method input parameters to 45 // build a Web Crypto Key: 46 // Web Crypto Key type <-- (deduced) 47 // Web Crypto Key extractable <-- JWK ext + input extractable 48 // Web Crypto Key algorithm <-- JWK alg + input algorithm 49 // Web Crypto Key keyUsage <-- JWK use, key_ops + input usage_mask 50 // Web Crypto Key keying material <-- kty-specific parameters 51 // 52 // Values for each JWK entry are case-sensitive and defined in 53 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18. 54 // Note that not all values specified by JOSE are handled by this code. Only 55 // handled values are listed. 56 // - kty (Key Type) 57 // +-------+--------------------------------------------------------------+ 58 // | "RSA" | RSA [RFC3447] | 59 // | "oct" | Octet sequence (used to represent symmetric keys) | 60 // +-------+--------------------------------------------------------------+ 61 // 62 // - key_ops (Key Use Details) 63 // The key_ops field is an array that contains one or more strings from 64 // the table below, and describes the operations for which this key may be 65 // used. 66 // +-------+--------------------------------------------------------------+ 67 // | "encrypt" | encrypt operations | 68 // | "decrypt" | decrypt operations | 69 // | "sign" | sign (MAC) operations | 70 // | "verify" | verify (MAC) operations | 71 // | "wrapKey" | key wrap | 72 // | "unwrapKey" | key unwrap | 73 // | "deriveKey" | key derivation | 74 // | "deriveBits" | key derivation | 75 // +-------+--------------------------------------------------------------+ 76 // 77 // - use (Key Use) 78 // The use field contains a single entry from the table below. 79 // +-------+--------------------------------------------------------------+ 80 // | "sig" | equivalent to key_ops of [sign, verify] | 81 // | "enc" | equivalent to key_ops of [encrypt, decrypt, wrapKey, | 82 // | | unwrapKey, deriveKey, deriveBits] | 83 // +-------+--------------------------------------------------------------+ 84 // 85 // NOTE: If both "use" and "key_ops" JWK members are present, the usages 86 // specified by them MUST be consistent. In particular, the "use" value 87 // "sig" corresponds to "sign" and/or "verify". The "use" value "enc" 88 // corresponds to all other values defined above. If "key_ops" values 89 // corresponding to both "sig" and "enc" "use" values are present, the "use" 90 // member SHOULD NOT be present, and if present, its value MUST NOT be 91 // either "sig" or "enc". 92 // 93 // - ext (Key Exportability) 94 // +-------+--------------------------------------------------------------+ 95 // | true | Key may be exported from the trusted environment | 96 // | false | Key cannot exit the trusted environment | 97 // +-------+--------------------------------------------------------------+ 98 // 99 // - alg (Algorithm) 100 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18 101 // +--------------+-------------------------------------------------------+ 102 // | Digital Signature or MAC Algorithm | 103 // +--------------+-------------------------------------------------------+ 104 // | "HS1" | HMAC using SHA-1 hash algorithm | 105 // | "HS256" | HMAC using SHA-256 hash algorithm | 106 // | "HS384" | HMAC using SHA-384 hash algorithm | 107 // | "HS512" | HMAC using SHA-512 hash algorithm | 108 // | "RS1" | RSASSA using SHA-1 hash algorithm 109 // | "RS256" | RSASSA using SHA-256 hash algorithm | 110 // | "RS384" | RSASSA using SHA-384 hash algorithm | 111 // | "RS512" | RSASSA using SHA-512 hash algorithm | 112 // +--------------+-------------------------------------------------------| 113 // | Key Management Algorithm | 114 // +--------------+-------------------------------------------------------+ 115 // | "RSA-OAEP" | RSAES using Optimal Asymmetric Encryption Padding | 116 // | | (OAEP) [RFC3447], with the default parameters | 117 // | | specified by RFC3447 in Section A.2.1 | 118 // | "A128KW" | Advanced Encryption Standard (AES) Key Wrap Algorithm | 119 // | | [RFC3394] using 128 bit keys | 120 // | "A192KW" | AES Key Wrap Algorithm using 192 bit keys | 121 // | "A256KW" | AES Key Wrap Algorithm using 256 bit keys | 122 // | "A128GCM" | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using | 123 // | | 128 bit keys | 124 // | "A192GCM" | AES GCM using 192 bit keys | 125 // | "A256GCM" | AES GCM using 256 bit keys | 126 // | "A128CBC" | AES in Cipher Block Chaining Mode (CBC) with PKCS #5 | 127 // | | padding [NIST.800-38A] | 128 // | "A192CBC" | AES CBC using 192 bit keys | 129 // | "A256CBC" | AES CBC using 256 bit keys | 130 // +--------------+-------------------------------------------------------+ 131 // 132 // kty-specific parameters 133 // The value of kty determines the type and content of the keying material 134 // carried in the JWK to be imported. 135 // // - kty == "oct" (symmetric or other raw key) 136 // +-------+--------------------------------------------------------------+ 137 // | "k" | Contains the value of the symmetric (or other single-valued) | 138 // | | key. It is represented as the base64url encoding of the | 139 // | | octet sequence containing the key value. | 140 // +-------+--------------------------------------------------------------+ 141 // - kty == "RSA" (RSA public key) 142 // +-------+--------------------------------------------------------------+ 143 // | "n" | Contains the modulus value for the RSA public key. It is | 144 // | | represented as the base64url encoding of the value's | 145 // | | unsigned big endian representation as an octet sequence. | 146 // +-------+--------------------------------------------------------------+ 147 // | "e" | Contains the exponent value for the RSA public key. It is | 148 // | | represented as the base64url encoding of the value's | 149 // | | unsigned big endian representation as an octet sequence. | 150 // +-------+--------------------------------------------------------------+ 151 // - If key == "RSA" and the "d" parameter is present then it is a private key. 152 // All the parameters above for public keys apply, as well as the following. 153 // (Note that except for "d", all of these are optional): 154 // +-------+--------------------------------------------------------------+ 155 // | "d" | Contains the private exponent value for the RSA private key. | 156 // | | It is represented as the base64url encoding of the value's | 157 // | | unsigned big endian representation as an octet sequence. | 158 // +-------+--------------------------------------------------------------+ 159 // | "p" | Contains the first prime factor value for the RSA private | 160 // | | key. It is represented as the base64url encoding of the | 161 // | | value's | 162 // | | unsigned big endian representation as an octet sequence. | 163 // +-------+--------------------------------------------------------------+ 164 // | "q" | Contains the second prime factor value for the RSA private | 165 // | | key. It is represented as the base64url encoding of the | 166 // | | value's unsigned big endian representation as an octet | 167 // | | sequence. | 168 // +-------+--------------------------------------------------------------+ 169 // | "dp" | Contains the first factor CRT exponent value for the RSA | 170 // | | private key. It is represented as the base64url encoding of | 171 // | | the value's unsigned big endian representation as an octet | 172 // | | sequence. | 173 // +-------+--------------------------------------------------------------+ 174 // | "dq" | Contains the second factor CRT exponent value for the RSA | 175 // | | private key. It is represented as the base64url encoding of | 176 // | | the value's unsigned big endian representation as an octet | 177 // | | sequence. | 178 // +-------+--------------------------------------------------------------+ 179 // | "dq" | Contains the first CRT coefficient value for the RSA private | 180 // | | key. It is represented as the base64url encoding of the | 181 // | | value's unsigned big endian representation as an octet | 182 // | | sequence. | 183 // +-------+--------------------------------------------------------------+ 184 // 185 // Consistency and conflict resolution 186 // The 'algorithm', 'extractable', and 'usage_mask' input parameters 187 // may be different than the corresponding values inside the JWK. The Web 188 // Crypto spec says that if a JWK value is present but is inconsistent with 189 // the input value, it is an error and the operation must fail. If no 190 // inconsistency is found then the input parameters are used. 191 // 192 // algorithm 193 // If the JWK algorithm is provided, it must match the web crypto input 194 // algorithm (both the algorithm ID and inner hash if applicable). 195 // 196 // extractable 197 // If the JWK ext field is true but the input parameter is false, make the 198 // Web Crypto Key non-extractable. Conversely, if the JWK ext field is 199 // false but the input parameter is true, it is an inconsistency. If both 200 // are true or both are false, use that value. 201 // 202 // usage_mask 203 // The input usage_mask must be a strict subset of the interpreted JWK use 204 // value, else it is judged inconsistent. In all cases the input usage_mask 205 // is used as the final usage_mask. 206 // 207 208 namespace content { 209 210 namespace webcrypto { 211 212 namespace { 213 214 // Web Crypto equivalent usage mask for JWK 'use' = 'enc'. 215 const blink::WebCryptoKeyUsageMask kJwkEncUsage = 216 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | 217 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey | 218 blink::WebCryptoKeyUsageDeriveKey | blink::WebCryptoKeyUsageDeriveBits; 219 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'. 220 const blink::WebCryptoKeyUsageMask kJwkSigUsage = 221 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; 222 223 class JwkWriter { 224 public: 225 JwkWriter(const std::string& algorithm, 226 bool extractable, 227 blink::WebCryptoKeyUsageMask usage_mask, 228 const std::string& kty) { 229 dict_.SetString("alg", algorithm); 230 dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usage_mask)); 231 dict_.SetBoolean("ext", extractable); 232 dict_.SetString("kty", kty); 233 } 234 235 void Set(const std::string& key, const std::string& value) { 236 dict_.SetString(key, value); 237 } 238 239 void SetBase64Encoded(const std::string& key, const CryptoData& value) { 240 dict_.SetString(key, 241 Base64EncodeUrlSafe(base::StringPiece( 242 reinterpret_cast<const char*>(value.bytes()), 243 value.byte_length()))); 244 } 245 246 void ToBytes(std::vector<uint8_t>* utf8_bytes) { 247 std::string json; 248 base::JSONWriter::Write(&dict_, &json); 249 utf8_bytes->assign(json.begin(), json.end()); 250 } 251 252 private: 253 base::DictionaryValue dict_; 254 }; 255 256 // Extracts the required string property with key |path| from |dict| and saves 257 // the result to |*result|. If the property does not exist or is not a string, 258 // returns an error. 259 Status GetJwkString(base::DictionaryValue* dict, 260 const std::string& path, 261 std::string* result) { 262 base::Value* value = NULL; 263 if (!dict->Get(path, &value)) 264 return Status::ErrorJwkPropertyMissing(path); 265 if (!value->GetAsString(result)) 266 return Status::ErrorJwkPropertyWrongType(path, "string"); 267 return Status::Success(); 268 } 269 270 // Extracts the optional string property with key |path| from |dict| and saves 271 // the result to |*result| if it was found. If the property exists and is not a 272 // string, returns an error. Otherwise returns success, and sets 273 // |*property_exists| if it was found. 274 Status GetOptionalJwkString(base::DictionaryValue* dict, 275 const std::string& path, 276 std::string* result, 277 bool* property_exists) { 278 *property_exists = false; 279 base::Value* value = NULL; 280 if (!dict->Get(path, &value)) 281 return Status::Success(); 282 283 if (!value->GetAsString(result)) 284 return Status::ErrorJwkPropertyWrongType(path, "string"); 285 286 *property_exists = true; 287 return Status::Success(); 288 } 289 290 // Extracts the optional array property with key |path| from |dict| and saves 291 // the result to |*result| if it was found. If the property exists and is not an 292 // array, returns an error. Otherwise returns success, and sets 293 // |*property_exists| if it was found. Note that |*result| is owned by |dict|. 294 Status GetOptionalJwkList(base::DictionaryValue* dict, 295 const std::string& path, 296 base::ListValue** result, 297 bool* property_exists) { 298 *property_exists = false; 299 base::Value* value = NULL; 300 if (!dict->Get(path, &value)) 301 return Status::Success(); 302 303 if (!value->GetAsList(result)) 304 return Status::ErrorJwkPropertyWrongType(path, "list"); 305 306 *property_exists = true; 307 return Status::Success(); 308 } 309 310 // Extracts the required string property with key |path| from |dict| and saves 311 // the base64url-decoded bytes to |*result|. If the property does not exist or 312 // is not a string, or could not be base64url-decoded, returns an error. 313 Status GetJwkBytes(base::DictionaryValue* dict, 314 const std::string& path, 315 std::string* result) { 316 std::string base64_string; 317 Status status = GetJwkString(dict, path, &base64_string); 318 if (status.IsError()) 319 return status; 320 321 if (!Base64DecodeUrlSafe(base64_string, result)) 322 return Status::ErrorJwkBase64Decode(path); 323 324 return Status::Success(); 325 } 326 327 // Extracts the required base64url property, which is interpreted as being a 328 // big-endian unsigned integer. 329 Status GetJwkBigInteger(base::DictionaryValue* dict, 330 const std::string& path, 331 std::string* result) { 332 Status status = GetJwkBytes(dict, path, result); 333 if (status.IsError()) 334 return status; 335 336 if (result->empty()) 337 return Status::ErrorJwkEmptyBigInteger(path); 338 339 // The JWA spec says that "The octet sequence MUST utilize the minimum number 340 // of octets to represent the value." This means there shouldn't be any 341 // leading zeros. 342 if (result->size() > 1 && (*result)[0] == 0) 343 return Status::ErrorJwkBigIntegerHasLeadingZero(path); 344 345 return Status::Success(); 346 } 347 348 // Extracts the optional boolean property with key |path| from |dict| and saves 349 // the result to |*result| if it was found. If the property exists and is not a 350 // boolean, returns an error. Otherwise returns success, and sets 351 // |*property_exists| if it was found. 352 Status GetOptionalJwkBool(base::DictionaryValue* dict, 353 const std::string& path, 354 bool* result, 355 bool* property_exists) { 356 *property_exists = false; 357 base::Value* value = NULL; 358 if (!dict->Get(path, &value)) 359 return Status::Success(); 360 361 if (!value->GetAsBoolean(result)) 362 return Status::ErrorJwkPropertyWrongType(path, "boolean"); 363 364 *property_exists = true; 365 return Status::Success(); 366 } 367 368 Status VerifyExt(base::DictionaryValue* dict, bool expected_extractable) { 369 // JWK "ext" (optional) --> extractable parameter 370 bool jwk_ext_value = false; 371 bool has_jwk_ext; 372 Status status = GetOptionalJwkBool(dict, "ext", &jwk_ext_value, &has_jwk_ext); 373 if (status.IsError()) 374 return status; 375 if (has_jwk_ext && expected_extractable && !jwk_ext_value) 376 return Status::ErrorJwkExtInconsistent(); 377 return Status::Success(); 378 } 379 380 Status VerifyUsages(base::DictionaryValue* dict, 381 blink::WebCryptoKeyUsageMask expected_usage_mask) { 382 // JWK "key_ops" (optional) --> usage_mask parameter 383 base::ListValue* jwk_key_ops_value = NULL; 384 bool has_jwk_key_ops; 385 Status status = 386 GetOptionalJwkList(dict, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops); 387 if (status.IsError()) 388 return status; 389 blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; 390 if (has_jwk_key_ops) { 391 status = 392 GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask); 393 if (status.IsError()) 394 return status; 395 // The input usage_mask must be a subset of jwk_key_ops_mask. 396 if (!ContainsKeyUsages(jwk_key_ops_mask, expected_usage_mask)) 397 return Status::ErrorJwkKeyopsInconsistent(); 398 } 399 400 // JWK "use" (optional) --> usage_mask parameter 401 std::string jwk_use_value; 402 bool has_jwk_use; 403 status = GetOptionalJwkString(dict, "use", &jwk_use_value, &has_jwk_use); 404 if (status.IsError()) 405 return status; 406 blink::WebCryptoKeyUsageMask jwk_use_mask = 0; 407 if (has_jwk_use) { 408 if (jwk_use_value == "enc") 409 jwk_use_mask = kJwkEncUsage; 410 else if (jwk_use_value == "sig") 411 jwk_use_mask = kJwkSigUsage; 412 else 413 return Status::ErrorJwkUnrecognizedUse(); 414 // The input usage_mask must be a subset of jwk_use_mask. 415 if (!ContainsKeyUsages(jwk_use_mask, expected_usage_mask)) 416 return Status::ErrorJwkUseInconsistent(); 417 } 418 419 // If both 'key_ops' and 'use' are present, ensure they are consistent. 420 if (has_jwk_key_ops && has_jwk_use && 421 !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask)) 422 return Status::ErrorJwkUseAndKeyopsInconsistent(); 423 424 return Status::Success(); 425 } 426 427 Status VerifyAlg(base::DictionaryValue* dict, 428 const std::string& expected_algorithm) { 429 // JWK "alg" --> algorithm parameter 430 bool has_jwk_alg; 431 std::string jwk_alg_value; 432 Status status = 433 GetOptionalJwkString(dict, "alg", &jwk_alg_value, &has_jwk_alg); 434 if (status.IsError()) 435 return status; 436 437 if (has_jwk_alg && jwk_alg_value != expected_algorithm) 438 return Status::ErrorJwkAlgorithmInconsistent(); 439 440 return Status::Success(); 441 } 442 443 Status ParseJwkCommon(const CryptoData& bytes, 444 bool expected_extractable, 445 blink::WebCryptoKeyUsageMask expected_usage_mask, 446 std::string* kty, 447 scoped_ptr<base::DictionaryValue>* dict) { 448 // Parse the incoming JWK JSON. 449 base::StringPiece json_string(reinterpret_cast<const char*>(bytes.bytes()), 450 bytes.byte_length()); 451 452 scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); 453 base::DictionaryValue* dict_value = NULL; 454 455 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) 456 return Status::ErrorJwkNotDictionary(); 457 458 // Release |value|, as ownership will be transferred to |dict| via 459 // |dict_value|, which points to the same object as |value|. 460 ignore_result(value.release()); 461 dict->reset(dict_value); 462 463 // JWK "kty". Exit early if this required JWK parameter is missing. 464 Status status = GetJwkString(dict_value, "kty", kty); 465 if (status.IsError()) 466 return status; 467 468 status = VerifyExt(dict_value, expected_extractable); 469 if (status.IsError()) 470 return status; 471 472 status = VerifyUsages(dict_value, expected_usage_mask); 473 if (status.IsError()) 474 return status; 475 476 return Status::Success(); 477 } 478 479 Status ReadSecretKeyNoExpectedAlg( 480 const CryptoData& key_data, 481 bool expected_extractable, 482 blink::WebCryptoKeyUsageMask expected_usage_mask, 483 std::vector<uint8_t>* raw_key_data, 484 scoped_ptr<base::DictionaryValue>* dict) { 485 if (!key_data.byte_length()) 486 return Status::ErrorImportEmptyKeyData(); 487 488 std::string kty; 489 Status status = ParseJwkCommon( 490 key_data, expected_extractable, expected_usage_mask, &kty, dict); 491 if (status.IsError()) 492 return status; 493 494 if (kty != "oct") 495 return Status::ErrorJwkUnexpectedKty("oct"); 496 497 std::string jwk_k_value; 498 status = GetJwkBytes(dict->get(), "k", &jwk_k_value); 499 if (status.IsError()) 500 return status; 501 raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end()); 502 503 return Status::Success(); 504 } 505 506 } // namespace 507 508 void WriteSecretKeyJwk(const CryptoData& raw_key_data, 509 const std::string& algorithm, 510 bool extractable, 511 blink::WebCryptoKeyUsageMask usage_mask, 512 std::vector<uint8_t>* jwk_key_data) { 513 JwkWriter writer(algorithm, extractable, usage_mask, "oct"); 514 writer.SetBase64Encoded("k", raw_key_data); 515 writer.ToBytes(jwk_key_data); 516 } 517 518 Status ReadSecretKeyJwk(const CryptoData& key_data, 519 const std::string& expected_algorithm, 520 bool expected_extractable, 521 blink::WebCryptoKeyUsageMask expected_usage_mask, 522 std::vector<uint8_t>* raw_key_data) { 523 scoped_ptr<base::DictionaryValue> dict; 524 Status status = ReadSecretKeyNoExpectedAlg( 525 key_data, expected_extractable, expected_usage_mask, raw_key_data, &dict); 526 if (status.IsError()) 527 return status; 528 return VerifyAlg(dict.get(), expected_algorithm); 529 } 530 531 std::string MakeJwkAesAlgorithmName(const std::string& suffix, 532 unsigned int keylen_bytes) { 533 if (keylen_bytes == 16) 534 return std::string("A128") + suffix; 535 if (keylen_bytes == 24) 536 return std::string("A192") + suffix; 537 if (keylen_bytes == 32) 538 return std::string("A256") + suffix; 539 return std::string(); 540 } 541 542 Status ReadAesSecretKeyJwk(const CryptoData& key_data, 543 const std::string& algorithm_name_suffix, 544 bool expected_extractable, 545 blink::WebCryptoKeyUsageMask expected_usage_mask, 546 std::vector<uint8_t>* raw_key_data) { 547 scoped_ptr<base::DictionaryValue> dict; 548 Status status = ReadSecretKeyNoExpectedAlg( 549 key_data, expected_extractable, expected_usage_mask, raw_key_data, &dict); 550 if (status.IsError()) 551 return status; 552 553 bool has_jwk_alg; 554 std::string jwk_alg; 555 status = GetOptionalJwkString(dict.get(), "alg", &jwk_alg, &has_jwk_alg); 556 if (status.IsError()) 557 return status; 558 559 if (has_jwk_alg) { 560 std::string expected_algorithm_name = 561 MakeJwkAesAlgorithmName(algorithm_name_suffix, raw_key_data->size()); 562 563 if (jwk_alg != expected_algorithm_name) { 564 // Give a different error message if the key length was wrong. 565 if (jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 16) || 566 jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 24) || 567 jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 32)) { 568 return Status::ErrorJwkIncorrectKeyLength(); 569 } 570 return Status::ErrorJwkAlgorithmInconsistent(); 571 } 572 } 573 574 return Status::Success(); 575 } 576 577 // Writes an RSA public key to a JWK dictionary 578 void WriteRsaPublicKeyJwk(const CryptoData& n, 579 const CryptoData& e, 580 const std::string& algorithm, 581 bool extractable, 582 blink::WebCryptoKeyUsageMask usage_mask, 583 std::vector<uint8_t>* jwk_key_data) { 584 JwkWriter writer(algorithm, extractable, usage_mask, "RSA"); 585 writer.SetBase64Encoded("n", n); 586 writer.SetBase64Encoded("e", e); 587 writer.ToBytes(jwk_key_data); 588 } 589 590 // Writes an RSA private key to a JWK dictionary 591 void WriteRsaPrivateKeyJwk(const CryptoData& n, 592 const CryptoData& e, 593 const CryptoData& d, 594 const CryptoData& p, 595 const CryptoData& q, 596 const CryptoData& dp, 597 const CryptoData& dq, 598 const CryptoData& qi, 599 const std::string& algorithm, 600 bool extractable, 601 blink::WebCryptoKeyUsageMask usage_mask, 602 std::vector<uint8_t>* jwk_key_data) { 603 JwkWriter writer(algorithm, extractable, usage_mask, "RSA"); 604 605 writer.SetBase64Encoded("n", n); 606 writer.SetBase64Encoded("e", e); 607 writer.SetBase64Encoded("d", d); 608 // Although these are "optional" in the JWA, WebCrypto spec requires them to 609 // be emitted. 610 writer.SetBase64Encoded("p", p); 611 writer.SetBase64Encoded("q", q); 612 writer.SetBase64Encoded("dp", dp); 613 writer.SetBase64Encoded("dq", dq); 614 writer.SetBase64Encoded("qi", qi); 615 writer.ToBytes(jwk_key_data); 616 } 617 618 JwkRsaInfo::JwkRsaInfo() : is_private_key(false) { 619 } 620 621 JwkRsaInfo::~JwkRsaInfo() { 622 } 623 624 Status ReadRsaKeyJwk(const CryptoData& key_data, 625 const std::string& expected_algorithm, 626 bool expected_extractable, 627 blink::WebCryptoKeyUsageMask expected_usage_mask, 628 JwkRsaInfo* result) { 629 if (!key_data.byte_length()) 630 return Status::ErrorImportEmptyKeyData(); 631 632 scoped_ptr<base::DictionaryValue> dict; 633 std::string kty; 634 Status status = ParseJwkCommon( 635 key_data, expected_extractable, expected_usage_mask, &kty, &dict); 636 if (status.IsError()) 637 return status; 638 639 status = VerifyAlg(dict.get(), expected_algorithm); 640 if (status.IsError()) 641 return status; 642 643 if (kty != "RSA") 644 return Status::ErrorJwkUnexpectedKty("RSA"); 645 646 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry 647 // in the JWK, while an RSA private key must have those, plus at least a "d" 648 // (private exponent) entry. 649 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, 650 // section 6.3. 651 status = GetJwkBigInteger(dict.get(), "n", &result->n); 652 if (status.IsError()) 653 return status; 654 status = GetJwkBigInteger(dict.get(), "e", &result->e); 655 if (status.IsError()) 656 return status; 657 658 result->is_private_key = dict->HasKey("d"); 659 if (!result->is_private_key) 660 return Status::Success(); 661 662 status = GetJwkBigInteger(dict.get(), "d", &result->d); 663 if (status.IsError()) 664 return status; 665 666 // The "p", "q", "dp", "dq", and "qi" properties are optional in the JWA 667 // spec. However they are required by Chromium's WebCrypto implementation. 668 669 status = GetJwkBigInteger(dict.get(), "p", &result->p); 670 if (status.IsError()) 671 return status; 672 673 status = GetJwkBigInteger(dict.get(), "q", &result->q); 674 if (status.IsError()) 675 return status; 676 677 status = GetJwkBigInteger(dict.get(), "dp", &result->dp); 678 if (status.IsError()) 679 return status; 680 681 status = GetJwkBigInteger(dict.get(), "dq", &result->dq); 682 if (status.IsError()) 683 return status; 684 685 status = GetJwkBigInteger(dict.get(), "qi", &result->qi); 686 if (status.IsError()) 687 return status; 688 689 return Status::Success(); 690 } 691 692 const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash) { 693 switch (hash) { 694 case blink::WebCryptoAlgorithmIdSha1: 695 return "HS1"; 696 case blink::WebCryptoAlgorithmIdSha256: 697 return "HS256"; 698 case blink::WebCryptoAlgorithmIdSha384: 699 return "HS384"; 700 case blink::WebCryptoAlgorithmIdSha512: 701 return "HS512"; 702 default: 703 return NULL; 704 } 705 } 706 707 // TODO(eroman): This accepts invalid inputs. http://crbug.com/378034 708 bool Base64DecodeUrlSafe(const std::string& input, std::string* output) { 709 std::string base64_encoded_text(input); 710 std::replace( 711 base64_encoded_text.begin(), base64_encoded_text.end(), '-', '+'); 712 std::replace( 713 base64_encoded_text.begin(), base64_encoded_text.end(), '_', '/'); 714 base64_encoded_text.append((4 - base64_encoded_text.size() % 4) % 4, '='); 715 return base::Base64Decode(base64_encoded_text, output); 716 } 717 718 std::string Base64EncodeUrlSafe(const base::StringPiece& input) { 719 std::string output; 720 base::Base64Encode(input, &output); 721 std::replace(output.begin(), output.end(), '+', '-'); 722 std::replace(output.begin(), output.end(), '/', '_'); 723 output.erase(std::remove(output.begin(), output.end(), '='), output.end()); 724 return output; 725 } 726 727 std::string Base64EncodeUrlSafe(const std::vector<uint8_t>& input) { 728 const base::StringPiece string_piece( 729 reinterpret_cast<const char*>(vector_as_array(&input)), input.size()); 730 return Base64EncodeUrlSafe(string_piece); 731 } 732 733 } // namespace webcrypto 734 735 } // namespace content 736