OLD | NEW |
---|---|
(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 | |
OLD | NEW |