Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: content/renderer/webcrypto/shared_crypto.cc

Issue 155623005: Refactor to share more code between OpenSSL and NSS implementations. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 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/renderer/webcrypto/shared_crypto.h"
6
7 #include "base/logging.h"
8 #include "content/renderer/webcrypto/crypto_data.h"
9 #include "content/renderer/webcrypto/platform_crypto.h"
10 #include "content/renderer/webcrypto/webcrypto_util.h"
11 #include "crypto/secure_util.h"
12 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h"
13 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
14 #include "third_party/WebKit/public/platform/WebCryptoKey.h"
15
16 namespace content {
17
18 namespace webcrypto {
19
20 namespace {
21
22 // TODO(eroman): Move this helper to WebCryptoKey.
23 bool KeyUsageAllows(const blink::WebCryptoKey& key,
24 const blink::WebCryptoKeyUsage usage) {
25 return ((key.usages() & usage) != 0);
26 }
27
28 Status ToPlatformSymKey(const blink::WebCryptoKey& key,
29 platform::SymKey** out) {
30 *out = static_cast<platform::Key*>(key.handle())->AsSymKey();
31 if (!*out)
32 return Status::ErrorUnexpectedKeyType();
33 return Status::Success();
34 }
35
36 Status ToPlatformPublicKey(const blink::WebCryptoKey& key,
37 platform::PublicKey** out) {
38 *out = static_cast<platform::Key*>(key.handle())->AsPublicKey();
39 if (!*out)
40 return Status::ErrorUnexpectedKeyType();
41 return Status::Success();
42 }
43
44 Status ToPlatformPrivateKey(const blink::WebCryptoKey& key,
45 platform::PrivateKey** out) {
46 *out = static_cast<platform::Key*>(key.handle())->AsPrivateKey();
47 if (!*out)
48 return Status::ErrorUnexpectedKeyType();
49 return Status::Success();
50 }
51
52 const size_t kAesBlockSizeBytes = 16;
53
54 Status EncryptDecryptAesCbc(EncryptOrDecrypt mode,
55 const blink::WebCryptoAlgorithm& algorithm,
56 const blink::WebCryptoKey& key,
57 const CryptoData& data,
58 blink::WebArrayBuffer* buffer) {
59 platform::SymKey* sym_key;
60 Status status = ToPlatformSymKey(key, &sym_key);
61 if (status.IsError())
62 return status;
63
64 const blink::WebCryptoAesCbcParams* params = algorithm.aesCbcParams();
65 if (!params)
66 return Status::ErrorUnexpected();
67
68 CryptoData iv(params->iv().data(), params->iv().size());
69 if (iv.byte_length() != kAesBlockSizeBytes)
70 return Status::ErrorIncorrectSizeAesCbcIv();
71
72 return platform::EncryptDecryptAesCbc(mode, sym_key, iv, data, buffer);
73 }
74
75 Status EncryptDecryptAesGcm(EncryptOrDecrypt mode,
76 const blink::WebCryptoAlgorithm& algorithm,
77 const blink::WebCryptoKey& key,
78 const CryptoData& data,
79 blink::WebArrayBuffer* buffer) {
80 platform::SymKey* sym_key;
81 Status status = ToPlatformSymKey(key, &sym_key);
82 if (status.IsError())
83 return status;
84
85 const blink::WebCryptoAesGcmParams* params = algorithm.aesGcmParams();
86 if (!params)
87 return Status::ErrorUnexpected();
88
89 // TODO(eroman): The spec doesn't define the default value. Assume 128 for now
90 // since that is the maximum tag length:
91 // http://www.w3.org/2012/webcrypto/track/issues/46
92 unsigned int tag_length_bits = 128;
93 if (params->hasTagLengthBits())
94 tag_length_bits = params->optionalTagLengthBits();
95 if (tag_length_bits > 128)
96 return Status::ErrorInvalidAesGcmTagLength();
97
98 return platform::EncryptDecryptAesGcm(mode, sym_key, data,
99 CryptoData(params->iv()), CryptoData(params->optionalAdditionalData()),
100 tag_length_bits, buffer);
Ryan Sleevi 2014/02/14 00:24:18 is this clang-cl formatted? I would expect it to
eroman 2014/02/14 05:52:42 My bad
101 }
102
103 Status EncryptRsaEsPkcs1v1_5(const blink::WebCryptoAlgorithm& algorithm,
104 const blink::WebCryptoKey& key,
105 const CryptoData& data,
106 blink::WebArrayBuffer* buffer) {
107 platform::PublicKey* public_key;
108 Status status = ToPlatformPublicKey(key, &public_key);
109 if (status.IsError())
110 return status;
111
112 // RSAES encryption does not support empty input
Ryan Sleevi 2014/02/14 00:24:18 Should you mention this in the pre-conditions for
eroman 2014/02/14 05:52:42 Yes. Done.
113 if (!data.byte_length())
114 return Status::Error();
115
116 return platform::EncryptRsaEsPkcs1v1_5(public_key, data, buffer);
117 }
118
119 Status DecryptRsaEsPkcs1v1_5(const blink::WebCryptoAlgorithm& algorithm,
120 const blink::WebCryptoKey& key,
121 const CryptoData& data,
122 blink::WebArrayBuffer* buffer) {
123 platform::PrivateKey* private_key;
124 Status status = ToPlatformPrivateKey(key, &private_key);
125 if (status.IsError())
126 return status;
127
128 // RSAES decryption does not support empty input
129 if (!data.byte_length())
130 return Status::Error();
131
132 return platform::DecryptRsaEsPkcs1v1_5(private_key, data, buffer);
133 }
134
135 Status SignHmac(const blink::WebCryptoAlgorithm& algorithm,
136 const blink::WebCryptoKey& key,
137 const CryptoData& data,
138 blink::WebArrayBuffer* buffer) {
139 platform::SymKey* sym_key;
140 Status status = ToPlatformSymKey(key, &sym_key);
141 if (status.IsError())
142 return status;
143
144 const blink::WebCryptoHmacParams* params = algorithm.hmacParams();
145 if (!params)
146 return Status::ErrorUnexpected();
147
148 if (!IsHashAlgorithm(params->hash().id()))
149 return Status::ErrorUnexpected();
150
151 if (params->hash().id() != GetInnerHashAlgorithm(key.algorithm()).id())
152 return Status::ErrorUnexpected();
153
154 return platform::SignHmac(sym_key, params->hash(), data, buffer);
155 }
156
157 Status VerifyHmac(const blink::WebCryptoAlgorithm& algorithm,
158 const blink::WebCryptoKey& key,
159 const CryptoData& signature,
160 const CryptoData& data,
161 bool* signature_match) {
162 blink::WebArrayBuffer result;
163 Status status = SignHmac(algorithm, key, data, &result);
164 if (status.IsError())
165 return status;
166
167 // Do not allow verification of truncated signatures.
Ryan Sleevi 2014/02/14 00:24:18 s/signatures/MACs/
eroman 2014/02/14 05:52:42 Done.
168 *signature_match =
169 result.byteLength() == signature.byte_length() &&
170 crypto::SecureMemEqual(
171 result.data(), signature.bytes(), signature.byte_length());
172
173 return Status::Success();
174 }
175
176 Status SignRsaSsaPkcs1v1_5(const blink::WebCryptoAlgorithm& algorithm,
177 const blink::WebCryptoKey& key,
178 const CryptoData& data,
179 blink::WebArrayBuffer* buffer) {
180 platform::PrivateKey* private_key;
181 Status status = ToPlatformPrivateKey(key, &private_key);
182 if (status.IsError())
183 return status;
184
185 const blink::WebCryptoRsaSsaParams* params = algorithm.rsaSsaParams();
186 if (!params)
187 return Status::ErrorUnexpected();
188
189 if (!IsHashAlgorithm(params->hash().id()))
190 return Status::ErrorUnexpected();
191
192 // TODO(eroman): Verify the key has not been used with any other hash.
193
194 return platform::SignRsaSsaPkcs1v1_5(
195 private_key, params->hash(), data, buffer);
196 }
197
198 Status VerifyRsaSsaPkcs1v1_5(const blink::WebCryptoAlgorithm& algorithm,
199 const blink::WebCryptoKey& key,
200 const CryptoData& signature,
201 const CryptoData& data,
202 bool* signature_match) {
203 platform::PublicKey* public_key;
204 Status status = ToPlatformPublicKey(key, &public_key);
205 if (status.IsError())
206 return status;
207
208 const blink::WebCryptoRsaSsaParams* params = algorithm.rsaSsaParams();
209 if (!params)
210 return Status::ErrorUnexpected();
211
212 if (!IsHashAlgorithm(params->hash().id()))
213 return Status::ErrorUnexpected();
214
215 // TODO(eroman): Verify the key has not been used with any other hash.
216
217 return platform::VerifyRsaSsaPkcs1v1_5(
218 public_key, params->hash(), signature, data, signature_match);
219 }
220
221 Status ImportKeyRaw(const CryptoData& key_data,
222 const blink::WebCryptoAlgorithm& algorithm_or_null,
223 bool extractable,
224 blink::WebCryptoKeyUsageMask usage_mask,
225 blink::WebCryptoKey* key) {
226 if (algorithm_or_null.isNull())
227 return Status::ErrorMissingAlgorithmImportRawKey();
228
229 switch (algorithm_or_null.id()) {
230 case blink::WebCryptoAlgorithmIdAesCbc:
231 if (key_data.byte_length() != 16 && key_data.byte_length() != 24 &&
232 key_data.byte_length() != 32) {
233 return Status::Error();
234 }
235 // Fallthrough intentional!
236 case blink::WebCryptoAlgorithmIdHmac:
237 case blink::WebCryptoAlgorithmIdAesKw:
238 case blink::WebCryptoAlgorithmIdAesGcm:
239 return platform::ImportKeyRaw(
240 algorithm_or_null, key_data, extractable, usage_mask, key);
241
242 default:
243 return Status::ErrorUnsupported();
244 }
245 }
246
247 } // namespace
248
249 void Init() {
250 platform::Init();
251 }
252
253 Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
254 const blink::WebCryptoKey& key,
255 const CryptoData& data,
256 blink::WebArrayBuffer* buffer) {
257 if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageEncrypt))
258 return Status::ErrorUnexpected();
259 if (algorithm.id() != key.algorithm().id())
260 return Status::ErrorUnexpected();
261
262 switch (algorithm.id()) {
263 case blink::WebCryptoAlgorithmIdAesCbc:
264 return EncryptDecryptAesCbc(ENCRYPT, algorithm, key, data, buffer);
265 case blink::WebCryptoAlgorithmIdAesGcm:
266 return EncryptDecryptAesGcm(ENCRYPT, algorithm, key, data, buffer);
267 case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5:
268 return EncryptRsaEsPkcs1v1_5(algorithm, key, data, buffer);
269 default:
270 return Status::ErrorUnsupported();
271 }
272 }
273
274 Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
275 const blink::WebCryptoKey& key,
276 const CryptoData& data,
277 blink::WebArrayBuffer* buffer) {
278 if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageDecrypt))
279 return Status::ErrorUnexpected();
280 if (algorithm.id() != key.algorithm().id())
281 return Status::ErrorUnexpected();
282
283 switch (algorithm.id()) {
284 case blink::WebCryptoAlgorithmIdAesCbc:
285 return EncryptDecryptAesCbc(DECRYPT, algorithm, key, data, buffer);
286 case blink::WebCryptoAlgorithmIdAesGcm:
287 return EncryptDecryptAesGcm(DECRYPT, algorithm, key, data, buffer);
288 case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5:
289 return DecryptRsaEsPkcs1v1_5(algorithm, key, data, buffer);
290 default:
291 return Status::ErrorUnsupported();
292 }
293 }
294
295 Status Digest(const blink::WebCryptoAlgorithm& algorithm,
296 const CryptoData& data,
297 blink::WebArrayBuffer* buffer) {
298 switch (algorithm.id()) {
299 case blink::WebCryptoAlgorithmIdSha1:
300 case blink::WebCryptoAlgorithmIdSha224:
301 case blink::WebCryptoAlgorithmIdSha256:
302 case blink::WebCryptoAlgorithmIdSha384:
303 case blink::WebCryptoAlgorithmIdSha512:
304 return platform::DigestSha(algorithm.id(), data, buffer);
305 default:
306 return Status::ErrorUnsupported();
307 }
308 }
309
310 Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm,
311 bool extractable,
312 blink::WebCryptoKeyUsageMask usage_mask,
313 blink::WebCryptoKey* key) {
314 unsigned int keylen_bytes = 0;
315
316 // Get the secret key length in bytes from generation parameters.
317 // This resolves any defaults.
318 switch (algorithm.id()) {
319 case blink::WebCryptoAlgorithmIdAesCbc:
320 case blink::WebCryptoAlgorithmIdAesGcm:
321 case blink::WebCryptoAlgorithmIdAesKw: {
322 if ((algorithm.aesKeyGenParams()->lengthBits() % 8) != 0)
323 return Status::ErrorGenerateKeyLength();
324 keylen_bytes = algorithm.aesKeyGenParams()->lengthBits() / 8;
325 break;
326 }
327 case blink::WebCryptoAlgorithmIdHmac: {
328 const blink::WebCryptoHmacKeyParams* params = algorithm.hmacKeyParams();
329 DCHECK(params);
330 if (params->hasLengthBytes()) {
331 keylen_bytes = params->optionalLengthBytes();
332 } else {
333 keylen_bytes = ShaBlockSizeBytes(params->hash().id());
334 if (keylen_bytes == 0)
335 return Status::ErrorUnsupported();
336 }
337 break;
338 }
339
340 default:
341 return Status::ErrorUnsupported();
342 }
343
344 // TODO(eroman): Is this correct? HMAC can import zero-length keys, so should
345 // probably be able to allowed to generate them too.
346 if (keylen_bytes == 0)
347 return Status::ErrorGenerateKeyLength();
348
349 return platform::GenerateSecretKey(
350 algorithm, extractable, usage_mask, keylen_bytes, key);
351 }
352
353 Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm,
354 bool extractable,
355 blink::WebCryptoKeyUsageMask usage_mask,
356 blink::WebCryptoKey* public_key,
357 blink::WebCryptoKey* private_key) {
358 // TODO(padolph): Handle other asymmetric algorithm key generation.
359 switch (algorithm.id()) {
360 case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5:
361 case blink::WebCryptoAlgorithmIdRsaOaep:
362 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5:
363 if (!algorithm.rsaKeyGenParams())
364 return Status::ErrorUnexpected();
365 return platform::GenerateRsaKeyPair(
366 algorithm, extractable, usage_mask, public_key, private_key);
367 default:
368 return Status::ErrorUnsupported();
369 }
370 }
371
372 Status ImportKey(blink::WebCryptoKeyFormat format,
373 const CryptoData& key_data,
374 const blink::WebCryptoAlgorithm& algorithm_or_null,
375 bool extractable,
376 blink::WebCryptoKeyUsageMask usage_mask,
377 blink::WebCryptoKey* key) {
378 switch (format) {
379 case blink::WebCryptoKeyFormatRaw:
380 return ImportKeyRaw(
381 key_data, algorithm_or_null, extractable, usage_mask, key);
382 return platform::ImportKeyRaw(
383 algorithm_or_null, key_data, extractable, usage_mask, key);
384 case blink::WebCryptoKeyFormatSpki:
385 return platform::ImportKeySpki(
386 algorithm_or_null, key_data, extractable, usage_mask, key);
387 case blink::WebCryptoKeyFormatPkcs8:
388 return platform::ImportKeyPkcs8(
389 algorithm_or_null, key_data, extractable, usage_mask, key);
390 case blink::WebCryptoKeyFormatJwk:
391 return ImportKeyJwk(
392 key_data, algorithm_or_null, extractable, usage_mask, key);
393 default:
394 return Status::ErrorUnsupported();
395 }
396 }
397
398 Status ExportKey(blink::WebCryptoKeyFormat format,
399 const blink::WebCryptoKey& key,
400 blink::WebArrayBuffer* buffer) {
401 if (!key.extractable())
402 return Status::ErrorKeyNotExtractable();
403
404 switch (format) {
405 case blink::WebCryptoKeyFormatRaw: {
406 platform::SymKey* sym_key;
407 Status status = ToPlatformSymKey(key, &sym_key);
408 if (status.IsError())
409 return status;
410 return platform::ExportKeyRaw(sym_key, buffer);
411 }
412 case blink::WebCryptoKeyFormatSpki: {
413 platform::PublicKey* public_key;
414 Status status = ToPlatformPublicKey(key, &public_key);
415 if (status.IsError())
416 return status;
417 return platform::ExportKeySpki(public_key, buffer);
418 }
419 case blink::WebCryptoKeyFormatPkcs8:
420 case blink::WebCryptoKeyFormatJwk:
421 // TODO(eroman):
422 return Status::ErrorUnsupported();
423 default:
424 return Status::ErrorUnsupported();
425 }
426 }
427
428 Status Sign(const blink::WebCryptoAlgorithm& algorithm,
429 const blink::WebCryptoKey& key,
430 const CryptoData& data,
431 blink::WebArrayBuffer* buffer) {
432 if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageSign))
433 return Status::ErrorUnexpected();
434 if (algorithm.id() != key.algorithm().id())
435 return Status::ErrorUnexpected();
436
437 switch (algorithm.id()) {
438 case blink::WebCryptoAlgorithmIdHmac:
439 return SignHmac(algorithm, key, data, buffer);
440 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5:
441 return SignRsaSsaPkcs1v1_5(algorithm, key, data, buffer);
442 default:
443 return Status::ErrorUnsupported();
444 }
445 }
446
447 Status VerifySignature(const blink::WebCryptoAlgorithm& algorithm,
448 const blink::WebCryptoKey& key,
449 const CryptoData& signature,
450 const CryptoData& data,
451 bool* signature_match) {
452 if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageVerify))
453 return Status::ErrorUnexpected();
454 if (algorithm.id() != key.algorithm().id())
455 return Status::ErrorUnexpected();
456
457 if (!signature.byte_length()) {
458 // None of the algorithms generate valid zero-length signatures so this
459 // will necessarily fail verification. Early return to protect
460 // implementations from dealing with a NULL signature pointer.
461 *signature_match = false;
462 return Status::Success();
463 }
464
465 switch (algorithm.id()) {
466 case blink::WebCryptoAlgorithmIdHmac:
467 return VerifyHmac(algorithm, key, signature, data, signature_match);
468 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5:
469 return VerifyRsaSsaPkcs1v1_5(
470 algorithm, key, signature, data, signature_match);
471 default:
472 return Status::ErrorUnsupported();
473 }
474 }
475
476 } // namespace webcrypto
477
478 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698