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" |
13 #include "base/strings/string_piece.h" | 14 #include "base/strings/string_piece.h" |
| 15 #include "base/strings/stringprintf.h" |
14 #include "content/child/webcrypto/crypto_data.h" | 16 #include "content/child/webcrypto/crypto_data.h" |
| 17 #include "content/child/webcrypto/platform_crypto.h" |
| 18 #include "content/child/webcrypto/shared_crypto.h" |
15 #include "content/child/webcrypto/status.h" | 19 #include "content/child/webcrypto/status.h" |
16 #include "content/child/webcrypto/webcrypto_util.h" | 20 #include "content/child/webcrypto/webcrypto_util.h" |
17 | 21 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" |
18 // TODO(eroman): The algorithm-specific logic in this file for AES and RSA | |
19 // should be moved into the corresponding AlgorithmImplementation file. It | |
20 // exists in this file to avoid duplication between OpenSSL and NSS | |
21 // implementations. | |
22 | 22 |
23 // JSON Web Key Format (JWK) | 23 // JSON Web Key Format (JWK) |
24 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21 | 24 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21 |
25 // | 25 // |
26 // A JWK is a simple JSON dictionary with the following entries | 26 // A JWK is a simple JSON dictionary with the following entries |
27 // - "kty" (Key Type) Parameter, REQUIRED | 27 // - "kty" (Key Type) Parameter, REQUIRED |
28 // - <kty-specific parameters, see below>, REQUIRED | 28 // - <kty-specific parameters, see below>, REQUIRED |
29 // - "use" (Key Use) Parameter, OPTIONAL | 29 // - "use" (Key Use) Parameter, OPTIONAL |
30 // - "key_ops" (Key Operations) Parameter, OPTIONAL | 30 // - "key_ops" (Key Operations) Parameter, OPTIONAL |
31 // - "alg" (Algorithm) Parameter, OPTIONAL | 31 // - "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 | 202 // value, else it is judged inconsistent. In all cases the input usage_mask |
203 // is used as the final usage_mask. | 203 // is used as the final usage_mask. |
204 // | 204 // |
205 | 205 |
206 namespace content { | 206 namespace content { |
207 | 207 |
208 namespace webcrypto { | 208 namespace webcrypto { |
209 | 209 |
210 namespace { | 210 namespace { |
211 | 211 |
| 212 // Creates an RSASSA-PKCS1-v1_5 algorithm. It is an error to call this with a |
| 213 // hash_id that is not a SHA*. |
| 214 blink::WebCryptoAlgorithm CreateRsaSsaImportAlgorithm( |
| 215 blink::WebCryptoAlgorithmId hash_id) { |
| 216 return CreateRsaHashedImportAlgorithm( |
| 217 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, hash_id); |
| 218 } |
| 219 |
| 220 // Creates an RSA-OAEP algorithm. It is an error to call this with a hash_id |
| 221 // that is not a SHA*. |
| 222 blink::WebCryptoAlgorithm CreateRsaOaepImportAlgorithm( |
| 223 blink::WebCryptoAlgorithmId hash_id) { |
| 224 return CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep, |
| 225 hash_id); |
| 226 } |
| 227 |
212 // Web Crypto equivalent usage mask for JWK 'use' = 'enc'. | 228 // Web Crypto equivalent usage mask for JWK 'use' = 'enc'. |
213 const blink::WebCryptoKeyUsageMask kJwkEncUsage = | 229 const blink::WebCryptoKeyUsageMask kJwkEncUsage = |
214 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | | 230 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | |
215 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey | | 231 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey | |
216 blink::WebCryptoKeyUsageDeriveKey | blink::WebCryptoKeyUsageDeriveBits; | 232 blink::WebCryptoKeyUsageDeriveKey | blink::WebCryptoKeyUsageDeriveBits; |
217 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'. | 233 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'. |
218 const blink::WebCryptoKeyUsageMask kJwkSigUsage = | 234 const blink::WebCryptoKeyUsageMask kJwkSigUsage = |
219 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; | 235 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; |
220 | 236 |
221 class JwkWriter { | 237 typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)(); |
| 238 |
| 239 class JwkAlgorithmInfo { |
222 public: | 240 public: |
223 JwkWriter(const std::string& algorithm, | 241 JwkAlgorithmInfo() |
224 bool extractable, | 242 : creation_func_(NULL), |
225 blink::WebCryptoKeyUsageMask usage_mask, | 243 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} |
226 const std::string& kty) { | 244 |
227 dict_.SetString("alg", algorithm); | 245 explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func) |
228 dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usage_mask)); | 246 : creation_func_(algorithm_creation_func), |
229 dict_.SetBoolean("ext", extractable); | 247 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} |
230 dict_.SetString("kty", kty); | 248 |
| 249 JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func, |
| 250 unsigned int required_key_length_bits) |
| 251 : creation_func_(algorithm_creation_func), |
| 252 required_key_length_bytes_(required_key_length_bits / 8) { |
| 253 DCHECK_EQ(0u, required_key_length_bits % 8); |
231 } | 254 } |
232 | 255 |
233 void Set(const std::string& key, const std::string& value) { | 256 bool CreateImportAlgorithm(blink::WebCryptoAlgorithm* algorithm) const { |
234 dict_.SetString(key, value); | 257 *algorithm = creation_func_(); |
| 258 return !algorithm->isNull(); |
235 } | 259 } |
236 | 260 |
237 void SetBase64Encoded(const std::string& key, const CryptoData& value) { | 261 bool IsInvalidKeyByteLength(size_t byte_length) const { |
238 dict_.SetString(key, | 262 if (required_key_length_bytes_ == NO_KEY_SIZE_REQUIREMENT) |
239 Base64EncodeUrlSafe(base::StringPiece( | 263 return false; |
240 reinterpret_cast<const char*>(value.bytes()), | 264 return required_key_length_bytes_ != byte_length; |
241 value.byte_length()))); | |
242 } | |
243 | |
244 void ToBytes(std::vector<uint8>* utf8_bytes) { | |
245 std::string json; | |
246 base::JSONWriter::Write(&dict_, &json); | |
247 utf8_bytes->assign(json.begin(), json.end()); | |
248 } | 265 } |
249 | 266 |
250 private: | 267 private: |
251 base::DictionaryValue dict_; | 268 static const unsigned int NO_KEY_SIZE_REQUIREMENT = UINT_MAX; |
| 269 |
| 270 AlgorithmCreationFunc creation_func_; |
| 271 |
| 272 // The expected key size for the algorithm or NO_KEY_SIZE_REQUIREMENT. |
| 273 unsigned int required_key_length_bytes_; |
252 }; | 274 }; |
253 | 275 |
| 276 typedef std::map<std::string, JwkAlgorithmInfo> JwkAlgorithmInfoMap; |
| 277 |
| 278 class JwkAlgorithmRegistry { |
| 279 public: |
| 280 JwkAlgorithmRegistry() { |
| 281 // TODO(eroman): |
| 282 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-20 |
| 283 // says HMAC with SHA-2 should have a key size at least as large as the |
| 284 // hash output. |
| 285 alg_to_info_["HS1"] = |
| 286 JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, |
| 287 blink::WebCryptoAlgorithmIdSha1>); |
| 288 alg_to_info_["HS256"] = |
| 289 JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, |
| 290 blink::WebCryptoAlgorithmIdSha256>); |
| 291 alg_to_info_["HS384"] = |
| 292 JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, |
| 293 blink::WebCryptoAlgorithmIdSha384>); |
| 294 alg_to_info_["HS512"] = |
| 295 JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, |
| 296 blink::WebCryptoAlgorithmIdSha512>); |
| 297 alg_to_info_["RS1"] = |
| 298 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, |
| 299 blink::WebCryptoAlgorithmIdSha1>); |
| 300 alg_to_info_["RS256"] = |
| 301 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, |
| 302 blink::WebCryptoAlgorithmIdSha256>); |
| 303 alg_to_info_["RS384"] = |
| 304 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, |
| 305 blink::WebCryptoAlgorithmIdSha384>); |
| 306 alg_to_info_["RS512"] = |
| 307 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, |
| 308 blink::WebCryptoAlgorithmIdSha512>); |
| 309 alg_to_info_["RSA-OAEP"] = |
| 310 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, |
| 311 blink::WebCryptoAlgorithmIdSha1>); |
| 312 alg_to_info_["RSA-OAEP-256"] = |
| 313 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, |
| 314 blink::WebCryptoAlgorithmIdSha256>); |
| 315 alg_to_info_["RSA-OAEP-384"] = |
| 316 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, |
| 317 blink::WebCryptoAlgorithmIdSha384>); |
| 318 alg_to_info_["RSA-OAEP-512"] = |
| 319 JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, |
| 320 blink::WebCryptoAlgorithmIdSha512>); |
| 321 alg_to_info_["A128KW"] = JwkAlgorithmInfo( |
| 322 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>, |
| 323 128); |
| 324 alg_to_info_["A192KW"] = JwkAlgorithmInfo( |
| 325 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>, |
| 326 192); |
| 327 alg_to_info_["A256KW"] = JwkAlgorithmInfo( |
| 328 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>, |
| 329 256); |
| 330 alg_to_info_["A128GCM"] = JwkAlgorithmInfo( |
| 331 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, |
| 332 128); |
| 333 alg_to_info_["A192GCM"] = JwkAlgorithmInfo( |
| 334 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, |
| 335 192); |
| 336 alg_to_info_["A256GCM"] = JwkAlgorithmInfo( |
| 337 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, |
| 338 256); |
| 339 alg_to_info_["A128CBC"] = JwkAlgorithmInfo( |
| 340 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, |
| 341 128); |
| 342 alg_to_info_["A192CBC"] = JwkAlgorithmInfo( |
| 343 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, |
| 344 192); |
| 345 alg_to_info_["A256CBC"] = JwkAlgorithmInfo( |
| 346 &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, |
| 347 256); |
| 348 } |
| 349 |
| 350 // Returns NULL if the algorithm name was not registered. |
| 351 const JwkAlgorithmInfo* GetAlgorithmInfo(const std::string& jwk_alg) const { |
| 352 const JwkAlgorithmInfoMap::const_iterator pos = alg_to_info_.find(jwk_alg); |
| 353 if (pos == alg_to_info_.end()) |
| 354 return NULL; |
| 355 return &pos->second; |
| 356 } |
| 357 |
| 358 private: |
| 359 // Binds a WebCryptoAlgorithmId value to a compatible factory function. |
| 360 typedef blink::WebCryptoAlgorithm (*FuncWithWebCryptoAlgIdArg)( |
| 361 blink::WebCryptoAlgorithmId); |
| 362 template <FuncWithWebCryptoAlgIdArg func, |
| 363 blink::WebCryptoAlgorithmId algorithm_id> |
| 364 static blink::WebCryptoAlgorithm BindAlgorithmId() { |
| 365 return func(algorithm_id); |
| 366 } |
| 367 |
| 368 JwkAlgorithmInfoMap alg_to_info_; |
| 369 }; |
| 370 |
| 371 base::LazyInstance<JwkAlgorithmRegistry> jwk_alg_registry = |
| 372 LAZY_INSTANCE_INITIALIZER; |
| 373 |
| 374 bool ImportAlgorithmsConsistent(const blink::WebCryptoAlgorithm& alg1, |
| 375 const blink::WebCryptoAlgorithm& alg2) { |
| 376 DCHECK(!alg1.isNull()); |
| 377 DCHECK(!alg2.isNull()); |
| 378 if (alg1.id() != alg2.id()) |
| 379 return false; |
| 380 if (alg1.paramsType() != alg2.paramsType()) |
| 381 return false; |
| 382 switch (alg1.paramsType()) { |
| 383 case blink::WebCryptoAlgorithmParamsTypeNone: |
| 384 return true; |
| 385 case blink::WebCryptoAlgorithmParamsTypeRsaHashedImportParams: |
| 386 return ImportAlgorithmsConsistent(alg1.rsaHashedImportParams()->hash(), |
| 387 alg2.rsaHashedImportParams()->hash()); |
| 388 case blink::WebCryptoAlgorithmParamsTypeHmacImportParams: |
| 389 return ImportAlgorithmsConsistent(alg1.hmacImportParams()->hash(), |
| 390 alg2.hmacImportParams()->hash()); |
| 391 default: |
| 392 return false; |
| 393 } |
| 394 } |
| 395 |
254 // Extracts the required string property with key |path| from |dict| and saves | 396 // Extracts the required string property with key |path| from |dict| and saves |
255 // the result to |*result|. If the property does not exist or is not a string, | 397 // the result to |*result|. If the property does not exist or is not a string, |
256 // returns an error. | 398 // returns an error. |
257 Status GetJwkString(base::DictionaryValue* dict, | 399 Status GetJwkString(base::DictionaryValue* dict, |
258 const std::string& path, | 400 const std::string& path, |
259 std::string* result) { | 401 std::string* result) { |
260 base::Value* value = NULL; | 402 base::Value* value = NULL; |
261 if (!dict->Get(path, &value)) | 403 if (!dict->Get(path, &value)) |
262 return Status::ErrorJwkPropertyMissing(path); | 404 return Status::ErrorJwkPropertyMissing(path); |
263 if (!value->GetAsString(result)) | 405 if (!value->GetAsString(result)) |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
360 if (!dict->Get(path, &value)) | 502 if (!dict->Get(path, &value)) |
361 return Status::Success(); | 503 return Status::Success(); |
362 | 504 |
363 if (!value->GetAsBoolean(result)) | 505 if (!value->GetAsBoolean(result)) |
364 return Status::ErrorJwkPropertyWrongType(path, "boolean"); | 506 return Status::ErrorJwkPropertyWrongType(path, "boolean"); |
365 | 507 |
366 *property_exists = true; | 508 *property_exists = true; |
367 return Status::Success(); | 509 return Status::Success(); |
368 } | 510 } |
369 | 511 |
370 Status VerifyExt(base::DictionaryValue* dict, bool expected_extractable) { | 512 // Writes a secret/symmetric key to a JWK dictionary. |
| 513 void WriteSecretKey(const std::vector<uint8>& raw_key, |
| 514 base::DictionaryValue* jwk_dict) { |
| 515 DCHECK(jwk_dict); |
| 516 jwk_dict->SetString("kty", "oct"); |
| 517 // For a secret/symmetric key, the only extra JWK field is 'k', containing the |
| 518 // base64url encoding of the raw key. |
| 519 const base::StringPiece key_str( |
| 520 reinterpret_cast<const char*>(Uint8VectorStart(raw_key)), raw_key.size()); |
| 521 jwk_dict->SetString("k", Base64EncodeUrlSafe(key_str)); |
| 522 } |
| 523 |
| 524 // Writes an RSA public key to a JWK dictionary |
| 525 void WriteRsaPublicKey(const std::vector<uint8>& modulus, |
| 526 const std::vector<uint8>& public_exponent, |
| 527 base::DictionaryValue* jwk_dict) { |
| 528 DCHECK(jwk_dict); |
| 529 DCHECK(modulus.size()); |
| 530 DCHECK(public_exponent.size()); |
| 531 jwk_dict->SetString("kty", "RSA"); |
| 532 jwk_dict->SetString("n", Base64EncodeUrlSafe(modulus)); |
| 533 jwk_dict->SetString("e", Base64EncodeUrlSafe(public_exponent)); |
| 534 } |
| 535 |
| 536 // Writes an RSA private key to a JWK dictionary |
| 537 Status ExportRsaPrivateKeyJwk(const blink::WebCryptoKey& key, |
| 538 base::DictionaryValue* jwk_dict) { |
| 539 platform::PrivateKey* private_key; |
| 540 Status status = ToPlatformPrivateKey(key, &private_key); |
| 541 if (status.IsError()) |
| 542 return status; |
| 543 |
| 544 // TODO(eroman): Copying the key properties to temporary vectors is |
| 545 // inefficient. Once there aren't two implementations of platform_crypto this |
| 546 // and other code will be easier to streamline. |
| 547 std::vector<uint8> modulus; |
| 548 std::vector<uint8> public_exponent; |
| 549 std::vector<uint8> private_exponent; |
| 550 std::vector<uint8> prime1; |
| 551 std::vector<uint8> prime2; |
| 552 std::vector<uint8> exponent1; |
| 553 std::vector<uint8> exponent2; |
| 554 std::vector<uint8> coefficient; |
| 555 |
| 556 status = platform::ExportRsaPrivateKey(private_key, |
| 557 &modulus, |
| 558 &public_exponent, |
| 559 &private_exponent, |
| 560 &prime1, |
| 561 &prime2, |
| 562 &exponent1, |
| 563 &exponent2, |
| 564 &coefficient); |
| 565 if (status.IsError()) |
| 566 return status; |
| 567 |
| 568 jwk_dict->SetString("kty", "RSA"); |
| 569 jwk_dict->SetString("n", Base64EncodeUrlSafe(modulus)); |
| 570 jwk_dict->SetString("e", Base64EncodeUrlSafe(public_exponent)); |
| 571 jwk_dict->SetString("d", Base64EncodeUrlSafe(private_exponent)); |
| 572 // Although these are "optional" in the JWA, WebCrypto spec requires them to |
| 573 // be emitted. |
| 574 jwk_dict->SetString("p", Base64EncodeUrlSafe(prime1)); |
| 575 jwk_dict->SetString("q", Base64EncodeUrlSafe(prime2)); |
| 576 jwk_dict->SetString("dp", Base64EncodeUrlSafe(exponent1)); |
| 577 jwk_dict->SetString("dq", Base64EncodeUrlSafe(exponent2)); |
| 578 jwk_dict->SetString("qi", Base64EncodeUrlSafe(coefficient)); |
| 579 |
| 580 return Status::Success(); |
| 581 } |
| 582 |
| 583 // Writes a Web Crypto usage mask to a JWK dictionary. |
| 584 void WriteKeyOps(blink::WebCryptoKeyUsageMask key_usages, |
| 585 base::DictionaryValue* jwk_dict) { |
| 586 jwk_dict->Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(key_usages)); |
| 587 } |
| 588 |
| 589 // Writes a Web Crypto extractable value to a JWK dictionary. |
| 590 void WriteExt(bool extractable, base::DictionaryValue* jwk_dict) { |
| 591 jwk_dict->SetBoolean("ext", extractable); |
| 592 } |
| 593 |
| 594 // Writes a Web Crypto algorithm to a JWK dictionary. |
| 595 Status WriteAlg(const blink::WebCryptoKeyAlgorithm& algorithm, |
| 596 base::DictionaryValue* jwk_dict) { |
| 597 switch (algorithm.paramsType()) { |
| 598 case blink::WebCryptoKeyAlgorithmParamsTypeAes: { |
| 599 DCHECK(algorithm.aesParams()); |
| 600 const char* aes_prefix = ""; |
| 601 switch (algorithm.aesParams()->lengthBits()) { |
| 602 case 128: |
| 603 aes_prefix = "A128"; |
| 604 break; |
| 605 case 192: |
| 606 aes_prefix = "A192"; |
| 607 break; |
| 608 case 256: |
| 609 aes_prefix = "A256"; |
| 610 break; |
| 611 default: |
| 612 NOTREACHED(); // bad key length means algorithm was built improperly |
| 613 return Status::ErrorUnexpected(); |
| 614 } |
| 615 const char* aes_suffix = ""; |
| 616 switch (algorithm.id()) { |
| 617 case blink::WebCryptoAlgorithmIdAesCbc: |
| 618 aes_suffix = "CBC"; |
| 619 break; |
| 620 case blink::WebCryptoAlgorithmIdAesCtr: |
| 621 aes_suffix = "CTR"; |
| 622 break; |
| 623 case blink::WebCryptoAlgorithmIdAesGcm: |
| 624 aes_suffix = "GCM"; |
| 625 break; |
| 626 case blink::WebCryptoAlgorithmIdAesKw: |
| 627 aes_suffix = "KW"; |
| 628 break; |
| 629 default: |
| 630 return Status::ErrorUnsupported(); |
| 631 } |
| 632 jwk_dict->SetString("alg", |
| 633 base::StringPrintf("%s%s", aes_prefix, aes_suffix)); |
| 634 break; |
| 635 } |
| 636 case blink::WebCryptoKeyAlgorithmParamsTypeHmac: { |
| 637 DCHECK(algorithm.hmacParams()); |
| 638 switch (algorithm.hmacParams()->hash().id()) { |
| 639 case blink::WebCryptoAlgorithmIdSha1: |
| 640 jwk_dict->SetString("alg", "HS1"); |
| 641 break; |
| 642 case blink::WebCryptoAlgorithmIdSha256: |
| 643 jwk_dict->SetString("alg", "HS256"); |
| 644 break; |
| 645 case blink::WebCryptoAlgorithmIdSha384: |
| 646 jwk_dict->SetString("alg", "HS384"); |
| 647 break; |
| 648 case blink::WebCryptoAlgorithmIdSha512: |
| 649 jwk_dict->SetString("alg", "HS512"); |
| 650 break; |
| 651 default: |
| 652 NOTREACHED(); |
| 653 return Status::ErrorUnexpected(); |
| 654 } |
| 655 break; |
| 656 } |
| 657 case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed: |
| 658 switch (algorithm.id()) { |
| 659 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: { |
| 660 switch (algorithm.rsaHashedParams()->hash().id()) { |
| 661 case blink::WebCryptoAlgorithmIdSha1: |
| 662 jwk_dict->SetString("alg", "RS1"); |
| 663 break; |
| 664 case blink::WebCryptoAlgorithmIdSha256: |
| 665 jwk_dict->SetString("alg", "RS256"); |
| 666 break; |
| 667 case blink::WebCryptoAlgorithmIdSha384: |
| 668 jwk_dict->SetString("alg", "RS384"); |
| 669 break; |
| 670 case blink::WebCryptoAlgorithmIdSha512: |
| 671 jwk_dict->SetString("alg", "RS512"); |
| 672 break; |
| 673 default: |
| 674 NOTREACHED(); |
| 675 return Status::ErrorUnexpected(); |
| 676 } |
| 677 break; |
| 678 } |
| 679 case blink::WebCryptoAlgorithmIdRsaOaep: { |
| 680 switch (algorithm.rsaHashedParams()->hash().id()) { |
| 681 case blink::WebCryptoAlgorithmIdSha1: |
| 682 jwk_dict->SetString("alg", "RSA-OAEP"); |
| 683 break; |
| 684 case blink::WebCryptoAlgorithmIdSha256: |
| 685 jwk_dict->SetString("alg", "RSA-OAEP-256"); |
| 686 break; |
| 687 case blink::WebCryptoAlgorithmIdSha384: |
| 688 jwk_dict->SetString("alg", "RSA-OAEP-384"); |
| 689 break; |
| 690 case blink::WebCryptoAlgorithmIdSha512: |
| 691 jwk_dict->SetString("alg", "RSA-OAEP-512"); |
| 692 break; |
| 693 default: |
| 694 NOTREACHED(); |
| 695 return Status::ErrorUnexpected(); |
| 696 } |
| 697 break; |
| 698 } |
| 699 default: |
| 700 NOTREACHED(); |
| 701 return Status::ErrorUnexpected(); |
| 702 } |
| 703 break; |
| 704 default: |
| 705 return Status::ErrorUnsupported(); |
| 706 } |
| 707 return Status::Success(); |
| 708 } |
| 709 |
| 710 bool IsRsaKey(const blink::WebCryptoKey& key) { |
| 711 return IsAlgorithmRsa(key.algorithm().id()); |
| 712 } |
| 713 |
| 714 Status ImportRsaKey(base::DictionaryValue* dict, |
| 715 const blink::WebCryptoAlgorithm& algorithm, |
| 716 bool extractable, |
| 717 blink::WebCryptoKeyUsageMask usage_mask, |
| 718 blink::WebCryptoKey* key) { |
| 719 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry |
| 720 // in the JWK, while an RSA private key must have those, plus at least a "d" |
| 721 // (private exponent) entry. |
| 722 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, |
| 723 // section 6.3. |
| 724 std::string jwk_n_value; |
| 725 Status status = GetJwkBytes(dict, "n", &jwk_n_value); |
| 726 if (status.IsError()) |
| 727 return status; |
| 728 std::string jwk_e_value; |
| 729 status = GetJwkBytes(dict, "e", &jwk_e_value); |
| 730 if (status.IsError()) |
| 731 return status; |
| 732 |
| 733 bool is_public_key = !dict->HasKey("d"); |
| 734 |
| 735 // Now that the key type is known, do an additional check on the usages to |
| 736 // make sure they are all applicable for this algorithm + key type. |
| 737 status = CheckKeyUsages(algorithm.id(), |
| 738 is_public_key ? blink::WebCryptoKeyTypePublic |
| 739 : blink::WebCryptoKeyTypePrivate, |
| 740 usage_mask); |
| 741 |
| 742 if (status.IsError()) |
| 743 return status; |
| 744 |
| 745 if (is_public_key) { |
| 746 return platform::ImportRsaPublicKey(algorithm, |
| 747 extractable, |
| 748 usage_mask, |
| 749 CryptoData(jwk_n_value), |
| 750 CryptoData(jwk_e_value), |
| 751 key); |
| 752 } |
| 753 |
| 754 std::string jwk_d_value; |
| 755 status = GetJwkBytes(dict, "d", &jwk_d_value); |
| 756 if (status.IsError()) |
| 757 return status; |
| 758 |
| 759 // The "p", "q", "dp", "dq", and "qi" properties are optional. Treat these |
| 760 // properties the same if they are unspecified, as if they were specified-but |
| 761 // empty, since ImportRsaPrivateKey() doesn't do validation checks anyway. |
| 762 |
| 763 std::string jwk_p_value; |
| 764 bool has_p; |
| 765 status = GetOptionalJwkBytes(dict, "p", &jwk_p_value, &has_p); |
| 766 if (status.IsError()) |
| 767 return status; |
| 768 |
| 769 std::string jwk_q_value; |
| 770 bool has_q; |
| 771 status = GetOptionalJwkBytes(dict, "q", &jwk_q_value, &has_q); |
| 772 if (status.IsError()) |
| 773 return status; |
| 774 |
| 775 std::string jwk_dp_value; |
| 776 bool has_dp; |
| 777 status = GetOptionalJwkBytes(dict, "dp", &jwk_dp_value, &has_dp); |
| 778 if (status.IsError()) |
| 779 return status; |
| 780 |
| 781 std::string jwk_dq_value; |
| 782 bool has_dq; |
| 783 status = GetOptionalJwkBytes(dict, "dq", &jwk_dq_value, &has_dq); |
| 784 if (status.IsError()) |
| 785 return status; |
| 786 |
| 787 std::string jwk_qi_value; |
| 788 bool has_qi; |
| 789 status = GetOptionalJwkBytes(dict, "qi", &jwk_qi_value, &has_qi); |
| 790 if (status.IsError()) |
| 791 return status; |
| 792 |
| 793 int num_optional_properties = has_p + has_q + has_dp + has_dq + has_qi; |
| 794 if (num_optional_properties != 0 && num_optional_properties != 5) |
| 795 return Status::ErrorJwkIncompleteOptionalRsaPrivateKey(); |
| 796 |
| 797 return platform::ImportRsaPrivateKey( |
| 798 algorithm, |
| 799 extractable, |
| 800 usage_mask, |
| 801 CryptoData(jwk_n_value), // modulus |
| 802 CryptoData(jwk_e_value), // public_exponent |
| 803 CryptoData(jwk_d_value), // private_exponent |
| 804 CryptoData(jwk_p_value), // prime1 |
| 805 CryptoData(jwk_q_value), // prime2 |
| 806 CryptoData(jwk_dp_value), // exponent1 |
| 807 CryptoData(jwk_dq_value), // exponent2 |
| 808 CryptoData(jwk_qi_value), // coefficient |
| 809 key); |
| 810 } |
| 811 |
| 812 } // namespace |
| 813 |
| 814 // TODO(eroman): Split this up into smaller functions. |
| 815 Status ImportKeyJwk(const CryptoData& key_data, |
| 816 const blink::WebCryptoAlgorithm& algorithm, |
| 817 bool extractable, |
| 818 blink::WebCryptoKeyUsageMask usage_mask, |
| 819 blink::WebCryptoKey* key) { |
| 820 if (!key_data.byte_length()) |
| 821 return Status::ErrorImportEmptyKeyData(); |
| 822 DCHECK(key); |
| 823 |
| 824 // Parse the incoming JWK JSON. |
| 825 base::StringPiece json_string(reinterpret_cast<const char*>(key_data.bytes()), |
| 826 key_data.byte_length()); |
| 827 scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); |
| 828 // Note, bare pointer dict_value is ok since it points into scoped value. |
| 829 base::DictionaryValue* dict_value = NULL; |
| 830 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) |
| 831 return Status::ErrorJwkNotDictionary(); |
| 832 |
| 833 // JWK "kty". Exit early if this required JWK parameter is missing. |
| 834 std::string jwk_kty_value; |
| 835 Status status = GetJwkString(dict_value, "kty", &jwk_kty_value); |
| 836 if (status.IsError()) |
| 837 return status; |
| 838 |
371 // JWK "ext" (optional) --> extractable parameter | 839 // JWK "ext" (optional) --> extractable parameter |
372 bool jwk_ext_value = false; | 840 { |
373 bool has_jwk_ext; | 841 bool jwk_ext_value = false; |
374 Status status = GetOptionalJwkBool(dict, "ext", &jwk_ext_value, &has_jwk_ext); | 842 bool has_jwk_ext; |
375 if (status.IsError()) | 843 status = |
376 return status; | 844 GetOptionalJwkBool(dict_value, "ext", &jwk_ext_value, &has_jwk_ext); |
377 if (has_jwk_ext && expected_extractable && !jwk_ext_value) | 845 if (status.IsError()) |
378 return Status::ErrorJwkExtInconsistent(); | 846 return status; |
379 return Status::Success(); | 847 if (has_jwk_ext && !jwk_ext_value && extractable) |
380 } | 848 return Status::ErrorJwkExtInconsistent(); |
381 | 849 } |
382 Status VerifyUsages(base::DictionaryValue* dict, | 850 |
383 blink::WebCryptoKeyUsageMask expected_usage_mask) { | 851 // JWK "alg" --> algorithm parameter |
| 852 // 1. JWK alg present but unrecognized: error |
| 853 // 2. JWK alg valid and inconsistent with input algorithm: error |
| 854 // 3. JWK alg valid and consistent with input algorithm: use input value |
| 855 // 4. JWK alg is missing: use input value |
| 856 const JwkAlgorithmInfo* algorithm_info = NULL; |
| 857 std::string jwk_alg_value; |
| 858 bool has_jwk_alg; |
| 859 status = |
| 860 GetOptionalJwkString(dict_value, "alg", &jwk_alg_value, &has_jwk_alg); |
| 861 if (status.IsError()) |
| 862 return status; |
| 863 |
| 864 if (has_jwk_alg) { |
| 865 // JWK alg present |
| 866 |
| 867 // TODO(padolph): Validate alg vs kty. For example kty="RSA" implies alg can |
| 868 // only be from the RSA family. |
| 869 |
| 870 blink::WebCryptoAlgorithm jwk_algorithm = |
| 871 blink::WebCryptoAlgorithm::createNull(); |
| 872 algorithm_info = jwk_alg_registry.Get().GetAlgorithmInfo(jwk_alg_value); |
| 873 if (!algorithm_info || |
| 874 !algorithm_info->CreateImportAlgorithm(&jwk_algorithm)) |
| 875 return Status::ErrorJwkUnrecognizedAlgorithm(); |
| 876 |
| 877 if (!ImportAlgorithmsConsistent(jwk_algorithm, algorithm)) |
| 878 return Status::ErrorJwkAlgorithmInconsistent(); |
| 879 } |
| 880 DCHECK(!algorithm.isNull()); |
| 881 |
384 // JWK "key_ops" (optional) --> usage_mask parameter | 882 // JWK "key_ops" (optional) --> usage_mask parameter |
385 base::ListValue* jwk_key_ops_value = NULL; | 883 base::ListValue* jwk_key_ops_value = NULL; |
386 bool has_jwk_key_ops; | 884 bool has_jwk_key_ops; |
387 Status status = | 885 status = GetOptionalJwkList( |
388 GetOptionalJwkList(dict, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops); | 886 dict_value, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops); |
389 if (status.IsError()) | 887 if (status.IsError()) |
390 return status; | 888 return status; |
391 blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; | 889 blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; |
392 if (has_jwk_key_ops) { | 890 if (has_jwk_key_ops) { |
393 status = | 891 status = |
394 GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask); | 892 GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask); |
395 if (status.IsError()) | 893 if (status.IsError()) |
396 return status; | 894 return status; |
397 // The input usage_mask must be a subset of jwk_key_ops_mask. | 895 // The input usage_mask must be a subset of jwk_key_ops_mask. |
398 if (!ContainsKeyUsages(jwk_key_ops_mask, expected_usage_mask)) | 896 if (!ContainsKeyUsages(jwk_key_ops_mask, usage_mask)) |
399 return Status::ErrorJwkKeyopsInconsistent(); | 897 return Status::ErrorJwkKeyopsInconsistent(); |
400 } | 898 } |
401 | 899 |
402 // JWK "use" (optional) --> usage_mask parameter | 900 // JWK "use" (optional) --> usage_mask parameter |
403 std::string jwk_use_value; | 901 std::string jwk_use_value; |
404 bool has_jwk_use; | 902 bool has_jwk_use; |
405 status = GetOptionalJwkString(dict, "use", &jwk_use_value, &has_jwk_use); | 903 status = |
| 904 GetOptionalJwkString(dict_value, "use", &jwk_use_value, &has_jwk_use); |
406 if (status.IsError()) | 905 if (status.IsError()) |
407 return status; | 906 return status; |
408 blink::WebCryptoKeyUsageMask jwk_use_mask = 0; | 907 blink::WebCryptoKeyUsageMask jwk_use_mask = 0; |
409 if (has_jwk_use) { | 908 if (has_jwk_use) { |
410 if (jwk_use_value == "enc") | 909 if (jwk_use_value == "enc") |
411 jwk_use_mask = kJwkEncUsage; | 910 jwk_use_mask = kJwkEncUsage; |
412 else if (jwk_use_value == "sig") | 911 else if (jwk_use_value == "sig") |
413 jwk_use_mask = kJwkSigUsage; | 912 jwk_use_mask = kJwkSigUsage; |
414 else | 913 else |
415 return Status::ErrorJwkUnrecognizedUse(); | 914 return Status::ErrorJwkUnrecognizedUse(); |
416 // The input usage_mask must be a subset of jwk_use_mask. | 915 // The input usage_mask must be a subset of jwk_use_mask. |
417 if (!ContainsKeyUsages(jwk_use_mask, expected_usage_mask)) | 916 if (!ContainsKeyUsages(jwk_use_mask, usage_mask)) |
418 return Status::ErrorJwkUseInconsistent(); | 917 return Status::ErrorJwkUseInconsistent(); |
419 } | 918 } |
420 | 919 |
421 // If both 'key_ops' and 'use' are present, ensure they are consistent. | 920 // If both 'key_ops' and 'use' are present, ensure they are consistent. |
422 if (has_jwk_key_ops && has_jwk_use && | 921 if (has_jwk_key_ops && has_jwk_use && |
423 !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask)) | 922 !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask)) |
424 return Status::ErrorJwkUseAndKeyopsInconsistent(); | 923 return Status::ErrorJwkUseAndKeyopsInconsistent(); |
425 | 924 |
| 925 // JWK keying material --> ImportKeyInternal() |
| 926 if (jwk_kty_value == "oct") { |
| 927 std::string jwk_k_value; |
| 928 status = GetJwkBytes(dict_value, "k", &jwk_k_value); |
| 929 if (status.IsError()) |
| 930 return status; |
| 931 |
| 932 // Some JWK alg ID's embed information about the key length in the alg ID |
| 933 // string. For example "A128CBC" implies the JWK carries 128 bits |
| 934 // of key material. For such keys validate that enough bytes were provided. |
| 935 // If this validation is not done, then it would be possible to select a |
| 936 // different algorithm by passing a different lengthed key, since that is |
| 937 // how WebCrypto interprets things. |
| 938 if (algorithm_info && |
| 939 algorithm_info->IsInvalidKeyByteLength(jwk_k_value.size())) { |
| 940 return Status::ErrorJwkIncorrectKeyLength(); |
| 941 } |
| 942 |
| 943 return ImportKey(blink::WebCryptoKeyFormatRaw, |
| 944 CryptoData(jwk_k_value), |
| 945 algorithm, |
| 946 extractable, |
| 947 usage_mask, |
| 948 key); |
| 949 } |
| 950 |
| 951 if (jwk_kty_value == "RSA") |
| 952 return ImportRsaKey(dict_value, algorithm, extractable, usage_mask, key); |
| 953 |
| 954 return Status::ErrorJwkUnrecognizedKty(); |
| 955 } |
| 956 |
| 957 Status ExportKeyJwk(const blink::WebCryptoKey& key, |
| 958 std::vector<uint8>* buffer) { |
| 959 DCHECK(key.extractable()); |
| 960 base::DictionaryValue jwk_dict; |
| 961 Status status = Status::OperationError(); |
| 962 |
| 963 switch (key.type()) { |
| 964 case blink::WebCryptoKeyTypeSecret: { |
| 965 std::vector<uint8> exported_key; |
| 966 status = ExportKey(blink::WebCryptoKeyFormatRaw, key, &exported_key); |
| 967 if (status.IsError()) |
| 968 return status; |
| 969 WriteSecretKey(exported_key, &jwk_dict); |
| 970 break; |
| 971 } |
| 972 case blink::WebCryptoKeyTypePublic: { |
| 973 // TODO(eroman): Update when there are asymmetric keys other than RSA. |
| 974 if (!IsRsaKey(key)) |
| 975 return Status::ErrorUnsupported(); |
| 976 platform::PublicKey* public_key; |
| 977 status = ToPlatformPublicKey(key, &public_key); |
| 978 if (status.IsError()) |
| 979 return status; |
| 980 std::vector<uint8> modulus; |
| 981 std::vector<uint8> public_exponent; |
| 982 status = |
| 983 platform::ExportRsaPublicKey(public_key, &modulus, &public_exponent); |
| 984 if (status.IsError()) |
| 985 return status; |
| 986 WriteRsaPublicKey(modulus, public_exponent, &jwk_dict); |
| 987 break; |
| 988 } |
| 989 case blink::WebCryptoKeyTypePrivate: { |
| 990 // TODO(eroman): Update when there are asymmetric keys other than RSA. |
| 991 if (!IsRsaKey(key)) |
| 992 return Status::ErrorUnsupported(); |
| 993 |
| 994 status = ExportRsaPrivateKeyJwk(key, &jwk_dict); |
| 995 if (status.IsError()) |
| 996 return status; |
| 997 break; |
| 998 } |
| 999 |
| 1000 default: |
| 1001 return Status::ErrorUnsupported(); |
| 1002 } |
| 1003 |
| 1004 WriteKeyOps(key.usages(), &jwk_dict); |
| 1005 WriteExt(key.extractable(), &jwk_dict); |
| 1006 status = WriteAlg(key.algorithm(), &jwk_dict); |
| 1007 if (status.IsError()) |
| 1008 return status; |
| 1009 |
| 1010 std::string json; |
| 1011 base::JSONWriter::Write(&jwk_dict, &json); |
| 1012 buffer->assign(json.data(), json.data() + json.size()); |
426 return Status::Success(); | 1013 return Status::Success(); |
427 } | 1014 } |
428 | 1015 |
429 Status VerifyAlg(base::DictionaryValue* dict, | |
430 const std::string& expected_algorithm) { | |
431 // JWK "alg" --> algorithm parameter | |
432 bool has_jwk_alg; | |
433 std::string jwk_alg_value; | |
434 Status status = | |
435 GetOptionalJwkString(dict, "alg", &jwk_alg_value, &has_jwk_alg); | |
436 if (status.IsError()) | |
437 return status; | |
438 | |
439 if (has_jwk_alg && jwk_alg_value != expected_algorithm) | |
440 return Status::ErrorJwkAlgorithmInconsistent(); | |
441 | |
442 return Status::Success(); | |
443 } | |
444 | |
445 Status ParseJwkCommon(const CryptoData& bytes, | |
446 bool expected_extractable, | |
447 blink::WebCryptoKeyUsageMask expected_usage_mask, | |
448 std::string* kty, | |
449 scoped_ptr<base::DictionaryValue>* dict) { | |
450 // Parse the incoming JWK JSON. | |
451 base::StringPiece json_string(reinterpret_cast<const char*>(bytes.bytes()), | |
452 bytes.byte_length()); | |
453 | |
454 scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); | |
455 base::DictionaryValue* dict_value = NULL; | |
456 | |
457 if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) | |
458 return Status::ErrorJwkNotDictionary(); | |
459 | |
460 // Release |value|, as ownership will be transferred to |dict| via | |
461 // |dict_value|, which points to the same object as |value|. | |
462 ignore_result(value.release()); | |
463 dict->reset(dict_value); | |
464 | |
465 // JWK "kty". Exit early if this required JWK parameter is missing. | |
466 Status status = GetJwkString(dict_value, "kty", kty); | |
467 if (status.IsError()) | |
468 return status; | |
469 | |
470 status = VerifyExt(dict_value, expected_extractable); | |
471 if (status.IsError()) | |
472 return status; | |
473 | |
474 status = VerifyUsages(dict_value, expected_usage_mask); | |
475 if (status.IsError()) | |
476 return status; | |
477 | |
478 return Status::Success(); | |
479 } | |
480 | |
481 Status ReadSecretKeyNoExpectedAlg( | |
482 const CryptoData& key_data, | |
483 bool expected_extractable, | |
484 blink::WebCryptoKeyUsageMask expected_usage_mask, | |
485 std::vector<uint8>* raw_key_data, | |
486 scoped_ptr<base::DictionaryValue>* dict) { | |
487 if (!key_data.byte_length()) | |
488 return Status::ErrorImportEmptyKeyData(); | |
489 | |
490 std::string kty; | |
491 Status status = ParseJwkCommon( | |
492 key_data, expected_extractable, expected_usage_mask, &kty, dict); | |
493 if (status.IsError()) | |
494 return status; | |
495 | |
496 if (kty != "oct") | |
497 return Status::ErrorJwkUnexpectedKty("oct"); | |
498 | |
499 std::string jwk_k_value; | |
500 status = GetJwkBytes(dict->get(), "k", &jwk_k_value); | |
501 if (status.IsError()) | |
502 return status; | |
503 raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end()); | |
504 | |
505 return Status::Success(); | |
506 } | |
507 | |
508 } // namespace | |
509 | |
510 void WriteSecretKeyJwk(const CryptoData& raw_key_data, | |
511 const std::string& algorithm, | |
512 bool extractable, | |
513 blink::WebCryptoKeyUsageMask usage_mask, | |
514 std::vector<uint8>* jwk_key_data) { | |
515 JwkWriter writer(algorithm, extractable, usage_mask, "oct"); | |
516 writer.SetBase64Encoded("k", raw_key_data); | |
517 writer.ToBytes(jwk_key_data); | |
518 } | |
519 | |
520 Status ReadSecretKeyJwk(const CryptoData& key_data, | |
521 const std::string& expected_algorithm, | |
522 bool expected_extractable, | |
523 blink::WebCryptoKeyUsageMask expected_usage_mask, | |
524 std::vector<uint8>* raw_key_data) { | |
525 scoped_ptr<base::DictionaryValue> dict; | |
526 Status status = ReadSecretKeyNoExpectedAlg( | |
527 key_data, expected_extractable, expected_usage_mask, raw_key_data, &dict); | |
528 if (status.IsError()) | |
529 return status; | |
530 return VerifyAlg(dict.get(), expected_algorithm); | |
531 } | |
532 | |
533 std::string MakeJwkAesAlgorithmName(const std::string& suffix, | |
534 unsigned int keylen_bytes) { | |
535 if (keylen_bytes == 16) | |
536 return std::string("A128") + suffix; | |
537 if (keylen_bytes == 24) | |
538 return std::string("A192") + suffix; | |
539 if (keylen_bytes == 32) | |
540 return std::string("A256") + suffix; | |
541 return std::string(); | |
542 } | |
543 | |
544 Status ReadAesSecretKeyJwk(const CryptoData& key_data, | |
545 const std::string& algorithm_name_suffix, | |
546 bool expected_extractable, | |
547 blink::WebCryptoKeyUsageMask expected_usage_mask, | |
548 std::vector<uint8>* raw_key_data) { | |
549 scoped_ptr<base::DictionaryValue> dict; | |
550 Status status = ReadSecretKeyNoExpectedAlg( | |
551 key_data, expected_extractable, expected_usage_mask, raw_key_data, &dict); | |
552 if (status.IsError()) | |
553 return status; | |
554 | |
555 bool has_jwk_alg; | |
556 std::string jwk_alg; | |
557 status = GetOptionalJwkString(dict.get(), "alg", &jwk_alg, &has_jwk_alg); | |
558 if (status.IsError()) | |
559 return status; | |
560 | |
561 if (has_jwk_alg) { | |
562 std::string expected_algorithm_name = | |
563 MakeJwkAesAlgorithmName(algorithm_name_suffix, raw_key_data->size()); | |
564 | |
565 if (jwk_alg != expected_algorithm_name) { | |
566 // Give a different error message if the key length was wrong. | |
567 if (jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 16) || | |
568 jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 24) || | |
569 jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 32)) { | |
570 return Status::ErrorJwkIncorrectKeyLength(); | |
571 } | |
572 return Status::ErrorJwkAlgorithmInconsistent(); | |
573 } | |
574 } | |
575 | |
576 return Status::Success(); | |
577 } | |
578 | |
579 // Writes an RSA public key to a JWK dictionary | |
580 void WriteRsaPublicKeyJwk(const CryptoData& n, | |
581 const CryptoData& e, | |
582 const std::string& algorithm, | |
583 bool extractable, | |
584 blink::WebCryptoKeyUsageMask usage_mask, | |
585 std::vector<uint8>* jwk_key_data) { | |
586 JwkWriter writer(algorithm, extractable, usage_mask, "RSA"); | |
587 writer.SetBase64Encoded("n", n); | |
588 writer.SetBase64Encoded("e", e); | |
589 writer.ToBytes(jwk_key_data); | |
590 } | |
591 | |
592 // Writes an RSA private key to a JWK dictionary | |
593 void WriteRsaPrivateKeyJwk(const CryptoData& n, | |
594 const CryptoData& e, | |
595 const CryptoData& d, | |
596 const CryptoData& p, | |
597 const CryptoData& q, | |
598 const CryptoData& dp, | |
599 const CryptoData& dq, | |
600 const CryptoData& qi, | |
601 const std::string& algorithm, | |
602 bool extractable, | |
603 blink::WebCryptoKeyUsageMask usage_mask, | |
604 std::vector<uint8>* jwk_key_data) { | |
605 JwkWriter writer(algorithm, extractable, usage_mask, "RSA"); | |
606 | |
607 writer.SetBase64Encoded("n", n); | |
608 writer.SetBase64Encoded("e", e); | |
609 writer.SetBase64Encoded("d", d); | |
610 // Although these are "optional" in the JWA, WebCrypto spec requires them to | |
611 // be emitted. | |
612 writer.SetBase64Encoded("p", p); | |
613 writer.SetBase64Encoded("q", q); | |
614 writer.SetBase64Encoded("dp", dp); | |
615 writer.SetBase64Encoded("dq", dq); | |
616 writer.SetBase64Encoded("qi", qi); | |
617 writer.ToBytes(jwk_key_data); | |
618 } | |
619 | |
620 JwkRsaInfo::JwkRsaInfo() : is_private_key(false) { | |
621 } | |
622 | |
623 JwkRsaInfo::~JwkRsaInfo() { | |
624 } | |
625 | |
626 Status ReadRsaKeyJwk(const CryptoData& key_data, | |
627 const std::string& expected_algorithm, | |
628 bool expected_extractable, | |
629 blink::WebCryptoKeyUsageMask expected_usage_mask, | |
630 JwkRsaInfo* result) { | |
631 if (!key_data.byte_length()) | |
632 return Status::ErrorImportEmptyKeyData(); | |
633 | |
634 scoped_ptr<base::DictionaryValue> dict; | |
635 std::string kty; | |
636 Status status = ParseJwkCommon( | |
637 key_data, expected_extractable, expected_usage_mask, &kty, &dict); | |
638 if (status.IsError()) | |
639 return status; | |
640 | |
641 status = VerifyAlg(dict.get(), expected_algorithm); | |
642 if (status.IsError()) | |
643 return status; | |
644 | |
645 if (kty != "RSA") | |
646 return Status::ErrorJwkUnexpectedKty("RSA"); | |
647 | |
648 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry | |
649 // in the JWK, while an RSA private key must have those, plus at least a "d" | |
650 // (private exponent) entry. | |
651 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, | |
652 // section 6.3. | |
653 status = GetJwkBytes(dict.get(), "n", &result->n); | |
654 if (status.IsError()) | |
655 return status; | |
656 status = GetJwkBytes(dict.get(), "e", &result->e); | |
657 if (status.IsError()) | |
658 return status; | |
659 | |
660 result->is_private_key = dict->HasKey("d"); | |
661 if (!result->is_private_key) | |
662 return Status::Success(); | |
663 | |
664 status = GetJwkBytes(dict.get(), "d", &result->d); | |
665 if (status.IsError()) | |
666 return status; | |
667 | |
668 // The "p", "q", "dp", "dq", and "qi" properties are optional. Treat these | |
669 // properties the same if they are unspecified, as if they were specified-but | |
670 // empty, since ImportRsaPrivateKey() doesn't do validation checks anyway. | |
671 | |
672 bool has_p; | |
673 status = GetOptionalJwkBytes(dict.get(), "p", &result->p, &has_p); | |
674 if (status.IsError()) | |
675 return status; | |
676 | |
677 bool has_q; | |
678 status = GetOptionalJwkBytes(dict.get(), "q", &result->q, &has_q); | |
679 if (status.IsError()) | |
680 return status; | |
681 | |
682 bool has_dp; | |
683 status = GetOptionalJwkBytes(dict.get(), "dp", &result->dp, &has_dp); | |
684 if (status.IsError()) | |
685 return status; | |
686 | |
687 bool has_dq; | |
688 status = GetOptionalJwkBytes(dict.get(), "dq", &result->dq, &has_dq); | |
689 if (status.IsError()) | |
690 return status; | |
691 | |
692 bool has_qi; | |
693 status = GetOptionalJwkBytes(dict.get(), "qi", &result->qi, &has_qi); | |
694 if (status.IsError()) | |
695 return status; | |
696 | |
697 int num_optional_properties = has_p + has_q + has_dp + has_dq + has_qi; | |
698 if (num_optional_properties != 0 && num_optional_properties != 5) | |
699 return Status::ErrorJwkIncompleteOptionalRsaPrivateKey(); | |
700 | |
701 return Status::Success(); | |
702 } | |
703 | |
704 const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash) { | |
705 switch (hash) { | |
706 case blink::WebCryptoAlgorithmIdSha1: | |
707 return "HS1"; | |
708 case blink::WebCryptoAlgorithmIdSha256: | |
709 return "HS256"; | |
710 case blink::WebCryptoAlgorithmIdSha384: | |
711 return "HS384"; | |
712 case blink::WebCryptoAlgorithmIdSha512: | |
713 return "HS512"; | |
714 default: | |
715 return NULL; | |
716 } | |
717 } | |
718 | |
719 } // namespace webcrypto | 1016 } // namespace webcrypto |
720 | 1017 |
721 } // namespace content | 1018 } // namespace content |
OLD | NEW |