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

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

Issue 188203003: Move webcrypto to child/ and add support to worker_webkitplatformsupport. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixes to gypis Created 6 years, 9 months 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 | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "content/renderer/webcrypto/platform_crypto.h"
6
7 #include <cryptohi.h>
8 #include <pk11pub.h>
9 #include <sechash.h>
10
11 #include <vector>
12
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "content/renderer/webcrypto/crypto_data.h"
16 #include "content/renderer/webcrypto/webcrypto_util.h"
17 #include "crypto/nss_util.h"
18 #include "crypto/scoped_nss_types.h"
19 #include "third_party/WebKit/public/platform/WebArrayBuffer.h"
20 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h"
21 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
22 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
23
24 #if defined(USE_NSS)
25 #include <dlfcn.h>
26 #endif
27
28 // At the time of this writing:
29 // * Windows and Mac builds ship with their own copy of NSS (3.15+)
30 // * Linux builds use the system's libnss, which is 3.14 on Debian (but 3.15+
31 // on other distros).
32 //
33 // Since NSS provides AES-GCM support starting in version 3.15, it may be
34 // unavailable for Linux Chrome users.
35 //
36 // * !defined(CKM_AES_GCM)
37 //
38 // This means that at build time, the NSS header pkcs11t.h is older than
39 // 3.15. However at runtime support may be present.
40 //
41 // * !defined(USE_NSS)
42 //
43 // This means that Chrome is being built with an embedded copy of NSS,
44 // which can be assumed to be >= 3.15. On the other hand if USE_NSS is
45 // defined, it also implies running on Linux.
46 //
47 // TODO(eroman): Simplify this once 3.15+ is required by Linux builds.
48 #if !defined(CKM_AES_GCM)
49 #define CKM_AES_GCM 0x00001087
50
51 struct CK_GCM_PARAMS {
52 CK_BYTE_PTR pIv;
53 CK_ULONG ulIvLen;
54 CK_BYTE_PTR pAAD;
55 CK_ULONG ulAADLen;
56 CK_ULONG ulTagBits;
57 };
58 #endif // !defined(CKM_AES_GCM)
59
60 // Signature for PK11_Encrypt and PK11_Decrypt.
61 typedef SECStatus (*PK11_EncryptDecryptFunction)(PK11SymKey*,
62 CK_MECHANISM_TYPE,
63 SECItem*,
64 unsigned char*,
65 unsigned int*,
66 unsigned int,
67 const unsigned char*,
68 unsigned int);
69
70 // Singleton to abstract away dynamically loading libnss3.so
71 class AesGcmSupport {
72 public:
73 bool IsSupported() const { return pk11_encrypt_func_ && pk11_decrypt_func_; }
74
75 // Returns NULL if unsupported.
76 PK11_EncryptDecryptFunction pk11_encrypt_func() const {
77 return pk11_encrypt_func_;
78 }
79
80 // Returns NULL if unsupported.
81 PK11_EncryptDecryptFunction pk11_decrypt_func() const {
82 return pk11_decrypt_func_;
83 }
84
85 private:
86 friend struct base::DefaultLazyInstanceTraits<AesGcmSupport>;
87
88 AesGcmSupport() {
89 #if !defined(USE_NSS)
90 // Using a bundled version of NSS that is guaranteed to have this symbol.
91 pk11_encrypt_func_ = PK11_Encrypt;
92 pk11_decrypt_func_ = PK11_Decrypt;
93 #else
94 // Using system NSS libraries and PCKS #11 modules, which may not have the
95 // necessary function (PK11_Encrypt) or mechanism support (CKM_AES_GCM).
96
97 // If PK11_Encrypt() was successfully resolved, then NSS will support
98 // AES-GCM directly. This was introduced in NSS 3.15.
99 pk11_encrypt_func_ = reinterpret_cast<PK11_EncryptDecryptFunction>(
100 dlsym(RTLD_DEFAULT, "PK11_Encrypt"));
101 pk11_decrypt_func_ = reinterpret_cast<PK11_EncryptDecryptFunction>(
102 dlsym(RTLD_DEFAULT, "PK11_Decrypt"));
103 #endif
104 }
105
106 PK11_EncryptDecryptFunction pk11_encrypt_func_;
107 PK11_EncryptDecryptFunction pk11_decrypt_func_;
108 };
109
110 base::LazyInstance<AesGcmSupport>::Leaky g_aes_gcm_support =
111 LAZY_INSTANCE_INITIALIZER;
112
113 namespace content {
114
115 namespace webcrypto {
116
117 namespace platform {
118
119 class SymKey : public Key {
120 public:
121 explicit SymKey(crypto::ScopedPK11SymKey key) : key_(key.Pass()) {}
122
123 PK11SymKey* key() { return key_.get(); }
124
125 virtual SymKey* AsSymKey() OVERRIDE { return this; }
126 virtual PublicKey* AsPublicKey() OVERRIDE { return NULL; }
127 virtual PrivateKey* AsPrivateKey() OVERRIDE { return NULL; }
128
129 private:
130 crypto::ScopedPK11SymKey key_;
131
132 DISALLOW_COPY_AND_ASSIGN(SymKey);
133 };
134
135 class PublicKey : public Key {
136 public:
137 explicit PublicKey(crypto::ScopedSECKEYPublicKey key) : key_(key.Pass()) {}
138
139 SECKEYPublicKey* key() { return key_.get(); }
140
141 virtual SymKey* AsSymKey() OVERRIDE { return NULL; }
142 virtual PublicKey* AsPublicKey() OVERRIDE { return this; }
143 virtual PrivateKey* AsPrivateKey() OVERRIDE { return NULL; }
144
145 private:
146 crypto::ScopedSECKEYPublicKey key_;
147
148 DISALLOW_COPY_AND_ASSIGN(PublicKey);
149 };
150
151 class PrivateKey : public Key {
152 public:
153 explicit PrivateKey(crypto::ScopedSECKEYPrivateKey key) : key_(key.Pass()) {}
154
155 SECKEYPrivateKey* key() { return key_.get(); }
156
157 virtual SymKey* AsSymKey() OVERRIDE { return NULL; }
158 virtual PublicKey* AsPublicKey() OVERRIDE { return NULL; }
159 virtual PrivateKey* AsPrivateKey() OVERRIDE { return this; }
160
161 private:
162 crypto::ScopedSECKEYPrivateKey key_;
163
164 DISALLOW_COPY_AND_ASSIGN(PrivateKey);
165 };
166
167 namespace {
168
169 // Creates a SECItem for the data in |buffer|. This does NOT make a copy, so
170 // |buffer| should outlive the SECItem.
171 SECItem MakeSECItemForBuffer(const CryptoData& buffer) {
172 SECItem item = {
173 siBuffer,
174 // NSS requires non-const data even though it is just for input.
175 const_cast<unsigned char*>(buffer.bytes()), buffer.byte_length()};
176 return item;
177 }
178
179 HASH_HashType WebCryptoAlgorithmToNSSHashType(
180 blink::WebCryptoAlgorithmId algorithm) {
181 switch (algorithm) {
182 case blink::WebCryptoAlgorithmIdSha1:
183 return HASH_AlgSHA1;
184 case blink::WebCryptoAlgorithmIdSha224:
185 return HASH_AlgSHA224;
186 case blink::WebCryptoAlgorithmIdSha256:
187 return HASH_AlgSHA256;
188 case blink::WebCryptoAlgorithmIdSha384:
189 return HASH_AlgSHA384;
190 case blink::WebCryptoAlgorithmIdSha512:
191 return HASH_AlgSHA512;
192 default:
193 // Not a digest algorithm.
194 return HASH_AlgNULL;
195 }
196 }
197
198 CK_MECHANISM_TYPE WebCryptoHashToHMACMechanism(
199 const blink::WebCryptoAlgorithm& algorithm) {
200 switch (algorithm.id()) {
201 case blink::WebCryptoAlgorithmIdSha1:
202 return CKM_SHA_1_HMAC;
203 case blink::WebCryptoAlgorithmIdSha224:
204 return CKM_SHA224_HMAC;
205 case blink::WebCryptoAlgorithmIdSha256:
206 return CKM_SHA256_HMAC;
207 case blink::WebCryptoAlgorithmIdSha384:
208 return CKM_SHA384_HMAC;
209 case blink::WebCryptoAlgorithmIdSha512:
210 return CKM_SHA512_HMAC;
211 default:
212 // Not a supported algorithm.
213 return CKM_INVALID_MECHANISM;
214 }
215 }
216
217 Status AesCbcEncryptDecrypt(EncryptOrDecrypt mode,
218 SymKey* key,
219 const CryptoData& iv,
220 const CryptoData& data,
221 blink::WebArrayBuffer* buffer) {
222 CK_ATTRIBUTE_TYPE operation = (mode == ENCRYPT) ? CKA_ENCRYPT : CKA_DECRYPT;
223
224 SECItem iv_item = MakeSECItemForBuffer(iv);
225
226 crypto::ScopedSECItem param(PK11_ParamFromIV(CKM_AES_CBC_PAD, &iv_item));
227 if (!param)
228 return Status::Error();
229
230 crypto::ScopedPK11Context context(PK11_CreateContextBySymKey(
231 CKM_AES_CBC_PAD, operation, key->key(), param.get()));
232
233 if (!context.get())
234 return Status::Error();
235
236 // Oddly PK11_CipherOp takes input and output lengths as "int" rather than
237 // "unsigned int". Do some checks now to avoid integer overflowing.
238 if (data.byte_length() >= INT_MAX - AES_BLOCK_SIZE) {
239 // TODO(eroman): Handle this by chunking the input fed into NSS. Right now
240 // it doesn't make much difference since the one-shot API would end up
241 // blowing out the memory and crashing anyway.
242 return Status::ErrorDataTooLarge();
243 }
244
245 // PK11_CipherOp does an invalid memory access when given empty decryption
246 // input, or input which is not a multiple of the block size. See also
247 // https://bugzilla.mozilla.com/show_bug.cgi?id=921687.
248 if (operation == CKA_DECRYPT &&
249 (data.byte_length() == 0 || (data.byte_length() % AES_BLOCK_SIZE != 0))) {
250 return Status::Error();
251 }
252
253 // TODO(eroman): Refine the output buffer size. It can be computed exactly for
254 // encryption, and can be smaller for decryption.
255 unsigned int output_max_len = data.byte_length() + AES_BLOCK_SIZE;
256 CHECK_GT(output_max_len, data.byte_length());
257
258 *buffer = blink::WebArrayBuffer::create(output_max_len, 1);
259
260 unsigned char* buffer_data = reinterpret_cast<unsigned char*>(buffer->data());
261
262 int output_len;
263 if (SECSuccess != PK11_CipherOp(context.get(),
264 buffer_data,
265 &output_len,
266 buffer->byteLength(),
267 data.bytes(),
268 data.byte_length())) {
269 return Status::Error();
270 }
271
272 unsigned int final_output_chunk_len;
273 if (SECSuccess != PK11_DigestFinal(context.get(),
274 buffer_data + output_len,
275 &final_output_chunk_len,
276 output_max_len - output_len)) {
277 return Status::Error();
278 }
279
280 ShrinkBuffer(buffer, final_output_chunk_len + output_len);
281 return Status::Success();
282 }
283
284 // Helper to either encrypt or decrypt for AES-GCM. The result of encryption is
285 // the concatenation of the ciphertext and the authentication tag. Similarly,
286 // this is the expectation for the input to decryption.
287 Status AesGcmEncryptDecrypt(EncryptOrDecrypt mode,
288 SymKey* key,
289 const CryptoData& data,
290 const CryptoData& iv,
291 const CryptoData& additional_data,
292 unsigned int tag_length_bits,
293 blink::WebArrayBuffer* buffer) {
294 if (!g_aes_gcm_support.Get().IsSupported())
295 return Status::ErrorUnsupported();
296
297 unsigned int tag_length_bytes = tag_length_bits / 8;
298
299 CK_GCM_PARAMS gcm_params = {0};
300 gcm_params.pIv = const_cast<unsigned char*>(iv.bytes());
301 gcm_params.ulIvLen = iv.byte_length();
302
303 gcm_params.pAAD = const_cast<unsigned char*>(additional_data.bytes());
304 gcm_params.ulAADLen = additional_data.byte_length();
305
306 gcm_params.ulTagBits = tag_length_bits;
307
308 SECItem param;
309 param.type = siBuffer;
310 param.data = reinterpret_cast<unsigned char*>(&gcm_params);
311 param.len = sizeof(gcm_params);
312
313 unsigned int buffer_size = 0;
314
315 // Calculate the output buffer size.
316 if (mode == ENCRYPT) {
317 // TODO(eroman): This is ugly, abstract away the safe integer arithmetic.
318 if (data.byte_length() > (UINT_MAX - tag_length_bytes))
319 return Status::ErrorDataTooLarge();
320 buffer_size = data.byte_length() + tag_length_bytes;
321 } else {
322 // TODO(eroman): In theory the buffer allocated for the plain text should be
323 // sized as |data.byte_length() - tag_length_bytes|.
324 //
325 // However NSS has a bug whereby it will fail if the output buffer size is
326 // not at least as large as the ciphertext:
327 //
328 // https://bugzilla.mozilla.org/show_bug.cgi?id=%20853674
329 //
330 // From the analysis of that bug it looks like it might be safe to pass a
331 // correctly sized buffer but lie about its size. Since resizing the
332 // WebCryptoArrayBuffer is expensive that hack may be worth looking into.
333 buffer_size = data.byte_length();
334 }
335
336 *buffer = blink::WebArrayBuffer::create(buffer_size, 1);
337 unsigned char* buffer_data = reinterpret_cast<unsigned char*>(buffer->data());
338
339 PK11_EncryptDecryptFunction func =
340 (mode == ENCRYPT) ? g_aes_gcm_support.Get().pk11_encrypt_func()
341 : g_aes_gcm_support.Get().pk11_decrypt_func();
342
343 unsigned int output_len = 0;
344 SECStatus result = func(key->key(),
345 CKM_AES_GCM,
346 &param,
347 buffer_data,
348 &output_len,
349 buffer->byteLength(),
350 data.bytes(),
351 data.byte_length());
352
353 if (result != SECSuccess)
354 return Status::Error();
355
356 // Unfortunately the buffer needs to be shrunk for decryption (see the NSS bug
357 // above).
358 ShrinkBuffer(buffer, output_len);
359
360 return Status::Success();
361 }
362
363 CK_MECHANISM_TYPE WebCryptoAlgorithmToGenMechanism(
364 const blink::WebCryptoAlgorithm& algorithm) {
365 switch (algorithm.id()) {
366 case blink::WebCryptoAlgorithmIdAesCbc:
367 case blink::WebCryptoAlgorithmIdAesGcm:
368 case blink::WebCryptoAlgorithmIdAesKw:
369 return CKM_AES_KEY_GEN;
370 case blink::WebCryptoAlgorithmIdHmac:
371 return WebCryptoHashToHMACMechanism(algorithm.hmacKeyGenParams()->hash());
372 default:
373 return CKM_INVALID_MECHANISM;
374 }
375 }
376
377 // Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros,
378 // to unsigned long.
379 bool BigIntegerToLong(const uint8* data,
380 unsigned int data_size,
381 unsigned long* result) {
382 // TODO(padolph): Is it correct to say that empty data is an error, or does it
383 // mean value 0? See https://www.w3.org/Bugs/Public/show_bug.cgi?id=23655
384 if (data_size == 0)
385 return false;
386
387 *result = 0;
388 for (size_t i = 0; i < data_size; ++i) {
389 size_t reverse_i = data_size - i - 1;
390
391 if (reverse_i >= sizeof(unsigned long) && data[i])
392 return false; // Too large for a long.
393
394 *result |= data[i] << 8 * reverse_i;
395 }
396 return true;
397 }
398
399 bool IsAlgorithmRsa(const blink::WebCryptoAlgorithm& algorithm) {
400 return algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 ||
401 algorithm.id() == blink::WebCryptoAlgorithmIdRsaOaep ||
402 algorithm.id() == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5;
403 }
404
405 bool CreatePublicKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm,
406 SECKEYPublicKey* key,
407 blink::WebCryptoKeyAlgorithm* key_algorithm) {
408 // TODO(eroman): What about other key types rsaPss, rsaOaep.
409 if (!key || key->keyType != rsaKey)
410 return false;
411
412 unsigned int modulus_length_bits = SECKEY_PublicKeyStrength(key) * 8;
413 CryptoData public_exponent(key->u.rsa.publicExponent.data,
414 key->u.rsa.publicExponent.len);
415
416 switch (algorithm.paramsType()) {
417 case blink::WebCryptoAlgorithmParamsTypeRsaHashedImportParams:
418 case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams:
419 *key_algorithm = blink::WebCryptoKeyAlgorithm::adoptParamsAndCreate(
420 algorithm.id(),
421 new blink::WebCryptoRsaHashedKeyAlgorithmParams(
422 modulus_length_bits,
423 public_exponent.bytes(),
424 public_exponent.byte_length(),
425 GetInnerHashAlgorithm(algorithm)));
426 return true;
427 case blink::WebCryptoAlgorithmParamsTypeRsaKeyGenParams:
428 case blink::WebCryptoAlgorithmParamsTypeNone:
429 *key_algorithm = blink::WebCryptoKeyAlgorithm::adoptParamsAndCreate(
430 algorithm.id(),
431 new blink::WebCryptoRsaKeyAlgorithmParams(
432 modulus_length_bits,
433 public_exponent.bytes(),
434 public_exponent.byte_length()));
435 return true;
436 default:
437 return false;
438 }
439 }
440
441 bool CreatePrivateKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm,
442 SECKEYPrivateKey* key,
443 blink::WebCryptoKeyAlgorithm* key_algorithm) {
444 crypto::ScopedSECKEYPublicKey public_key(SECKEY_ConvertToPublicKey(key));
445 return CreatePublicKeyAlgorithm(algorithm, public_key.get(), key_algorithm);
446 }
447
448 } // namespace
449
450 Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm,
451 const CryptoData& key_data,
452 bool extractable,
453 blink::WebCryptoKeyUsageMask usage_mask,
454 blink::WebCryptoKey* key) {
455
456 DCHECK(!algorithm.isNull());
457
458 // TODO(bryaneyler): Need to split handling for symmetric and asymmetric keys.
459 // Currently only supporting symmetric.
460 CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM;
461 // Flags are verified at the Blink layer; here the flags are set to all
462 // possible operations for this key type.
463 CK_FLAGS flags = 0;
464
465 switch (algorithm.id()) {
466 case blink::WebCryptoAlgorithmIdHmac: {
467 const blink::WebCryptoAlgorithm& hash = GetInnerHashAlgorithm(algorithm);
468
469 mechanism = WebCryptoHashToHMACMechanism(hash);
470 if (mechanism == CKM_INVALID_MECHANISM)
471 return Status::ErrorUnsupported();
472
473 flags |= CKF_SIGN | CKF_VERIFY;
474 break;
475 }
476 case blink::WebCryptoAlgorithmIdAesCbc: {
477 mechanism = CKM_AES_CBC;
478 flags |= CKF_ENCRYPT | CKF_DECRYPT;
479 break;
480 }
481 case blink::WebCryptoAlgorithmIdAesKw: {
482 mechanism = CKM_NSS_AES_KEY_WRAP;
483 flags |= CKF_WRAP | CKF_WRAP;
484 break;
485 }
486 case blink::WebCryptoAlgorithmIdAesGcm: {
487 if (!g_aes_gcm_support.Get().IsSupported())
488 return Status::ErrorUnsupported();
489 mechanism = CKM_AES_GCM;
490 flags |= CKF_ENCRYPT | CKF_DECRYPT;
491 break;
492 }
493 default:
494 return Status::ErrorUnsupported();
495 }
496
497 DCHECK_NE(CKM_INVALID_MECHANISM, mechanism);
498 DCHECK_NE(0ul, flags);
499
500 SECItem key_item = MakeSECItemForBuffer(key_data);
501
502 crypto::ScopedPK11Slot slot(PK11_GetInternalSlot());
503 crypto::ScopedPK11SymKey pk11_sym_key(
504 PK11_ImportSymKeyWithFlags(slot.get(),
505 mechanism,
506 PK11_OriginUnwrap,
507 CKA_FLAGS_ONLY,
508 &key_item,
509 flags,
510 false,
511 NULL));
512 if (!pk11_sym_key.get())
513 return Status::Error();
514
515 blink::WebCryptoKeyAlgorithm key_algorithm;
516 if (!CreateSecretKeyAlgorithm(
517 algorithm, key_data.byte_length(), &key_algorithm))
518 return Status::ErrorUnexpected();
519
520 *key = blink::WebCryptoKey::create(new SymKey(pk11_sym_key.Pass()),
521 blink::WebCryptoKeyTypeSecret,
522 extractable,
523 key_algorithm,
524 usage_mask);
525 return Status::Success();
526 }
527
528 Status ExportKeyRaw(SymKey* key, blink::WebArrayBuffer* buffer) {
529 if (PK11_ExtractKeyValue(key->key()) != SECSuccess)
530 return Status::Error();
531
532 const SECItem* key_data = PK11_GetKeyData(key->key());
533 if (!key_data)
534 return Status::Error();
535
536 *buffer = CreateArrayBuffer(key_data->data, key_data->len);
537
538 return Status::Success();
539 }
540
541 namespace {
542
543 typedef scoped_ptr<CERTSubjectPublicKeyInfo,
544 crypto::NSSDestroyer<CERTSubjectPublicKeyInfo,
545 SECKEY_DestroySubjectPublicKeyInfo> >
546 ScopedCERTSubjectPublicKeyInfo;
547
548 // Validates an NSS KeyType against a WebCrypto algorithm. Some NSS KeyTypes
549 // contain enough information to fabricate a Web Crypto algorithm, which is
550 // returned if the input algorithm isNull(). This function indicates failure by
551 // returning a Null algorithm.
552 blink::WebCryptoAlgorithm ResolveNssKeyTypeWithInputAlgorithm(
553 KeyType key_type,
554 const blink::WebCryptoAlgorithm& algorithm_or_null) {
555 switch (key_type) {
556 case rsaKey:
557 // NSS's rsaKey KeyType maps to keys with SEC_OID_PKCS1_RSA_ENCRYPTION and
558 // according to RFCs 4055/5756 this can be used for both encryption and
559 // signatures. However, this is not specific enough to build a compatible
560 // Web Crypto algorithm, since in Web Crypto, RSA encryption and signature
561 // algorithms are distinct. So if the input algorithm isNull() here, we
562 // have to fail.
563 if (!algorithm_or_null.isNull() && IsAlgorithmRsa(algorithm_or_null))
564 return algorithm_or_null;
565 break;
566 case dsaKey:
567 case ecKey:
568 case rsaPssKey:
569 case rsaOaepKey:
570 // TODO(padolph): Handle other key types.
571 break;
572 default:
573 break;
574 }
575 return blink::WebCryptoAlgorithm::createNull();
576 }
577
578 } // namespace
579
580 Status ImportKeySpki(const blink::WebCryptoAlgorithm& algorithm_or_null,
581 const CryptoData& key_data,
582 bool extractable,
583 blink::WebCryptoKeyUsageMask usage_mask,
584 blink::WebCryptoKey* key) {
585
586 DCHECK(key);
587
588 if (!key_data.byte_length())
589 return Status::ErrorImportEmptyKeyData();
590 DCHECK(key_data.bytes());
591
592 // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 Subject
593 // Public Key Info. Decode this to a CERTSubjectPublicKeyInfo.
594 SECItem spki_item = MakeSECItemForBuffer(key_data);
595 const ScopedCERTSubjectPublicKeyInfo spki(
596 SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item));
597 if (!spki)
598 return Status::Error();
599
600 crypto::ScopedSECKEYPublicKey sec_public_key(
601 SECKEY_ExtractPublicKey(spki.get()));
602 if (!sec_public_key)
603 return Status::Error();
604
605 const KeyType sec_key_type = SECKEY_GetPublicKeyType(sec_public_key.get());
606 blink::WebCryptoAlgorithm algorithm =
607 ResolveNssKeyTypeWithInputAlgorithm(sec_key_type, algorithm_or_null);
608 if (algorithm.isNull())
609 return Status::Error();
610
611 blink::WebCryptoKeyAlgorithm key_algorithm;
612 if (!CreatePublicKeyAlgorithm(
613 algorithm, sec_public_key.get(), &key_algorithm))
614 return Status::ErrorUnexpected();
615
616 *key = blink::WebCryptoKey::create(new PublicKey(sec_public_key.Pass()),
617 blink::WebCryptoKeyTypePublic,
618 extractable,
619 key_algorithm,
620 usage_mask);
621
622 return Status::Success();
623 }
624
625 Status ExportKeySpki(PublicKey* key, blink::WebArrayBuffer* buffer) {
626 const crypto::ScopedSECItem spki_der(
627 SECKEY_EncodeDERSubjectPublicKeyInfo(key->key()));
628 if (!spki_der)
629 return Status::Error();
630
631 DCHECK(spki_der->data);
632 DCHECK(spki_der->len);
633
634 *buffer = CreateArrayBuffer(spki_der->data, spki_der->len);
635
636 return Status::Success();
637 }
638
639 Status ImportKeyPkcs8(const blink::WebCryptoAlgorithm& algorithm_or_null,
640 const CryptoData& key_data,
641 bool extractable,
642 blink::WebCryptoKeyUsageMask usage_mask,
643 blink::WebCryptoKey* key) {
644
645 DCHECK(key);
646
647 if (!key_data.byte_length())
648 return Status::ErrorImportEmptyKeyData();
649 DCHECK(key_data.bytes());
650
651 // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 PKCS#8
652 // private key info object.
653 SECItem pki_der = MakeSECItemForBuffer(key_data);
654
655 SECKEYPrivateKey* seckey_private_key = NULL;
656 crypto::ScopedPK11Slot slot(PK11_GetInternalSlot());
657 if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot.get(),
658 &pki_der,
659 NULL, // nickname
660 NULL, // publicValue
661 false, // isPerm
662 false, // isPrivate
663 KU_ALL, // usage
664 &seckey_private_key,
665 NULL) != SECSuccess) {
666 return Status::Error();
667 }
668 DCHECK(seckey_private_key);
669 crypto::ScopedSECKEYPrivateKey private_key(seckey_private_key);
670
671 const KeyType sec_key_type = SECKEY_GetPrivateKeyType(private_key.get());
672 blink::WebCryptoAlgorithm algorithm =
673 ResolveNssKeyTypeWithInputAlgorithm(sec_key_type, algorithm_or_null);
674 if (algorithm.isNull())
675 return Status::Error();
676
677 blink::WebCryptoKeyAlgorithm key_algorithm;
678 if (!CreatePrivateKeyAlgorithm(algorithm, private_key.get(), &key_algorithm))
679 return Status::ErrorUnexpected();
680
681 *key = blink::WebCryptoKey::create(new PrivateKey(private_key.Pass()),
682 blink::WebCryptoKeyTypePrivate,
683 extractable,
684 key_algorithm,
685 usage_mask);
686
687 return Status::Success();
688 }
689
690 // -----------------------------------
691 // Hmac
692 // -----------------------------------
693
694 Status SignHmac(SymKey* key,
695 const blink::WebCryptoAlgorithm& hash,
696 const CryptoData& data,
697 blink::WebArrayBuffer* buffer) {
698 DCHECK_EQ(PK11_GetMechanism(key->key()), WebCryptoHashToHMACMechanism(hash));
699
700 SECItem param_item = {siBuffer, NULL, 0};
701 SECItem data_item = MakeSECItemForBuffer(data);
702 // First call is to figure out the length.
703 SECItem signature_item = {siBuffer, NULL, 0};
704
705 if (PK11_SignWithSymKey(key->key(),
706 PK11_GetMechanism(key->key()),
707 &param_item,
708 &signature_item,
709 &data_item) != SECSuccess) {
710 return Status::Error();
711 }
712
713 DCHECK_NE(0u, signature_item.len);
714
715 *buffer = blink::WebArrayBuffer::create(signature_item.len, 1);
716 signature_item.data = reinterpret_cast<unsigned char*>(buffer->data());
717
718 if (PK11_SignWithSymKey(key->key(),
719 PK11_GetMechanism(key->key()),
720 &param_item,
721 &signature_item,
722 &data_item) != SECSuccess) {
723 return Status::Error();
724 }
725
726 DCHECK_EQ(buffer->byteLength(), signature_item.len);
727 return Status::Success();
728 }
729
730 // -----------------------------------
731 // RsaEsPkcs1v1_5
732 // -----------------------------------
733
734 Status EncryptRsaEsPkcs1v1_5(PublicKey* key,
735 const CryptoData& data,
736 blink::WebArrayBuffer* buffer) {
737 const unsigned int encrypted_length_bytes =
738 SECKEY_PublicKeyStrength(key->key());
739
740 // RSAES can operate on messages up to a length of k - 11, where k is the
741 // octet length of the RSA modulus.
742 if (encrypted_length_bytes < 11 ||
743 encrypted_length_bytes - 11 < data.byte_length())
744 return Status::ErrorDataTooLarge();
745
746 *buffer = blink::WebArrayBuffer::create(encrypted_length_bytes, 1);
747 unsigned char* const buffer_data =
748 reinterpret_cast<unsigned char*>(buffer->data());
749
750 if (PK11_PubEncryptPKCS1(key->key(),
751 buffer_data,
752 const_cast<unsigned char*>(data.bytes()),
753 data.byte_length(),
754 NULL) != SECSuccess) {
755 return Status::Error();
756 }
757 return Status::Success();
758 }
759
760 Status DecryptRsaEsPkcs1v1_5(PrivateKey* key,
761 const CryptoData& data,
762 blink::WebArrayBuffer* buffer) {
763 const int modulus_length_bytes = PK11_GetPrivateModulusLen(key->key());
764 if (modulus_length_bytes <= 0)
765 return Status::ErrorUnexpected();
766 const unsigned int max_output_length_bytes = modulus_length_bytes;
767
768 *buffer = blink::WebArrayBuffer::create(max_output_length_bytes, 1);
769 unsigned char* const buffer_data =
770 reinterpret_cast<unsigned char*>(buffer->data());
771
772 unsigned int output_length_bytes = 0;
773 if (PK11_PrivDecryptPKCS1(key->key(),
774 buffer_data,
775 &output_length_bytes,
776 max_output_length_bytes,
777 const_cast<unsigned char*>(data.bytes()),
778 data.byte_length()) != SECSuccess) {
779 return Status::Error();
780 }
781 DCHECK_LE(output_length_bytes, max_output_length_bytes);
782 ShrinkBuffer(buffer, output_length_bytes);
783 return Status::Success();
784 }
785
786 // -----------------------------------
787 // RsaSsaPkcs1v1_5
788 // -----------------------------------
789
790 Status SignRsaSsaPkcs1v1_5(PrivateKey* key,
791 const blink::WebCryptoAlgorithm& hash,
792 const CryptoData& data,
793 blink::WebArrayBuffer* buffer) {
794 // Pick the NSS signing algorithm by combining RSA-SSA (RSA PKCS1) and the
795 // inner hash of the input Web Crypto algorithm.
796 SECOidTag sign_alg_tag;
797 switch (hash.id()) {
798 case blink::WebCryptoAlgorithmIdSha1:
799 sign_alg_tag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
800 break;
801 case blink::WebCryptoAlgorithmIdSha224:
802 sign_alg_tag = SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION;
803 break;
804 case blink::WebCryptoAlgorithmIdSha256:
805 sign_alg_tag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
806 break;
807 case blink::WebCryptoAlgorithmIdSha384:
808 sign_alg_tag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION;
809 break;
810 case blink::WebCryptoAlgorithmIdSha512:
811 sign_alg_tag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION;
812 break;
813 default:
814 return Status::ErrorUnsupported();
815 }
816
817 crypto::ScopedSECItem signature_item(SECITEM_AllocItem(NULL, NULL, 0));
818 if (SEC_SignData(signature_item.get(),
819 data.bytes(),
820 data.byte_length(),
821 key->key(),
822 sign_alg_tag) != SECSuccess) {
823 return Status::Error();
824 }
825
826 *buffer = CreateArrayBuffer(signature_item->data, signature_item->len);
827 return Status::Success();
828 }
829
830 Status VerifyRsaSsaPkcs1v1_5(PublicKey* key,
831 const blink::WebCryptoAlgorithm& hash,
832 const CryptoData& signature,
833 const CryptoData& data,
834 bool* signature_match) {
835 const SECItem signature_item = MakeSECItemForBuffer(signature);
836
837 SECOidTag hash_alg_tag;
838 switch (hash.id()) {
839 case blink::WebCryptoAlgorithmIdSha1:
840 hash_alg_tag = SEC_OID_SHA1;
841 break;
842 case blink::WebCryptoAlgorithmIdSha224:
843 hash_alg_tag = SEC_OID_SHA224;
844 break;
845 case blink::WebCryptoAlgorithmIdSha256:
846 hash_alg_tag = SEC_OID_SHA256;
847 break;
848 case blink::WebCryptoAlgorithmIdSha384:
849 hash_alg_tag = SEC_OID_SHA384;
850 break;
851 case blink::WebCryptoAlgorithmIdSha512:
852 hash_alg_tag = SEC_OID_SHA512;
853 break;
854 default:
855 return Status::ErrorUnsupported();
856 }
857
858 *signature_match =
859 SECSuccess == VFY_VerifyDataDirect(data.bytes(),
860 data.byte_length(),
861 key->key(),
862 &signature_item,
863 SEC_OID_PKCS1_RSA_ENCRYPTION,
864 hash_alg_tag,
865 NULL,
866 NULL);
867 return Status::Success();
868 }
869
870 Status EncryptDecryptAesCbc(EncryptOrDecrypt mode,
871 SymKey* key,
872 const CryptoData& data,
873 const CryptoData& iv,
874 blink::WebArrayBuffer* buffer) {
875 // TODO(eroman): Inline.
876 return AesCbcEncryptDecrypt(mode, key, iv, data, buffer);
877 }
878
879 Status EncryptDecryptAesGcm(EncryptOrDecrypt mode,
880 SymKey* key,
881 const CryptoData& data,
882 const CryptoData& iv,
883 const CryptoData& additional_data,
884 unsigned int tag_length_bits,
885 blink::WebArrayBuffer* buffer) {
886 // TODO(eroman): Inline.
887 return AesGcmEncryptDecrypt(
888 mode, key, data, iv, additional_data, tag_length_bits, buffer);
889 }
890
891 // -----------------------------------
892 // Key generation
893 // -----------------------------------
894
895 Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm,
896 bool extractable,
897 blink::WebCryptoKeyUsageMask usage_mask,
898 unsigned int modulus_length_bits,
899 const CryptoData& public_exponent,
900 const blink::WebCryptoAlgorithm& hash_or_null,
901 blink::WebCryptoKey* public_key,
902 blink::WebCryptoKey* private_key) {
903 crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot());
904 if (!slot)
905 return Status::Error();
906
907 unsigned long public_exponent_long;
908 if (!BigIntegerToLong(public_exponent.bytes(),
909 public_exponent.byte_length(),
910 &public_exponent_long) ||
911 !public_exponent_long) {
912 return Status::ErrorGenerateKeyPublicExponent();
913 }
914
915 PK11RSAGenParams rsa_gen_params;
916 rsa_gen_params.keySizeInBits = modulus_length_bits;
917 rsa_gen_params.pe = public_exponent_long;
918
919 // Flags are verified at the Blink layer; here the flags are set to all
920 // possible operations for the given key type.
921 CK_FLAGS operation_flags;
922 switch (algorithm.id()) {
923 case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5:
924 case blink::WebCryptoAlgorithmIdRsaOaep:
925 operation_flags = CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP;
926 break;
927 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5:
928 operation_flags = CKF_SIGN | CKF_VERIFY;
929 break;
930 default:
931 NOTREACHED();
932 return Status::ErrorUnexpected();
933 }
934 const CK_FLAGS operation_flags_mask =
935 CKF_ENCRYPT | CKF_DECRYPT | CKF_SIGN | CKF_VERIFY | CKF_WRAP | CKF_UNWRAP;
936 const PK11AttrFlags attribute_flags = 0; // Default all PK11_ATTR_ flags.
937
938 // Note: NSS does not generate an sec_public_key if the call below fails,
939 // so there is no danger of a leaked sec_public_key.
940 SECKEYPublicKey* sec_public_key;
941 crypto::ScopedSECKEYPrivateKey scoped_sec_private_key(
942 PK11_GenerateKeyPairWithOpFlags(slot.get(),
943 CKM_RSA_PKCS_KEY_PAIR_GEN,
944 &rsa_gen_params,
945 &sec_public_key,
946 attribute_flags,
947 operation_flags,
948 operation_flags_mask,
949 NULL));
950 if (!private_key)
951 return Status::Error();
952
953 blink::WebCryptoKeyAlgorithm key_algorithm;
954 if (!CreatePublicKeyAlgorithm(algorithm, sec_public_key, &key_algorithm))
955 return Status::ErrorUnexpected();
956
957 *public_key = blink::WebCryptoKey::create(
958 new PublicKey(crypto::ScopedSECKEYPublicKey(sec_public_key)),
959 blink::WebCryptoKeyTypePublic,
960 true,
961 key_algorithm,
962 usage_mask);
963 *private_key =
964 blink::WebCryptoKey::create(new PrivateKey(scoped_sec_private_key.Pass()),
965 blink::WebCryptoKeyTypePrivate,
966 extractable,
967 key_algorithm,
968 usage_mask);
969
970 return Status::Success();
971 }
972
973 void Init() { crypto::EnsureNSSInit(); }
974
975 Status DigestSha(blink::WebCryptoAlgorithmId algorithm,
976 const CryptoData& data,
977 blink::WebArrayBuffer* buffer) {
978 HASH_HashType hash_type = WebCryptoAlgorithmToNSSHashType(algorithm);
979 if (hash_type == HASH_AlgNULL)
980 return Status::ErrorUnsupported();
981
982 HASHContext* context = HASH_Create(hash_type);
983 if (!context)
984 return Status::Error();
985
986 HASH_Begin(context);
987
988 HASH_Update(context, data.bytes(), data.byte_length());
989
990 unsigned int hash_result_length = HASH_ResultLenContext(context);
991 DCHECK_LE(hash_result_length, static_cast<size_t>(HASH_LENGTH_MAX));
992
993 *buffer = blink::WebArrayBuffer::create(hash_result_length, 1);
994
995 unsigned char* digest = reinterpret_cast<unsigned char*>(buffer->data());
996
997 unsigned int result_length = 0;
998 HASH_End(context, digest, &result_length, hash_result_length);
999
1000 HASH_Destroy(context);
1001
1002 if (result_length != hash_result_length)
1003 return Status::ErrorUnexpected();
1004 return Status::Success();
1005 }
1006
1007 Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm,
1008 bool extractable,
1009 blink::WebCryptoKeyUsageMask usage_mask,
1010 unsigned keylen_bytes,
1011 blink::WebCryptoKey* key) {
1012 CK_MECHANISM_TYPE mech = WebCryptoAlgorithmToGenMechanism(algorithm);
1013 blink::WebCryptoKeyType key_type = blink::WebCryptoKeyTypeSecret;
1014
1015 if (mech == CKM_INVALID_MECHANISM)
1016 return Status::ErrorUnsupported();
1017
1018 crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot());
1019 if (!slot)
1020 return Status::Error();
1021
1022 crypto::ScopedPK11SymKey pk11_key(
1023 PK11_KeyGen(slot.get(), mech, NULL, keylen_bytes, NULL));
1024
1025 if (!pk11_key)
1026 return Status::Error();
1027
1028 blink::WebCryptoKeyAlgorithm key_algorithm;
1029 if (!CreateSecretKeyAlgorithm(algorithm, keylen_bytes, &key_algorithm))
1030 return Status::ErrorUnexpected();
1031
1032 *key = blink::WebCryptoKey::create(new SymKey(pk11_key.Pass()),
1033 key_type,
1034 extractable,
1035 key_algorithm,
1036 usage_mask);
1037 return Status::Success();
1038 }
1039
1040 Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm,
1041 bool extractable,
1042 blink::WebCryptoKeyUsageMask usage_mask,
1043 const CryptoData& modulus_data,
1044 const CryptoData& exponent_data,
1045 blink::WebCryptoKey* key) {
1046
1047 if (!modulus_data.byte_length())
1048 return Status::ErrorImportRsaEmptyModulus();
1049
1050 if (!exponent_data.byte_length())
1051 return Status::ErrorImportRsaEmptyExponent();
1052
1053 DCHECK(modulus_data.bytes());
1054 DCHECK(exponent_data.bytes());
1055
1056 // NSS does not provide a way to create an RSA public key directly from the
1057 // modulus and exponent values, but it can import an DER-encoded ASN.1 blob
1058 // with these values and create the public key from that. The code below
1059 // follows the recommendation described in
1060 // https://developer.mozilla.org/en-US/docs/NSS/NSS_Tech_Notes/nss_tech_note7
1061
1062 // Pack the input values into a struct compatible with NSS ASN.1 encoding, and
1063 // set up an ASN.1 encoder template for it.
1064 struct RsaPublicKeyData {
1065 SECItem modulus;
1066 SECItem exponent;
1067 };
1068 const RsaPublicKeyData pubkey_in = {
1069 {siUnsignedInteger, const_cast<unsigned char*>(modulus_data.bytes()),
1070 modulus_data.byte_length()},
1071 {siUnsignedInteger, const_cast<unsigned char*>(exponent_data.bytes()),
1072 exponent_data.byte_length()}};
1073 const SEC_ASN1Template rsa_public_key_template[] = {
1074 {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(RsaPublicKeyData)},
1075 {SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, modulus), },
1076 {SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, exponent), },
1077 {0, }};
1078
1079 // DER-encode the public key.
1080 crypto::ScopedSECItem pubkey_der(
1081 SEC_ASN1EncodeItem(NULL, NULL, &pubkey_in, rsa_public_key_template));
1082 if (!pubkey_der)
1083 return Status::Error();
1084
1085 // Import the DER-encoded public key to create an RSA SECKEYPublicKey.
1086 crypto::ScopedSECKEYPublicKey pubkey(
1087 SECKEY_ImportDERPublicKey(pubkey_der.get(), CKK_RSA));
1088 if (!pubkey)
1089 return Status::Error();
1090
1091 blink::WebCryptoKeyAlgorithm key_algorithm;
1092 if (!CreatePublicKeyAlgorithm(algorithm, pubkey.get(), &key_algorithm))
1093 return Status::ErrorUnexpected();
1094
1095 *key = blink::WebCryptoKey::create(new PublicKey(pubkey.Pass()),
1096 blink::WebCryptoKeyTypePublic,
1097 extractable,
1098 key_algorithm,
1099 usage_mask);
1100 return Status::Success();
1101 }
1102
1103 } // namespace platform
1104
1105 } // namespace webcrypto
1106
1107 } // namespace content
OLDNEW
« no previous file with comments | « content/renderer/webcrypto/platform_crypto.h ('k') | content/renderer/webcrypto/platform_crypto_openssl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698