• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3 // found in the LICENSE file.
4 
5 #include "content/renderer/webcrypto/webcrypto_impl.h"
6 
7 #include <vector>
8 #include <openssl/aes.h>
9 #include <openssl/evp.h>
10 #include <openssl/hmac.h>
11 #include <openssl/sha.h>
12 #include <openssl/evp.h>
13 #include <openssl/rand.h>
14 
15 #include "base/logging.h"
16 #include "content/renderer/webcrypto/webcrypto_util.h"
17 #include "crypto/openssl_util.h"
18 #include "crypto/secure_util.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 
23 namespace content {
24 
25 namespace {
26 
27 class SymKeyHandle : public blink::WebCryptoKeyHandle {
28  public:
SymKeyHandle(const unsigned char * key_data,unsigned key_data_size)29   SymKeyHandle(const unsigned char* key_data, unsigned key_data_size)
30       : key_(key_data, key_data + key_data_size) {}
31 
key() const32   const std::vector<unsigned char>& key() const { return key_; }
33 
34  private:
35   const std::vector<unsigned char> key_;
36 
37   DISALLOW_COPY_AND_ASSIGN(SymKeyHandle);
38 };
39 
GetAESCipherByKeyLength(unsigned key_length_bytes)40 const EVP_CIPHER* GetAESCipherByKeyLength(unsigned key_length_bytes) {
41   // OpenSSL supports AES CBC ciphers for only 3 key lengths: 128, 192, 256 bits
42   switch (key_length_bytes) {
43     case 16:
44       return EVP_aes_128_cbc();
45     case 24:
46       return EVP_aes_192_cbc();
47     case 32:
48       return EVP_aes_256_cbc();
49     default:
50       return NULL;
51   }
52 }
53 
WebCryptoHmacParamsToBlockSize(const blink::WebCryptoHmacKeyParams * params)54 unsigned WebCryptoHmacParamsToBlockSize(
55     const blink::WebCryptoHmacKeyParams* params) {
56   DCHECK(params);
57   switch (params->hash().id()) {
58     case blink::WebCryptoAlgorithmIdSha1:
59       return SHA_DIGEST_LENGTH / 8;
60     case blink::WebCryptoAlgorithmIdSha224:
61       return SHA224_DIGEST_LENGTH / 8;
62     case blink::WebCryptoAlgorithmIdSha256:
63       return SHA256_DIGEST_LENGTH / 8;
64     case blink::WebCryptoAlgorithmIdSha384:
65       return SHA384_DIGEST_LENGTH / 8;
66     case blink::WebCryptoAlgorithmIdSha512:
67       return SHA512_DIGEST_LENGTH / 8;
68     default:
69       return 0;
70   }
71 }
72 
73 // OpenSSL constants for EVP_CipherInit_ex(), do not change
74 enum CipherOperation {
75   kDoDecrypt = 0,
76   kDoEncrypt = 1
77 };
78 
AesCbcEncryptDecrypt(CipherOperation cipher_operation,const blink::WebCryptoAlgorithm & algorithm,const blink::WebCryptoKey & key,const unsigned char * data,unsigned data_size,blink::WebArrayBuffer * buffer)79 bool AesCbcEncryptDecrypt(CipherOperation cipher_operation,
80                           const blink::WebCryptoAlgorithm& algorithm,
81                           const blink::WebCryptoKey& key,
82                           const unsigned char* data,
83                           unsigned data_size,
84                           blink::WebArrayBuffer* buffer) {
85 
86   // TODO(padolph): Handle other encrypt operations and then remove this gate
87   if (algorithm.id() != blink::WebCryptoAlgorithmIdAesCbc)
88     return false;
89 
90   DCHECK_EQ(algorithm.id(), key.algorithm().id());
91   DCHECK_EQ(blink::WebCryptoKeyTypeSecret, key.type());
92 
93   if (data_size >= INT_MAX - AES_BLOCK_SIZE) {
94     // TODO(padolph): Handle this by chunking the input fed into OpenSSL. Right
95     // now it doesn't make much difference since the one-shot API would end up
96     // blowing out the memory and crashing anyway. However a newer version of
97     // the spec allows for a sequence<CryptoData> so this will be relevant.
98     return false;
99   }
100 
101   // Note: PKCS padding is enabled by default
102   crypto::ScopedOpenSSL<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free> context(
103       EVP_CIPHER_CTX_new());
104 
105   if (!context.get())
106     return false;
107 
108   SymKeyHandle* const sym_key = reinterpret_cast<SymKeyHandle*>(key.handle());
109 
110   const EVP_CIPHER* const cipher =
111       GetAESCipherByKeyLength(sym_key->key().size());
112   DCHECK(cipher);
113 
114   const blink::WebCryptoAesCbcParams* const params = algorithm.aesCbcParams();
115   if (params->iv().size() != AES_BLOCK_SIZE)
116     return false;
117 
118   if (!EVP_CipherInit_ex(context.get(),
119                          cipher,
120                          NULL,
121                          &sym_key->key()[0],
122                          params->iv().data(),
123                          cipher_operation)) {
124     return false;
125   }
126 
127   // According to the openssl docs, the amount of data written may be as large
128   // as (data_size + cipher_block_size - 1), constrained to a multiple of
129   // cipher_block_size.
130   unsigned output_max_len = data_size + AES_BLOCK_SIZE - 1;
131   const unsigned remainder = output_max_len % AES_BLOCK_SIZE;
132   if (remainder != 0)
133     output_max_len += AES_BLOCK_SIZE - remainder;
134   DCHECK_GT(output_max_len, data_size);
135 
136   *buffer = blink::WebArrayBuffer::create(output_max_len, 1);
137 
138   unsigned char* const buffer_data =
139       reinterpret_cast<unsigned char*>(buffer->data());
140 
141   int output_len = 0;
142   if (!EVP_CipherUpdate(
143           context.get(), buffer_data, &output_len, data, data_size))
144     return false;
145   int final_output_chunk_len = 0;
146   if (!EVP_CipherFinal_ex(
147           context.get(), buffer_data + output_len, &final_output_chunk_len))
148     return false;
149 
150   const unsigned final_output_len =
151       static_cast<unsigned>(output_len) +
152       static_cast<unsigned>(final_output_chunk_len);
153   DCHECK_LE(final_output_len, output_max_len);
154 
155   webcrypto::ShrinkBuffer(buffer, final_output_len);
156 
157   return true;
158 }
159 
ExportKeyInternalRaw(const blink::WebCryptoKey & key,blink::WebArrayBuffer * buffer)160 bool ExportKeyInternalRaw(
161     const blink::WebCryptoKey& key,
162     blink::WebArrayBuffer* buffer) {
163 
164   DCHECK(key.handle());
165   DCHECK(buffer);
166 
167   if (key.type() != blink::WebCryptoKeyTypeSecret || !key.extractable())
168     return false;
169 
170   const SymKeyHandle* sym_key = reinterpret_cast<SymKeyHandle*>(key.handle());
171 
172   *buffer = webcrypto::CreateArrayBuffer(
173       webcrypto::Uint8VectorStart(sym_key->key()), sym_key->key().size());
174 
175   return true;
176 }
177 
178 }  // namespace
179 
Init()180 void WebCryptoImpl::Init() { crypto::EnsureOpenSSLInit(); }
181 
EncryptInternal(const blink::WebCryptoAlgorithm & algorithm,const blink::WebCryptoKey & key,const unsigned char * data,unsigned data_size,blink::WebArrayBuffer * buffer)182 bool WebCryptoImpl::EncryptInternal(const blink::WebCryptoAlgorithm& algorithm,
183                                     const blink::WebCryptoKey& key,
184                                     const unsigned char* data,
185                                     unsigned data_size,
186                                     blink::WebArrayBuffer* buffer) {
187   if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) {
188     return AesCbcEncryptDecrypt(
189         kDoEncrypt, algorithm, key, data, data_size, buffer);
190   }
191 
192   return false;
193 }
194 
DecryptInternal(const blink::WebCryptoAlgorithm & algorithm,const blink::WebCryptoKey & key,const unsigned char * data,unsigned data_size,blink::WebArrayBuffer * buffer)195 bool WebCryptoImpl::DecryptInternal(const blink::WebCryptoAlgorithm& algorithm,
196                                     const blink::WebCryptoKey& key,
197                                     const unsigned char* data,
198                                     unsigned data_size,
199                                     blink::WebArrayBuffer* buffer) {
200   if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) {
201     return AesCbcEncryptDecrypt(
202         kDoDecrypt, algorithm, key, data, data_size, buffer);
203   }
204 
205   return false;
206 }
207 
DigestInternal(const blink::WebCryptoAlgorithm & algorithm,const unsigned char * data,unsigned data_size,blink::WebArrayBuffer * buffer)208 bool WebCryptoImpl::DigestInternal(const blink::WebCryptoAlgorithm& algorithm,
209                                    const unsigned char* data,
210                                    unsigned data_size,
211                                    blink::WebArrayBuffer* buffer) {
212 
213   crypto::OpenSSLErrStackTracer(FROM_HERE);
214 
215   const EVP_MD* digest_algorithm;
216   switch (algorithm.id()) {
217     case blink::WebCryptoAlgorithmIdSha1:
218       digest_algorithm = EVP_sha1();
219       break;
220     case blink::WebCryptoAlgorithmIdSha224:
221       digest_algorithm = EVP_sha224();
222       break;
223     case blink::WebCryptoAlgorithmIdSha256:
224       digest_algorithm = EVP_sha256();
225       break;
226     case blink::WebCryptoAlgorithmIdSha384:
227       digest_algorithm = EVP_sha384();
228       break;
229     case blink::WebCryptoAlgorithmIdSha512:
230       digest_algorithm = EVP_sha512();
231       break;
232     default:
233       // Not a digest algorithm.
234       return false;
235   }
236 
237   crypto::ScopedOpenSSL<EVP_MD_CTX, EVP_MD_CTX_destroy> digest_context(
238       EVP_MD_CTX_create());
239   if (!digest_context.get()) {
240     return false;
241   }
242 
243   if (!EVP_DigestInit_ex(digest_context.get(), digest_algorithm, NULL) ||
244       !EVP_DigestUpdate(digest_context.get(), data, data_size)) {
245     return false;
246   }
247 
248   const int hash_expected_size = EVP_MD_CTX_size(digest_context.get());
249   if (hash_expected_size <= 0) {
250     return false;
251   }
252   DCHECK_LE(hash_expected_size, EVP_MAX_MD_SIZE);
253 
254   *buffer = blink::WebArrayBuffer::create(hash_expected_size, 1);
255   unsigned char* const hash_buffer =
256       reinterpret_cast<unsigned char* const>(buffer->data());
257 
258   unsigned hash_size = 0;
259   if (!EVP_DigestFinal_ex(digest_context.get(), hash_buffer, &hash_size) ||
260       static_cast<int>(hash_size) != hash_expected_size) {
261     buffer->reset();
262     return false;
263   }
264 
265   return true;
266 }
267 
GenerateKeyInternal(const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,blink::WebCryptoKey * key)268 bool WebCryptoImpl::GenerateKeyInternal(
269     const blink::WebCryptoAlgorithm& algorithm,
270     bool extractable,
271     blink::WebCryptoKeyUsageMask usage_mask,
272     blink::WebCryptoKey* key) {
273 
274   unsigned keylen_bytes = 0;
275   blink::WebCryptoKeyType key_type;
276   switch (algorithm.id()) {
277     case blink::WebCryptoAlgorithmIdAesCbc: {
278       const blink::WebCryptoAesKeyGenParams* params =
279           algorithm.aesKeyGenParams();
280       DCHECK(params);
281       if (params->length() % 8)
282         return false;
283       keylen_bytes = params->length() / 8;
284       if (!GetAESCipherByKeyLength(keylen_bytes)) {
285         return false;
286       }
287       key_type = blink::WebCryptoKeyTypeSecret;
288       break;
289     }
290     case blink::WebCryptoAlgorithmIdHmac: {
291       const blink::WebCryptoHmacKeyParams* params = algorithm.hmacKeyParams();
292       DCHECK(params);
293       if (!params->getLength(keylen_bytes)) {
294         keylen_bytes = WebCryptoHmacParamsToBlockSize(params);
295       }
296       key_type = blink::WebCryptoKeyTypeSecret;
297       break;
298     }
299 
300     default: { return false; }
301   }
302 
303   if (keylen_bytes == 0) {
304     return false;
305   }
306 
307   crypto::OpenSSLErrStackTracer(FROM_HERE);
308 
309   std::vector<unsigned char> random_bytes(keylen_bytes, 0);
310   if (!(RAND_bytes(&random_bytes[0], keylen_bytes))) {
311     return false;
312   }
313 
314   *key = blink::WebCryptoKey::create(
315       new SymKeyHandle(&random_bytes[0], random_bytes.size()),
316       key_type, extractable, algorithm, usage_mask);
317 
318   return true;
319 }
320 
GenerateKeyPairInternal(const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,blink::WebCryptoKey * public_key,blink::WebCryptoKey * private_key)321 bool WebCryptoImpl::GenerateKeyPairInternal(
322     const blink::WebCryptoAlgorithm& algorithm,
323     bool extractable,
324     blink::WebCryptoKeyUsageMask usage_mask,
325     blink::WebCryptoKey* public_key,
326     blink::WebCryptoKey* private_key) {
327   // TODO(padolph): Placeholder for OpenSSL implementation.
328   // Issue http://crbug.com/267888.
329   return false;
330 }
331 
ImportKeyInternal(blink::WebCryptoKeyFormat format,const unsigned char * key_data,unsigned key_data_size,const blink::WebCryptoAlgorithm & algorithm_or_null,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,blink::WebCryptoKey * key)332 bool WebCryptoImpl::ImportKeyInternal(
333     blink::WebCryptoKeyFormat format,
334     const unsigned char* key_data,
335     unsigned key_data_size,
336     const blink::WebCryptoAlgorithm& algorithm_or_null,
337     bool extractable,
338     blink::WebCryptoKeyUsageMask usage_mask,
339     blink::WebCryptoKey* key) {
340   // TODO(eroman): Currently expects algorithm to always be specified, as it is
341   //               required for raw format.
342   if (algorithm_or_null.isNull())
343     return false;
344   const blink::WebCryptoAlgorithm& algorithm = algorithm_or_null;
345 
346   // TODO(padolph): Support all relevant alg types and then remove this gate.
347   if (algorithm.id() != blink::WebCryptoAlgorithmIdHmac &&
348       algorithm.id() != blink::WebCryptoAlgorithmIdAesCbc) {
349     return false;
350   }
351 
352   // TODO(padolph): Need to split handling for symmetric (raw format) and
353   // asymmetric (spki or pkcs8 format) keys.
354   // Currently only supporting symmetric.
355 
356   // Symmetric keys are always type secret
357   blink::WebCryptoKeyType type = blink::WebCryptoKeyTypeSecret;
358 
359   const unsigned char* raw_key_data;
360   unsigned raw_key_data_size;
361   switch (format) {
362     case blink::WebCryptoKeyFormatRaw:
363       raw_key_data = key_data;
364       raw_key_data_size = key_data_size;
365       // The NSS implementation fails when importing a raw AES key with a length
366       // incompatible with AES. The line below is to match this behavior.
367       if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc &&
368           !GetAESCipherByKeyLength(raw_key_data_size)) {
369         return false;
370       }
371       break;
372     case blink::WebCryptoKeyFormatJwk:
373       // TODO(padolph): Handle jwk format; need simple JSON parser.
374       // break;
375       return false;
376     default:
377       return false;
378   }
379 
380   *key = blink::WebCryptoKey::create(
381       new SymKeyHandle(raw_key_data, raw_key_data_size),
382       type, extractable, algorithm, usage_mask);
383 
384   return true;
385 }
386 
ExportKeyInternal(blink::WebCryptoKeyFormat format,const blink::WebCryptoKey & key,blink::WebArrayBuffer * buffer)387 bool WebCryptoImpl::ExportKeyInternal(
388     blink::WebCryptoKeyFormat format,
389     const blink::WebCryptoKey& key,
390     blink::WebArrayBuffer* buffer) {
391   switch (format) {
392     case blink::WebCryptoKeyFormatRaw:
393       return ExportKeyInternalRaw(key, buffer);
394     case blink::WebCryptoKeyFormatSpki:
395       // TODO(padolph): Implement spki export
396       return false;
397     case blink::WebCryptoKeyFormatPkcs8:
398       // TODO(padolph): Implement pkcs8 export
399       return false;
400     default:
401       return false;
402   }
403   return false;
404 }
405 
SignInternal(const blink::WebCryptoAlgorithm & algorithm,const blink::WebCryptoKey & key,const unsigned char * data,unsigned data_size,blink::WebArrayBuffer * buffer)406 bool WebCryptoImpl::SignInternal(
407     const blink::WebCryptoAlgorithm& algorithm,
408     const blink::WebCryptoKey& key,
409     const unsigned char* data,
410     unsigned data_size,
411     blink::WebArrayBuffer* buffer) {
412 
413   blink::WebArrayBuffer result;
414 
415   switch (algorithm.id()) {
416     case blink::WebCryptoAlgorithmIdHmac: {
417 
418       DCHECK_EQ(key.algorithm().id(), blink::WebCryptoAlgorithmIdHmac);
419       DCHECK_NE(0, key.usages() & blink::WebCryptoKeyUsageSign);
420 
421       const blink::WebCryptoHmacParams* const params = algorithm.hmacParams();
422       if (!params)
423         return false;
424 
425       const EVP_MD* evp_sha = 0;
426       unsigned int hmac_expected_length = 0;
427       // Note that HMAC length is determined by the hash used.
428       switch (params->hash().id()) {
429         case blink::WebCryptoAlgorithmIdSha1:
430           evp_sha = EVP_sha1();
431           hmac_expected_length = SHA_DIGEST_LENGTH;
432           break;
433         case blink::WebCryptoAlgorithmIdSha224:
434           evp_sha = EVP_sha224();
435           hmac_expected_length = SHA224_DIGEST_LENGTH;
436           break;
437         case blink::WebCryptoAlgorithmIdSha256:
438           evp_sha = EVP_sha256();
439           hmac_expected_length = SHA256_DIGEST_LENGTH;
440           break;
441         case blink::WebCryptoAlgorithmIdSha384:
442           evp_sha = EVP_sha384();
443           hmac_expected_length = SHA384_DIGEST_LENGTH;
444           break;
445         case blink::WebCryptoAlgorithmIdSha512:
446           evp_sha = EVP_sha512();
447           hmac_expected_length = SHA512_DIGEST_LENGTH;
448           break;
449         default:
450           // Not a digest algorithm.
451           return false;
452       }
453 
454       SymKeyHandle* const sym_key =
455           reinterpret_cast<SymKeyHandle*>(key.handle());
456       const std::vector<unsigned char>& raw_key = sym_key->key();
457 
458       // OpenSSL wierdness here.
459       // First, HMAC() needs a void* for the key data, so make one up front as a
460       // cosmetic to avoid a cast. Second, OpenSSL does not like a NULL key,
461       // which will result if the raw_key vector is empty; an entirely valid
462       // case. Handle this specific case by pointing to an empty array.
463       const unsigned char null_key[] = {};
464       const void* const raw_key_voidp = raw_key.size() ? &raw_key[0] : null_key;
465 
466       result = blink::WebArrayBuffer::create(hmac_expected_length, 1);
467       crypto::ScopedOpenSSLSafeSizeBuffer<EVP_MAX_MD_SIZE> hmac_result(
468           reinterpret_cast<unsigned char*>(result.data()),
469           hmac_expected_length);
470 
471       crypto::OpenSSLErrStackTracer(FROM_HERE);
472 
473       unsigned int hmac_actual_length;
474       unsigned char* const success = HMAC(evp_sha,
475                                           raw_key_voidp,
476                                           raw_key.size(),
477                                           data,
478                                           data_size,
479                                           hmac_result.safe_buffer(),
480                                           &hmac_actual_length);
481       if (!success || hmac_actual_length != hmac_expected_length)
482         return false;
483 
484       break;
485     }
486     default:
487       return false;
488   }
489 
490   *buffer = result;
491   return true;
492 }
493 
VerifySignatureInternal(const blink::WebCryptoAlgorithm & algorithm,const blink::WebCryptoKey & key,const unsigned char * signature,unsigned signature_size,const unsigned char * data,unsigned data_size,bool * signature_match)494 bool WebCryptoImpl::VerifySignatureInternal(
495     const blink::WebCryptoAlgorithm& algorithm,
496     const blink::WebCryptoKey& key,
497     const unsigned char* signature,
498     unsigned signature_size,
499     const unsigned char* data,
500     unsigned data_size,
501     bool* signature_match) {
502   switch (algorithm.id()) {
503     case blink::WebCryptoAlgorithmIdHmac: {
504       blink::WebArrayBuffer result;
505       if (!SignInternal(algorithm, key, data, data_size, &result)) {
506         return false;
507       }
508 
509       // Handling of truncated signatures is underspecified in the WebCrypto
510       // spec, so here we fail verification if a truncated signature is being
511       // verified.
512       // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=23097
513       *signature_match =
514           result.byteLength() == signature_size &&
515           crypto::SecureMemEqual(result.data(), signature, signature_size);
516 
517       break;
518     }
519     default:
520       return false;
521   }
522   return true;
523 }
524 
ImportRsaPublicKeyInternal(const unsigned char * modulus_data,unsigned modulus_size,const unsigned char * exponent_data,unsigned exponent_size,const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,blink::WebCryptoKey * key)525 bool WebCryptoImpl::ImportRsaPublicKeyInternal(
526     const unsigned char* modulus_data,
527     unsigned modulus_size,
528     const unsigned char* exponent_data,
529     unsigned exponent_size,
530     const blink::WebCryptoAlgorithm& algorithm,
531     bool extractable,
532     blink::WebCryptoKeyUsageMask usage_mask,
533     blink::WebCryptoKey* key) {
534   // TODO(padolph): Placeholder for OpenSSL implementation.
535   // Issue http://crbug.com/267888.
536   return false;
537 }
538 
539 }  // namespace content
540