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

Side by Side Diff: components/webcrypto/openssl/rsa_hashed_algorithm_openssl.cc

Issue 1304063015: [refactor] Rename the webcrypto/openssl and webcrypto/test directories. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@jwk_refactor
Patch Set: Created 5 years, 3 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
OLDNEW
(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 "components/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 "components/webcrypto/crypto_data.h"
12 #include "components/webcrypto/generate_key_result.h"
13 #include "components/webcrypto/jwk.h"
14 #include "components/webcrypto/openssl/key_openssl.h"
15 #include "components/webcrypto/openssl/util_openssl.h"
16 #include "components/webcrypto/status.h"
17 #include "components/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 webcrypto {
24
25 namespace {
26
27 // Describes the RSA components for a parsed key. The names of the properties
28 // correspond with those from the JWK spec. Note that Chromium's WebCrypto
29 // implementation does not support multi-primes, so there is no parsed field
30 // for othinfo.
31 struct JwkRsaInfo {
32 bool is_private_key = false;
33 std::string n;
34 std::string e;
35 std::string d;
36 std::string p;
37 std::string q;
38 std::string dp;
39 std::string dq;
40 std::string qi;
41 };
42
43 // Parses a UTF-8 encoded JWK (key_data), and extracts the RSA components to
44 // |*result|. Returns Status::Success() on success, otherwise an error.
45 // In order for this to succeed:
46 // * expected_alg must match the JWK's "alg", if present.
47 // * expected_extractable must be consistent with the JWK's "ext", if
48 // present.
49 // * expected_usages must be a subset of the JWK's "key_ops" if present.
50 Status ReadRsaKeyJwk(const CryptoData& key_data,
51 const std::string& expected_alg,
52 bool expected_extractable,
53 blink::WebCryptoKeyUsageMask expected_usages,
54 JwkRsaInfo* result) {
55 JwkReader jwk;
56 Status status = jwk.Init(key_data, expected_extractable, expected_usages,
57 "RSA", expected_alg);
58 if (status.IsError())
59 return status;
60
61 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry
62 // in the JWK, while an RSA private key must have those, plus at least a "d"
63 // (private exponent) entry.
64 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18,
65 // section 6.3.
66 status = jwk.GetBigInteger("n", &result->n);
67 if (status.IsError())
68 return status;
69 status = jwk.GetBigInteger("e", &result->e);
70 if (status.IsError())
71 return status;
72
73 result->is_private_key = jwk.HasMember("d");
74 if (!result->is_private_key)
75 return Status::Success();
76
77 status = jwk.GetBigInteger("d", &result->d);
78 if (status.IsError())
79 return status;
80
81 // The "p", "q", "dp", "dq", and "qi" properties are optional in the JWA
82 // spec. However they are required by Chromium's WebCrypto implementation.
83
84 status = jwk.GetBigInteger("p", &result->p);
85 if (status.IsError())
86 return status;
87
88 status = jwk.GetBigInteger("q", &result->q);
89 if (status.IsError())
90 return status;
91
92 status = jwk.GetBigInteger("dp", &result->dp);
93 if (status.IsError())
94 return status;
95
96 status = jwk.GetBigInteger("dq", &result->dq);
97 if (status.IsError())
98 return status;
99
100 status = jwk.GetBigInteger("qi", &result->qi);
101 if (status.IsError())
102 return status;
103
104 return Status::Success();
105 }
106
107 // Creates a blink::WebCryptoAlgorithm having the modulus length and public
108 // exponent of |key|.
109 Status CreateRsaHashedKeyAlgorithm(
110 blink::WebCryptoAlgorithmId rsa_algorithm,
111 blink::WebCryptoAlgorithmId hash_algorithm,
112 EVP_PKEY* key,
113 blink::WebCryptoKeyAlgorithm* key_algorithm) {
114 DCHECK_EQ(EVP_PKEY_RSA, EVP_PKEY_id(key));
115
116 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(key));
117 if (!rsa.get())
118 return Status::ErrorUnexpected();
119
120 unsigned int modulus_length_bits = BN_num_bits(rsa.get()->n);
121
122 // Convert the public exponent to big-endian representation.
123 std::vector<uint8_t> e(BN_num_bytes(rsa.get()->e));
124 if (e.size() == 0)
125 return Status::ErrorUnexpected();
126 if (e.size() != BN_bn2bin(rsa.get()->e, &e[0]))
127 return Status::ErrorUnexpected();
128
129 *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed(
130 rsa_algorithm, modulus_length_bits, &e[0],
131 static_cast<unsigned int>(e.size()), hash_algorithm);
132
133 return Status::Success();
134 }
135
136 // Creates a WebCryptoKey that wraps |private_key|.
137 Status CreateWebCryptoRsaPrivateKey(
138 crypto::ScopedEVP_PKEY private_key,
139 const blink::WebCryptoAlgorithmId rsa_algorithm_id,
140 const blink::WebCryptoAlgorithm& hash,
141 bool extractable,
142 blink::WebCryptoKeyUsageMask usages,
143 blink::WebCryptoKey* key) {
144 blink::WebCryptoKeyAlgorithm key_algorithm;
145 Status status = CreateRsaHashedKeyAlgorithm(
146 rsa_algorithm_id, hash.id(), private_key.get(), &key_algorithm);
147 if (status.IsError())
148 return status;
149
150 return CreateWebCryptoPrivateKey(private_key.Pass(), key_algorithm,
151 extractable, usages, key);
152 }
153
154 // Creates a WebCryptoKey that wraps |public_key|.
155 Status CreateWebCryptoRsaPublicKey(
156 crypto::ScopedEVP_PKEY public_key,
157 const blink::WebCryptoAlgorithmId rsa_algorithm_id,
158 const blink::WebCryptoAlgorithm& hash,
159 bool extractable,
160 blink::WebCryptoKeyUsageMask usages,
161 blink::WebCryptoKey* key) {
162 blink::WebCryptoKeyAlgorithm key_algorithm;
163 Status status = CreateRsaHashedKeyAlgorithm(rsa_algorithm_id, hash.id(),
164 public_key.get(), &key_algorithm);
165 if (status.IsError())
166 return status;
167
168 return CreateWebCryptoPublicKey(public_key.Pass(), key_algorithm, extractable,
169 usages, key);
170 }
171
172 Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm,
173 bool extractable,
174 blink::WebCryptoKeyUsageMask usages,
175 const JwkRsaInfo& params,
176 blink::WebCryptoKey* key) {
177 crypto::ScopedRSA rsa(RSA_new());
178
179 rsa->n = CreateBIGNUM(params.n);
180 rsa->e = CreateBIGNUM(params.e);
181 rsa->d = CreateBIGNUM(params.d);
182 rsa->p = CreateBIGNUM(params.p);
183 rsa->q = CreateBIGNUM(params.q);
184 rsa->dmp1 = CreateBIGNUM(params.dp);
185 rsa->dmq1 = CreateBIGNUM(params.dq);
186 rsa->iqmp = CreateBIGNUM(params.qi);
187
188 if (!rsa->n || !rsa->e || !rsa->d || !rsa->p || !rsa->q || !rsa->dmp1 ||
189 !rsa->dmq1 || !rsa->iqmp) {
190 return Status::OperationError();
191 }
192
193 // TODO(eroman): This should be a DataError.
194 if (!RSA_check_key(rsa.get()))
195 return Status::OperationError();
196
197 // Create a corresponding EVP_PKEY.
198 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
199 if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get()))
200 return Status::OperationError();
201
202 return CreateWebCryptoRsaPrivateKey(pkey.Pass(), algorithm.id(),
203 algorithm.rsaHashedImportParams()->hash(),
204 extractable, usages, key);
205 }
206
207 Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm,
208 bool extractable,
209 blink::WebCryptoKeyUsageMask usages,
210 const CryptoData& n,
211 const CryptoData& e,
212 blink::WebCryptoKey* key) {
213 crypto::ScopedRSA rsa(RSA_new());
214
215 rsa->n = BN_bin2bn(n.bytes(), n.byte_length(), NULL);
216 rsa->e = BN_bin2bn(e.bytes(), e.byte_length(), NULL);
217
218 if (!rsa->n || !rsa->e)
219 return Status::OperationError();
220
221 // Create a corresponding EVP_PKEY.
222 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
223 if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get()))
224 return Status::OperationError();
225
226 return CreateWebCryptoRsaPublicKey(pkey.Pass(), algorithm.id(),
227 algorithm.rsaHashedImportParams()->hash(),
228 extractable, usages, key);
229 }
230
231 } // namespace
232
233 Status RsaHashedAlgorithm::GenerateKey(
234 const blink::WebCryptoAlgorithm& algorithm,
235 bool extractable,
236 blink::WebCryptoKeyUsageMask combined_usages,
237 GenerateKeyResult* result) const {
238 blink::WebCryptoKeyUsageMask public_usages = 0;
239 blink::WebCryptoKeyUsageMask private_usages = 0;
240
241 Status status = GetUsagesForGenerateAsymmetricKey(
242 combined_usages, all_public_key_usages_, all_private_key_usages_,
243 &public_usages, &private_usages);
244 if (status.IsError())
245 return status;
246
247 const blink::WebCryptoRsaHashedKeyGenParams* params =
248 algorithm.rsaHashedKeyGenParams();
249
250 unsigned int public_exponent = 0;
251 unsigned int modulus_length_bits = 0;
252 status =
253 GetRsaKeyGenParameters(params, &public_exponent, &modulus_length_bits);
254 if (status.IsError())
255 return status;
256
257 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
258
259 // Generate an RSA key pair.
260 crypto::ScopedRSA rsa_private_key(RSA_new());
261 crypto::ScopedBIGNUM bn(BN_new());
262 if (!rsa_private_key.get() || !bn.get() ||
263 !BN_set_word(bn.get(), public_exponent)) {
264 return Status::OperationError();
265 }
266
267 if (!RSA_generate_key_ex(rsa_private_key.get(), modulus_length_bits, bn.get(),
268 NULL)) {
269 return Status::OperationError();
270 }
271
272 // Construct an EVP_PKEY for the private key.
273 crypto::ScopedEVP_PKEY private_pkey(EVP_PKEY_new());
274 if (!private_pkey ||
275 !EVP_PKEY_set1_RSA(private_pkey.get(), rsa_private_key.get())) {
276 return Status::OperationError();
277 }
278
279 // Construct an EVP_PKEY for the public key.
280 crypto::ScopedRSA rsa_public_key(RSAPublicKey_dup(rsa_private_key.get()));
281 crypto::ScopedEVP_PKEY public_pkey(EVP_PKEY_new());
282 if (!public_pkey ||
283 !EVP_PKEY_set1_RSA(public_pkey.get(), rsa_public_key.get())) {
284 return Status::OperationError();
285 }
286
287 blink::WebCryptoKey public_key;
288 blink::WebCryptoKey private_key;
289
290 // Note that extractable is unconditionally set to true. This is because per
291 // the WebCrypto spec generated public keys are always extractable.
292 status = CreateWebCryptoRsaPublicKey(public_pkey.Pass(), algorithm.id(),
293 params->hash(), true, public_usages,
294 &public_key);
295 if (status.IsError())
296 return status;
297
298 status = CreateWebCryptoRsaPrivateKey(private_pkey.Pass(), algorithm.id(),
299 params->hash(), extractable,
300 private_usages, &private_key);
301 if (status.IsError())
302 return status;
303
304 result->AssignKeyPair(public_key, private_key);
305 return Status::Success();
306 }
307
308 Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey(
309 blink::WebCryptoKeyFormat format,
310 blink::WebCryptoKeyUsageMask usages) const {
311 return VerifyUsagesBeforeImportAsymmetricKey(format, all_public_key_usages_,
312 all_private_key_usages_, usages);
313 }
314
315 Status RsaHashedAlgorithm::ImportKeyPkcs8(
316 const CryptoData& key_data,
317 const blink::WebCryptoAlgorithm& algorithm,
318 bool extractable,
319 blink::WebCryptoKeyUsageMask usages,
320 blink::WebCryptoKey* key) const {
321 crypto::ScopedEVP_PKEY private_key;
322 Status status =
323 ImportUnverifiedPkeyFromPkcs8(key_data, EVP_PKEY_RSA, &private_key);
324 if (status.IsError())
325 return status;
326
327 // Verify the parameters of the key.
328 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(private_key.get()));
329 if (!rsa.get())
330 return Status::ErrorUnexpected();
331 if (!RSA_check_key(rsa.get()))
332 return Status::DataError();
333
334 // TODO(eroman): Validate the algorithm OID against the webcrypto provided
335 // hash. http://crbug.com/389400
336
337 return CreateWebCryptoRsaPrivateKey(private_key.Pass(), algorithm.id(),
338 algorithm.rsaHashedImportParams()->hash(),
339 extractable, usages, key);
340 }
341
342 Status RsaHashedAlgorithm::ImportKeySpki(
343 const CryptoData& key_data,
344 const blink::WebCryptoAlgorithm& algorithm,
345 bool extractable,
346 blink::WebCryptoKeyUsageMask usages,
347 blink::WebCryptoKey* key) const {
348 crypto::ScopedEVP_PKEY public_key;
349 Status status =
350 ImportUnverifiedPkeyFromSpki(key_data, EVP_PKEY_RSA, &public_key);
351 if (status.IsError())
352 return status;
353
354 // TODO(eroman): Validate the algorithm OID against the webcrypto provided
355 // hash. http://crbug.com/389400
356
357 return CreateWebCryptoRsaPublicKey(public_key.Pass(), algorithm.id(),
358 algorithm.rsaHashedImportParams()->hash(),
359 extractable, usages, key);
360 }
361
362 Status RsaHashedAlgorithm::ImportKeyJwk(
363 const CryptoData& key_data,
364 const blink::WebCryptoAlgorithm& algorithm,
365 bool extractable,
366 blink::WebCryptoKeyUsageMask usages,
367 blink::WebCryptoKey* key) const {
368 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
369
370 const char* jwk_algorithm =
371 GetJwkAlgorithm(algorithm.rsaHashedImportParams()->hash().id());
372
373 if (!jwk_algorithm)
374 return Status::ErrorUnexpected();
375
376 JwkRsaInfo jwk;
377 Status status =
378 ReadRsaKeyJwk(key_data, jwk_algorithm, extractable, usages, &jwk);
379 if (status.IsError())
380 return status;
381
382 // Once the key type is known, verify the usages.
383 status = CheckKeyCreationUsages(
384 jwk.is_private_key ? all_private_key_usages_ : all_public_key_usages_,
385 usages, !jwk.is_private_key);
386 if (status.IsError())
387 return status;
388
389 return jwk.is_private_key
390 ? ImportRsaPrivateKey(algorithm, extractable, usages, jwk, key)
391 : ImportRsaPublicKey(algorithm, extractable, usages,
392 CryptoData(jwk.n), CryptoData(jwk.e), key);
393 }
394
395 Status RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key,
396 std::vector<uint8_t>* buffer) const {
397 if (key.type() != blink::WebCryptoKeyTypePrivate)
398 return Status::ErrorUnexpectedKeyType();
399 *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data();
400 return Status::Success();
401 }
402
403 Status RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key,
404 std::vector<uint8_t>* buffer) const {
405 if (key.type() != blink::WebCryptoKeyTypePublic)
406 return Status::ErrorUnexpectedKeyType();
407 *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data();
408 return Status::Success();
409 }
410
411 Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key,
412 std::vector<uint8_t>* buffer) const {
413 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
414
415 EVP_PKEY* pkey = AsymKeyOpenSsl::Cast(key)->key();
416 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(pkey));
417 if (!rsa.get())
418 return Status::ErrorUnexpected();
419
420 const char* jwk_algorithm =
421 GetJwkAlgorithm(key.algorithm().rsaHashedParams()->hash().id());
422 if (!jwk_algorithm)
423 return Status::ErrorUnexpected();
424
425 switch (key.type()) {
426 case blink::WebCryptoKeyTypePublic: {
427 JwkWriter writer(jwk_algorithm, key.extractable(), key.usages(), "RSA");
428 writer.SetBytes("n", CryptoData(BIGNUMToVector(rsa->n)));
429 writer.SetBytes("e", CryptoData(BIGNUMToVector(rsa->e)));
430 writer.ToJson(buffer);
431 return Status::Success();
432 }
433 case blink::WebCryptoKeyTypePrivate: {
434 JwkWriter writer(jwk_algorithm, key.extractable(), key.usages(), "RSA");
435 writer.SetBytes("n", CryptoData(BIGNUMToVector(rsa->n)));
436 writer.SetBytes("e", CryptoData(BIGNUMToVector(rsa->e)));
437 writer.SetBytes("d", CryptoData(BIGNUMToVector(rsa->d)));
438 // Although these are "optional" in the JWA, WebCrypto spec requires them
439 // to be emitted.
440 writer.SetBytes("p", CryptoData(BIGNUMToVector(rsa->p)));
441 writer.SetBytes("q", CryptoData(BIGNUMToVector(rsa->q)));
442 writer.SetBytes("dp", CryptoData(BIGNUMToVector(rsa->dmp1)));
443 writer.SetBytes("dq", CryptoData(BIGNUMToVector(rsa->dmq1)));
444 writer.SetBytes("qi", CryptoData(BIGNUMToVector(rsa->iqmp)));
445 writer.ToJson(buffer);
446 return Status::Success();
447 }
448
449 default:
450 return Status::ErrorUnexpected();
451 }
452 }
453
454 Status RsaHashedAlgorithm::SerializeKeyForClone(
455 const blink::WebCryptoKey& key,
456 blink::WebVector<uint8_t>* key_data) const {
457 key_data->assign(AsymKeyOpenSsl::Cast(key)->serialized_key_data());
458 return Status::Success();
459 }
460
461 // TODO(eroman): Defer import to the crypto thread. http://crbug.com/430763
462 Status RsaHashedAlgorithm::DeserializeKeyForClone(
463 const blink::WebCryptoKeyAlgorithm& algorithm,
464 blink::WebCryptoKeyType type,
465 bool extractable,
466 blink::WebCryptoKeyUsageMask usages,
467 const CryptoData& key_data,
468 blink::WebCryptoKey* key) const {
469 blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm(
470 algorithm.id(), algorithm.rsaHashedParams()->hash().id());
471
472 Status status;
473
474 switch (type) {
475 case blink::WebCryptoKeyTypePublic:
476 status =
477 ImportKeySpki(key_data, import_algorithm, extractable, usages, key);
478 break;
479 case blink::WebCryptoKeyTypePrivate:
480 status =
481 ImportKeyPkcs8(key_data, import_algorithm, extractable, usages, key);
482 break;
483 default:
484 return Status::ErrorUnexpected();
485 }
486
487 // There is some duplicated information in the serialized format used by
488 // structured clone (since the KeyAlgorithm is serialized separately from the
489 // key data). Use this extra information to further validate what was
490 // deserialized from the key data.
491
492 if (algorithm.id() != key->algorithm().id())
493 return Status::ErrorUnexpected();
494
495 if (key->type() != type)
496 return Status::ErrorUnexpected();
497
498 if (algorithm.rsaHashedParams()->modulusLengthBits() !=
499 key->algorithm().rsaHashedParams()->modulusLengthBits()) {
500 return Status::ErrorUnexpected();
501 }
502
503 if (algorithm.rsaHashedParams()->publicExponent().size() !=
504 key->algorithm().rsaHashedParams()->publicExponent().size() ||
505 0 !=
506 memcmp(algorithm.rsaHashedParams()->publicExponent().data(),
507 key->algorithm().rsaHashedParams()->publicExponent().data(),
508 key->algorithm().rsaHashedParams()->publicExponent().size())) {
509 return Status::ErrorUnexpected();
510 }
511
512 return Status::Success();
513 }
514
515 } // namespace webcrypto
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698