Home | History | Annotate | Download | only in webcrypto
      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/webcrypto_util.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/strings/stringprintf.h"
      9 #include "content/child/webcrypto/status.h"
     10 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h"
     11 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
     12 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
     13 
     14 namespace content {
     15 
     16 namespace webcrypto {
     17 
     18 namespace {
     19 
     20 // Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros,
     21 // to unsigned int.
     22 bool BigIntegerToUint(const uint8_t* data,
     23                       unsigned int data_size,
     24                       unsigned int* result) {
     25   // TODO(eroman): Fix handling of empty biginteger. http://crbug.com/373552
     26   if (data_size == 0)
     27     return false;
     28 
     29   *result = 0;
     30   for (size_t i = 0; i < data_size; ++i) {
     31     size_t reverse_i = data_size - i - 1;
     32 
     33     if (reverse_i >= sizeof(*result) && data[i])
     34       return false;  // Too large for a uint.
     35 
     36     *result |= data[i] << 8 * reverse_i;
     37   }
     38   return true;
     39 }
     40 
     41 }  // namespace
     42 
     43 struct JwkToWebCryptoUsage {
     44   const char* const jwk_key_op;
     45   const blink::WebCryptoKeyUsage webcrypto_usage;
     46 };
     47 
     48 // Keep this ordered according to the definition
     49 // order of WebCrypto's "recognized key usage
     50 // values".
     51 //
     52 // This is not required for spec compliance,
     53 // however it makes the ordering of key_ops match
     54 // that of WebCrypto's Key.usages.
     55 const JwkToWebCryptoUsage kJwkWebCryptoUsageMap[] = {
     56     {"encrypt", blink::WebCryptoKeyUsageEncrypt},
     57     {"decrypt", blink::WebCryptoKeyUsageDecrypt},
     58     {"sign", blink::WebCryptoKeyUsageSign},
     59     {"verify", blink::WebCryptoKeyUsageVerify},
     60     {"deriveKey", blink::WebCryptoKeyUsageDeriveKey},
     61     {"deriveBits", blink::WebCryptoKeyUsageDeriveBits},
     62     {"wrapKey", blink::WebCryptoKeyUsageWrapKey},
     63     {"unwrapKey", blink::WebCryptoKeyUsageUnwrapKey}};
     64 
     65 // Modifies the input usage_mask by according to the key_op value.
     66 bool JwkKeyOpToWebCryptoUsage(const std::string& key_op,
     67                               blink::WebCryptoKeyUsageMask* usage_mask) {
     68   for (size_t i = 0; i < arraysize(kJwkWebCryptoUsageMap); ++i) {
     69     if (kJwkWebCryptoUsageMap[i].jwk_key_op == key_op) {
     70       *usage_mask |= kJwkWebCryptoUsageMap[i].webcrypto_usage;
     71       return true;
     72     }
     73   }
     74   return false;
     75 }
     76 
     77 // Composes a Web Crypto usage mask from an array of JWK key_ops values.
     78 Status GetWebCryptoUsagesFromJwkKeyOps(
     79     const base::ListValue* jwk_key_ops_value,
     80     blink::WebCryptoKeyUsageMask* usage_mask) {
     81   *usage_mask = 0;
     82   for (size_t i = 0; i < jwk_key_ops_value->GetSize(); ++i) {
     83     std::string key_op;
     84     if (!jwk_key_ops_value->GetString(i, &key_op)) {
     85       return Status::ErrorJwkPropertyWrongType(
     86           base::StringPrintf("key_ops[%d]", static_cast<int>(i)), "string");
     87     }
     88     // Unrecognized key_ops are silently skipped.
     89     ignore_result(JwkKeyOpToWebCryptoUsage(key_op, usage_mask));
     90   }
     91   return Status::Success();
     92 }
     93 
     94 // Composes a JWK key_ops List from a Web Crypto usage mask.
     95 // Note: Caller must assume ownership of returned instance.
     96 base::ListValue* CreateJwkKeyOpsFromWebCryptoUsages(
     97     blink::WebCryptoKeyUsageMask usage_mask) {
     98   base::ListValue* jwk_key_ops = new base::ListValue();
     99   for (size_t i = 0; i < arraysize(kJwkWebCryptoUsageMap); ++i) {
    100     if (usage_mask & kJwkWebCryptoUsageMap[i].webcrypto_usage)
    101       jwk_key_ops->AppendString(kJwkWebCryptoUsageMap[i].jwk_key_op);
    102   }
    103   return jwk_key_ops;
    104 }
    105 
    106 blink::WebCryptoAlgorithm CreateAlgorithm(blink::WebCryptoAlgorithmId id) {
    107   return blink::WebCryptoAlgorithm::adoptParamsAndCreate(id, NULL);
    108 }
    109 
    110 blink::WebCryptoAlgorithm CreateHmacImportAlgorithm(
    111     blink::WebCryptoAlgorithmId hash_id) {
    112   DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id));
    113   return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
    114       blink::WebCryptoAlgorithmIdHmac,
    115       new blink::WebCryptoHmacImportParams(CreateAlgorithm(hash_id)));
    116 }
    117 
    118 blink::WebCryptoAlgorithm CreateRsaHashedImportAlgorithm(
    119     blink::WebCryptoAlgorithmId id,
    120     blink::WebCryptoAlgorithmId hash_id) {
    121   DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id));
    122   DCHECK(id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 ||
    123          id == blink::WebCryptoAlgorithmIdRsaOaep);
    124   return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
    125       id, new blink::WebCryptoRsaHashedImportParams(CreateAlgorithm(hash_id)));
    126 }
    127 
    128 bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a,
    129                        blink::WebCryptoKeyUsageMask b) {
    130   return (a & b) == b;
    131 }
    132 
    133 // TODO(eroman): Move this helper to WebCryptoKey.
    134 bool KeyUsageAllows(const blink::WebCryptoKey& key,
    135                     const blink::WebCryptoKeyUsage usage) {
    136   return ((key.usages() & usage) != 0);
    137 }
    138 
    139 bool IsAlgorithmRsa(blink::WebCryptoAlgorithmId alg_id) {
    140   return alg_id == blink::WebCryptoAlgorithmIdRsaOaep ||
    141          alg_id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5;
    142 }
    143 
    144 bool IsAlgorithmAsymmetric(blink::WebCryptoAlgorithmId alg_id) {
    145   // TODO(padolph): include all other asymmetric algorithms once they are
    146   // defined, e.g. EC and DH.
    147   return IsAlgorithmRsa(alg_id);
    148 }
    149 
    150 // The WebCrypto spec defines the default value for the tag length, as well as
    151 // the allowed values for tag length.
    152 Status GetAesGcmTagLengthInBits(const blink::WebCryptoAesGcmParams* params,
    153                                 unsigned int* tag_length_bits) {
    154   *tag_length_bits = 128;
    155   if (params->hasTagLengthBits())
    156     *tag_length_bits = params->optionalTagLengthBits();
    157 
    158   if (*tag_length_bits != 32 && *tag_length_bits != 64 &&
    159       *tag_length_bits != 96 && *tag_length_bits != 104 &&
    160       *tag_length_bits != 112 && *tag_length_bits != 120 &&
    161       *tag_length_bits != 128)
    162     return Status::ErrorInvalidAesGcmTagLength();
    163 
    164   return Status::Success();
    165 }
    166 
    167 Status GetAesKeyGenLengthInBits(const blink::WebCryptoAesKeyGenParams* params,
    168                                 unsigned int* keylen_bits) {
    169   *keylen_bits = params->lengthBits();
    170 
    171   if (*keylen_bits == 128 || *keylen_bits == 256)
    172     return Status::Success();
    173 
    174   // BoringSSL does not support 192-bit AES.
    175   if (*keylen_bits == 192)
    176     return Status::ErrorAes192BitUnsupported();
    177 
    178   return Status::ErrorGenerateKeyLength();
    179 }
    180 
    181 Status GetHmacKeyGenLengthInBits(const blink::WebCryptoHmacKeyGenParams* params,
    182                                  unsigned int* keylen_bits) {
    183   if (!params->hasLengthBits()) {
    184     switch (params->hash().id()) {
    185       case blink::WebCryptoAlgorithmIdSha1:
    186       case blink::WebCryptoAlgorithmIdSha256:
    187         *keylen_bits = 512;
    188         return Status::Success();
    189       case blink::WebCryptoAlgorithmIdSha384:
    190       case blink::WebCryptoAlgorithmIdSha512:
    191         *keylen_bits = 1024;
    192         return Status::Success();
    193       default:
    194         return Status::ErrorUnsupported();
    195     }
    196   }
    197 
    198   if (params->optionalLengthBits() % 8)
    199     return Status::ErrorGenerateKeyLength();
    200 
    201   *keylen_bits = params->optionalLengthBits();
    202 
    203   // TODO(eroman): NSS fails when generating a zero-length secret key.
    204   if (*keylen_bits == 0)
    205     return Status::ErrorGenerateKeyLength();
    206 
    207   return Status::Success();
    208 }
    209 
    210 Status VerifyAesKeyLengthForImport(unsigned int keylen_bytes) {
    211   if (keylen_bytes == 16 || keylen_bytes == 32)
    212     return Status::Success();
    213 
    214   // BoringSSL does not support 192-bit AES.
    215   if (keylen_bytes == 24)
    216     return Status::ErrorAes192BitUnsupported();
    217 
    218   return Status::ErrorImportAesKeyLength();
    219 }
    220 
    221 Status CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages,
    222                               blink::WebCryptoKeyUsageMask actual_usages) {
    223   if (!ContainsKeyUsages(all_possible_usages, actual_usages))
    224     return Status::ErrorCreateKeyBadUsages();
    225   return Status::Success();
    226 }
    227 
    228 Status GetRsaKeyGenParameters(
    229     const blink::WebCryptoRsaHashedKeyGenParams* params,
    230     unsigned int* public_exponent,
    231     unsigned int* modulus_length_bits) {
    232   *modulus_length_bits = params->modulusLengthBits();
    233 
    234   // Limit key sizes to those supported by NSS:
    235   //   * Multiple of 8 bits
    236   //   * 256 bits to 16K bits
    237   if (*modulus_length_bits < 256 || *modulus_length_bits > 16384 ||
    238       (*modulus_length_bits % 8) != 0) {
    239     return Status::ErrorGenerateRsaUnsupportedModulus();
    240   }
    241 
    242   if (!BigIntegerToUint(params->publicExponent().data(),
    243                         params->publicExponent().size(),
    244                         public_exponent)) {
    245     return Status::ErrorGenerateKeyPublicExponent();
    246   }
    247 
    248   // OpenSSL hangs when given bad public exponents, whereas NSS simply fails. To
    249   // avoid feeding OpenSSL data that will hang use a whitelist.
    250   if (*public_exponent != 3 && *public_exponent != 65537)
    251     return Status::ErrorGenerateKeyPublicExponent();
    252 
    253   return Status::Success();
    254 }
    255 
    256 }  // namespace webcrypto
    257 
    258 }  // namespace content
    259