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"
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"
26 class SymKeyHandle
: public blink::WebCryptoKeyHandle
{
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_
; }
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
) {
43 return EVP_aes_128_cbc();
45 return EVP_aes_192_cbc();
47 return EVP_aes_256_cbc();
53 // OpenSSL constants for EVP_CipherInit_ex(), do not change
54 enum CipherOperation
{
59 bool AesCbcEncryptDecrypt(CipherOperation cipher_operation
,
60 const blink::WebCryptoAlgorithm
& algorithm
,
61 const blink::WebCryptoKey
& key
,
62 const unsigned char* data
,
64 blink::WebArrayBuffer
* buffer
) {
66 // TODO(padolph): Handle other encrypt operations and then remove this gate
67 if (algorithm
.id() != blink::WebCryptoAlgorithmIdAesCbc
)
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.
81 // Note: PKCS padding is enabled by default
82 crypto::ScopedOpenSSL
<EVP_CIPHER_CTX
, EVP_CIPHER_CTX_free
> context(
83 EVP_CIPHER_CTX_new());
88 SymKeyHandle
* const sym_key
= reinterpret_cast<SymKeyHandle
*>(key
.handle());
90 const EVP_CIPHER
* const cipher
=
91 GetAESCipherByKeyLength(sym_key
->key().size());
94 const blink::WebCryptoAesCbcParams
* const params
= algorithm
.aesCbcParams();
95 if (params
->iv().size() != AES_BLOCK_SIZE
)
98 if (!EVP_CipherInit_ex(context
.get(),
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
;
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());
122 if (!EVP_CipherUpdate(
123 context
.get(), buffer_data
, &output_len
, data
, data_size
))
125 int final_output_chunk_len
= 0;
126 if (!EVP_CipherFinal_ex(
127 context
.get(), buffer_data
+ output_len
, &final_output_chunk_len
))
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
);
140 bool ExportKeyInternalRaw(
141 const blink::WebCryptoKey
& key
,
142 blink::WebArrayBuffer
* buffer
) {
144 DCHECK(key
.handle());
147 if (key
.type() != blink::WebCryptoKeyTypeSecret
|| !key
.extractable())
150 const SymKeyHandle
* sym_key
= reinterpret_cast<SymKeyHandle
*>(key
.handle());
152 *buffer
= webcrypto::CreateArrayBuffer(
153 webcrypto::Uint8VectorStart(sym_key
->key()), sym_key
->key().size());
160 void WebCryptoImpl::Init() { crypto::EnsureOpenSSLInit(); }
162 bool WebCryptoImpl::EncryptInternal(const blink::WebCryptoAlgorithm
& algorithm
,
163 const blink::WebCryptoKey
& key
,
164 const unsigned char* data
,
166 blink::WebArrayBuffer
* buffer
) {
167 if (algorithm
.id() == blink::WebCryptoAlgorithmIdAesCbc
) {
168 return AesCbcEncryptDecrypt(
169 kDoEncrypt
, algorithm
, key
, data
, data_size
, buffer
);
175 bool WebCryptoImpl::DecryptInternal(const blink::WebCryptoAlgorithm
& algorithm
,
176 const blink::WebCryptoKey
& key
,
177 const unsigned char* data
,
179 blink::WebArrayBuffer
* buffer
) {
180 if (algorithm
.id() == blink::WebCryptoAlgorithmIdAesCbc
) {
181 return AesCbcEncryptDecrypt(
182 kDoDecrypt
, algorithm
, key
, data
, data_size
, buffer
);
188 bool WebCryptoImpl::DigestInternal(const blink::WebCryptoAlgorithm
& algorithm
,
189 const unsigned char* data
,
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();
200 case blink::WebCryptoAlgorithmIdSha224
:
201 digest_algorithm
= EVP_sha224();
203 case blink::WebCryptoAlgorithmIdSha256
:
204 digest_algorithm
= EVP_sha256();
206 case blink::WebCryptoAlgorithmIdSha384
:
207 digest_algorithm
= EVP_sha384();
209 case blink::WebCryptoAlgorithmIdSha512
:
210 digest_algorithm
= EVP_sha512();
213 // Not a digest algorithm.
217 crypto::ScopedOpenSSL
<EVP_MD_CTX
, EVP_MD_CTX_destroy
> digest_context(
218 EVP_MD_CTX_create());
219 if (!digest_context
.get()) {
223 if (!EVP_DigestInit_ex(digest_context
.get(), digest_algorithm
, NULL
) ||
224 !EVP_DigestUpdate(digest_context
.get(), data
, data_size
)) {
228 const int hash_expected_size
= EVP_MD_CTX_size(digest_context
.get());
229 if (hash_expected_size
<= 0) {
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
) {
248 bool WebCryptoImpl::GenerateKeyInternal(
249 const blink::WebCryptoAlgorithm
& algorithm
,
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();
261 if (params
->lengthBits() % 8)
263 keylen_bytes
= params
->lengthBits() / 8;
264 if (!GetAESCipherByKeyLength(keylen_bytes
)) {
267 key_type
= blink::WebCryptoKeyTypeSecret
;
270 case blink::WebCryptoAlgorithmIdHmac
: {
271 const blink::WebCryptoHmacKeyParams
* params
= algorithm
.hmacKeyParams();
273 if (params
->hasLengthBytes()) {
274 keylen_bytes
= params
->optionalLengthBytes();
276 keylen_bytes
= webcrypto::ShaBlockSizeBytes(params
->hash().id());
278 key_type
= blink::WebCryptoKeyTypeSecret
;
282 default: { return false; }
285 if (keylen_bytes
== 0) {
289 crypto::OpenSSLErrStackTracer(FROM_HERE
);
291 std::vector
<unsigned char> random_bytes(keylen_bytes
, 0);
292 if (!(RAND_bytes(&random_bytes
[0], keylen_bytes
))) {
296 *key
= blink::WebCryptoKey::create(
297 new SymKeyHandle(&random_bytes
[0], random_bytes
.size()),
298 key_type
, extractable
, algorithm
, usage_mask
);
303 bool WebCryptoImpl::GenerateKeyPairInternal(
304 const blink::WebCryptoAlgorithm
& algorithm
,
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.
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
,
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())
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
) {
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
;
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
)) {
354 case blink::WebCryptoKeyFormatJwk
:
355 // TODO(padolph): Handle jwk format; need simple JSON parser.
362 *key
= blink::WebCryptoKey::create(
363 new SymKeyHandle(raw_key_data
, raw_key_data_size
),
364 type
, extractable
, algorithm
, usage_mask
);
369 bool WebCryptoImpl::ExportKeyInternal(
370 blink::WebCryptoKeyFormat format
,
371 const blink::WebCryptoKey
& key
,
372 blink::WebArrayBuffer
* buffer
) {
374 case blink::WebCryptoKeyFormatRaw
:
375 return ExportKeyInternalRaw(key
, buffer
);
376 case blink::WebCryptoKeyFormatSpki
:
377 // TODO(padolph): Implement spki export
379 case blink::WebCryptoKeyFormatPkcs8
:
380 // TODO(padolph): Implement pkcs8 export
388 bool WebCryptoImpl::SignInternal(
389 const blink::WebCryptoAlgorithm
& algorithm
,
390 const blink::WebCryptoKey
& key
,
391 const unsigned char* data
,
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();
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
;
415 case blink::WebCryptoAlgorithmIdSha224
:
416 evp_sha
= EVP_sha224();
417 hmac_expected_length
= SHA224_DIGEST_LENGTH
;
419 case blink::WebCryptoAlgorithmIdSha256
:
420 evp_sha
= EVP_sha256();
421 hmac_expected_length
= SHA256_DIGEST_LENGTH
;
423 case blink::WebCryptoAlgorithmIdSha384
:
424 evp_sha
= EVP_sha384();
425 hmac_expected_length
= SHA384_DIGEST_LENGTH
;
427 case blink::WebCryptoAlgorithmIdSha512
:
428 evp_sha
= EVP_sha512();
429 hmac_expected_length
= SHA512_DIGEST_LENGTH
;
432 // Not a digest algorithm.
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
,
461 hmac_result
.safe_buffer(),
462 &hmac_actual_length
);
463 if (!success
|| hmac_actual_length
!= hmac_expected_length
)
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
,
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
)) {
491 // Handling of truncated signatures is underspecified in the WebCrypto
492 // spec, so here we fail verification if a truncated signature is being
494 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=23097
496 result
.byteLength() == signature_size
&&
497 crypto::SecureMemEqual(result
.data(), signature
, signature_size
);
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
,
514 blink::WebCryptoKeyUsageMask usage_mask
,
515 blink::WebCryptoKey
* key
) {
516 // TODO(padolph): Placeholder for OpenSSL implementation.
517 // Issue http://crbug.com/267888.
521 } // namespace content