OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "content/renderer/webcrypto/webcrypto_impl.h" | 5 #include "content/renderer/webcrypto/webcrypto_impl.h" |
6 | 6 |
7 #include <cryptohi.h> | 7 #include <cryptohi.h> |
8 #include <pk11pub.h> | 8 #include <pk11pub.h> |
9 #include <sechash.h> | 9 #include <sechash.h> |
10 | 10 |
11 #include <algorithm> | |
11 #include <vector> | 12 #include <vector> |
12 | 13 |
13 #include "base/logging.h" | 14 #include "base/logging.h" |
14 #include "crypto/nss_util.h" | 15 #include "crypto/nss_util.h" |
15 #include "crypto/scoped_nss_types.h" | 16 #include "crypto/scoped_nss_types.h" |
16 #include "crypto/secure_util.h" | 17 #include "crypto/secure_util.h" |
17 #include "third_party/WebKit/public/platform/WebArrayBuffer.h" | 18 #include "third_party/WebKit/public/platform/WebArrayBuffer.h" |
18 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" | 19 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" |
19 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" | 20 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" |
20 | 21 |
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
231 size_t reverse_i = data_size - i - 1; | 232 size_t reverse_i = data_size - i - 1; |
232 | 233 |
233 if (reverse_i >= sizeof(unsigned long) && data[i]) | 234 if (reverse_i >= sizeof(unsigned long) && data[i]) |
234 return false; // Too large for a long. | 235 return false; // Too large for a long. |
235 | 236 |
236 *result |= data[i] << 8 * reverse_i; | 237 *result |= data[i] << 8 * reverse_i; |
237 } | 238 } |
238 return true; | 239 return true; |
239 } | 240 } |
240 | 241 |
242 bool IsAlgorithmRsa(const WebKit::WebCryptoAlgorithm& algorithm) { | |
243 return algorithm.id() == WebKit::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 || | |
244 algorithm.id() == WebKit::WebCryptoAlgorithmIdRsaOaep || | |
245 algorithm.id() == WebKit::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5; | |
246 } | |
247 | |
248 bool ImportKeyInternalRaw( | |
249 const unsigned char* key_data, | |
250 unsigned key_data_size, | |
251 const WebKit::WebCryptoAlgorithm& algorithm, | |
252 bool extractable, | |
253 WebKit::WebCryptoKeyUsageMask usage_mask, | |
254 WebKit::WebCryptoKey* key) { | |
255 | |
256 DCHECK(!algorithm.isNull()); | |
257 | |
258 WebKit::WebCryptoKeyType type; | |
259 switch (algorithm.id()) { | |
260 case WebKit::WebCryptoAlgorithmIdHmac: | |
261 case WebKit::WebCryptoAlgorithmIdAesCbc: | |
262 type = WebKit::WebCryptoKeyTypeSecret; | |
263 break; | |
264 // TODO(bryaneyler): Support more key types. | |
265 default: | |
266 return false; | |
267 } | |
268 | |
269 // TODO(bryaneyler): Need to split handling for symmetric and asymmetric keys. | |
270 // Currently only supporting symmetric. | |
271 CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; | |
272 // Flags are verified at the Blink layer; here the flags are set to all | |
273 // possible operations for this key type. | |
274 CK_FLAGS flags = 0; | |
275 | |
276 switch (algorithm.id()) { | |
277 case WebKit::WebCryptoAlgorithmIdHmac: { | |
278 const WebKit::WebCryptoHmacParams* params = algorithm.hmacParams(); | |
279 if (!params) { | |
280 return false; | |
281 } | |
282 | |
283 mechanism = WebCryptoAlgorithmToHMACMechanism(params->hash()); | |
284 if (mechanism == CKM_INVALID_MECHANISM) { | |
285 return false; | |
286 } | |
287 | |
288 flags |= CKF_SIGN | CKF_VERIFY; | |
289 | |
290 break; | |
291 } | |
292 case WebKit::WebCryptoAlgorithmIdAesCbc: { | |
293 mechanism = CKM_AES_CBC; | |
294 flags |= CKF_ENCRYPT | CKF_DECRYPT; | |
295 break; | |
296 } | |
297 default: | |
298 return false; | |
299 } | |
300 | |
301 DCHECK_NE(CKM_INVALID_MECHANISM, mechanism); | |
302 DCHECK_NE(0ul, flags); | |
303 | |
304 SECItem key_item = { | |
305 siBuffer, | |
306 const_cast<unsigned char*>(key_data), | |
307 key_data_size | |
308 }; | |
309 | |
310 crypto::ScopedPK11SymKey pk11_sym_key( | |
311 PK11_ImportSymKeyWithFlags(PK11_GetInternalSlot(), | |
312 mechanism, | |
313 PK11_OriginUnwrap, | |
314 CKA_FLAGS_ONLY, | |
315 &key_item, | |
316 flags, | |
317 false, | |
318 NULL)); | |
319 if (!pk11_sym_key.get()) { | |
320 return false; | |
321 } | |
322 | |
323 *key = WebKit::WebCryptoKey::create(new SymKeyHandle(pk11_sym_key.Pass()), | |
324 type, extractable, algorithm, usage_mask); | |
325 return true; | |
326 } | |
327 | |
328 typedef scoped_ptr_malloc< | |
329 CERTSubjectPublicKeyInfo, | |
330 crypto::NSSDestroyer<CERTSubjectPublicKeyInfo, | |
331 SECKEY_DestroySubjectPublicKeyInfo> > | |
332 ScopedCERTSubjectPublicKeyInfo; | |
333 | |
334 bool ImportKeyInternalSpki( | |
335 const unsigned char* key_data, | |
336 unsigned key_data_size, | |
337 const WebKit::WebCryptoAlgorithm& algorithm_or_null, | |
338 bool extractable, | |
339 WebKit::WebCryptoKeyUsageMask usage_mask, | |
340 WebKit::WebCryptoKey* key) { | |
341 | |
342 DCHECK(key); | |
343 | |
344 if (!key_data || !key_data_size) | |
eroman
2013/11/07 00:43:29
technically you only need to test the key_data_siz
padolph
2013/11/07 01:19:08
Done.
| |
345 return false; | |
346 | |
347 // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 Subject | |
348 // Public Key Info. Decode this to a CERTSubjectPublicKeyInfo. | |
349 SECItem spki_item = { | |
350 siBuffer, | |
351 const_cast<uint8*>(key_data), | |
352 key_data_size | |
353 }; | |
354 const ScopedCERTSubjectPublicKeyInfo spki( | |
355 SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); | |
356 if (!spki) | |
357 return false; | |
358 | |
359 crypto::ScopedSECKEYPublicKey sec_public_key( | |
360 SECKEY_ExtractPublicKey(spki.get())); | |
361 if (!sec_public_key) | |
362 return false; | |
363 | |
364 const KeyType sec_key_type = SECKEY_GetPublicKeyType(sec_public_key.get()); | |
365 | |
366 // Validate the sec_key_type against the input algorithm. Some NSS KeyType's | |
367 // contain enough information to fabricate a Web Crypto Algorithm, which will | |
368 // be used if the input algorithm isNull(). Others like 'rsaKey' do not (see | |
369 // below). | |
370 WebKit::WebCryptoAlgorithm algorithm = | |
371 WebKit::WebCryptoAlgorithm::createNull(); | |
372 switch (sec_key_type) { | |
373 case rsaKey: | |
374 // NSS's rsaKey KeyType maps to keys with SEC_OID_PKCS1_RSA_ENCRYPTION and | |
375 // according to RFC 4055 this can be used for both encryption and | |
376 // signatures. However, this is not specific enough to build a compatible | |
377 // Web Crypto algorithm, since in Web Crypto RSA encryption and signature | |
378 // algorithms are distinct. So if the input algorithm isNull() here, we | |
379 // have to fail. | |
380 if (algorithm_or_null.isNull() || !IsAlgorithmRsa(algorithm_or_null)) | |
381 return false; | |
382 algorithm = algorithm_or_null; | |
383 break; | |
384 case dsaKey: | |
385 case ecKey: | |
386 case rsaPssKey: | |
387 case rsaOaepKey: | |
388 // TODO(padolph): Handle other key types. | |
389 return false; | |
390 default: | |
391 return false; | |
392 } | |
393 | |
394 *key = WebKit::WebCryptoKey::create( | |
395 new PublicKeyHandle(sec_public_key.Pass()), | |
396 WebKit::WebCryptoKeyTypePublic, | |
397 extractable, | |
398 algorithm, | |
399 usage_mask); | |
400 | |
401 return true; | |
402 } | |
403 | |
404 bool ExportKeyInternalSpki( | |
405 const WebKit::WebCryptoKey& key, | |
406 WebKit::WebArrayBuffer* buffer) { | |
407 | |
408 DCHECK(key.handle()); | |
409 DCHECK(buffer); | |
410 | |
411 if (key.type() != WebKit::WebCryptoKeyTypePublic || !key.extractable()) | |
eroman
2013/11/07 00:43:29
note: I believe Blink's exportKey() bindings won't
| |
412 return false; | |
413 | |
414 PublicKeyHandle* const pub_key = | |
415 reinterpret_cast<PublicKeyHandle*>(key.handle()); | |
416 | |
417 const crypto::ScopedSECItem spki_der( | |
418 SECKEY_EncodeDERSubjectPublicKeyInfo(pub_key->key())); | |
419 if (!spki_der) | |
420 return false; | |
421 | |
422 DCHECK(spki_der->data); | |
423 DCHECK(spki_der->len); | |
424 | |
425 *buffer = WebKit::WebArrayBuffer::create(spki_der->len, 1); | |
426 std::copy(spki_der->data, | |
427 spki_der->data + spki_der->len, | |
428 static_cast<unsigned char*>(buffer->data())); | |
429 | |
430 return true; | |
431 } | |
432 | |
241 } // namespace | 433 } // namespace |
242 | 434 |
243 void WebCryptoImpl::Init() { | 435 void WebCryptoImpl::Init() { |
244 crypto::EnsureNSSInit(); | 436 crypto::EnsureNSSInit(); |
245 } | 437 } |
246 | 438 |
247 bool WebCryptoImpl::EncryptInternal( | 439 bool WebCryptoImpl::EncryptInternal( |
248 const WebKit::WebCryptoAlgorithm& algorithm, | 440 const WebKit::WebCryptoAlgorithm& algorithm, |
249 const WebKit::WebCryptoKey& key, | 441 const WebKit::WebCryptoKey& key, |
250 const unsigned char* data, | 442 const unsigned char* data, |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
437 | 629 |
438 // One extractable input parameter is provided, and the Web Crypto API | 630 // One extractable input parameter is provided, and the Web Crypto API |
439 // spec at this time says it applies to both members of the key pair. | 631 // spec at this time says it applies to both members of the key pair. |
440 // This is probably not correct: it makes more operational sense to have | 632 // This is probably not correct: it makes more operational sense to have |
441 // extractable apply only to the private key and make the public key | 633 // extractable apply only to the private key and make the public key |
442 // always extractable. For now implement what the spec says and track the | 634 // always extractable. For now implement what the spec says and track the |
443 // spec bug here: https://www.w3.org/Bugs/Public/show_bug.cgi?id=23695 | 635 // spec bug here: https://www.w3.org/Bugs/Public/show_bug.cgi?id=23695 |
444 *public_key = WebKit::WebCryptoKey::create( | 636 *public_key = WebKit::WebCryptoKey::create( |
445 new PublicKeyHandle(crypto::ScopedSECKEYPublicKey(sec_public_key)), | 637 new PublicKeyHandle(crypto::ScopedSECKEYPublicKey(sec_public_key)), |
446 WebKit::WebCryptoKeyTypePublic, | 638 WebKit::WebCryptoKeyTypePublic, |
447 extractable, // probably should be 'true' always | 639 extractable, // probably should be 'true' always |
448 algorithm, | 640 algorithm, |
449 usage_mask); | 641 usage_mask); |
450 *private_key = WebKit::WebCryptoKey::create( | 642 *private_key = WebKit::WebCryptoKey::create( |
451 new PrivateKeyHandle(scoped_sec_private_key.Pass()), | 643 new PrivateKeyHandle(scoped_sec_private_key.Pass()), |
452 WebKit::WebCryptoKeyTypePrivate, | 644 WebKit::WebCryptoKeyTypePrivate, |
453 extractable, | 645 extractable, |
454 algorithm, | 646 algorithm, |
455 usage_mask); | 647 usage_mask); |
456 | 648 |
457 return true; | 649 return true; |
458 } | 650 } |
459 default: | 651 default: |
460 return false; | 652 return false; |
461 } | 653 } |
462 } | 654 } |
463 | 655 |
464 bool WebCryptoImpl::ImportKeyInternal( | 656 bool WebCryptoImpl::ImportKeyInternal( |
465 WebKit::WebCryptoKeyFormat format, | 657 WebKit::WebCryptoKeyFormat format, |
466 const unsigned char* key_data, | 658 const unsigned char* key_data, |
467 unsigned key_data_size, | 659 unsigned key_data_size, |
468 const WebKit::WebCryptoAlgorithm& algorithm_or_null, | 660 const WebKit::WebCryptoAlgorithm& algorithm_or_null, |
469 bool extractable, | 661 bool extractable, |
470 WebKit::WebCryptoKeyUsageMask usage_mask, | 662 WebKit::WebCryptoKeyUsageMask usage_mask, |
471 WebKit::WebCryptoKey* key) { | 663 WebKit::WebCryptoKey* key) { |
472 // TODO(eroman): Currently expects algorithm to always be specified, as it is | |
473 // required for raw format. | |
474 if (algorithm_or_null.isNull()) | |
475 return false; | |
476 const WebKit::WebCryptoAlgorithm& algorithm = algorithm_or_null; | |
477 | 664 |
478 WebKit::WebCryptoKeyType type; | 665 switch (format) { |
479 switch (algorithm.id()) { | 666 case WebKit::WebCryptoKeyFormatRaw: |
480 case WebKit::WebCryptoAlgorithmIdHmac: | 667 // A 'raw'-formatted key import requires an input algorithm. |
481 case WebKit::WebCryptoAlgorithmIdAesCbc: | 668 if (algorithm_or_null.isNull()) |
482 type = WebKit::WebCryptoKeyTypeSecret; | 669 return false; |
483 break; | 670 return ImportKeyInternalRaw(key_data, |
484 // TODO(bryaneyler): Support more key types. | 671 key_data_size, |
672 algorithm_or_null, | |
673 extractable, | |
674 usage_mask, | |
675 key); | |
676 case WebKit::WebCryptoKeyFormatSpki: | |
677 return ImportKeyInternalSpki(key_data, | |
678 key_data_size, | |
679 algorithm_or_null, | |
680 extractable, | |
681 usage_mask, | |
682 key); | |
683 case WebKit::WebCryptoKeyFormatPkcs8: | |
684 // TODO(padolph): Handle PKCS#8 private key import | |
685 return false; | |
485 default: | 686 default: |
486 return false; | 687 return false; |
487 } | 688 } |
689 } | |
488 | 690 |
489 // TODO(bryaneyler): Need to split handling for symmetric and asymmetric keys. | 691 bool WebCryptoImpl::ExportKeyInternal( |
490 // Currently only supporting symmetric. | 692 WebKit::WebCryptoKeyFormat format, |
491 CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; | 693 const WebKit::WebCryptoKey& key, |
492 // Flags are verified at the Blink layer; here the flags are set to all | 694 WebKit::WebArrayBuffer* buffer) { |
493 // possible operations for this key type. | 695 switch (format) { |
494 CK_FLAGS flags = 0; | 696 case WebKit::WebCryptoKeyFormatRaw: |
495 | 697 // TODO(padolph): Implement raw export |
496 switch(algorithm.id()) { | 698 return false; |
497 case WebKit::WebCryptoAlgorithmIdHmac: { | 699 case WebKit::WebCryptoKeyFormatSpki: |
498 const WebKit::WebCryptoHmacParams* params = algorithm.hmacParams(); | 700 return ExportKeyInternalSpki(key, buffer); |
499 if (!params) { | 701 case WebKit::WebCryptoKeyFormatPkcs8: |
500 return false; | 702 // TODO(padolph): Implement pkcs8 export |
501 } | 703 return false; |
502 | |
503 mechanism = WebCryptoAlgorithmToHMACMechanism(params->hash()); | |
504 if (mechanism == CKM_INVALID_MECHANISM) { | |
505 return false; | |
506 } | |
507 | |
508 flags |= CKF_SIGN | CKF_VERIFY; | |
509 | |
510 break; | |
511 } | |
512 case WebKit::WebCryptoAlgorithmIdAesCbc: { | |
513 mechanism = CKM_AES_CBC; | |
514 flags |= CKF_ENCRYPT | CKF_DECRYPT; | |
515 break; | |
516 } | |
517 default: | 704 default: |
518 return false; | 705 return false; |
519 } | 706 } |
520 | |
521 DCHECK_NE(CKM_INVALID_MECHANISM, mechanism); | |
522 DCHECK_NE(0ul, flags); | |
523 | |
524 SECItem key_item = { siBuffer, NULL, 0 }; | |
525 | |
526 switch (format) { | |
527 case WebKit::WebCryptoKeyFormatRaw: | |
528 key_item.data = const_cast<unsigned char*>(key_data); | |
529 key_item.len = key_data_size; | |
530 break; | |
531 // TODO(bryaneyler): Handle additional formats. | |
532 default: | |
533 return false; | |
534 } | |
535 | |
536 crypto::ScopedPK11SymKey pk11_sym_key( | |
537 PK11_ImportSymKeyWithFlags(PK11_GetInternalSlot(), | |
538 mechanism, | |
539 PK11_OriginUnwrap, | |
540 CKA_FLAGS_ONLY, | |
541 &key_item, | |
542 flags, | |
543 false, | |
544 NULL)); | |
545 if (!pk11_sym_key.get()) { | |
546 return false; | |
547 } | |
548 | |
549 *key = WebKit::WebCryptoKey::create(new SymKeyHandle(pk11_sym_key.Pass()), | |
550 type, extractable, algorithm, usage_mask); | |
551 return true; | |
552 } | 707 } |
553 | 708 |
554 bool WebCryptoImpl::SignInternal( | 709 bool WebCryptoImpl::SignInternal( |
555 const WebKit::WebCryptoAlgorithm& algorithm, | 710 const WebKit::WebCryptoAlgorithm& algorithm, |
556 const WebKit::WebCryptoKey& key, | 711 const WebKit::WebCryptoKey& key, |
557 const unsigned char* data, | 712 const unsigned char* data, |
558 unsigned data_size, | 713 unsigned data_size, |
559 WebKit::WebArrayBuffer* buffer) { | 714 WebKit::WebArrayBuffer* buffer) { |
560 WebKit::WebArrayBuffer result; | 715 WebKit::WebArrayBuffer result; |
561 | 716 |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
642 break; | 797 break; |
643 } | 798 } |
644 default: | 799 default: |
645 return false; | 800 return false; |
646 } | 801 } |
647 | 802 |
648 return true; | 803 return true; |
649 } | 804 } |
650 | 805 |
651 } // namespace content | 806 } // namespace content |
OLD | NEW |