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/platform_crypto.h" | |
6 | |
7 #include <vector> | |
8 #include <openssl/aes.h> | |
9 #include <openssl/evp.h> | |
10 #include <openssl/hmac.h> | |
11 #include <openssl/rand.h> | |
12 #include <openssl/sha.h> | |
13 | |
14 #include "base/logging.h" | |
15 #include "content/renderer/webcrypto/crypto_data.h" | |
16 #include "content/renderer/webcrypto/webcrypto_util.h" | |
17 #include "crypto/openssl_util.h" | |
18 #include "third_party/WebKit/public/platform/WebArrayBuffer.h" | |
19 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" | |
20 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" | |
21 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" | |
22 | |
23 namespace content { | |
24 | |
25 namespace webcrypto { | |
26 | |
27 namespace platform { | |
28 | |
29 class SymKey : public Key { | |
30 public: | |
31 explicit SymKey(const CryptoData& key_data) | |
32 : key_(key_data.bytes(), key_data.bytes() + key_data.byte_length()) {} | |
33 | |
34 virtual SymKey* AsSymKey() OVERRIDE { return this; } | |
35 virtual PublicKey* AsPublicKey() OVERRIDE { return NULL; } | |
36 virtual PrivateKey* AsPrivateKey() OVERRIDE { return NULL; } | |
37 | |
38 const std::vector<unsigned char>& key() const { return key_; } | |
39 | |
40 private: | |
41 const std::vector<unsigned char> key_; | |
42 | |
43 DISALLOW_COPY_AND_ASSIGN(SymKey); | |
44 }; | |
45 | |
46 namespace { | |
47 | |
48 const EVP_CIPHER* GetAESCipherByKeyLength(unsigned int key_length_bytes) { | |
49 // OpenSSL supports AES CBC ciphers for only 3 key lengths: 128, 192, 256 bits | |
50 switch (key_length_bytes) { | |
51 case 16: | |
52 return EVP_aes_128_cbc(); | |
53 case 24: | |
54 return EVP_aes_192_cbc(); | |
55 case 32: | |
56 return EVP_aes_256_cbc(); | |
57 default: | |
58 return NULL; | |
59 } | |
60 } | |
61 | |
62 const EVP_MD* GetDigest(blink::WebCryptoAlgorithmId id) { | |
63 switch (id) { | |
64 case blink::WebCryptoAlgorithmIdSha1: | |
65 return EVP_sha1(); | |
66 case blink::WebCryptoAlgorithmIdSha224: | |
67 return EVP_sha224(); | |
68 case blink::WebCryptoAlgorithmIdSha256: | |
69 return EVP_sha256(); | |
70 case blink::WebCryptoAlgorithmIdSha384: | |
71 return EVP_sha384(); | |
72 case blink::WebCryptoAlgorithmIdSha512: | |
73 return EVP_sha512(); | |
74 default: | |
75 return NULL; | |
76 } | |
77 } | |
78 | |
79 // OpenSSL constants for EVP_CipherInit_ex(), do not change | |
80 enum CipherOperation { kDoDecrypt = 0, kDoEncrypt = 1 }; | |
81 | |
82 Status AesCbcEncryptDecrypt(EncryptOrDecrypt mode, | |
83 SymKey* key, | |
84 const CryptoData& iv, | |
85 const CryptoData& data, | |
86 blink::WebArrayBuffer* buffer) { | |
87 CipherOperation cipher_operation = | |
88 (mode == ENCRYPT) ? kDoEncrypt : kDoDecrypt; | |
89 | |
90 if (data.byte_length() >= INT_MAX - AES_BLOCK_SIZE) { | |
91 // TODO(padolph): Handle this by chunking the input fed into OpenSSL. Right | |
92 // now it doesn't make much difference since the one-shot API would end up | |
93 // blowing out the memory and crashing anyway. | |
94 return Status::ErrorDataTooLarge(); | |
95 } | |
96 | |
97 // Note: PKCS padding is enabled by default | |
98 crypto::ScopedOpenSSL<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free> context( | |
99 EVP_CIPHER_CTX_new()); | |
100 | |
101 if (!context.get()) | |
102 return Status::Error(); | |
103 | |
104 const EVP_CIPHER* const cipher = GetAESCipherByKeyLength(key->key().size()); | |
105 DCHECK(cipher); | |
106 | |
107 if (!EVP_CipherInit_ex(context.get(), | |
108 cipher, | |
109 NULL, | |
110 &key->key()[0], | |
111 iv.bytes(), | |
112 cipher_operation)) { | |
113 return Status::Error(); | |
114 } | |
115 | |
116 // According to the openssl docs, the amount of data written may be as large | |
117 // as (data_size + cipher_block_size - 1), constrained to a multiple of | |
118 // cipher_block_size. | |
119 unsigned int output_max_len = data.byte_length() + AES_BLOCK_SIZE - 1; | |
120 const unsigned remainder = output_max_len % AES_BLOCK_SIZE; | |
121 if (remainder != 0) | |
122 output_max_len += AES_BLOCK_SIZE - remainder; | |
123 DCHECK_GT(output_max_len, data.byte_length()); | |
124 | |
125 *buffer = blink::WebArrayBuffer::create(output_max_len, 1); | |
126 | |
127 unsigned char* const buffer_data = | |
128 reinterpret_cast<unsigned char*>(buffer->data()); | |
129 | |
130 int output_len = 0; | |
131 if (!EVP_CipherUpdate(context.get(), | |
132 buffer_data, | |
133 &output_len, | |
134 data.bytes(), | |
135 data.byte_length())) | |
136 return Status::Error(); | |
137 int final_output_chunk_len = 0; | |
138 if (!EVP_CipherFinal_ex( | |
139 context.get(), buffer_data + output_len, &final_output_chunk_len)) { | |
140 return Status::Error(); | |
141 } | |
142 | |
143 const unsigned int final_output_len = | |
144 static_cast<unsigned int>(output_len) + | |
145 static_cast<unsigned int>(final_output_chunk_len); | |
146 DCHECK_LE(final_output_len, output_max_len); | |
147 | |
148 ShrinkBuffer(buffer, final_output_len); | |
149 | |
150 return Status::Success(); | |
151 } | |
152 | |
153 } // namespace | |
154 | |
155 Status ExportKeyRaw(SymKey* key, blink::WebArrayBuffer* buffer) { | |
156 *buffer = CreateArrayBuffer(Uint8VectorStart(key->key()), key->key().size()); | |
157 return Status::Success(); | |
158 } | |
159 | |
160 void Init() { crypto::EnsureOpenSSLInit(); } | |
161 | |
162 Status EncryptDecryptAesCbc(EncryptOrDecrypt mode, | |
163 SymKey* key, | |
164 const CryptoData& data, | |
165 const CryptoData& iv, | |
166 blink::WebArrayBuffer* buffer) { | |
167 // TODO(eroman): inline the function here. | |
168 return AesCbcEncryptDecrypt(mode, key, iv, data, buffer); | |
169 } | |
170 | |
171 Status DigestSha(blink::WebCryptoAlgorithmId algorithm, | |
172 const CryptoData& data, | |
173 blink::WebArrayBuffer* buffer) { | |
174 crypto::OpenSSLErrStackTracer(FROM_HERE); | |
175 | |
176 const EVP_MD* digest_algorithm = GetDigest(algorithm); | |
177 if (!digest_algorithm) | |
178 return Status::ErrorUnexpected(); | |
179 | |
180 crypto::ScopedOpenSSL<EVP_MD_CTX, EVP_MD_CTX_destroy> digest_context( | |
181 EVP_MD_CTX_create()); | |
182 if (!digest_context.get()) | |
183 return Status::Error(); | |
184 | |
185 if (!EVP_DigestInit_ex(digest_context.get(), digest_algorithm, NULL) || | |
186 !EVP_DigestUpdate( | |
187 digest_context.get(), data.bytes(), data.byte_length())) { | |
188 return Status::Error(); | |
189 } | |
190 | |
191 const int hash_expected_size = EVP_MD_CTX_size(digest_context.get()); | |
192 if (hash_expected_size <= 0) | |
193 return Status::ErrorUnexpected(); | |
194 DCHECK_LE(hash_expected_size, EVP_MAX_MD_SIZE); | |
195 | |
196 *buffer = blink::WebArrayBuffer::create(hash_expected_size, 1); | |
197 unsigned char* const hash_buffer = | |
198 reinterpret_cast<unsigned char* const>(buffer->data()); | |
199 | |
200 unsigned int hash_size = 0; | |
201 if (!EVP_DigestFinal_ex(digest_context.get(), hash_buffer, &hash_size) || | |
202 static_cast<int>(hash_size) != hash_expected_size) { | |
203 buffer->reset(); | |
204 return Status::Error(); | |
205 } | |
206 | |
207 return Status::Success(); | |
208 } | |
209 | |
210 Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, | |
211 bool extractable, | |
212 blink::WebCryptoKeyUsageMask usage_mask, | |
213 unsigned keylen_bytes, | |
214 blink::WebCryptoKey* key) { | |
215 // TODO(eroman): Is this right? | |
216 if (keylen_bytes == 0) | |
217 return Status::ErrorGenerateKeyLength(); | |
218 | |
219 crypto::OpenSSLErrStackTracer(FROM_HERE); | |
220 | |
221 std::vector<unsigned char> random_bytes(keylen_bytes, 0); | |
222 if (!(RAND_bytes(&random_bytes[0], keylen_bytes))) | |
223 return Status::Error(); | |
224 | |
225 blink::WebCryptoKeyAlgorithm key_algorithm; | |
226 if (!CreateSecretKeyAlgorithm(algorithm, keylen_bytes, &key_algorithm)) | |
227 return Status::ErrorUnexpected(); | |
228 | |
229 *key = blink::WebCryptoKey::create(new SymKey(CryptoData(random_bytes)), | |
230 blink::WebCryptoKeyTypeSecret, | |
231 extractable, | |
232 key_algorithm, | |
233 usage_mask); | |
234 | |
235 return Status::Success(); | |
236 } | |
237 | |
238 Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm, | |
239 bool extractable, | |
240 blink::WebCryptoKeyUsageMask usage_mask, | |
241 unsigned int modulus_length_bits, | |
242 const CryptoData& public_exponent, | |
243 const blink::WebCryptoAlgorithm& hash, | |
244 blink::WebCryptoKey* public_key, | |
245 blink::WebCryptoKey* private_key) { | |
246 // TODO(padolph): Placeholder for OpenSSL implementation. | |
247 // Issue http://crbug.com/267888. | |
248 return Status::ErrorUnsupported(); | |
249 } | |
250 | |
251 Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm, | |
252 const CryptoData& key_data, | |
253 bool extractable, | |
254 blink::WebCryptoKeyUsageMask usage_mask, | |
255 blink::WebCryptoKey* key) { | |
256 | |
257 blink::WebCryptoKeyAlgorithm key_algorithm; | |
258 if (!CreateSecretKeyAlgorithm( | |
259 algorithm, key_data.byte_length(), &key_algorithm)) | |
260 return Status::ErrorUnexpected(); | |
261 | |
262 *key = blink::WebCryptoKey::create(new SymKey(key_data), | |
263 blink::WebCryptoKeyTypeSecret, | |
264 extractable, | |
265 key_algorithm, | |
266 usage_mask); | |
267 | |
268 return Status::Success(); | |
269 } | |
270 | |
271 Status SignHmac(SymKey* key, | |
272 const blink::WebCryptoAlgorithm& hash, | |
273 const CryptoData& data, | |
274 blink::WebArrayBuffer* buffer) { | |
275 blink::WebArrayBuffer result; | |
276 | |
277 const EVP_MD* digest_algorithm = GetDigest(hash.id()); | |
278 if (!digest_algorithm) | |
279 return Status::ErrorUnsupported(); | |
280 unsigned int hmac_expected_length = EVP_MD_size(digest_algorithm); | |
281 | |
282 const std::vector<unsigned char>& raw_key = key->key(); | |
283 | |
284 // OpenSSL wierdness here. | |
285 // First, HMAC() needs a void* for the key data, so make one up front as a | |
286 // cosmetic to avoid a cast. Second, OpenSSL does not like a NULL key, | |
287 // which will result if the raw_key vector is empty; an entirely valid | |
288 // case. Handle this specific case by pointing to an empty array. | |
289 const unsigned char null_key[] = {}; | |
290 const void* const raw_key_voidp = raw_key.size() ? &raw_key[0] : null_key; | |
291 | |
292 result = blink::WebArrayBuffer::create(hmac_expected_length, 1); | |
293 crypto::ScopedOpenSSLSafeSizeBuffer<EVP_MAX_MD_SIZE> hmac_result( | |
294 reinterpret_cast<unsigned char*>(result.data()), hmac_expected_length); | |
295 | |
296 crypto::OpenSSLErrStackTracer(FROM_HERE); | |
297 | |
298 unsigned int hmac_actual_length; | |
299 unsigned char* const success = HMAC(digest_algorithm, | |
300 raw_key_voidp, | |
301 raw_key.size(), | |
302 data.bytes(), | |
303 data.byte_length(), | |
304 hmac_result.safe_buffer(), | |
305 &hmac_actual_length); | |
306 if (!success || hmac_actual_length != hmac_expected_length) | |
307 return Status::Error(); | |
308 | |
309 *buffer = result; | |
310 return Status::Success(); | |
311 } | |
312 | |
313 Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm, | |
314 bool extractable, | |
315 blink::WebCryptoKeyUsageMask usage_mask, | |
316 const CryptoData& modulus_data, | |
317 const CryptoData& exponent_data, | |
318 blink::WebCryptoKey* key) { | |
319 // TODO(padolph): Placeholder for OpenSSL implementation. | |
320 // Issue | |
321 return Status::ErrorUnsupported(); | |
322 } | |
323 | |
324 Status EncryptDecryptAesGcm(EncryptOrDecrypt mode, | |
325 SymKey* key, | |
326 const CryptoData& data, | |
327 const CryptoData& iv, | |
328 const CryptoData& additional_data, | |
329 unsigned int tag_length_bits, | |
330 blink::WebArrayBuffer* buffer) { | |
331 // TODO(eroman): http://crbug.com/267888 | |
332 return Status::ErrorUnsupported(); | |
333 } | |
334 | |
335 // Guaranteed that key is valid. | |
336 Status EncryptRsaEsPkcs1v1_5(PublicKey* key, | |
337 const CryptoData& data, | |
338 blink::WebArrayBuffer* buffer) { | |
339 // TODO(eroman): http://crbug.com/267888 | |
340 return Status::ErrorUnsupported(); | |
341 } | |
342 | |
343 Status DecryptRsaEsPkcs1v1_5(PrivateKey* key, | |
344 const CryptoData& data, | |
345 blink::WebArrayBuffer* buffer) { | |
346 // TODO(eroman): http://crbug.com/267888 | |
347 return Status::ErrorUnsupported(); | |
348 } | |
349 | |
350 Status SignRsaSsaPkcs1v1_5(PrivateKey* key, | |
351 const blink::WebCryptoAlgorithm& hash, | |
352 const CryptoData& data, | |
353 blink::WebArrayBuffer* buffer) { | |
354 // TODO(eroman): http://crbug.com/267888 | |
355 return Status::ErrorUnsupported(); | |
356 } | |
357 | |
358 // Key is guaranteed to be an RSA SSA key. | |
359 Status VerifyRsaSsaPkcs1v1_5(PublicKey* key, | |
360 const blink::WebCryptoAlgorithm& hash, | |
361 const CryptoData& signature, | |
362 const CryptoData& data, | |
363 bool* signature_match) { | |
364 // TODO(eroman): http://crbug.com/267888 | |
365 return Status::ErrorUnsupported(); | |
366 } | |
367 | |
368 Status ImportKeySpki(const blink::WebCryptoAlgorithm& algorithm_or_null, | |
369 const CryptoData& key_data, | |
370 bool extractable, | |
371 blink::WebCryptoKeyUsageMask usage_mask, | |
372 blink::WebCryptoKey* key) { | |
373 // TODO(eroman): http://crbug.com/267888 | |
374 return Status::ErrorUnsupported(); | |
375 } | |
376 | |
377 Status ImportKeyPkcs8(const blink::WebCryptoAlgorithm& algorithm_or_null, | |
378 const CryptoData& key_data, | |
379 bool extractable, | |
380 blink::WebCryptoKeyUsageMask usage_mask, | |
381 blink::WebCryptoKey* key) { | |
382 // TODO(eroman): http://crbug.com/267888 | |
383 return Status::ErrorUnsupported(); | |
384 } | |
385 | |
386 Status ExportKeySpki(PublicKey* key, blink::WebArrayBuffer* buffer) { | |
387 // TODO(eroman): http://crbug.com/267888 | |
388 return Status::ErrorUnsupported(); | |
389 } | |
390 | |
391 } // namespace platform | |
392 | |
393 } // namespace webcrypto | |
394 | |
395 } // namespace content | |
OLD | NEW |