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
WebCryptoHashToHMACMechanism(const blink::WebCryptoAlgorithm & algorithm,CK_MECHANISM_TYPE * mechanism)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:
HmacImplementation()56 HmacImplementation() {}
57
GenerateSecretKey(const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,blink::WebCryptoKey * key) const58 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
VerifyKeyUsagesBeforeImportKey(blink::WebCryptoKeyFormat format,blink::WebCryptoKeyUsageMask usage_mask) const84 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
VerifyKeyUsagesBeforeGenerateKey(blink::WebCryptoKeyUsageMask usage_mask) const96 virtual Status VerifyKeyUsagesBeforeGenerateKey(
97 blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE {
98 return CheckKeyCreationUsages(kAllKeyUsages, usage_mask);
99 }
100
ImportKeyRaw(const CryptoData & key_data,const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,blink::WebCryptoKey * key) const101 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
ImportKeyJwk(const CryptoData & key_data,const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,blink::WebCryptoKey * key) const129 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
ExportKeyRaw(const blink::WebCryptoKey & key,std::vector<uint8_t> * buffer) const149 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
ExportKeyJwk(const blink::WebCryptoKey & key,std::vector<uint8_t> * buffer) const155 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
Sign(const blink::WebCryptoAlgorithm & algorithm,const blink::WebCryptoKey & key,const CryptoData & data,std::vector<uint8_t> * buffer) const174 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, ¶m_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, ¶m_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
Verify(const blink::WebCryptoAlgorithm & algorithm,const blink::WebCryptoKey & key,const CryptoData & signature,const CryptoData & data,bool * signature_match) const212 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
CreatePlatformHmacImplementation()235 AlgorithmImplementation* CreatePlatformHmacImplementation() {
236 return new HmacImplementation;
237 }
238
239 } // namespace webcrypto
240
241 } // namespace content
242