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 208namespace content { 209 210namespace webcrypto { 211 212namespace { 213 214// Web Crypto equivalent usage mask for JWK 'use' = 'enc'. 215const 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'. 220const blink::WebCryptoKeyUsageMask kJwkSigUsage = 221 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; 222 223class 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. 259Status 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. 274Status 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|. 294Status 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. 313Status 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. 329Status 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. 352Status 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 368Status 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 380Status 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 427Status 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 443Status 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 479Status 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 508void 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 518Status 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 531std::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 542Status 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 578void 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 591void 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 618JwkRsaInfo::JwkRsaInfo() : is_private_key(false) { 619} 620 621JwkRsaInfo::~JwkRsaInfo() { 622} 623 624Status 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 692const 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 708bool 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 718std::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 727std::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