Home | History | Annotate | Download | only in nss
      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 <cryptohi.h>
      6 #include <pk11pub.h>
      7 #include <secerr.h>
      8 #include <sechash.h>
      9 
     10 #include "base/logging.h"
     11 #include "base/numerics/safe_math.h"
     12 #include "base/stl_util.h"
     13 #include "content/child/webcrypto/algorithm_implementation.h"
     14 #include "content/child/webcrypto/crypto_data.h"
     15 #include "content/child/webcrypto/jwk.h"
     16 #include "content/child/webcrypto/nss/key_nss.h"
     17 #include "content/child/webcrypto/nss/sym_key_nss.h"
     18 #include "content/child/webcrypto/nss/util_nss.h"
     19 #include "content/child/webcrypto/status.h"
     20 #include "content/child/webcrypto/webcrypto_util.h"
     21 #include "crypto/secure_util.h"
     22 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
     23 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
     24 
     25 namespace content {
     26 
     27 namespace webcrypto {
     28 
     29 namespace {
     30 
     31 const blink::WebCryptoKeyUsageMask kAllKeyUsages =
     32     blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;
     33 
     34 bool WebCryptoHashToHMACMechanism(const blink::WebCryptoAlgorithm& algorithm,
     35                                   CK_MECHANISM_TYPE* mechanism) {
     36   switch (algorithm.id()) {
     37     case blink::WebCryptoAlgorithmIdSha1:
     38       *mechanism = CKM_SHA_1_HMAC;
     39       return true;
     40     case blink::WebCryptoAlgorithmIdSha256:
     41       *mechanism = CKM_SHA256_HMAC;
     42       return true;
     43     case blink::WebCryptoAlgorithmIdSha384:
     44       *mechanism = CKM_SHA384_HMAC;
     45       return true;
     46     case blink::WebCryptoAlgorithmIdSha512:
     47       *mechanism = CKM_SHA512_HMAC;
     48       return true;
     49     default:
     50       return false;
     51   }
     52 }
     53 
     54 class HmacImplementation : public AlgorithmImplementation {
     55  public:
     56   HmacImplementation() {}
     57 
     58   virtual Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm,
     59                                    bool extractable,
     60                                    blink::WebCryptoKeyUsageMask usage_mask,
     61                                    blink::WebCryptoKey* key) const OVERRIDE {
     62     const blink::WebCryptoHmacKeyGenParams* params =
     63         algorithm.hmacKeyGenParams();
     64 
     65     const blink::WebCryptoAlgorithm& hash = params->hash();
     66     CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM;
     67     if (!WebCryptoHashToHMACMechanism(hash, &mechanism))
     68       return Status::ErrorUnsupported();
     69 
     70     unsigned int keylen_bits = 0;
     71     Status status = GetHmacKeyGenLengthInBits(params, &keylen_bits);
     72     if (status.IsError())
     73       return status;
     74 
     75     return GenerateSecretKeyNss(
     76         blink::WebCryptoKeyAlgorithm::createHmac(hash.id(), keylen_bits),
     77         extractable,
     78         usage_mask,
     79         keylen_bits / 8,
     80         mechanism,
     81         key);
     82   }
     83 
     84   virtual Status VerifyKeyUsagesBeforeImportKey(
     85       blink::WebCryptoKeyFormat format,
     86       blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE {
     87     switch (format) {
     88       case blink::WebCryptoKeyFormatRaw:
     89       case blink::WebCryptoKeyFormatJwk:
     90         return CheckKeyCreationUsages(kAllKeyUsages, usage_mask);
     91       default:
     92         return Status::ErrorUnsupportedImportKeyFormat();
     93     }
     94   }
     95 
     96   virtual Status VerifyKeyUsagesBeforeGenerateKey(
     97       blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE {
     98     return CheckKeyCreationUsages(kAllKeyUsages, usage_mask);
     99   }
    100 
    101   virtual Status ImportKeyRaw(const CryptoData& key_data,
    102                               const blink::WebCryptoAlgorithm& algorithm,
    103                               bool extractable,
    104                               blink::WebCryptoKeyUsageMask usage_mask,
    105                               blink::WebCryptoKey* key) const OVERRIDE {
    106     const blink::WebCryptoAlgorithm& hash =
    107         algorithm.hmacImportParams()->hash();
    108 
    109     CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM;
    110     if (!WebCryptoHashToHMACMechanism(hash, &mechanism))
    111       return Status::ErrorUnsupported();
    112 
    113     base::CheckedNumeric<unsigned int> keylen_bits(key_data.byte_length());
    114     keylen_bits *= 8;
    115 
    116     if (!keylen_bits.IsValid())
    117       return Status::ErrorDataTooLarge();
    118 
    119     return ImportKeyRawNss(key_data,
    120                            blink::WebCryptoKeyAlgorithm::createHmac(
    121                                hash.id(), keylen_bits.ValueOrDie()),
    122                            extractable,
    123                            usage_mask,
    124                            mechanism,
    125                            CKF_SIGN | CKF_VERIFY,
    126                            key);
    127   }
    128 
    129   virtual Status ImportKeyJwk(const CryptoData& key_data,
    130                               const blink::WebCryptoAlgorithm& algorithm,
    131                               bool extractable,
    132                               blink::WebCryptoKeyUsageMask usage_mask,
    133                               blink::WebCryptoKey* key) const OVERRIDE {
    134     const char* algorithm_name =
    135         GetJwkHmacAlgorithmName(algorithm.hmacImportParams()->hash().id());
    136     if (!algorithm_name)
    137       return Status::ErrorUnexpected();
    138 
    139     std::vector<uint8_t> raw_data;
    140     Status status = ReadSecretKeyJwk(
    141         key_data, algorithm_name, extractable, usage_mask, &raw_data);
    142     if (status.IsError())
    143       return status;
    144 
    145     return ImportKeyRaw(
    146         CryptoData(raw_data), algorithm, extractable, usage_mask, key);
    147   }
    148 
    149   virtual Status ExportKeyRaw(const blink::WebCryptoKey& key,
    150                               std::vector<uint8_t>* buffer) const OVERRIDE {
    151     *buffer = SymKeyNss::Cast(key)->raw_key_data();
    152     return Status::Success();
    153   }
    154 
    155   virtual Status ExportKeyJwk(const blink::WebCryptoKey& key,
    156                               std::vector<uint8_t>* buffer) const OVERRIDE {
    157     SymKeyNss* sym_key = SymKeyNss::Cast(key);
    158     const std::vector<uint8_t>& raw_data = sym_key->raw_key_data();
    159 
    160     const char* algorithm_name =
    161         GetJwkHmacAlgorithmName(key.algorithm().hmacParams()->hash().id());
    162     if (!algorithm_name)
    163       return Status::ErrorUnexpected();
    164 
    165     WriteSecretKeyJwk(CryptoData(raw_data),
    166                       algorithm_name,
    167                       key.extractable(),
    168                       key.usages(),
    169                       buffer);
    170 
    171     return Status::Success();
    172   }
    173 
    174   virtual Status Sign(const blink::WebCryptoAlgorithm& algorithm,
    175                       const blink::WebCryptoKey& key,
    176                       const CryptoData& data,
    177                       std::vector<uint8_t>* buffer) const OVERRIDE {
    178     const blink::WebCryptoAlgorithm& hash =
    179         key.algorithm().hmacParams()->hash();
    180     PK11SymKey* sym_key = SymKeyNss::Cast(key)->key();
    181 
    182     CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM;
    183     if (!WebCryptoHashToHMACMechanism(hash, &mechanism))
    184       return Status::ErrorUnexpected();
    185 
    186     SECItem param_item = {siBuffer, NULL, 0};
    187     SECItem data_item = MakeSECItemForBuffer(data);
    188     // First call is to figure out the length.
    189     SECItem signature_item = {siBuffer, NULL, 0};
    190 
    191     if (PK11_SignWithSymKey(
    192             sym_key, mechanism, &param_item, &signature_item, &data_item) !=
    193         SECSuccess) {
    194       return Status::OperationError();
    195     }
    196 
    197     DCHECK_NE(0u, signature_item.len);
    198 
    199     buffer->resize(signature_item.len);
    200     signature_item.data = vector_as_array(buffer);
    201 
    202     if (PK11_SignWithSymKey(
    203             sym_key, mechanism, &param_item, &signature_item, &data_item) !=
    204         SECSuccess) {
    205       return Status::OperationError();
    206     }
    207 
    208     CHECK_EQ(buffer->size(), signature_item.len);
    209     return Status::Success();
    210   }
    211 
    212   virtual Status Verify(const blink::WebCryptoAlgorithm& algorithm,
    213                         const blink::WebCryptoKey& key,
    214                         const CryptoData& signature,
    215                         const CryptoData& data,
    216                         bool* signature_match) const OVERRIDE {
    217     std::vector<uint8_t> result;
    218     Status status = Sign(algorithm, key, data, &result);
    219 
    220     if (status.IsError())
    221       return status;
    222 
    223     // Do not allow verification of truncated MACs.
    224     *signature_match = result.size() == signature.byte_length() &&
    225                        crypto::SecureMemEqual(vector_as_array(&result),
    226                                               signature.bytes(),
    227                                               signature.byte_length());
    228 
    229     return Status::Success();
    230   }
    231 };
    232 
    233 }  // namespace
    234 
    235 AlgorithmImplementation* CreatePlatformHmacImplementation() {
    236   return new HmacImplementation;
    237 }
    238 
    239 }  // namespace webcrypto
    240 
    241 }  // namespace content
    242