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