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
23namespace content {
24
25namespace webcrypto {
26
27namespace {
28
29const blink::WebCryptoKeyUsageMask kAllKeyUsages =
30    blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;
31
32Status 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
69class 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
211AlgorithmImplementation* CreatePlatformHmacImplementation() {
212  return new HmacImplementation;
213}
214
215}  // namespace webcrypto
216
217}  // namespace content
218