Chromium Code Reviews| 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_or_null, | |
| 252 bool extractable, | |
| 253 WebKit::WebCryptoKeyUsageMask usage_mask, | |
| 254 WebKit::WebCryptoKey* key) { | |
| 255 | |
| 256 // TODO(eroman): Currently expects algorithm to always be specified, as it is | |
|
eroman
2013/11/06 23:48:40
Not sure this needs a TODO anymore, since for "raw
padolph
2013/11/07 00:23:50
Done.
| |
| 257 // required for raw format. | |
| 258 if (algorithm_or_null.isNull()) | |
| 259 return false; | |
| 260 const WebKit::WebCryptoAlgorithm& algorithm = algorithm_or_null; | |
| 261 | |
| 262 WebKit::WebCryptoKeyType type; | |
| 263 switch (algorithm.id()) { | |
| 264 case WebKit::WebCryptoAlgorithmIdHmac: | |
| 265 case WebKit::WebCryptoAlgorithmIdAesCbc: | |
| 266 type = WebKit::WebCryptoKeyTypeSecret; | |
| 267 break; | |
| 268 // TODO(bryaneyler): Support more key types. | |
| 269 default: | |
| 270 return false; | |
| 271 } | |
| 272 | |
| 273 // TODO(bryaneyler): Need to split handling for symmetric and asymmetric keys. | |
| 274 // Currently only supporting symmetric. | |
|
eroman
2013/11/06 23:48:40
I presume nothing else changed in this function? (
padolph
2013/11/07 00:23:50
Yes, I made sure this was a strict copy-paste (asi
| |
| 275 CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; | |
| 276 // Flags are verified at the Blink layer; here the flags are set to all | |
| 277 // possible operations for this key type. | |
| 278 CK_FLAGS flags = 0; | |
| 279 | |
| 280 switch (algorithm.id()) { | |
| 281 case WebKit::WebCryptoAlgorithmIdHmac: { | |
| 282 const WebKit::WebCryptoHmacParams* params = algorithm.hmacParams(); | |
| 283 if (!params) { | |
| 284 return false; | |
| 285 } | |
| 286 | |
| 287 mechanism = WebCryptoAlgorithmToHMACMechanism(params->hash()); | |
| 288 if (mechanism == CKM_INVALID_MECHANISM) { | |
| 289 return false; | |
| 290 } | |
| 291 | |
| 292 flags |= CKF_SIGN | CKF_VERIFY; | |
| 293 | |
| 294 break; | |
| 295 } | |
| 296 case WebKit::WebCryptoAlgorithmIdAesCbc: { | |
| 297 mechanism = CKM_AES_CBC; | |
| 298 flags |= CKF_ENCRYPT | CKF_DECRYPT; | |
| 299 break; | |
| 300 } | |
| 301 default: | |
| 302 return false; | |
| 303 } | |
| 304 | |
| 305 DCHECK_NE(CKM_INVALID_MECHANISM, mechanism); | |
| 306 DCHECK_NE(0ul, flags); | |
| 307 | |
| 308 SECItem key_item = { | |
| 309 siBuffer, | |
| 310 const_cast<unsigned char*>(key_data), | |
| 311 key_data_size | |
| 312 }; | |
| 313 | |
| 314 crypto::ScopedPK11SymKey pk11_sym_key( | |
| 315 PK11_ImportSymKeyWithFlags(PK11_GetInternalSlot(), | |
| 316 mechanism, | |
| 317 PK11_OriginUnwrap, | |
| 318 CKA_FLAGS_ONLY, | |
| 319 &key_item, | |
| 320 flags, | |
| 321 false, | |
| 322 NULL)); | |
| 323 if (!pk11_sym_key.get()) { | |
| 324 return false; | |
| 325 } | |
| 326 | |
| 327 *key = WebKit::WebCryptoKey::create(new SymKeyHandle(pk11_sym_key.Pass()), | |
| 328 type, extractable, algorithm, usage_mask); | |
| 329 return true; | |
| 330 } | |
| 331 | |
| 332 typedef scoped_ptr_malloc< | |
| 333 CERTSubjectPublicKeyInfo, | |
| 334 crypto::NSSDestroyer<CERTSubjectPublicKeyInfo, | |
| 335 SECKEY_DestroySubjectPublicKeyInfo> > | |
| 336 ScopedCERTSubjectPublicKeyInfo; | |
| 337 | |
| 338 bool ImportKeyInternalSpki( | |
| 339 const unsigned char* key_data, | |
| 340 unsigned key_data_size, | |
| 341 const WebKit::WebCryptoAlgorithm& algorithm_or_null, | |
| 342 bool extractable, | |
| 343 WebKit::WebCryptoKeyUsageMask usage_mask, | |
| 344 WebKit::WebCryptoKey* key) { | |
| 345 | |
| 346 DCHECK(key_data); | |
| 347 DCHECK(key_data_size); | |
|
eroman
2013/11/06 23:48:40
These shouldn't be DCHECKs --> it is possible for
padolph
2013/11/07 00:23:50
Done.
| |
| 348 DCHECK(key); | |
| 349 | |
| 350 const WebKit::WebCryptoAlgorithm& algorithm = algorithm_or_null; | |
|
eroman
2013/11/06 23:48:40
The same thing could be accomplished by just renam
padolph
2013/11/07 00:23:50
Can't do it in the caller, since fail for null mig
| |
| 351 | |
| 352 // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 Subject | |
| 353 // Public Key Info. Decode this to a CERTSubjectPublicKeyInfo. | |
| 354 SECItem spki_item = { | |
| 355 siBuffer, | |
| 356 const_cast<uint8*>(key_data), | |
| 357 key_data_size | |
| 358 }; | |
| 359 const ScopedCERTSubjectPublicKeyInfo spki( | |
| 360 SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); | |
| 361 if (!spki) | |
| 362 return false; | |
| 363 | |
| 364 crypto::ScopedSECKEYPublicKey sec_public_key( | |
| 365 SECKEY_ExtractPublicKey(spki.get())); | |
| 366 if (!sec_public_key) | |
| 367 return false; | |
| 368 | |
| 369 // Validate the key type. | |
| 370 const KeyType sec_key_type = SECKEY_GetPublicKeyType(sec_public_key.get()); | |
|
eroman
2013/11/06 23:48:40
FYI: I am not familiar with these specifics; I wil
| |
| 371 switch (sec_key_type) { | |
| 372 case rsaKey: | |
| 373 // NSS's rsaKey KeyType maps to keys with SEC_OID_PKCS1_RSA_ENCRYPTION and | |
| 374 // according to RFC 4055 this can be used for both encryption and | |
| 375 // signatures. However, this is not specific enough to build a compatible | |
| 376 // Web Crypto algorithm, since in Web Crypto RSA encryption and signature | |
| 377 // algorithms are distinct. So if the input algorithm is NULL here, we | |
|
eroman
2013/11/06 23:48:40
Useful comment. Small wording nit: rather than "NU
padolph
2013/11/07 00:23:50
Done.
| |
| 378 // have to fail. | |
| 379 if (algorithm.isNull() || !IsAlgorithmRsa(algorithm)) | |
| 380 return false; | |
| 381 break; | |
| 382 case dsaKey: | |
| 383 case ecKey: | |
| 384 case rsaPssKey: | |
| 385 case rsaOaepKey: | |
| 386 // TODO(padolph): Handle other key types | |
| 387 return false; | |
| 388 default: | |
| 389 NOTREACHED(); | |
|
eroman
2013/11/06 23:48:40
Only use NOTREACHED() for code which really can't
padolph
2013/11/07 00:23:50
Done.
| |
| 390 return false; | |
| 391 } | |
| 392 | |
| 393 *key = WebKit::WebCryptoKey::create( | |
| 394 new PublicKeyHandle(sec_public_key.Pass()), | |
| 395 WebKit::WebCryptoKeyTypePublic, | |
| 396 extractable, | |
| 397 algorithm, | |
| 398 usage_mask); | |
| 399 | |
| 400 return true; | |
| 401 } | |
| 402 | |
| 403 bool ExportKeyInternalSpki( | |
| 404 const WebKit::WebCryptoKey& key, | |
| 405 WebKit::WebArrayBuffer* buffer) { | |
| 406 | |
| 407 DCHECK(key.handle()); | |
| 408 DCHECK(buffer); | |
| 409 | |
| 410 if (key.type() != WebKit::WebCryptoKeyTypePublic || !key.extractable()) | |
| 411 return false; | |
| 412 | |
| 413 PublicKeyHandle* const pub_key = | |
| 414 reinterpret_cast<PublicKeyHandle*>(key.handle()); | |
| 415 | |
| 416 const crypto::ScopedSECItem spki_der( | |
| 417 SECKEY_EncodeDERSubjectPublicKeyInfo(pub_key->key())); | |
| 418 if (!spki_der) | |
| 419 return false; | |
| 420 | |
| 421 DCHECK(spki_der->data); | |
| 422 DCHECK(spki_der->len); | |
| 423 | |
| 424 *buffer = WebKit::WebArrayBuffer::create(spki_der->len, 1); | |
| 425 std::copy(spki_der->data, | |
| 426 spki_der->data + spki_der->len, | |
| 427 static_cast<unsigned char*>(buffer->data())); | |
| 428 | |
| 429 return true; | |
| 430 } | |
| 431 | |
| 241 } // namespace | 432 } // namespace |
| 242 | 433 |
| 243 void WebCryptoImpl::Init() { | 434 void WebCryptoImpl::Init() { |
| 244 crypto::EnsureNSSInit(); | 435 crypto::EnsureNSSInit(); |
| 245 } | 436 } |
| 246 | 437 |
| 247 bool WebCryptoImpl::EncryptInternal( | 438 bool WebCryptoImpl::EncryptInternal( |
| 248 const WebKit::WebCryptoAlgorithm& algorithm, | 439 const WebKit::WebCryptoAlgorithm& algorithm, |
| 249 const WebKit::WebCryptoKey& key, | 440 const WebKit::WebCryptoKey& key, |
| 250 const unsigned char* data, | 441 const unsigned char* data, |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 437 | 628 |
| 438 // One extractable input parameter is provided, and the Web Crypto API | 629 // 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. | 630 // 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 | 631 // 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 | 632 // 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 | 633 // 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 | 634 // spec bug here: https://www.w3.org/Bugs/Public/show_bug.cgi?id=23695 |
| 444 *public_key = WebKit::WebCryptoKey::create( | 635 *public_key = WebKit::WebCryptoKey::create( |
| 445 new PublicKeyHandle(crypto::ScopedSECKEYPublicKey(sec_public_key)), | 636 new PublicKeyHandle(crypto::ScopedSECKEYPublicKey(sec_public_key)), |
| 446 WebKit::WebCryptoKeyTypePublic, | 637 WebKit::WebCryptoKeyTypePublic, |
| 447 extractable, // probably should be 'true' always | 638 extractable, // probably should be 'true' always |
| 448 algorithm, | 639 algorithm, |
| 449 usage_mask); | 640 usage_mask); |
| 450 *private_key = WebKit::WebCryptoKey::create( | 641 *private_key = WebKit::WebCryptoKey::create( |
| 451 new PrivateKeyHandle(scoped_sec_private_key.Pass()), | 642 new PrivateKeyHandle(scoped_sec_private_key.Pass()), |
| 452 WebKit::WebCryptoKeyTypePrivate, | 643 WebKit::WebCryptoKeyTypePrivate, |
| 453 extractable, | 644 extractable, |
| 454 algorithm, | 645 algorithm, |
| 455 usage_mask); | 646 usage_mask); |
| 456 | 647 |
| 457 return true; | 648 return true; |
| 458 } | 649 } |
| 459 default: | 650 default: |
| 460 return false; | 651 return false; |
| 461 } | 652 } |
| 462 } | 653 } |
| 463 | 654 |
| 464 bool WebCryptoImpl::ImportKeyInternal( | 655 bool WebCryptoImpl::ImportKeyInternal( |
| 465 WebKit::WebCryptoKeyFormat format, | 656 WebKit::WebCryptoKeyFormat format, |
| 466 const unsigned char* key_data, | 657 const unsigned char* key_data, |
| 467 unsigned key_data_size, | 658 unsigned key_data_size, |
| 468 const WebKit::WebCryptoAlgorithm& algorithm_or_null, | 659 const WebKit::WebCryptoAlgorithm& algorithm_or_null, |
| 469 bool extractable, | 660 bool extractable, |
| 470 WebKit::WebCryptoKeyUsageMask usage_mask, | 661 WebKit::WebCryptoKeyUsageMask usage_mask, |
| 471 WebKit::WebCryptoKey* key) { | 662 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 | |
| 478 WebKit::WebCryptoKeyType type; | |
| 479 switch (algorithm.id()) { | |
| 480 case WebKit::WebCryptoAlgorithmIdHmac: | |
| 481 case WebKit::WebCryptoAlgorithmIdAesCbc: | |
| 482 type = WebKit::WebCryptoKeyTypeSecret; | |
| 483 break; | |
| 484 // TODO(bryaneyler): Support more key types. | |
| 485 default: | |
| 486 return false; | |
| 487 } | |
| 488 | |
| 489 // TODO(bryaneyler): Need to split handling for symmetric and asymmetric keys. | |
| 490 // Currently only supporting symmetric. | |
| 491 CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; | |
| 492 // Flags are verified at the Blink layer; here the flags are set to all | |
| 493 // possible operations for this key type. | |
| 494 CK_FLAGS flags = 0; | |
| 495 | |
| 496 switch(algorithm.id()) { | |
| 497 case WebKit::WebCryptoAlgorithmIdHmac: { | |
| 498 const WebKit::WebCryptoHmacParams* params = algorithm.hmacParams(); | |
| 499 if (!params) { | |
| 500 return false; | |
| 501 } | |
| 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: | |
| 518 return false; | |
| 519 } | |
| 520 | |
| 521 DCHECK_NE(CKM_INVALID_MECHANISM, mechanism); | |
| 522 DCHECK_NE(0ul, flags); | |
| 523 | |
| 524 SECItem key_item = { siBuffer, NULL, 0 }; | |
| 525 | 663 |
| 526 switch (format) { | 664 switch (format) { |
| 527 case WebKit::WebCryptoKeyFormatRaw: | 665 case WebKit::WebCryptoKeyFormatRaw: |
| 528 key_item.data = const_cast<unsigned char*>(key_data); | 666 return ImportKeyInternalRaw(key_data, |
| 529 key_item.len = key_data_size; | 667 key_data_size, |
| 530 break; | 668 algorithm_or_null, |
| 531 // TODO(bryaneyler): Handle additional formats. | 669 extractable, |
| 670 usage_mask, | |
| 671 key); | |
| 672 case WebKit::WebCryptoKeyFormatSpki: | |
| 673 return ImportKeyInternalSpki(key_data, | |
| 674 key_data_size, | |
| 675 algorithm_or_null, | |
| 676 extractable, | |
| 677 usage_mask, | |
| 678 key); | |
| 679 case WebKit::WebCryptoKeyFormatPkcs8: | |
| 680 // TODO(padolph): Handle PKCS#8 private key import | |
| 681 return false; | |
| 532 default: | 682 default: |
| 683 NOTREACHED(); | |
|
eroman
2013/11/06 23:48:40
Please leave this off since it is reachable (i.e.
padolph
2013/11/07 00:23:50
Done.
| |
| 533 return false; | 684 return false; |
| 534 } | 685 } |
| 686 } | |
| 535 | 687 |
| 536 crypto::ScopedPK11SymKey pk11_sym_key( | 688 bool WebCryptoImpl::ExportKeyInternal( |
| 537 PK11_ImportSymKeyWithFlags(PK11_GetInternalSlot(), | 689 WebKit::WebCryptoKeyFormat format, |
| 538 mechanism, | 690 const WebKit::WebCryptoKey& key, |
| 539 PK11_OriginUnwrap, | 691 WebKit::WebArrayBuffer* buffer) { |
| 540 CKA_FLAGS_ONLY, | 692 switch (format) { |
| 541 &key_item, | 693 case WebKit::WebCryptoKeyFormatRaw: |
| 542 flags, | 694 // TODO(padolph): Implement raw export |
| 543 false, | 695 NOTREACHED(); |
|
eroman
2013/11/06 23:48:40
Same comment here. Use NOTREACHED() only for code
padolph
2013/11/07 00:23:50
Done.
| |
| 544 NULL)); | 696 return false; |
| 545 if (!pk11_sym_key.get()) { | 697 case WebKit::WebCryptoKeyFormatSpki: |
| 546 return false; | 698 return ExportKeyInternalSpki(key, buffer); |
| 699 case WebKit::WebCryptoKeyFormatPkcs8: | |
| 700 // TODO(padolph): Implement pkcs8 export | |
| 701 NOTREACHED(); | |
| 702 return false; | |
| 703 default: | |
| 704 NOTREACHED(); | |
| 705 return false; | |
| 547 } | 706 } |
| 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 |