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