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

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

Powered by Google App Engine
This is Rietveld 408576698