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