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()); | |
Ryan Sleevi
2014/07/12 00:55:27
Why don't you need to cast here?
json is std::str
eroman
2014/07/12 01:59:30
Done.
| |
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) { |
436 if (jwk_alg_value.empty()) | |
437 return Status::ErrorJwkAlgorithmInconsistent(); | |
Ryan Sleevi
2014/07/12 00:55:27
Why do you place this check here, but then you ret
eroman
2014/07/12 01:59:30
Thanks for pointing this out, the current structur
eroman
2014/07/14 21:56:10
Done.
| |
438 *actual_algorithm = jwk_alg_value; | |
439 } else { | |
440 *actual_algorithm = std::string(); | |
441 } | |
442 } | |
443 | |
444 if (has_jwk_alg && !expected_algorithm.empty() && | |
445 jwk_alg_value != expected_algorithm) { | |
446 return Status::ErrorJwkAlgorithmInconsistent(); | |
447 } | |
448 | |
449 return Status::Success(); | |
450 } | |
451 | |
452 Status ParseJwkCommon(const CryptoData& bytes, | |
453 const std::string& expected_algorithm, | |
454 bool expected_extractable, | |
455 blink::WebCryptoKeyUsageMask expected_usage_mask, | |
456 std::string* kty, | |
457 std::string* actual_algorithm, | |
458 scoped_ptr<base::DictionaryValue>* dict) { | |
459 // Parse the incoming JWK JSON. | |
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()); | |
Ryan Sleevi
2014/07/12 00:55:27
This needs more documentation, because I had serio
eroman
2014/07/12 01:59:30
Done.
| |
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 Status ReadSecretKeyJwkHelper(const CryptoData& key_data, | |
494 const std::string& expected_algorithm, | |
495 bool expected_extractable, | |
496 blink::WebCryptoKeyUsageMask expected_usage_mask, | |
497 std::string* actual_algorithm, | |
498 std::vector<uint8>* raw_key_data) { | |
499 if (!key_data.byte_length()) | |
500 return Status::ErrorImportEmptyKeyData(); | |
501 | |
502 scoped_ptr<base::DictionaryValue> dict; | |
503 std::string kty; | |
504 Status status = ParseJwkCommon(key_data, | |
505 expected_algorithm, | |
506 expected_extractable, | |
507 expected_usage_mask, | |
508 &kty, | |
509 actual_algorithm, | |
510 &dict); | |
511 if (status.IsError()) | |
512 return status; | |
513 | |
514 if (kty != "oct") | |
515 return Status::ErrorJwkUnexpectedKty("oct"); | |
516 | |
517 std::string jwk_k_value; | |
518 status = GetJwkBytes(dict.get(), "k", &jwk_k_value); | |
519 if (status.IsError()) | |
520 return status; | |
521 raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end()); | |
522 | |
523 return Status::Success(); | |
524 } | |
525 | |
526 } // namespace | |
527 | |
528 void WriteSecretKeyJwk(const CryptoData& raw_key_data, | |
529 const std::string& algorithm, | |
530 bool extractable, | |
531 blink::WebCryptoKeyUsageMask usage_mask, | |
532 std::vector<uint8>* jwk_key_data) { | |
533 JwkWriter writer(algorithm, extractable, usage_mask, "oct"); | |
534 writer.SetBase64Encoded("k", raw_key_data); | |
535 writer.ToBytes(jwk_key_data); | |
536 } | |
537 | |
538 Status ReadSecretKeyJwk(const CryptoData& key_data, | |
539 const std::string& expected_algorithm, | |
540 bool expected_extractable, | |
541 blink::WebCryptoKeyUsageMask expected_usage_mask, | |
542 std::vector<uint8>* raw_key_data) { | |
543 return ReadSecretKeyJwkHelper(key_data, | |
544 expected_algorithm, | |
545 expected_extractable, | |
546 expected_usage_mask, | |
547 NULL, | |
548 raw_key_data); | |
549 } | |
550 | |
551 std::string MakeJwkAesAlgorithmName(const std::string& suffix, | |
552 unsigned int keylen_bytes) { | |
553 if (keylen_bytes == 16) | |
554 return std::string("A128") + suffix; | |
555 if (keylen_bytes == 24) | |
556 return std::string("A192") + suffix; | |
557 if (keylen_bytes == 32) | |
558 return std::string("A256") + suffix; | |
559 return std::string(); | |
560 } | |
561 | |
562 Status ReadAesSecretKeyJwk(const CryptoData& key_data, | |
563 const std::string& algorithm_name_suffix, | |
564 bool expected_extractable, | |
565 blink::WebCryptoKeyUsageMask expected_usage_mask, | |
566 std::vector<uint8>* raw_key_data) { | |
567 std::string jwk_algorithm_name; | |
568 Status status = ReadSecretKeyJwkHelper(key_data, | |
569 "", | |
Ryan Sleevi
2014/07/12 00:55:27
std::string()
| |
570 expected_extractable, | |
571 expected_usage_mask, | |
572 &jwk_algorithm_name, | |
573 raw_key_data); | |
574 if (status.IsError()) | |
575 return status; | |
576 | |
577 std::string expected_algorithm_name = | |
578 MakeJwkAesAlgorithmName(algorithm_name_suffix, raw_key_data->size()); | |
579 | |
580 if (!jwk_algorithm_name.empty() && | |
581 jwk_algorithm_name != expected_algorithm_name) { | |
582 // Give a different error message if the key length was wrong. | |
583 if (jwk_algorithm_name == | |
584 MakeJwkAesAlgorithmName(algorithm_name_suffix, 16) || | |
585 jwk_algorithm_name == | |
586 MakeJwkAesAlgorithmName(algorithm_name_suffix, 24) || | |
587 jwk_algorithm_name == | |
588 MakeJwkAesAlgorithmName(algorithm_name_suffix, 32)) { | |
Ryan Sleevi
2014/07/12 00:55:27
Doesn't this seem like it should be up to the impl
eroman
2014/07/12 01:59:30
Indeed. Initially I had the code as part of the im
| |
940 return Status::ErrorJwkIncorrectKeyLength(); | 589 return Status::ErrorJwkIncorrectKeyLength(); |
941 } | 590 } |
942 | 591 return Status::ErrorJwkAlgorithmInconsistent(); |
943 return ImportKey(blink::WebCryptoKeyFormatRaw, | 592 } |
944 CryptoData(jwk_k_value), | 593 |
945 algorithm, | 594 return Status::Success(); |
946 extractable, | 595 } |
947 usage_mask, | 596 |
948 key); | 597 // Writes an RSA public key to a JWK dictionary |
949 } | 598 void WriteRsaPublicKeyJwk(const CryptoData& n, |
950 | 599 const CryptoData& e, |
951 if (jwk_kty_value == "RSA") | 600 const std::string& algorithm, |
952 return ImportRsaKey(dict_value, algorithm, extractable, usage_mask, key); | 601 bool extractable, |
953 | 602 blink::WebCryptoKeyUsageMask usage_mask, |
954 return Status::ErrorJwkUnrecognizedKty(); | 603 std::vector<uint8>* jwk_key_data) { |
955 } | 604 JwkWriter writer(algorithm, extractable, usage_mask, "RSA"); |
956 | 605 writer.SetBase64Encoded("n", n); |
957 Status ExportKeyJwk(const blink::WebCryptoKey& key, | 606 writer.SetBase64Encoded("e", e); |
958 std::vector<uint8>* buffer) { | 607 writer.ToBytes(jwk_key_data); |
959 DCHECK(key.extractable()); | 608 } |
960 base::DictionaryValue jwk_dict; | 609 |
961 Status status = Status::OperationError(); | 610 // Writes an RSA private key to a JWK dictionary |
962 | 611 void WriteRsaPrivateKeyJwk(const CryptoData& n, |
963 switch (key.type()) { | 612 const CryptoData& e, |
964 case blink::WebCryptoKeyTypeSecret: { | 613 const CryptoData& d, |
965 std::vector<uint8> exported_key; | 614 const CryptoData& p, |
966 status = ExportKey(blink::WebCryptoKeyFormatRaw, key, &exported_key); | 615 const CryptoData& q, |
967 if (status.IsError()) | 616 const CryptoData& dp, |
968 return status; | 617 const CryptoData& dq, |
969 WriteSecretKey(exported_key, &jwk_dict); | 618 const CryptoData& qi, |
970 break; | 619 const std::string& algorithm, |
971 } | 620 bool extractable, |
972 case blink::WebCryptoKeyTypePublic: { | 621 blink::WebCryptoKeyUsageMask usage_mask, |
973 // TODO(eroman): Update when there are asymmetric keys other than RSA. | 622 std::vector<uint8>* jwk_key_data) { |
974 if (!IsRsaKey(key)) | 623 JwkWriter writer(algorithm, extractable, usage_mask, "RSA"); |
975 return Status::ErrorUnsupported(); | 624 |
976 platform::PublicKey* public_key; | 625 writer.SetBase64Encoded("n", n); |
977 status = ToPlatformPublicKey(key, &public_key); | 626 writer.SetBase64Encoded("e", e); |
978 if (status.IsError()) | 627 writer.SetBase64Encoded("d", d); |
979 return status; | 628 // Although these are "optional" in the JWA, WebCrypto spec requires them to |
980 std::vector<uint8> modulus; | 629 // be emitted. |
981 std::vector<uint8> public_exponent; | 630 writer.SetBase64Encoded("p", p); |
982 status = | 631 writer.SetBase64Encoded("q", q); |
983 platform::ExportRsaPublicKey(public_key, &modulus, &public_exponent); | 632 writer.SetBase64Encoded("dp", dp); |
984 if (status.IsError()) | 633 writer.SetBase64Encoded("dq", dq); |
985 return status; | 634 writer.SetBase64Encoded("qi", qi); |
986 WriteRsaPublicKey(modulus, public_exponent, &jwk_dict); | 635 writer.ToBytes(jwk_key_data); |
987 break; | 636 } |
988 } | 637 |
989 case blink::WebCryptoKeyTypePrivate: { | 638 JwkRsaInfo::JwkRsaInfo() : is_private_key(false) { |
990 // TODO(eroman): Update when there are asymmetric keys other than RSA. | 639 } |
991 if (!IsRsaKey(key)) | 640 |
992 return Status::ErrorUnsupported(); | 641 JwkRsaInfo::~JwkRsaInfo() { |
993 | 642 } |
994 status = ExportRsaPrivateKeyJwk(key, &jwk_dict); | 643 |
995 if (status.IsError()) | 644 Status ReadRsaKeyJwk(const CryptoData& key_data, |
996 return status; | 645 const std::string& expected_algorithm, |
997 break; | 646 bool expected_extractable, |
998 } | 647 blink::WebCryptoKeyUsageMask expected_usage_mask, |
999 | 648 JwkRsaInfo* result) { |
649 if (!key_data.byte_length()) | |
650 return Status::ErrorImportEmptyKeyData(); | |
651 | |
652 scoped_ptr<base::DictionaryValue> dict; | |
653 std::string kty; | |
654 Status status = ParseJwkCommon(key_data, | |
655 expected_algorithm, | |
656 expected_extractable, | |
657 expected_usage_mask, | |
658 &kty, | |
659 NULL, | |
660 &dict); | |
661 if (status.IsError()) | |
662 return status; | |
663 | |
664 if (kty != "RSA") | |
665 return Status::ErrorJwkUnexpectedKty("RSA"); | |
666 | |
667 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry | |
668 // in the JWK, while an RSA private key must have those, plus at least a "d" | |
669 // (private exponent) entry. | |
670 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, | |
671 // section 6.3. | |
672 status = GetJwkBytes(dict.get(), "n", &result->n); | |
673 if (status.IsError()) | |
674 return status; | |
675 status = GetJwkBytes(dict.get(), "e", &result->e); | |
676 if (status.IsError()) | |
677 return status; | |
678 | |
679 result->is_private_key = dict->HasKey("d"); | |
680 if (!result->is_private_key) | |
681 return Status::Success(); | |
682 | |
683 status = GetJwkBytes(dict.get(), "d", &result->d); | |
684 if (status.IsError()) | |
685 return status; | |
686 | |
687 // The "p", "q", "dp", "dq", and "qi" properties are optional. Treat these | |
688 // properties the same if they are unspecified, as if they were specified-but | |
689 // empty, since ImportRsaPrivateKey() doesn't do validation checks anyway. | |
690 | |
691 bool has_p; | |
692 status = GetOptionalJwkBytes(dict.get(), "p", &result->p, &has_p); | |
693 if (status.IsError()) | |
694 return status; | |
695 | |
696 bool has_q; | |
697 status = GetOptionalJwkBytes(dict.get(), "q", &result->q, &has_q); | |
698 if (status.IsError()) | |
699 return status; | |
700 | |
701 bool has_dp; | |
702 status = GetOptionalJwkBytes(dict.get(), "dp", &result->dp, &has_dp); | |
703 if (status.IsError()) | |
704 return status; | |
705 | |
706 bool has_dq; | |
707 status = GetOptionalJwkBytes(dict.get(), "dq", &result->dq, &has_dq); | |
708 if (status.IsError()) | |
709 return status; | |
710 | |
711 bool has_qi; | |
712 status = GetOptionalJwkBytes(dict.get(), "qi", &result->qi, &has_qi); | |
713 if (status.IsError()) | |
714 return status; | |
715 | |
716 int num_optional_properties = has_p + has_q + has_dp + has_dq + has_qi; | |
717 if (num_optional_properties != 0 && num_optional_properties != 5) | |
718 return Status::ErrorJwkIncompleteOptionalRsaPrivateKey(); | |
719 | |
720 return Status::Success(); | |
721 } | |
722 | |
723 const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash) { | |
724 switch (hash) { | |
725 case blink::WebCryptoAlgorithmIdSha1: | |
726 return "HS1"; | |
727 case blink::WebCryptoAlgorithmIdSha256: | |
728 return "HS256"; | |
729 case blink::WebCryptoAlgorithmIdSha384: | |
730 return "HS384"; | |
731 case blink::WebCryptoAlgorithmIdSha512: | |
732 return "HS512"; | |
1000 default: | 733 default: |
1001 return Status::ErrorUnsupported(); | 734 return NULL; |
1002 } | 735 } |
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 } | 736 } |
1015 | 737 |
1016 } // namespace webcrypto | 738 } // namespace webcrypto |
1017 | 739 |
1018 } // namespace content | 740 } // namespace content |
OLD | NEW |