| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "jwk.h" | 5 #include "jwk.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <functional> | 8 #include <functional> |
| 9 #include <map> | 9 #include <map> |
| 10 | 10 |
| 11 #include "base/json/json_reader.h" | 11 #include "base/json/json_reader.h" |
| 12 #include "base/json/json_writer.h" | 12 #include "base/json/json_writer.h" |
| 13 #include "base/lazy_instance.h" | |
| 14 #include "base/strings/string_piece.h" | 13 #include "base/strings/string_piece.h" |
| 15 #include "base/strings/stringprintf.h" | |
| 16 #include "content/child/webcrypto/crypto_data.h" | 14 #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" | 15 #include "content/child/webcrypto/status.h" |
| 20 #include "content/child/webcrypto/webcrypto_util.h" | 16 #include "content/child/webcrypto/webcrypto_util.h" |
| 21 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" | |
| 22 | 17 |
| 23 // JSON Web Key Format (JWK) | 18 // JSON Web Key Format (JWK) |
| 24 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21 | 19 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21 |
| 25 // | 20 // |
| 26 // A JWK is a simple JSON dictionary with the following entries | 21 // A JWK is a simple JSON dictionary with the following entries |
| 27 // - "kty" (Key Type) Parameter, REQUIRED | 22 // - "kty" (Key Type) Parameter, REQUIRED |
| 28 // - <kty-specific parameters, see below>, REQUIRED | 23 // - <kty-specific parameters, see below>, REQUIRED |
| 29 // - "use" (Key Use) Parameter, OPTIONAL | 24 // - "use" (Key Use) Parameter, OPTIONAL |
| 30 // - "key_ops" (Key Operations) Parameter, OPTIONAL | 25 // - "key_ops" (Key Operations) Parameter, OPTIONAL |
| 31 // - "alg" (Algorithm) Parameter, OPTIONAL | 26 // - "alg" (Algorithm) Parameter, OPTIONAL |
| (...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 202 // value, else it is judged inconsistent. In all cases the input usage_mask | 197 // value, else it is judged inconsistent. In all cases the input usage_mask |
| 203 // is used as the final usage_mask. | 198 // is used as the final usage_mask. |
| 204 // | 199 // |
| 205 | 200 |
| 206 namespace content { | 201 namespace content { |
| 207 | 202 |
| 208 namespace webcrypto { | 203 namespace webcrypto { |
| 209 | 204 |
| 210 namespace { | 205 namespace { |
| 211 | 206 |
| 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'. | 207 // Web Crypto equivalent usage mask for JWK 'use' = 'enc'. |
| 229 const blink::WebCryptoKeyUsageMask kJwkEncUsage = | 208 const blink::WebCryptoKeyUsageMask kJwkEncUsage = |
| 230 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | | 209 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | |
| 231 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey | | 210 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey | |
| 232 blink::WebCryptoKeyUsageDeriveKey | blink::WebCryptoKeyUsageDeriveBits; | 211 blink::WebCryptoKeyUsageDeriveKey | blink::WebCryptoKeyUsageDeriveBits; |
| 233 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'. | 212 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'. |
| 234 const blink::WebCryptoKeyUsageMask kJwkSigUsage = | 213 const blink::WebCryptoKeyUsageMask kJwkSigUsage = |
| 235 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; | 214 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; |
| 236 | 215 |
| 237 typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)(); | 216 class JwkWriter { |
| 238 | |
| 239 class JwkAlgorithmInfo { | |
| 240 public: | 217 public: |
| 241 JwkAlgorithmInfo() | 218 JwkWriter(const std::string& algorithm, |
| 242 : creation_func_(NULL), | 219 bool extractable, |
| 243 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} | 220 blink::WebCryptoKeyUsageMask usage_mask, |
| 244 | 221 const std::string& kty) { |
| 245 explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func) | 222 dict_.SetString("alg", algorithm); |
| 246 : creation_func_(algorithm_creation_func), | 223 dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usage_mask)); |
| 247 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} | 224 dict_.SetBoolean("ext", extractable); |
| 248 | 225 dict_.SetString("kty", kty); |
| 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 } | 226 } |
| 255 | 227 |
| 256 bool CreateImportAlgorithm(blink::WebCryptoAlgorithm* algorithm) const { | 228 void Set(const std::string& key, const std::string& value) { |
| 257 *algorithm = creation_func_(); | 229 dict_.SetString(key, value); |
| 258 return !algorithm->isNull(); | |
| 259 } | 230 } |
| 260 | 231 |
| 261 bool IsInvalidKeyByteLength(size_t byte_length) const { | 232 void SetBase64Encoded(const std::string& key, const CryptoData& value) { |
| 262 if (required_key_length_bytes_ == NO_KEY_SIZE_REQUIREMENT) | 233 dict_.SetString(key, |
| 263 return false; | 234 Base64EncodeUrlSafe(base::StringPiece( |
| 264 return required_key_length_bytes_ != byte_length; | 235 reinterpret_cast<const char*>(value.bytes()), |
| 236 value.byte_length()))); |
| 237 } |
| 238 |
| 239 void ToBytes(std::vector<uint8>* utf8_bytes) { |
| 240 std::string json; |
| 241 base::JSONWriter::Write(&dict_, &json); |
| 242 utf8_bytes->assign(json.data(), json.data() + json.size()); |
| 265 } | 243 } |
| 266 | 244 |
| 267 private: | 245 private: |
| 268 enum { NO_KEY_SIZE_REQUIREMENT = UINT_MAX }; | 246 base::DictionaryValue dict_; |
| 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 }; | 247 }; |
| 275 | 248 |
| 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 | 249 // 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, | 250 // the result to |*result|. If the property does not exist or is not a string, |
| 398 // returns an error. | 251 // returns an error. |
| 399 Status GetJwkString(base::DictionaryValue* dict, | 252 Status GetJwkString(base::DictionaryValue* dict, |
| 400 const std::string& path, | 253 const std::string& path, |
| 401 std::string* result) { | 254 std::string* result) { |
| 402 base::Value* value = NULL; | 255 base::Value* value = NULL; |
| 403 if (!dict->Get(path, &value)) | 256 if (!dict->Get(path, &value)) |
| 404 return Status::ErrorJwkPropertyMissing(path); | 257 return Status::ErrorJwkPropertyMissing(path); |
| 405 if (!value->GetAsString(result)) | 258 if (!value->GetAsString(result)) |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 502 if (!dict->Get(path, &value)) | 355 if (!dict->Get(path, &value)) |
| 503 return Status::Success(); | 356 return Status::Success(); |
| 504 | 357 |
| 505 if (!value->GetAsBoolean(result)) | 358 if (!value->GetAsBoolean(result)) |
| 506 return Status::ErrorJwkPropertyWrongType(path, "boolean"); | 359 return Status::ErrorJwkPropertyWrongType(path, "boolean"); |
| 507 | 360 |
| 508 *property_exists = true; | 361 *property_exists = true; |
| 509 return Status::Success(); | 362 return Status::Success(); |
| 510 } | 363 } |
| 511 | 364 |
| 512 // Writes a secret/symmetric key to a JWK dictionary. | 365 Status VerifyExt(base::DictionaryValue* dict, bool expected_extractable) { |
| 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 | 366 // JWK "ext" (optional) --> extractable parameter |
| 840 { | 367 bool jwk_ext_value = false; |
| 841 bool jwk_ext_value = false; | 368 bool has_jwk_ext; |
| 842 bool has_jwk_ext; | 369 Status status = GetOptionalJwkBool(dict, "ext", &jwk_ext_value, &has_jwk_ext); |
| 843 status = | 370 if (status.IsError()) |
| 844 GetOptionalJwkBool(dict_value, "ext", &jwk_ext_value, &has_jwk_ext); | 371 return status; |
| 845 if (status.IsError()) | 372 if (has_jwk_ext && expected_extractable && !jwk_ext_value) |
| 846 return status; | 373 return Status::ErrorJwkExtInconsistent(); |
| 847 if (has_jwk_ext && !jwk_ext_value && extractable) | 374 return Status::Success(); |
| 848 return Status::ErrorJwkExtInconsistent(); | 375 } |
| 849 } | 376 |
| 850 | 377 Status VerifyUsages(base::DictionaryValue* dict, |
| 851 // JWK "alg" --> algorithm parameter | 378 blink::WebCryptoKeyUsageMask expected_usage_mask) { |
| 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 | 379 // JWK "key_ops" (optional) --> usage_mask parameter |
| 883 base::ListValue* jwk_key_ops_value = NULL; | 380 base::ListValue* jwk_key_ops_value = NULL; |
| 884 bool has_jwk_key_ops; | 381 bool has_jwk_key_ops; |
| 885 status = GetOptionalJwkList( | 382 Status status = |
| 886 dict_value, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops); | 383 GetOptionalJwkList(dict, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops); |
| 887 if (status.IsError()) | 384 if (status.IsError()) |
| 888 return status; | 385 return status; |
| 889 blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; | 386 blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; |
| 890 if (has_jwk_key_ops) { | 387 if (has_jwk_key_ops) { |
| 891 status = | 388 status = |
| 892 GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask); | 389 GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask); |
| 893 if (status.IsError()) | 390 if (status.IsError()) |
| 894 return status; | 391 return status; |
| 895 // The input usage_mask must be a subset of jwk_key_ops_mask. | 392 // The input usage_mask must be a subset of jwk_key_ops_mask. |
| 896 if (!ContainsKeyUsages(jwk_key_ops_mask, usage_mask)) | 393 if (!ContainsKeyUsages(jwk_key_ops_mask, expected_usage_mask)) |
| 897 return Status::ErrorJwkKeyopsInconsistent(); | 394 return Status::ErrorJwkKeyopsInconsistent(); |
| 898 } | 395 } |
| 899 | 396 |
| 900 // JWK "use" (optional) --> usage_mask parameter | 397 // JWK "use" (optional) --> usage_mask parameter |
| 901 std::string jwk_use_value; | 398 std::string jwk_use_value; |
| 902 bool has_jwk_use; | 399 bool has_jwk_use; |
| 903 status = | 400 status = GetOptionalJwkString(dict, "use", &jwk_use_value, &has_jwk_use); |
| 904 GetOptionalJwkString(dict_value, "use", &jwk_use_value, &has_jwk_use); | |
| 905 if (status.IsError()) | 401 if (status.IsError()) |
| 906 return status; | 402 return status; |
| 907 blink::WebCryptoKeyUsageMask jwk_use_mask = 0; | 403 blink::WebCryptoKeyUsageMask jwk_use_mask = 0; |
| 908 if (has_jwk_use) { | 404 if (has_jwk_use) { |
| 909 if (jwk_use_value == "enc") | 405 if (jwk_use_value == "enc") |
| 910 jwk_use_mask = kJwkEncUsage; | 406 jwk_use_mask = kJwkEncUsage; |
| 911 else if (jwk_use_value == "sig") | 407 else if (jwk_use_value == "sig") |
| 912 jwk_use_mask = kJwkSigUsage; | 408 jwk_use_mask = kJwkSigUsage; |
| 913 else | 409 else |
| 914 return Status::ErrorJwkUnrecognizedUse(); | 410 return Status::ErrorJwkUnrecognizedUse(); |
| 915 // The input usage_mask must be a subset of jwk_use_mask. | 411 // The input usage_mask must be a subset of jwk_use_mask. |
| 916 if (!ContainsKeyUsages(jwk_use_mask, usage_mask)) | 412 if (!ContainsKeyUsages(jwk_use_mask, expected_usage_mask)) |
| 917 return Status::ErrorJwkUseInconsistent(); | 413 return Status::ErrorJwkUseInconsistent(); |
| 918 } | 414 } |
| 919 | 415 |
| 920 // If both 'key_ops' and 'use' are present, ensure they are consistent. | 416 // If both 'key_ops' and 'use' are present, ensure they are consistent. |
| 921 if (has_jwk_key_ops && has_jwk_use && | 417 if (has_jwk_key_ops && has_jwk_use && |
| 922 !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask)) | 418 !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask)) |
| 923 return Status::ErrorJwkUseAndKeyopsInconsistent(); | 419 return Status::ErrorJwkUseAndKeyopsInconsistent(); |
| 924 | 420 |
| 925 // JWK keying material --> ImportKeyInternal() | 421 return Status::Success(); |
| 926 if (jwk_kty_value == "oct") { | 422 } |
| 927 std::string jwk_k_value; | 423 |
| 928 status = GetJwkBytes(dict_value, "k", &jwk_k_value); | 424 Status VerifyAlg(base::DictionaryValue* dict, |
| 929 if (status.IsError()) | 425 const std::string& expected_algorithm, |
| 930 return status; | 426 std::string* actual_algorithm) { |
| 931 | 427 // JWK "alg" --> algorithm parameter |
| 932 // Some JWK alg ID's embed information about the key length in the alg ID | 428 bool has_jwk_alg; |
| 933 // string. For example "A128CBC" implies the JWK carries 128 bits | 429 std::string jwk_alg_value; |
| 934 // of key material. For such keys validate that enough bytes were provided. | 430 Status status = |
| 935 // If this validation is not done, then it would be possible to select a | 431 GetOptionalJwkString(dict, "alg", &jwk_alg_value, &has_jwk_alg); |
| 936 // different algorithm by passing a different lengthed key, since that is | 432 if (status.IsError()) |
| 937 // how WebCrypto interprets things. | 433 return status; |
| 938 if (algorithm_info && | 434 if (actual_algorithm) { |
| 939 algorithm_info->IsInvalidKeyByteLength(jwk_k_value.size())) { | 435 if (has_jwk_alg) { |
| 940 return Status::ErrorJwkIncorrectKeyLength(); | 436 if (jwk_alg_value.empty()) |
| 437 return Status::ErrorJwkAlgorithmInconsistent(); |
| 438 *actual_algorithm = jwk_alg_value; |
| 439 } else { |
| 440 *actual_algorithm = std::string(); |
| 941 } | 441 } |
| 942 | |
| 943 return ImportKey(blink::WebCryptoKeyFormatRaw, | |
| 944 CryptoData(jwk_k_value), | |
| 945 algorithm, | |
| 946 extractable, | |
| 947 usage_mask, | |
| 948 key); | |
| 949 } | 442 } |
| 950 | 443 |
| 951 if (jwk_kty_value == "RSA") | 444 if (has_jwk_alg && !expected_algorithm.empty() && |
| 952 return ImportRsaKey(dict_value, algorithm, extractable, usage_mask, key); | 445 jwk_alg_value != expected_algorithm) { |
| 953 | 446 return Status::ErrorJwkAlgorithmInconsistent(); |
| 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 } | 447 } |
| 1003 | 448 |
| 1004 WriteKeyOps(key.usages(), &jwk_dict); | 449 return Status::Success(); |
| 1005 WriteExt(key.extractable(), &jwk_dict); | 450 } |
| 1006 status = WriteAlg(key.algorithm(), &jwk_dict); | 451 |
| 1007 if (status.IsError()) | 452 Status ParseJwkCommon(const CryptoData& bytes, |
| 1008 return status; | 453 const std::string& expected_algorithm, |
| 1009 | 454 bool expected_extractable, |
| 1010 std::string json; | 455 blink::WebCryptoKeyUsageMask expected_usage_mask, |
| 1011 base::JSONWriter::Write(&jwk_dict, &json); | 456 std::string* kty, |
| 1012 buffer->assign(json.data(), json.data() + json.size()); | 457 std::string* actual_algorithm, |
| 1013 return Status::Success(); | 458 scoped_ptr<base::DictionaryValue>* dict) { |
| 1014 } | 459 // Parse the incoming JWK JSON. |
| 1015 | 460 base::StringPiece json_string(reinterpret_cast<const char*>(bytes.bytes()), |
| 461 bytes.byte_length()); |
| 462 |
| 463 scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); |
| 464 base::DictionaryValue* dict_value = NULL; |
| 465 |
| 466 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) |
| 467 return Status::ErrorJwkNotDictionary(); |
| 468 |
| 469 // Transfer to caller. |
| 470 ignore_result(value.release()); |
| 471 dict->reset(dict_value); |
| 472 |
| 473 // JWK "kty". Exit early if this required JWK parameter is missing. |
| 474 Status status = GetJwkString(dict_value, "kty", kty); |
| 475 if (status.IsError()) |
| 476 return status; |
| 477 |
| 478 status = VerifyAlg(dict_value, expected_algorithm, actual_algorithm); |
| 479 if (status.IsError()) |
| 480 return status; |
| 481 |
| 482 status = VerifyExt(dict_value, expected_extractable); |
| 483 if (status.IsError()) |
| 484 return status; |
| 485 |
| 486 status = VerifyUsages(dict_value, expected_usage_mask); |
| 487 if (status.IsError()) |
| 488 return status; |
| 489 |
| 490 return Status::Success(); |
| 491 } |
| 492 |
| 493 } // namespace |
| 494 |
| 495 void WriteSecretKeyJwk(const CryptoData& raw_key_data, |
| 496 const std::string& algorithm, |
| 497 bool extractable, |
| 498 blink::WebCryptoKeyUsageMask usage_mask, |
| 499 std::vector<uint8>* jwk_key_data) { |
| 500 JwkWriter writer(algorithm, extractable, usage_mask, "oct"); |
| 501 writer.SetBase64Encoded("k", raw_key_data); |
| 502 writer.ToBytes(jwk_key_data); |
| 503 } |
| 504 |
| 505 Status ReadSecretKeyJwk(const CryptoData& key_data, |
| 506 const std::string& expected_algorithm, |
| 507 bool expected_extractable, |
| 508 blink::WebCryptoKeyUsageMask expected_usage_mask, |
| 509 std::vector<uint8>* raw_key_data, |
| 510 std::string* actual_algorithm) { |
| 511 if (!key_data.byte_length()) |
| 512 return Status::ErrorImportEmptyKeyData(); |
| 513 |
| 514 scoped_ptr<base::DictionaryValue> dict; |
| 515 std::string kty; |
| 516 Status status = ParseJwkCommon(key_data, |
| 517 expected_algorithm, |
| 518 expected_extractable, |
| 519 expected_usage_mask, |
| 520 &kty, |
| 521 actual_algorithm, |
| 522 &dict); |
| 523 if (status.IsError()) |
| 524 return status; |
| 525 |
| 526 if (kty != "oct") |
| 527 return Status::ErrorJwkUnexpectedKty("oct"); |
| 528 |
| 529 std::string jwk_k_value; |
| 530 status = GetJwkBytes(dict.get(), "k", &jwk_k_value); |
| 531 if (status.IsError()) |
| 532 return status; |
| 533 raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end()); |
| 534 |
| 535 return Status::Success(); |
| 536 } |
| 537 |
| 538 // Writes an RSA public key to a JWK dictionary |
| 539 void WriteRsaPublicKeyJwk(const CryptoData& n, |
| 540 const CryptoData& e, |
| 541 const std::string& algorithm, |
| 542 bool extractable, |
| 543 blink::WebCryptoKeyUsageMask usage_mask, |
| 544 std::vector<uint8>* jwk_key_data) { |
| 545 JwkWriter writer(algorithm, extractable, usage_mask, "RSA"); |
| 546 writer.SetBase64Encoded("n", n); |
| 547 writer.SetBase64Encoded("e", e); |
| 548 writer.ToBytes(jwk_key_data); |
| 549 } |
| 550 |
| 551 // Writes an RSA private key to a JWK dictionary |
| 552 void WriteRsaPrivateKeyJwk(const CryptoData& n, |
| 553 const CryptoData& e, |
| 554 const CryptoData& d, |
| 555 const CryptoData& p, |
| 556 const CryptoData& q, |
| 557 const CryptoData& dp, |
| 558 const CryptoData& dq, |
| 559 const CryptoData& qi, |
| 560 const std::string& algorithm, |
| 561 bool extractable, |
| 562 blink::WebCryptoKeyUsageMask usage_mask, |
| 563 std::vector<uint8>* jwk_key_data) { |
| 564 JwkWriter writer(algorithm, extractable, usage_mask, "RSA"); |
| 565 |
| 566 writer.SetBase64Encoded("n", n); |
| 567 writer.SetBase64Encoded("e", e); |
| 568 writer.SetBase64Encoded("d", d); |
| 569 // Although these are "optional" in the JWA, WebCrypto spec requires them to |
| 570 // be emitted. |
| 571 writer.SetBase64Encoded("p", p); |
| 572 writer.SetBase64Encoded("q", q); |
| 573 writer.SetBase64Encoded("dp", dp); |
| 574 writer.SetBase64Encoded("dq", dq); |
| 575 writer.SetBase64Encoded("qi", qi); |
| 576 writer.ToBytes(jwk_key_data); |
| 577 } |
| 578 |
| 579 JwkRsaInfo::JwkRsaInfo() : is_private_key(false) { |
| 580 } |
| 581 |
| 582 JwkRsaInfo::~JwkRsaInfo() { |
| 583 } |
| 584 |
| 585 Status ReadRsaKeyJwk(const CryptoData& key_data, |
| 586 const std::string& expected_algorithm, |
| 587 bool expected_extractable, |
| 588 blink::WebCryptoKeyUsageMask expected_usage_mask, |
| 589 JwkRsaInfo* result) { |
| 590 if (!key_data.byte_length()) |
| 591 return Status::ErrorImportEmptyKeyData(); |
| 592 |
| 593 scoped_ptr<base::DictionaryValue> dict; |
| 594 std::string kty; |
| 595 Status status = ParseJwkCommon(key_data, |
| 596 expected_algorithm, |
| 597 expected_extractable, |
| 598 expected_usage_mask, |
| 599 &kty, |
| 600 NULL, |
| 601 &dict); |
| 602 if (status.IsError()) |
| 603 return status; |
| 604 |
| 605 if (kty != "RSA") |
| 606 return Status::ErrorJwkUnexpectedKty("RSA"); |
| 607 |
| 608 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry |
| 609 // in the JWK, while an RSA private key must have those, plus at least a "d" |
| 610 // (private exponent) entry. |
| 611 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, |
| 612 // section 6.3. |
| 613 status = GetJwkBytes(dict.get(), "n", &result->n); |
| 614 if (status.IsError()) |
| 615 return status; |
| 616 status = GetJwkBytes(dict.get(), "e", &result->e); |
| 617 if (status.IsError()) |
| 618 return status; |
| 619 |
| 620 result->is_private_key = dict->HasKey("d"); |
| 621 if (!result->is_private_key) |
| 622 return Status::Success(); |
| 623 |
| 624 status = GetJwkBytes(dict.get(), "d", &result->d); |
| 625 if (status.IsError()) |
| 626 return status; |
| 627 |
| 628 // The "p", "q", "dp", "dq", and "qi" properties are optional. Treat these |
| 629 // properties the same if they are unspecified, as if they were specified-but |
| 630 // empty, since ImportRsaPrivateKey() doesn't do validation checks anyway. |
| 631 |
| 632 bool has_p; |
| 633 status = GetOptionalJwkBytes(dict.get(), "p", &result->p, &has_p); |
| 634 if (status.IsError()) |
| 635 return status; |
| 636 |
| 637 bool has_q; |
| 638 status = GetOptionalJwkBytes(dict.get(), "q", &result->q, &has_q); |
| 639 if (status.IsError()) |
| 640 return status; |
| 641 |
| 642 bool has_dp; |
| 643 status = GetOptionalJwkBytes(dict.get(), "dp", &result->dp, &has_dp); |
| 644 if (status.IsError()) |
| 645 return status; |
| 646 |
| 647 bool has_dq; |
| 648 status = GetOptionalJwkBytes(dict.get(), "dq", &result->dq, &has_dq); |
| 649 if (status.IsError()) |
| 650 return status; |
| 651 |
| 652 bool has_qi; |
| 653 status = GetOptionalJwkBytes(dict.get(), "qi", &result->qi, &has_qi); |
| 654 if (status.IsError()) |
| 655 return status; |
| 656 |
| 657 int num_optional_properties = has_p + has_q + has_dp + has_dq + has_qi; |
| 658 if (num_optional_properties != 0 && num_optional_properties != 5) |
| 659 return Status::ErrorJwkIncompleteOptionalRsaPrivateKey(); |
| 660 |
| 661 return Status::Success(); |
| 662 } |
| 663 |
| 1016 } // namespace webcrypto | 664 } // namespace webcrypto |
| 1017 | 665 |
| 1018 } // namespace content | 666 } // namespace content |
| OLD | NEW |