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

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

Powered by Google App Engine
This is Rietveld 408576698