Home | History | Annotate | Download | only in openssl
      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 <openssl/hmac.h>
      6 
      7 #include "base/logging.h"
      8 #include "base/numerics/safe_math.h"
      9 #include "base/stl_util.h"
     10 #include "content/child/webcrypto/algorithm_implementation.h"
     11 #include "content/child/webcrypto/crypto_data.h"
     12 #include "content/child/webcrypto/jwk.h"
     13 #include "content/child/webcrypto/openssl/key_openssl.h"
     14 #include "content/child/webcrypto/openssl/sym_key_openssl.h"
     15 #include "content/child/webcrypto/openssl/util_openssl.h"
     16 #include "content/child/webcrypto/status.h"
     17 #include "content/child/webcrypto/webcrypto_util.h"
     18 #include "crypto/openssl_util.h"
     19 #include "crypto/secure_util.h"
     20 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
     21 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
     22 
     23 namespace content {
     24 
     25 namespace webcrypto {
     26 
     27 namespace {
     28 
     29 const blink::WebCryptoKeyUsageMask kAllKeyUsages =
     30     blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;
     31 
     32 Status SignHmac(const std::vector<uint8_t>& raw_key,
     33                 const blink::WebCryptoAlgorithm& hash,
     34                 const CryptoData& data,
     35                 std::vector<uint8_t>* buffer) {
     36   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
     37 
     38   const EVP_MD* digest_algorithm = GetDigest(hash.id());
     39   if (!digest_algorithm)
     40     return Status::ErrorUnsupported();
     41   unsigned int hmac_expected_length = EVP_MD_size(digest_algorithm);
     42 
     43   // OpenSSL wierdness here.
     44   // First, HMAC() needs a void* for the key data, so make one up front as a
     45   // cosmetic to avoid a cast. Second, OpenSSL does not like a NULL key,
     46   // which will result if the raw_key vector is empty; an entirely valid
     47   // case. Handle this specific case by pointing to an empty array.
     48   const unsigned char null_key[] = {};
     49   const void* const raw_key_voidp = raw_key.size() ? &raw_key[0] : null_key;
     50 
     51   buffer->resize(hmac_expected_length);
     52   crypto::ScopedOpenSSLSafeSizeBuffer<EVP_MAX_MD_SIZE> hmac_result(
     53       vector_as_array(buffer), hmac_expected_length);
     54 
     55   unsigned int hmac_actual_length;
     56   unsigned char* const success = HMAC(digest_algorithm,
     57                                       raw_key_voidp,
     58                                       raw_key.size(),
     59                                       data.bytes(),
     60                                       data.byte_length(),
     61                                       hmac_result.safe_buffer(),
     62                                       &hmac_actual_length);
     63   if (!success || hmac_actual_length != hmac_expected_length)
     64     return Status::OperationError();
     65 
     66   return Status::Success();
     67 }
     68 
     69 class HmacImplementation : public AlgorithmImplementation {
     70  public:
     71   HmacImplementation() {}
     72 
     73   virtual Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm,
     74                                    bool extractable,
     75                                    blink::WebCryptoKeyUsageMask usage_mask,
     76                                    blink::WebCryptoKey* key) const OVERRIDE {
     77     const blink::WebCryptoHmacKeyGenParams* params =
     78         algorithm.hmacKeyGenParams();
     79 
     80     unsigned int keylen_bits = 0;
     81     Status status = GetHmacKeyGenLengthInBits(params, &keylen_bits);
     82     if (status.IsError())
     83       return status;
     84 
     85     return GenerateSecretKeyOpenSsl(blink::WebCryptoKeyAlgorithm::createHmac(
     86                                         params->hash().id(), keylen_bits),
     87                                     extractable,
     88                                     usage_mask,
     89                                     keylen_bits / 8,
     90                                     key);
     91   }
     92 
     93   virtual Status VerifyKeyUsagesBeforeImportKey(
     94       blink::WebCryptoKeyFormat format,
     95       blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE {
     96     switch (format) {
     97       case blink::WebCryptoKeyFormatRaw:
     98       case blink::WebCryptoKeyFormatJwk:
     99         return CheckKeyCreationUsages(kAllKeyUsages, usage_mask);
    100       default:
    101         return Status::ErrorUnsupportedImportKeyFormat();
    102     }
    103   }
    104 
    105   virtual Status VerifyKeyUsagesBeforeGenerateKey(
    106       blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE {
    107     return CheckKeyCreationUsages(kAllKeyUsages, usage_mask);
    108   }
    109 
    110   virtual Status ImportKeyRaw(const CryptoData& key_data,
    111                               const blink::WebCryptoAlgorithm& algorithm,
    112                               bool extractable,
    113                               blink::WebCryptoKeyUsageMask usage_mask,
    114                               blink::WebCryptoKey* key) const OVERRIDE {
    115     const blink::WebCryptoAlgorithm& hash =
    116         algorithm.hmacImportParams()->hash();
    117 
    118     base::CheckedNumeric<unsigned int> keylen_bits(key_data.byte_length());
    119     keylen_bits *= 8;
    120 
    121     if (!keylen_bits.IsValid())
    122       return Status::ErrorDataTooLarge();
    123 
    124     return ImportKeyRawOpenSsl(key_data,
    125                                blink::WebCryptoKeyAlgorithm::createHmac(
    126                                    hash.id(), keylen_bits.ValueOrDie()),
    127                                extractable,
    128                                usage_mask,
    129                                key);
    130   }
    131 
    132   virtual Status ImportKeyJwk(const CryptoData& key_data,
    133                               const blink::WebCryptoAlgorithm& algorithm,
    134                               bool extractable,
    135                               blink::WebCryptoKeyUsageMask usage_mask,
    136                               blink::WebCryptoKey* key) const OVERRIDE {
    137     const char* algorithm_name =
    138         GetJwkHmacAlgorithmName(algorithm.hmacImportParams()->hash().id());
    139     if (!algorithm_name)
    140       return Status::ErrorUnexpected();
    141 
    142     std::vector<uint8_t> raw_data;
    143     Status status = ReadSecretKeyJwk(
    144         key_data, algorithm_name, extractable, usage_mask, &raw_data);
    145     if (status.IsError())
    146       return status;
    147 
    148     return ImportKeyRaw(
    149         CryptoData(raw_data), algorithm, extractable, usage_mask, key);
    150   }
    151 
    152   virtual Status ExportKeyRaw(const blink::WebCryptoKey& key,
    153                               std::vector<uint8_t>* buffer) const OVERRIDE {
    154     *buffer = SymKeyOpenSsl::Cast(key)->raw_key_data();
    155     return Status::Success();
    156   }
    157 
    158   virtual Status ExportKeyJwk(const blink::WebCryptoKey& key,
    159                               std::vector<uint8_t>* buffer) const OVERRIDE {
    160     SymKeyOpenSsl* sym_key = SymKeyOpenSsl::Cast(key);
    161     const std::vector<uint8_t>& raw_data = sym_key->raw_key_data();
    162 
    163     const char* algorithm_name =
    164         GetJwkHmacAlgorithmName(key.algorithm().hmacParams()->hash().id());
    165     if (!algorithm_name)
    166       return Status::ErrorUnexpected();
    167 
    168     WriteSecretKeyJwk(CryptoData(raw_data),
    169                       algorithm_name,
    170                       key.extractable(),
    171                       key.usages(),
    172                       buffer);
    173 
    174     return Status::Success();
    175   }
    176 
    177   virtual Status Sign(const blink::WebCryptoAlgorithm& algorithm,
    178                       const blink::WebCryptoKey& key,
    179                       const CryptoData& data,
    180                       std::vector<uint8_t>* buffer) const OVERRIDE {
    181     const blink::WebCryptoAlgorithm& hash =
    182         key.algorithm().hmacParams()->hash();
    183 
    184     return SignHmac(
    185         SymKeyOpenSsl::Cast(key)->raw_key_data(), hash, data, buffer);
    186   }
    187 
    188   virtual Status Verify(const blink::WebCryptoAlgorithm& algorithm,
    189                         const blink::WebCryptoKey& key,
    190                         const CryptoData& signature,
    191                         const CryptoData& data,
    192                         bool* signature_match) const OVERRIDE {
    193     std::vector<uint8_t> result;
    194     Status status = Sign(algorithm, key, data, &result);
    195 
    196     if (status.IsError())
    197       return status;
    198 
    199     // Do not allow verification of truncated MACs.
    200     *signature_match = result.size() == signature.byte_length() &&
    201                        crypto::SecureMemEqual(vector_as_array(&result),
    202                                               signature.bytes(),
    203                                               signature.byte_length());
    204 
    205     return Status::Success();
    206   }
    207 };
    208 
    209 }  // namespace
    210 
    211 AlgorithmImplementation* CreatePlatformHmacImplementation() {
    212   return new HmacImplementation;
    213 }
    214 
    215 }  // namespace webcrypto
    216 
    217 }  // namespace content
    218