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/child/webcrypto/openssl/rsa_hashed_algorithm_openssl.h" | |
6 | |
7 #include <openssl/evp.h> | |
8 | |
9 #include "base/logging.h" | |
10 #include "base/stl_util.h" | |
11 #include "content/child/webcrypto/crypto_data.h" | |
12 #include "content/child/webcrypto/generate_key_result.h" | |
13 #include "content/child/webcrypto/jwk.h" | |
14 #include "content/child/webcrypto/openssl/key_openssl.h" | |
15 #include "content/child/webcrypto/openssl/util_openssl.h" | |
16 #include "content/child/webcrypto/status.h" | |
17 #include "content/child/webcrypto/webcrypto_util.h" | |
18 #include "crypto/openssl_util.h" | |
19 #include "crypto/scoped_openssl_types.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 { | |
28 | |
29 // Creates a blink::WebCryptoAlgorithm having the modulus length and public | |
30 // exponent of |key|. | |
31 Status CreateRsaHashedKeyAlgorithm( | |
32 blink::WebCryptoAlgorithmId rsa_algorithm, | |
33 blink::WebCryptoAlgorithmId hash_algorithm, | |
34 EVP_PKEY* key, | |
35 blink::WebCryptoKeyAlgorithm* key_algorithm) { | |
36 DCHECK_EQ(EVP_PKEY_RSA, EVP_PKEY_id(key)); | |
37 | |
38 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(key)); | |
39 if (!rsa.get()) | |
40 return Status::ErrorUnexpected(); | |
41 | |
42 unsigned int modulus_length_bits = BN_num_bits(rsa.get()->n); | |
43 | |
44 // Convert the public exponent to big-endian representation. | |
45 std::vector<uint8_t> e(BN_num_bytes(rsa.get()->e)); | |
46 if (e.size() == 0) | |
47 return Status::ErrorUnexpected(); | |
48 if (e.size() != BN_bn2bin(rsa.get()->e, &e[0])) | |
49 return Status::ErrorUnexpected(); | |
50 | |
51 *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed( | |
52 rsa_algorithm, modulus_length_bits, &e[0], e.size(), hash_algorithm); | |
53 | |
54 return Status::Success(); | |
55 } | |
56 | |
57 // Creates a WebCryptoKey that wraps |private_key|. | |
58 Status CreateWebCryptoRsaPrivateKey( | |
59 crypto::ScopedEVP_PKEY private_key, | |
60 const blink::WebCryptoAlgorithmId rsa_algorithm_id, | |
61 const blink::WebCryptoAlgorithm& hash, | |
62 bool extractable, | |
63 blink::WebCryptoKeyUsageMask usages, | |
64 blink::WebCryptoKey* key) { | |
65 blink::WebCryptoKeyAlgorithm key_algorithm; | |
66 Status status = CreateRsaHashedKeyAlgorithm( | |
67 rsa_algorithm_id, hash.id(), private_key.get(), &key_algorithm); | |
68 if (status.IsError()) | |
69 return status; | |
70 | |
71 return CreateWebCryptoPrivateKey(private_key.Pass(), key_algorithm, | |
72 extractable, usages, key); | |
73 } | |
74 | |
75 // Creates a WebCryptoKey that wraps |public_key|. | |
76 Status CreateWebCryptoRsaPublicKey( | |
77 crypto::ScopedEVP_PKEY public_key, | |
78 const blink::WebCryptoAlgorithmId rsa_algorithm_id, | |
79 const blink::WebCryptoAlgorithm& hash, | |
80 bool extractable, | |
81 blink::WebCryptoKeyUsageMask usages, | |
82 blink::WebCryptoKey* key) { | |
83 blink::WebCryptoKeyAlgorithm key_algorithm; | |
84 Status status = CreateRsaHashedKeyAlgorithm(rsa_algorithm_id, hash.id(), | |
85 public_key.get(), &key_algorithm); | |
86 if (status.IsError()) | |
87 return status; | |
88 | |
89 return CreateWebCryptoPublicKey(public_key.Pass(), key_algorithm, extractable, | |
90 usages, key); | |
91 } | |
92 | |
93 Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm, | |
94 bool extractable, | |
95 blink::WebCryptoKeyUsageMask usages, | |
96 const JwkRsaInfo& params, | |
97 blink::WebCryptoKey* key) { | |
98 crypto::ScopedRSA rsa(RSA_new()); | |
99 | |
100 rsa->n = CreateBIGNUM(params.n); | |
101 rsa->e = CreateBIGNUM(params.e); | |
102 rsa->d = CreateBIGNUM(params.d); | |
103 rsa->p = CreateBIGNUM(params.p); | |
104 rsa->q = CreateBIGNUM(params.q); | |
105 rsa->dmp1 = CreateBIGNUM(params.dp); | |
106 rsa->dmq1 = CreateBIGNUM(params.dq); | |
107 rsa->iqmp = CreateBIGNUM(params.qi); | |
108 | |
109 if (!rsa->n || !rsa->e || !rsa->d || !rsa->p || !rsa->q || !rsa->dmp1 || | |
110 !rsa->dmq1 || !rsa->iqmp) { | |
111 return Status::OperationError(); | |
112 } | |
113 | |
114 // TODO(eroman): This should really be a DataError, however for compatibility | |
115 // with NSS it is an OperationError. | |
116 if (!RSA_check_key(rsa.get())) | |
117 return Status::OperationError(); | |
118 | |
119 // Create a corresponding EVP_PKEY. | |
120 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new()); | |
121 if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get())) | |
122 return Status::OperationError(); | |
123 | |
124 return CreateWebCryptoRsaPrivateKey(pkey.Pass(), algorithm.id(), | |
125 algorithm.rsaHashedImportParams()->hash(), | |
126 extractable, usages, key); | |
127 } | |
128 | |
129 Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm, | |
130 bool extractable, | |
131 blink::WebCryptoKeyUsageMask usages, | |
132 const CryptoData& n, | |
133 const CryptoData& e, | |
134 blink::WebCryptoKey* key) { | |
135 crypto::ScopedRSA rsa(RSA_new()); | |
136 | |
137 rsa->n = BN_bin2bn(n.bytes(), n.byte_length(), NULL); | |
138 rsa->e = BN_bin2bn(e.bytes(), e.byte_length(), NULL); | |
139 | |
140 if (!rsa->n || !rsa->e) | |
141 return Status::OperationError(); | |
142 | |
143 // Create a corresponding EVP_PKEY. | |
144 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new()); | |
145 if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get())) | |
146 return Status::OperationError(); | |
147 | |
148 return CreateWebCryptoRsaPublicKey(pkey.Pass(), algorithm.id(), | |
149 algorithm.rsaHashedImportParams()->hash(), | |
150 extractable, usages, key); | |
151 } | |
152 | |
153 } // namespace | |
154 | |
155 Status RsaHashedAlgorithm::GenerateKey( | |
156 const blink::WebCryptoAlgorithm& algorithm, | |
157 bool extractable, | |
158 blink::WebCryptoKeyUsageMask combined_usages, | |
159 GenerateKeyResult* result) const { | |
160 blink::WebCryptoKeyUsageMask public_usages = 0; | |
161 blink::WebCryptoKeyUsageMask private_usages = 0; | |
162 | |
163 Status status = GetUsagesForGenerateAsymmetricKey( | |
164 combined_usages, all_public_key_usages_, all_private_key_usages_, | |
165 &public_usages, &private_usages); | |
166 if (status.IsError()) | |
167 return status; | |
168 | |
169 const blink::WebCryptoRsaHashedKeyGenParams* params = | |
170 algorithm.rsaHashedKeyGenParams(); | |
171 | |
172 unsigned int public_exponent = 0; | |
173 unsigned int modulus_length_bits = 0; | |
174 status = | |
175 GetRsaKeyGenParameters(params, &public_exponent, &modulus_length_bits); | |
176 if (status.IsError()) | |
177 return status; | |
178 | |
179 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
180 | |
181 // Generate an RSA key pair. | |
182 crypto::ScopedRSA rsa_private_key(RSA_new()); | |
183 crypto::ScopedBIGNUM bn(BN_new()); | |
184 if (!rsa_private_key.get() || !bn.get() || | |
185 !BN_set_word(bn.get(), public_exponent)) { | |
186 return Status::OperationError(); | |
187 } | |
188 | |
189 if (!RSA_generate_key_ex(rsa_private_key.get(), modulus_length_bits, bn.get(), | |
190 NULL)) { | |
191 return Status::OperationError(); | |
192 } | |
193 | |
194 // Construct an EVP_PKEY for the private key. | |
195 crypto::ScopedEVP_PKEY private_pkey(EVP_PKEY_new()); | |
196 if (!private_pkey || | |
197 !EVP_PKEY_set1_RSA(private_pkey.get(), rsa_private_key.get())) { | |
198 return Status::OperationError(); | |
199 } | |
200 | |
201 // Construct an EVP_PKEY for the public key. | |
202 crypto::ScopedRSA rsa_public_key(RSAPublicKey_dup(rsa_private_key.get())); | |
203 crypto::ScopedEVP_PKEY public_pkey(EVP_PKEY_new()); | |
204 if (!public_pkey || | |
205 !EVP_PKEY_set1_RSA(public_pkey.get(), rsa_public_key.get())) { | |
206 return Status::OperationError(); | |
207 } | |
208 | |
209 blink::WebCryptoKey public_key; | |
210 blink::WebCryptoKey private_key; | |
211 | |
212 // Note that extractable is unconditionally set to true. This is because per | |
213 // the WebCrypto spec generated public keys are always extractable. | |
214 status = CreateWebCryptoRsaPublicKey(public_pkey.Pass(), algorithm.id(), | |
215 params->hash(), true, public_usages, | |
216 &public_key); | |
217 if (status.IsError()) | |
218 return status; | |
219 | |
220 status = CreateWebCryptoRsaPrivateKey(private_pkey.Pass(), algorithm.id(), | |
221 params->hash(), extractable, | |
222 private_usages, &private_key); | |
223 if (status.IsError()) | |
224 return status; | |
225 | |
226 result->AssignKeyPair(public_key, private_key); | |
227 return Status::Success(); | |
228 } | |
229 | |
230 Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey( | |
231 blink::WebCryptoKeyFormat format, | |
232 blink::WebCryptoKeyUsageMask usages) const { | |
233 return VerifyUsagesBeforeImportAsymmetricKey(format, all_public_key_usages_, | |
234 all_private_key_usages_, usages); | |
235 } | |
236 | |
237 Status RsaHashedAlgorithm::ImportKeyPkcs8( | |
238 const CryptoData& key_data, | |
239 const blink::WebCryptoAlgorithm& algorithm, | |
240 bool extractable, | |
241 blink::WebCryptoKeyUsageMask usages, | |
242 blink::WebCryptoKey* key) const { | |
243 crypto::ScopedEVP_PKEY private_key; | |
244 Status status = | |
245 ImportUnverifiedPkeyFromPkcs8(key_data, EVP_PKEY_RSA, &private_key); | |
246 if (status.IsError()) | |
247 return status; | |
248 | |
249 // Verify the parameters of the key. | |
250 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(private_key.get())); | |
251 if (!rsa.get()) | |
252 return Status::ErrorUnexpected(); | |
253 if (!RSA_check_key(rsa.get())) | |
254 return Status::DataError(); | |
255 | |
256 // TODO(eroman): Validate the algorithm OID against the webcrypto provided | |
257 // hash. http://crbug.com/389400 | |
258 | |
259 return CreateWebCryptoRsaPrivateKey(private_key.Pass(), algorithm.id(), | |
260 algorithm.rsaHashedImportParams()->hash(), | |
261 extractable, usages, key); | |
262 } | |
263 | |
264 Status RsaHashedAlgorithm::ImportKeySpki( | |
265 const CryptoData& key_data, | |
266 const blink::WebCryptoAlgorithm& algorithm, | |
267 bool extractable, | |
268 blink::WebCryptoKeyUsageMask usages, | |
269 blink::WebCryptoKey* key) const { | |
270 crypto::ScopedEVP_PKEY public_key; | |
271 Status status = | |
272 ImportUnverifiedPkeyFromSpki(key_data, EVP_PKEY_RSA, &public_key); | |
273 if (status.IsError()) | |
274 return status; | |
275 | |
276 // TODO(eroman): Validate the algorithm OID against the webcrypto provided | |
277 // hash. http://crbug.com/389400 | |
278 | |
279 return CreateWebCryptoRsaPublicKey(public_key.Pass(), algorithm.id(), | |
280 algorithm.rsaHashedImportParams()->hash(), | |
281 extractable, usages, key); | |
282 } | |
283 | |
284 Status RsaHashedAlgorithm::ImportKeyJwk( | |
285 const CryptoData& key_data, | |
286 const blink::WebCryptoAlgorithm& algorithm, | |
287 bool extractable, | |
288 blink::WebCryptoKeyUsageMask usages, | |
289 blink::WebCryptoKey* key) const { | |
290 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
291 | |
292 const char* jwk_algorithm = | |
293 GetJwkAlgorithm(algorithm.rsaHashedImportParams()->hash().id()); | |
294 | |
295 if (!jwk_algorithm) | |
296 return Status::ErrorUnexpected(); | |
297 | |
298 JwkRsaInfo jwk; | |
299 Status status = | |
300 ReadRsaKeyJwk(key_data, jwk_algorithm, extractable, usages, &jwk); | |
301 if (status.IsError()) | |
302 return status; | |
303 | |
304 // Once the key type is known, verify the usages. | |
305 status = CheckKeyCreationUsages( | |
306 jwk.is_private_key ? all_private_key_usages_ : all_public_key_usages_, | |
307 usages, !jwk.is_private_key); | |
308 if (status.IsError()) | |
309 return status; | |
310 | |
311 return jwk.is_private_key | |
312 ? ImportRsaPrivateKey(algorithm, extractable, usages, jwk, key) | |
313 : ImportRsaPublicKey(algorithm, extractable, usages, | |
314 CryptoData(jwk.n), CryptoData(jwk.e), key); | |
315 } | |
316 | |
317 Status RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key, | |
318 std::vector<uint8_t>* buffer) const { | |
319 if (key.type() != blink::WebCryptoKeyTypePrivate) | |
320 return Status::ErrorUnexpectedKeyType(); | |
321 *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data(); | |
322 return Status::Success(); | |
323 } | |
324 | |
325 Status RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key, | |
326 std::vector<uint8_t>* buffer) const { | |
327 if (key.type() != blink::WebCryptoKeyTypePublic) | |
328 return Status::ErrorUnexpectedKeyType(); | |
329 *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data(); | |
330 return Status::Success(); | |
331 } | |
332 | |
333 Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, | |
334 std::vector<uint8_t>* buffer) const { | |
335 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
336 | |
337 EVP_PKEY* pkey = AsymKeyOpenSsl::Cast(key)->key(); | |
338 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(pkey)); | |
339 if (!rsa.get()) | |
340 return Status::ErrorUnexpected(); | |
341 | |
342 const char* jwk_algorithm = | |
343 GetJwkAlgorithm(key.algorithm().rsaHashedParams()->hash().id()); | |
344 if (!jwk_algorithm) | |
345 return Status::ErrorUnexpected(); | |
346 | |
347 switch (key.type()) { | |
348 case blink::WebCryptoKeyTypePublic: | |
349 WriteRsaPublicKeyJwk(CryptoData(BIGNUMToVector(rsa->n)), | |
350 CryptoData(BIGNUMToVector(rsa->e)), jwk_algorithm, | |
351 key.extractable(), key.usages(), buffer); | |
352 return Status::Success(); | |
353 case blink::WebCryptoKeyTypePrivate: | |
354 WriteRsaPrivateKeyJwk(CryptoData(BIGNUMToVector(rsa->n)), | |
355 CryptoData(BIGNUMToVector(rsa->e)), | |
356 CryptoData(BIGNUMToVector(rsa->d)), | |
357 CryptoData(BIGNUMToVector(rsa->p)), | |
358 CryptoData(BIGNUMToVector(rsa->q)), | |
359 CryptoData(BIGNUMToVector(rsa->dmp1)), | |
360 CryptoData(BIGNUMToVector(rsa->dmq1)), | |
361 CryptoData(BIGNUMToVector(rsa->iqmp)), | |
362 jwk_algorithm, key.extractable(), key.usages(), | |
363 buffer); | |
364 return Status::Success(); | |
365 | |
366 default: | |
367 return Status::ErrorUnexpected(); | |
368 } | |
369 } | |
370 | |
371 Status RsaHashedAlgorithm::SerializeKeyForClone( | |
372 const blink::WebCryptoKey& key, | |
373 blink::WebVector<uint8_t>* key_data) const { | |
374 key_data->assign(AsymKeyOpenSsl::Cast(key)->serialized_key_data()); | |
375 return Status::Success(); | |
376 } | |
377 | |
378 // TODO(eroman): Defer import to the crypto thread. http://crbug.com/430763 | |
379 Status RsaHashedAlgorithm::DeserializeKeyForClone( | |
380 const blink::WebCryptoKeyAlgorithm& algorithm, | |
381 blink::WebCryptoKeyType type, | |
382 bool extractable, | |
383 blink::WebCryptoKeyUsageMask usages, | |
384 const CryptoData& key_data, | |
385 blink::WebCryptoKey* key) const { | |
386 blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( | |
387 algorithm.id(), algorithm.rsaHashedParams()->hash().id()); | |
388 | |
389 Status status; | |
390 | |
391 switch (type) { | |
392 case blink::WebCryptoKeyTypePublic: | |
393 status = | |
394 ImportKeySpki(key_data, import_algorithm, extractable, usages, key); | |
395 break; | |
396 case blink::WebCryptoKeyTypePrivate: | |
397 status = | |
398 ImportKeyPkcs8(key_data, import_algorithm, extractable, usages, key); | |
399 break; | |
400 default: | |
401 return Status::ErrorUnexpected(); | |
402 } | |
403 | |
404 // There is some duplicated information in the serialized format used by | |
405 // structured clone (since the KeyAlgorithm is serialized separately from the | |
406 // key data). Use this extra information to further validate what was | |
407 // deserialized from the key data. | |
408 | |
409 if (algorithm.id() != key->algorithm().id()) | |
410 return Status::ErrorUnexpected(); | |
411 | |
412 if (key->type() != type) | |
413 return Status::ErrorUnexpected(); | |
414 | |
415 if (algorithm.rsaHashedParams()->modulusLengthBits() != | |
416 key->algorithm().rsaHashedParams()->modulusLengthBits()) { | |
417 return Status::ErrorUnexpected(); | |
418 } | |
419 | |
420 if (algorithm.rsaHashedParams()->publicExponent().size() != | |
421 key->algorithm().rsaHashedParams()->publicExponent().size() || | |
422 0 != | |
423 memcmp(algorithm.rsaHashedParams()->publicExponent().data(), | |
424 key->algorithm().rsaHashedParams()->publicExponent().data(), | |
425 key->algorithm().rsaHashedParams()->publicExponent().size())) { | |
426 return Status::ErrorUnexpected(); | |
427 } | |
428 | |
429 return Status::Success(); | |
430 } | |
431 | |
432 } // namespace webcrypto | |
433 | |
434 } // namespace content | |
OLD | NEW |