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" | 17 |
| 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 | |
228 // Web Crypto equivalent usage mask for JWK 'use' = 'enc'. | 212 // Web Crypto equivalent usage mask for JWK 'use' = 'enc'. |
229 const blink::WebCryptoKeyUsageMask kJwkEncUsage = | 213 const blink::WebCryptoKeyUsageMask kJwkEncUsage = |
230 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | | 214 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | |
231 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey | | 215 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey | |
232 blink::WebCryptoKeyUsageDeriveKey | blink::WebCryptoKeyUsageDeriveBits; | 216 blink::WebCryptoKeyUsageDeriveKey | blink::WebCryptoKeyUsageDeriveBits; |
233 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'. | 217 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'. |
234 const blink::WebCryptoKeyUsageMask kJwkSigUsage = | 218 const blink::WebCryptoKeyUsageMask kJwkSigUsage = |
235 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; | 219 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; |
236 | 220 |
237 typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)(); | 221 class JwkWriter { |
238 | |
239 class JwkAlgorithmInfo { | |
240 public: | 222 public: |
241 JwkAlgorithmInfo() | 223 JwkWriter(const std::string& algorithm, |
242 : creation_func_(NULL), | 224 bool extractable, |
243 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} | 225 blink::WebCryptoKeyUsageMask usage_mask, |
244 | 226 const std::string& kty) { |
245 explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func) | 227 dict_.SetString("alg", algorithm); |
246 : creation_func_(algorithm_creation_func), | 228 dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usage_mask)); |
247 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} | 229 dict_.SetBoolean("ext", extractable); |
248 | 230 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 } | 231 } |
255 | 232 |
256 bool CreateImportAlgorithm(blink::WebCryptoAlgorithm* algorithm) const { | 233 void Set(const std::string& key, const std::string& value) { |
257 *algorithm = creation_func_(); | 234 dict_.SetString(key, value); |
258 return !algorithm->isNull(); | |
259 } | 235 } |
260 | 236 |
261 bool IsInvalidKeyByteLength(size_t byte_length) const { | 237 void SetBase64Encoded(const std::string& key, const CryptoData& value) { |
262 if (required_key_length_bytes_ == NO_KEY_SIZE_REQUIREMENT) | 238 dict_.SetString(key, |
263 return false; | 239 Base64EncodeUrlSafe(base::StringPiece( |
264 return required_key_length_bytes_ != byte_length; | 240 reinterpret_cast<const char*>(value.bytes()), |
| 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()); |
265 } | 248 } |
266 | 249 |
267 private: | 250 private: |
268 static const unsigned int NO_KEY_SIZE_REQUIREMENT = UINT_MAX; | 251 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 }; | 252 }; |
275 | 253 |
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 | 254 // 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, | 255 // the result to |*result|. If the property does not exist or is not a string, |
398 // returns an error. | 256 // returns an error. |
399 Status GetJwkString(base::DictionaryValue* dict, | 257 Status GetJwkString(base::DictionaryValue* dict, |
400 const std::string& path, | 258 const std::string& path, |
401 std::string* result) { | 259 std::string* result) { |
402 base::Value* value = NULL; | 260 base::Value* value = NULL; |
403 if (!dict->Get(path, &value)) | 261 if (!dict->Get(path, &value)) |
404 return Status::ErrorJwkPropertyMissing(path); | 262 return Status::ErrorJwkPropertyMissing(path); |
405 if (!value->GetAsString(result)) | 263 if (!value->GetAsString(result)) |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
502 if (!dict->Get(path, &value)) | 360 if (!dict->Get(path, &value)) |
503 return Status::Success(); | 361 return Status::Success(); |
504 | 362 |
505 if (!value->GetAsBoolean(result)) | 363 if (!value->GetAsBoolean(result)) |
506 return Status::ErrorJwkPropertyWrongType(path, "boolean"); | 364 return Status::ErrorJwkPropertyWrongType(path, "boolean"); |
507 | 365 |
508 *property_exists = true; | 366 *property_exists = true; |
509 return Status::Success(); | 367 return Status::Success(); |
510 } | 368 } |
511 | 369 |
512 // Writes a secret/symmetric key to a JWK dictionary. | 370 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 | 371 // JWK "ext" (optional) --> extractable parameter |
840 { | 372 bool jwk_ext_value = false; |
841 bool jwk_ext_value = false; | 373 bool has_jwk_ext; |
842 bool has_jwk_ext; | 374 Status status = GetOptionalJwkBool(dict, "ext", &jwk_ext_value, &has_jwk_ext); |
843 status = | 375 if (status.IsError()) |
844 GetOptionalJwkBool(dict_value, "ext", &jwk_ext_value, &has_jwk_ext); | 376 return status; |
845 if (status.IsError()) | 377 if (has_jwk_ext && expected_extractable && !jwk_ext_value) |
846 return status; | 378 return Status::ErrorJwkExtInconsistent(); |
847 if (has_jwk_ext && !jwk_ext_value && extractable) | 379 return Status::Success(); |
848 return Status::ErrorJwkExtInconsistent(); | 380 } |
849 } | 381 |
850 | 382 Status VerifyUsages(base::DictionaryValue* dict, |
851 // JWK "alg" --> algorithm parameter | 383 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 | 384 // JWK "key_ops" (optional) --> usage_mask parameter |
883 base::ListValue* jwk_key_ops_value = NULL; | 385 base::ListValue* jwk_key_ops_value = NULL; |
884 bool has_jwk_key_ops; | 386 bool has_jwk_key_ops; |
885 status = GetOptionalJwkList( | 387 Status status = |
886 dict_value, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops); | 388 GetOptionalJwkList(dict, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops); |
887 if (status.IsError()) | 389 if (status.IsError()) |
888 return status; | 390 return status; |
889 blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; | 391 blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; |
890 if (has_jwk_key_ops) { | 392 if (has_jwk_key_ops) { |
891 status = | 393 status = |
892 GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask); | 394 GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask); |
893 if (status.IsError()) | 395 if (status.IsError()) |
894 return status; | 396 return status; |
895 // The input usage_mask must be a subset of jwk_key_ops_mask. | 397 // The input usage_mask must be a subset of jwk_key_ops_mask. |
896 if (!ContainsKeyUsages(jwk_key_ops_mask, usage_mask)) | 398 if (!ContainsKeyUsages(jwk_key_ops_mask, expected_usage_mask)) |
897 return Status::ErrorJwkKeyopsInconsistent(); | 399 return Status::ErrorJwkKeyopsInconsistent(); |
898 } | 400 } |
899 | 401 |
900 // JWK "use" (optional) --> usage_mask parameter | 402 // JWK "use" (optional) --> usage_mask parameter |
901 std::string jwk_use_value; | 403 std::string jwk_use_value; |
902 bool has_jwk_use; | 404 bool has_jwk_use; |
903 status = | 405 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()) | 406 if (status.IsError()) |
906 return status; | 407 return status; |
907 blink::WebCryptoKeyUsageMask jwk_use_mask = 0; | 408 blink::WebCryptoKeyUsageMask jwk_use_mask = 0; |
908 if (has_jwk_use) { | 409 if (has_jwk_use) { |
909 if (jwk_use_value == "enc") | 410 if (jwk_use_value == "enc") |
910 jwk_use_mask = kJwkEncUsage; | 411 jwk_use_mask = kJwkEncUsage; |
911 else if (jwk_use_value == "sig") | 412 else if (jwk_use_value == "sig") |
912 jwk_use_mask = kJwkSigUsage; | 413 jwk_use_mask = kJwkSigUsage; |
913 else | 414 else |
914 return Status::ErrorJwkUnrecognizedUse(); | 415 return Status::ErrorJwkUnrecognizedUse(); |
915 // The input usage_mask must be a subset of jwk_use_mask. | 416 // The input usage_mask must be a subset of jwk_use_mask. |
916 if (!ContainsKeyUsages(jwk_use_mask, usage_mask)) | 417 if (!ContainsKeyUsages(jwk_use_mask, expected_usage_mask)) |
917 return Status::ErrorJwkUseInconsistent(); | 418 return Status::ErrorJwkUseInconsistent(); |
918 } | 419 } |
919 | 420 |
920 // If both 'key_ops' and 'use' are present, ensure they are consistent. | 421 // If both 'key_ops' and 'use' are present, ensure they are consistent. |
921 if (has_jwk_key_ops && has_jwk_use && | 422 if (has_jwk_key_ops && has_jwk_use && |
922 !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask)) | 423 !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask)) |
923 return Status::ErrorJwkUseAndKeyopsInconsistent(); | 424 return Status::ErrorJwkUseAndKeyopsInconsistent(); |
924 | 425 |
925 // JWK keying material --> ImportKeyInternal() | 426 return Status::Success(); |
926 if (jwk_kty_value == "oct") { | 427 } |
927 std::string jwk_k_value; | 428 |
928 status = GetJwkBytes(dict_value, "k", &jwk_k_value); | 429 Status VerifyAlg(base::DictionaryValue* dict, |
929 if (status.IsError()) | 430 const std::string& expected_algorithm) { |
930 return status; | 431 // JWK "alg" --> algorithm parameter |
931 | 432 bool has_jwk_alg; |
932 // Some JWK alg ID's embed information about the key length in the alg ID | 433 std::string jwk_alg_value; |
933 // string. For example "A128CBC" implies the JWK carries 128 bits | 434 Status status = |
934 // of key material. For such keys validate that enough bytes were provided. | 435 GetOptionalJwkString(dict, "alg", &jwk_alg_value, &has_jwk_alg); |
935 // If this validation is not done, then it would be possible to select a | 436 if (status.IsError()) |
936 // different algorithm by passing a different lengthed key, since that is | 437 return status; |
937 // how WebCrypto interprets things. | 438 |
938 if (algorithm_info && | 439 if (has_jwk_alg && jwk_alg_value != expected_algorithm) |
939 algorithm_info->IsInvalidKeyByteLength(jwk_k_value.size())) { | 440 return Status::ErrorJwkAlgorithmInconsistent(); |
940 return Status::ErrorJwkIncorrectKeyLength(); | 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(); |
941 } | 573 } |
942 | |
943 return ImportKey(blink::WebCryptoKeyFormatRaw, | |
944 CryptoData(jwk_k_value), | |
945 algorithm, | |
946 extractable, | |
947 usage_mask, | |
948 key); | |
949 } | 574 } |
950 | 575 |
951 if (jwk_kty_value == "RSA") | 576 return Status::Success(); |
952 return ImportRsaKey(dict_value, algorithm, extractable, usage_mask, key); | 577 } |
953 | 578 |
954 return Status::ErrorJwkUnrecognizedKty(); | 579 // Writes an RSA public key to a JWK dictionary |
955 } | 580 void WriteRsaPublicKeyJwk(const CryptoData& n, |
956 | 581 const CryptoData& e, |
957 Status ExportKeyJwk(const blink::WebCryptoKey& key, | 582 const std::string& algorithm, |
958 std::vector<uint8>* buffer) { | 583 bool extractable, |
959 DCHECK(key.extractable()); | 584 blink::WebCryptoKeyUsageMask usage_mask, |
960 base::DictionaryValue jwk_dict; | 585 std::vector<uint8>* jwk_key_data) { |
961 Status status = Status::OperationError(); | 586 JwkWriter writer(algorithm, extractable, usage_mask, "RSA"); |
962 | 587 writer.SetBase64Encoded("n", n); |
963 switch (key.type()) { | 588 writer.SetBase64Encoded("e", e); |
964 case blink::WebCryptoKeyTypeSecret: { | 589 writer.ToBytes(jwk_key_data); |
965 std::vector<uint8> exported_key; | 590 } |
966 status = ExportKey(blink::WebCryptoKeyFormatRaw, key, &exported_key); | 591 |
967 if (status.IsError()) | 592 // Writes an RSA private key to a JWK dictionary |
968 return status; | 593 void WriteRsaPrivateKeyJwk(const CryptoData& n, |
969 WriteSecretKey(exported_key, &jwk_dict); | 594 const CryptoData& e, |
970 break; | 595 const CryptoData& d, |
971 } | 596 const CryptoData& p, |
972 case blink::WebCryptoKeyTypePublic: { | 597 const CryptoData& q, |
973 // TODO(eroman): Update when there are asymmetric keys other than RSA. | 598 const CryptoData& dp, |
974 if (!IsRsaKey(key)) | 599 const CryptoData& dq, |
975 return Status::ErrorUnsupported(); | 600 const CryptoData& qi, |
976 platform::PublicKey* public_key; | 601 const std::string& algorithm, |
977 status = ToPlatformPublicKey(key, &public_key); | 602 bool extractable, |
978 if (status.IsError()) | 603 blink::WebCryptoKeyUsageMask usage_mask, |
979 return status; | 604 std::vector<uint8>* jwk_key_data) { |
980 std::vector<uint8> modulus; | 605 JwkWriter writer(algorithm, extractable, usage_mask, "RSA"); |
981 std::vector<uint8> public_exponent; | 606 |
982 status = | 607 writer.SetBase64Encoded("n", n); |
983 platform::ExportRsaPublicKey(public_key, &modulus, &public_exponent); | 608 writer.SetBase64Encoded("e", e); |
984 if (status.IsError()) | 609 writer.SetBase64Encoded("d", d); |
985 return status; | 610 // Although these are "optional" in the JWA, WebCrypto spec requires them to |
986 WriteRsaPublicKey(modulus, public_exponent, &jwk_dict); | 611 // be emitted. |
987 break; | 612 writer.SetBase64Encoded("p", p); |
988 } | 613 writer.SetBase64Encoded("q", q); |
989 case blink::WebCryptoKeyTypePrivate: { | 614 writer.SetBase64Encoded("dp", dp); |
990 // TODO(eroman): Update when there are asymmetric keys other than RSA. | 615 writer.SetBase64Encoded("dq", dq); |
991 if (!IsRsaKey(key)) | 616 writer.SetBase64Encoded("qi", qi); |
992 return Status::ErrorUnsupported(); | 617 writer.ToBytes(jwk_key_data); |
993 | 618 } |
994 status = ExportRsaPrivateKeyJwk(key, &jwk_dict); | 619 |
995 if (status.IsError()) | 620 JwkRsaInfo::JwkRsaInfo() : is_private_key(false) { |
996 return status; | 621 } |
997 break; | 622 |
998 } | 623 JwkRsaInfo::~JwkRsaInfo() { |
999 | 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"; |
1000 default: | 714 default: |
1001 return Status::ErrorUnsupported(); | 715 return NULL; |
1002 } | 716 } |
1003 | |
1004 WriteKeyOps(key.usages(), &jwk_dict); | |
1005 WriteExt(key.extractable(), &jwk_dict); | |
1006 status = WriteAlg(key.algorithm(), &jwk_dict); | |
1007 if (status.IsError()) | |
1008 return status; | |
1009 | |
1010 std::string json; | |
1011 base::JSONWriter::Write(&jwk_dict, &json); | |
1012 buffer->assign(json.data(), json.data() + json.size()); | |
1013 return Status::Success(); | |
1014 } | 717 } |
1015 | 718 |
1016 } // namespace webcrypto | 719 } // namespace webcrypto |
1017 | 720 |
1018 } // namespace content | 721 } // namespace content |
OLD | NEW |