Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / child / webcrypto / platform_crypto_openssl.cc
1 // Copyright 2014 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/child/webcrypto/platform_crypto.h"
6
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>
13
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "content/child/webcrypto/crypto_data.h"
17 #include "content/child/webcrypto/status.h"
18 #include "content/child/webcrypto/webcrypto_util.h"
19 #include "crypto/openssl_util.h"
20 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h"
21 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
22 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
23
24 namespace content {
25
26 namespace webcrypto {
27
28 namespace platform {
29
30 class SymKey : public Key {
31  public:
32   explicit SymKey(const CryptoData& key_data)
33       : key_(key_data.bytes(), key_data.bytes() + key_data.byte_length()) {}
34
35   virtual SymKey* AsSymKey() OVERRIDE { return this; }
36   virtual PublicKey* AsPublicKey() OVERRIDE { return NULL; }
37   virtual PrivateKey* AsPrivateKey() OVERRIDE { return NULL; }
38   virtual bool ThreadSafeSerializeForClone(
39       blink::WebVector<uint8>* key_data) OVERRIDE {
40     key_data->assign(Uint8VectorStart(key_), key_.size());
41     return true;
42   }
43
44   const std::vector<unsigned char>& key() const { return key_; }
45
46  private:
47   const std::vector<unsigned char> key_;
48
49   DISALLOW_COPY_AND_ASSIGN(SymKey);
50 };
51
52 namespace {
53
54 const EVP_CIPHER* GetAESCipherByKeyLength(unsigned int key_length_bytes) {
55   // OpenSSL supports AES CBC ciphers for only 3 key lengths: 128, 192, 256 bits
56   switch (key_length_bytes) {
57     case 16:
58       return EVP_aes_128_cbc();
59     case 24:
60       return EVP_aes_192_cbc();
61     case 32:
62       return EVP_aes_256_cbc();
63     default:
64       return NULL;
65   }
66 }
67
68 const EVP_MD* GetDigest(blink::WebCryptoAlgorithmId id) {
69   switch (id) {
70     case blink::WebCryptoAlgorithmIdSha1:
71       return EVP_sha1();
72     case blink::WebCryptoAlgorithmIdSha256:
73       return EVP_sha256();
74     case blink::WebCryptoAlgorithmIdSha384:
75       return EVP_sha384();
76     case blink::WebCryptoAlgorithmIdSha512:
77       return EVP_sha512();
78     default:
79       return NULL;
80   }
81 }
82
83 // OpenSSL constants for EVP_CipherInit_ex(), do not change
84 enum CipherOperation { kDoDecrypt = 0, kDoEncrypt = 1 };
85
86 Status AesCbcEncryptDecrypt(EncryptOrDecrypt mode,
87                             SymKey* key,
88                             const CryptoData& iv,
89                             const CryptoData& data,
90                             std::vector<uint8>* buffer) {
91   CipherOperation cipher_operation =
92       (mode == ENCRYPT) ? kDoEncrypt : kDoDecrypt;
93
94   if (data.byte_length() >= INT_MAX - AES_BLOCK_SIZE) {
95     // TODO(padolph): Handle this by chunking the input fed into OpenSSL. Right
96     // now it doesn't make much difference since the one-shot API would end up
97     // blowing out the memory and crashing anyway.
98     return Status::ErrorDataTooLarge();
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 Status::OperationError();
107
108   const EVP_CIPHER* const cipher = GetAESCipherByKeyLength(key->key().size());
109   DCHECK(cipher);
110
111   if (!EVP_CipherInit_ex(context.get(),
112                          cipher,
113                          NULL,
114                          &key->key()[0],
115                          iv.bytes(),
116                          cipher_operation)) {
117     return Status::OperationError();
118   }
119
120   // According to the openssl docs, the amount of data written may be as large
121   // as (data_size + cipher_block_size - 1), constrained to a multiple of
122   // cipher_block_size.
123   unsigned int output_max_len = data.byte_length() + AES_BLOCK_SIZE - 1;
124   const unsigned remainder = output_max_len % AES_BLOCK_SIZE;
125   if (remainder != 0)
126     output_max_len += AES_BLOCK_SIZE - remainder;
127   DCHECK_GT(output_max_len, data.byte_length());
128
129   buffer->resize(output_max_len);
130
131   unsigned char* const buffer_data = Uint8VectorStart(buffer);
132
133   int output_len = 0;
134   if (!EVP_CipherUpdate(context.get(),
135                         buffer_data,
136                         &output_len,
137                         data.bytes(),
138                         data.byte_length()))
139     return Status::OperationError();
140   int final_output_chunk_len = 0;
141   if (!EVP_CipherFinal_ex(
142           context.get(), buffer_data + output_len, &final_output_chunk_len)) {
143     return Status::OperationError();
144   }
145
146   const unsigned int final_output_len =
147       static_cast<unsigned int>(output_len) +
148       static_cast<unsigned int>(final_output_chunk_len);
149   DCHECK_LE(final_output_len, output_max_len);
150
151   buffer->resize(final_output_len);
152
153   return Status::Success();
154 }
155
156 }  // namespace
157
158 class DigestorOpenSSL : public blink::WebCryptoDigestor {
159  public:
160   explicit DigestorOpenSSL(blink::WebCryptoAlgorithmId algorithm_id)
161       : initialized_(false),
162         digest_context_(EVP_MD_CTX_create()),
163         algorithm_id_(algorithm_id) {}
164
165   virtual bool consume(const unsigned char* data, unsigned int size) {
166     return ConsumeWithStatus(data, size).IsSuccess();
167   }
168
169   Status ConsumeWithStatus(const unsigned char* data, unsigned int size) {
170     crypto::OpenSSLErrStackTracer(FROM_HERE);
171     Status error = Init();
172     if (!error.IsSuccess())
173       return error;
174
175     if (!EVP_DigestUpdate(digest_context_.get(), data, size))
176       return Status::OperationError();
177
178     return Status::Success();
179   }
180
181   virtual bool finish(unsigned char*& result_data,
182                       unsigned int& result_data_size) {
183     Status error = FinishInternal(result_, &result_data_size);
184     if (!error.IsSuccess())
185       return false;
186     result_data = result_;
187     return true;
188   }
189
190   Status FinishWithVectorAndStatus(std::vector<uint8>* result) {
191     const int hash_expected_size = EVP_MD_CTX_size(digest_context_.get());
192     result->resize(hash_expected_size);
193     unsigned char* const hash_buffer = Uint8VectorStart(result);
194     unsigned int hash_buffer_size;  // ignored
195     return FinishInternal(hash_buffer, &hash_buffer_size);
196   }
197
198  private:
199   Status Init() {
200     if (initialized_)
201       return Status::Success();
202
203     const EVP_MD* digest_algorithm = GetDigest(algorithm_id_);
204     if (!digest_algorithm)
205       return Status::ErrorUnexpected();
206
207     if (!digest_context_.get())
208       return Status::OperationError();
209
210     if (!EVP_DigestInit_ex(digest_context_.get(), digest_algorithm, NULL))
211       return Status::OperationError();
212
213     initialized_ = true;
214     return Status::Success();
215   }
216
217   Status FinishInternal(unsigned char* result, unsigned int* result_size) {
218     crypto::OpenSSLErrStackTracer(FROM_HERE);
219     Status error = Init();
220     if (!error.IsSuccess())
221       return error;
222
223     const int hash_expected_size = EVP_MD_CTX_size(digest_context_.get());
224     if (hash_expected_size <= 0)
225       return Status::ErrorUnexpected();
226     DCHECK_LE(hash_expected_size, EVP_MAX_MD_SIZE);
227
228     if (!EVP_DigestFinal_ex(digest_context_.get(), result, result_size) ||
229         static_cast<int>(*result_size) != hash_expected_size)
230       return Status::OperationError();
231
232     return Status::Success();
233   }
234
235   bool initialized_;
236   crypto::ScopedOpenSSL<EVP_MD_CTX, EVP_MD_CTX_destroy> digest_context_;
237   blink::WebCryptoAlgorithmId algorithm_id_;
238   unsigned char result_[EVP_MAX_MD_SIZE];
239 };
240
241 Status ExportKeyRaw(SymKey* key, std::vector<uint8>* buffer) {
242   *buffer = key->key();
243   return Status::Success();
244 }
245
246 void Init() { crypto::EnsureOpenSSLInit(); }
247
248 Status EncryptDecryptAesCbc(EncryptOrDecrypt mode,
249                             SymKey* key,
250                             const CryptoData& data,
251                             const CryptoData& iv,
252                             std::vector<uint8>* buffer) {
253   // TODO(eroman): inline the function here.
254   return AesCbcEncryptDecrypt(mode, key, iv, data, buffer);
255 }
256
257 Status DigestSha(blink::WebCryptoAlgorithmId algorithm,
258                  const CryptoData& data,
259                  std::vector<uint8>* buffer) {
260   DigestorOpenSSL digestor(algorithm);
261   Status error = digestor.ConsumeWithStatus(data.bytes(), data.byte_length());
262   if (!error.IsSuccess())
263     return error;
264   return digestor.FinishWithVectorAndStatus(buffer);
265 }
266
267 scoped_ptr<blink::WebCryptoDigestor> CreateDigestor(
268     blink::WebCryptoAlgorithmId algorithm_id) {
269   return scoped_ptr<blink::WebCryptoDigestor>(
270       new DigestorOpenSSL(algorithm_id));
271 }
272
273 Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm,
274                          bool extractable,
275                          blink::WebCryptoKeyUsageMask usage_mask,
276                          unsigned keylen_bytes,
277                          blink::WebCryptoKey* key) {
278   // TODO(eroman): Is this right?
279   if (keylen_bytes == 0)
280     return Status::ErrorGenerateKeyLength();
281
282   crypto::OpenSSLErrStackTracer(FROM_HERE);
283
284   std::vector<unsigned char> random_bytes(keylen_bytes, 0);
285   if (!(RAND_bytes(&random_bytes[0], keylen_bytes)))
286     return Status::OperationError();
287
288   blink::WebCryptoKeyAlgorithm key_algorithm;
289   if (!CreateSecretKeyAlgorithm(algorithm, keylen_bytes, &key_algorithm))
290     return Status::ErrorUnexpected();
291
292   *key = blink::WebCryptoKey::create(new SymKey(CryptoData(random_bytes)),
293                                      blink::WebCryptoKeyTypeSecret,
294                                      extractable,
295                                      key_algorithm,
296                                      usage_mask);
297
298   return Status::Success();
299 }
300
301 Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm,
302                           bool extractable,
303                           blink::WebCryptoKeyUsageMask usage_mask,
304                           unsigned int modulus_length_bits,
305                           const CryptoData& public_exponent,
306                           const blink::WebCryptoAlgorithm& hash,
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 Status::ErrorUnsupported();
312 }
313
314 Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm,
315                     const CryptoData& key_data,
316                     bool extractable,
317                     blink::WebCryptoKeyUsageMask usage_mask,
318                     blink::WebCryptoKey* key) {
319
320   blink::WebCryptoKeyAlgorithm key_algorithm;
321   if (!CreateSecretKeyAlgorithm(
322           algorithm, key_data.byte_length(), &key_algorithm))
323     return Status::ErrorUnexpected();
324
325   *key = blink::WebCryptoKey::create(new SymKey(key_data),
326                                      blink::WebCryptoKeyTypeSecret,
327                                      extractable,
328                                      key_algorithm,
329                                      usage_mask);
330
331   return Status::Success();
332 }
333
334 Status SignHmac(SymKey* key,
335                 const blink::WebCryptoAlgorithm& hash,
336                 const CryptoData& data,
337                 std::vector<uint8>* buffer) {
338   const EVP_MD* digest_algorithm = GetDigest(hash.id());
339   if (!digest_algorithm)
340     return Status::ErrorUnsupported();
341   unsigned int hmac_expected_length = EVP_MD_size(digest_algorithm);
342
343   const std::vector<unsigned char>& raw_key = key->key();
344
345   // OpenSSL wierdness here.
346   // First, HMAC() needs a void* for the key data, so make one up front as a
347   // cosmetic to avoid a cast. Second, OpenSSL does not like a NULL key,
348   // which will result if the raw_key vector is empty; an entirely valid
349   // case. Handle this specific case by pointing to an empty array.
350   const unsigned char null_key[] = {};
351   const void* const raw_key_voidp = raw_key.size() ? &raw_key[0] : null_key;
352
353   buffer->resize(hmac_expected_length);
354   crypto::ScopedOpenSSLSafeSizeBuffer<EVP_MAX_MD_SIZE> hmac_result(
355       Uint8VectorStart(buffer), hmac_expected_length);
356
357   crypto::OpenSSLErrStackTracer(FROM_HERE);
358
359   unsigned int hmac_actual_length;
360   unsigned char* const success = HMAC(digest_algorithm,
361                                       raw_key_voidp,
362                                       raw_key.size(),
363                                       data.bytes(),
364                                       data.byte_length(),
365                                       hmac_result.safe_buffer(),
366                                       &hmac_actual_length);
367   if (!success || hmac_actual_length != hmac_expected_length)
368     return Status::OperationError();
369
370   return Status::Success();
371 }
372
373 Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm,
374                           bool extractable,
375                           blink::WebCryptoKeyUsageMask usage_mask,
376                           const CryptoData& modulus_data,
377                           const CryptoData& exponent_data,
378                           blink::WebCryptoKey* key) {
379   // TODO(padolph): Placeholder for OpenSSL implementation.
380   // Issue
381   return Status::ErrorUnsupported();
382 }
383
384 Status EncryptDecryptAesGcm(EncryptOrDecrypt mode,
385                             SymKey* key,
386                             const CryptoData& data,
387                             const CryptoData& iv,
388                             const CryptoData& additional_data,
389                             unsigned int tag_length_bits,
390                             std::vector<uint8>* buffer) {
391   // TODO(eroman): http://crbug.com/267888
392   return Status::ErrorUnsupported();
393 }
394
395 // Guaranteed that key is valid.
396 Status EncryptRsaEsPkcs1v1_5(PublicKey* key,
397                              const CryptoData& data,
398                              std::vector<uint8>* buffer) {
399   // TODO(eroman): http://crbug.com/267888
400   return Status::ErrorUnsupported();
401 }
402
403 Status DecryptRsaEsPkcs1v1_5(PrivateKey* key,
404                              const CryptoData& data,
405                              std::vector<uint8>* buffer) {
406   // TODO(eroman): http://crbug.com/267888
407   return Status::ErrorUnsupported();
408 }
409
410 Status SignRsaSsaPkcs1v1_5(PrivateKey* key,
411                            const blink::WebCryptoAlgorithm& hash,
412                            const CryptoData& data,
413                            std::vector<uint8>* buffer) {
414   // TODO(eroman): http://crbug.com/267888
415   return Status::ErrorUnsupported();
416 }
417
418 // Key is guaranteed to be an RSA SSA key.
419 Status VerifyRsaSsaPkcs1v1_5(PublicKey* key,
420                              const blink::WebCryptoAlgorithm& hash,
421                              const CryptoData& signature,
422                              const CryptoData& data,
423                              bool* signature_match) {
424   // TODO(eroman): http://crbug.com/267888
425   return Status::ErrorUnsupported();
426 }
427
428 Status ImportKeySpki(const blink::WebCryptoAlgorithm& algorithm,
429                      const CryptoData& key_data,
430                      bool extractable,
431                      blink::WebCryptoKeyUsageMask usage_mask,
432                      blink::WebCryptoKey* key) {
433   // TODO(eroman): http://crbug.com/267888
434   return Status::ErrorUnsupported();
435 }
436
437 Status ImportKeyPkcs8(const blink::WebCryptoAlgorithm& algorithm,
438                       const CryptoData& key_data,
439                       bool extractable,
440                       blink::WebCryptoKeyUsageMask usage_mask,
441                       blink::WebCryptoKey* key) {
442   // TODO(eroman): http://crbug.com/267888
443   return Status::ErrorUnsupported();
444 }
445
446 Status ExportKeySpki(PublicKey* key, std::vector<uint8>* buffer) {
447   // TODO(eroman): http://crbug.com/267888
448   return Status::ErrorUnsupported();
449 }
450
451 Status ExportKeyPkcs8(PrivateKey* key,
452                       const blink::WebCryptoKeyAlgorithm& key_algorithm,
453                       std::vector<uint8>* buffer) {
454   // TODO(eroman): http://crbug.com/267888
455   return Status::ErrorUnsupported();
456 }
457
458 Status ExportRsaPublicKey(PublicKey* key,
459                           std::vector<uint8>* modulus,
460                           std::vector<uint8>* public_exponent) {
461   // TODO(eroman): http://crbug.com/267888
462   return Status::ErrorUnsupported();
463 }
464
465 Status WrapSymKeyAesKw(SymKey* wrapping_key,
466                        SymKey* key,
467                        std::vector<uint8>* buffer) {
468   // TODO(eroman): http://crbug.com/267888
469   return Status::ErrorUnsupported();
470 }
471
472 Status UnwrapSymKeyAesKw(const CryptoData& wrapped_key_data,
473                          SymKey* wrapping_key,
474                          const blink::WebCryptoAlgorithm& algorithm,
475                          bool extractable,
476                          blink::WebCryptoKeyUsageMask usage_mask,
477                          blink::WebCryptoKey* key) {
478   // TODO(eroman): http://crbug.com/267888
479   return Status::ErrorUnsupported();
480 }
481
482 Status DecryptAesKw(SymKey* key,
483                     const CryptoData& data,
484                     std::vector<uint8>* buffer) {
485   // TODO(eroman): http://crbug.com/267888
486   return Status::ErrorUnsupported();
487 }
488
489 Status WrapSymKeyRsaEs(PublicKey* wrapping_key,
490                        SymKey* key,
491                        std::vector<uint8>* buffer) {
492   // TODO(eroman): http://crbug.com/267888
493   return Status::ErrorUnsupported();
494 }
495
496 Status UnwrapSymKeyRsaEs(const CryptoData& wrapped_key_data,
497                          PrivateKey* wrapping_key,
498                          const blink::WebCryptoAlgorithm& algorithm,
499                          bool extractable,
500                          blink::WebCryptoKeyUsageMask usage_mask,
501                          blink::WebCryptoKey* key) {
502   // TODO(eroman): http://crbug.com/267888
503   return Status::ErrorUnsupported();
504 }
505
506 bool ThreadSafeDeserializeKeyForClone(
507     const blink::WebCryptoKeyAlgorithm& algorithm,
508     blink::WebCryptoKeyType type,
509     bool extractable,
510     blink::WebCryptoKeyUsageMask usages,
511     const CryptoData& key_data,
512     blink::WebCryptoKey* key) {
513   // TODO(eroman): http://crbug.com/267888
514   return false;
515 }
516
517 }  // namespace platform
518
519 }  // namespace webcrypto
520
521 }  // namespace content