Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(187)

Side by Side Diff: content/renderer/webcrypto/webcrypto_impl_nss.cc

Issue 62633004: [webcrypto] Add RSA public key SPKI import/export for NSS. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixes for eroman Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
OLDNEW
« no previous file with comments | « content/renderer/webcrypto/webcrypto_impl.cc ('k') | content/renderer/webcrypto/webcrypto_impl_openssl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698