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
23namespace content {
24
25namespace {
26
27class SymKeyHandle : public blink::WebCryptoKeyHandle {
28 public:
29  SymKeyHandle(const unsigned char* key_data, unsigned key_data_size)
30      : key_(key_data, key_data + key_data_size) {}
31
32  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
40const 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
54unsigned 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
74enum CipherOperation {
75  kDoDecrypt = 0,
76  kDoEncrypt = 1
77};
78
79bool 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
160bool 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
180void WebCryptoImpl::Init() { crypto::EnsureOpenSSLInit(); }
181
182bool 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
195bool 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
208bool 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
268bool 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
321bool 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
332bool 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
387bool 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
406bool 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
494bool 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
525bool 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