Home | History | Annotate | Download | only in webcrypto
      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:
     29   SymKeyHandle(const unsigned char* key_data, unsigned key_data_size)
     30       : key_(key_data, key_data + key_data_size) {}
     31 
     32   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 
     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 
     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 
     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 
    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 
    180 void WebCryptoImpl::Init() { crypto::EnsureOpenSSLInit(); }
    181 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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