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