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

Side by Side Diff: components/webcrypto/openssl/ec_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/ec_algorithm_openssl.h"
6
7 #include <openssl/ec.h>
8 #include <openssl/ec_key.h>
9 #include <openssl/evp.h>
10 #include <openssl/pkcs12.h>
11
12 #include "base/logging.h"
13 #include "base/stl_util.h"
14 #include "components/webcrypto/crypto_data.h"
15 #include "components/webcrypto/generate_key_result.h"
16 #include "components/webcrypto/jwk.h"
17 #include "components/webcrypto/openssl/key_openssl.h"
18 #include "components/webcrypto/openssl/util_openssl.h"
19 #include "components/webcrypto/status.h"
20 #include "components/webcrypto/webcrypto_util.h"
21 #include "crypto/openssl_util.h"
22 #include "crypto/scoped_openssl_types.h"
23 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
24 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
25
26 namespace webcrypto {
27
28 namespace {
29
30 // Maps a blink::WebCryptoNamedCurve to the corresponding NID used by
31 // BoringSSL.
32 Status WebCryptoCurveToNid(blink::WebCryptoNamedCurve named_curve, int* nid) {
33 switch (named_curve) {
34 case blink::WebCryptoNamedCurveP256:
35 *nid = NID_X9_62_prime256v1;
36 return Status::Success();
37 case blink::WebCryptoNamedCurveP384:
38 *nid = NID_secp384r1;
39 return Status::Success();
40 case blink::WebCryptoNamedCurveP521:
41 *nid = NID_secp521r1;
42 return Status::Success();
43 }
44 return Status::ErrorUnsupported();
45 }
46
47 // Maps a BoringSSL NID to the corresponding WebCrypto named curve.
48 Status NidToWebCryptoCurve(int nid, blink::WebCryptoNamedCurve* named_curve) {
49 switch (nid) {
50 case NID_X9_62_prime256v1:
51 *named_curve = blink::WebCryptoNamedCurveP256;
52 return Status::Success();
53 case NID_secp384r1:
54 *named_curve = blink::WebCryptoNamedCurveP384;
55 return Status::Success();
56 case NID_secp521r1:
57 *named_curve = blink::WebCryptoNamedCurveP521;
58 return Status::Success();
59 }
60 return Status::ErrorImportedEcKeyIncorrectCurve();
61 }
62
63 struct JwkCrvMapping {
64 const char* jwk_curve;
65 blink::WebCryptoNamedCurve named_curve;
66 };
67
68 const JwkCrvMapping kJwkCrvMappings[] = {
69 {"P-256", blink::WebCryptoNamedCurveP256},
70 {"P-384", blink::WebCryptoNamedCurveP384},
71 {"P-521", blink::WebCryptoNamedCurveP521},
72 };
73
74 // Gets the "crv" parameter from a JWK and converts it to a WebCryptoNamedCurve.
75 Status ReadJwkCrv(const JwkReader& jwk,
76 blink::WebCryptoNamedCurve* named_curve) {
77 std::string jwk_curve;
78 Status status = jwk.GetString("crv", &jwk_curve);
79 if (status.IsError())
80 return status;
81
82 for (size_t i = 0; i < arraysize(kJwkCrvMappings); ++i) {
83 if (kJwkCrvMappings[i].jwk_curve == jwk_curve) {
84 *named_curve = kJwkCrvMappings[i].named_curve;
85 return Status::Success();
86 }
87 }
88
89 return Status::ErrorJwkIncorrectCrv();
90 }
91
92 // Converts a WebCryptoNamedCurve to an equivalent JWK "crv".
93 Status WebCryptoCurveToJwkCrv(blink::WebCryptoNamedCurve named_curve,
94 std::string* jwk_crv) {
95 for (size_t i = 0; i < arraysize(kJwkCrvMappings); ++i) {
96 if (kJwkCrvMappings[i].named_curve == named_curve) {
97 *jwk_crv = kJwkCrvMappings[i].jwk_curve;
98 return Status::Success();
99 }
100 }
101 return Status::ErrorUnexpected();
102 }
103
104 // Verifies that an EC key imported from PKCS8 or SPKI format is correct.
105 // This involves verifying the key validity, and the NID for the named curve.
106 // Also removes the EC_PKEY_NO_PUBKEY flag if present.
107 Status VerifyEcKeyAfterSpkiOrPkcs8Import(
108 EVP_PKEY* pkey,
109 blink::WebCryptoNamedCurve expected_named_curve) {
110 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
111
112 crypto::ScopedEC_KEY ec(EVP_PKEY_get1_EC_KEY(pkey));
113 if (!ec.get())
114 return Status::ErrorUnexpected();
115
116 // When importing an ECPrivateKey, the public key is optional. If it was
117 // omitted then the public key will be calculated by BoringSSL and added into
118 // the EC_KEY. However an encoding flag is set such that when exporting to
119 // PKCS8 format the public key is once again omitted. Remove this flag.
120 unsigned int enc_flags = EC_KEY_get_enc_flags(ec.get());
121 enc_flags &= ~EC_PKEY_NO_PUBKEY;
122 EC_KEY_set_enc_flags(ec.get(), enc_flags);
123
124 if (!EC_KEY_check_key(ec.get()))
125 return Status::ErrorEcKeyInvalid();
126
127 // Make sure the curve matches the expected curve name.
128 int curve_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec.get()));
129 blink::WebCryptoNamedCurve named_curve = blink::WebCryptoNamedCurveP256;
130 Status status = NidToWebCryptoCurve(curve_nid, &named_curve);
131 if (status.IsError())
132 return status;
133
134 if (named_curve != expected_named_curve)
135 return Status::ErrorImportedEcKeyIncorrectCurve();
136
137 return Status::Success();
138 }
139
140 // Creates an EC_KEY for the given WebCryptoNamedCurve.
141 Status CreateEC_KEY(blink::WebCryptoNamedCurve named_curve,
142 crypto::ScopedEC_KEY* ec) {
143 int curve_nid = 0;
144 Status status = WebCryptoCurveToNid(named_curve, &curve_nid);
145 if (status.IsError())
146 return status;
147
148 ec->reset(EC_KEY_new_by_curve_name(curve_nid));
149 if (!ec->get())
150 return Status::OperationError();
151
152 return Status::Success();
153 }
154
155 // Writes an unsigned BIGNUM into |jwk|, zero-padding it to a length of
156 // |padded_length|.
157 Status WritePaddedBIGNUM(const std::string& member_name,
158 const BIGNUM* value,
159 size_t padded_length,
160 JwkWriter* jwk) {
161 std::vector<uint8_t> padded_bytes(padded_length);
162 if (!BN_bn2bin_padded(vector_as_array(&padded_bytes), padded_bytes.size(),
163 value)) {
164 return Status::OperationError();
165 }
166 jwk->SetBytes(member_name, CryptoData(padded_bytes));
167 return Status::Success();
168 }
169
170 // Reads a fixed length BIGNUM from a JWK.
171 Status ReadPaddedBIGNUM(const JwkReader& jwk,
172 const std::string& member_name,
173 size_t expected_length,
174 crypto::ScopedBIGNUM* out) {
175 std::string bytes;
176 Status status = jwk.GetBytes(member_name, &bytes);
177 if (status.IsError())
178 return status;
179
180 if (bytes.size() != expected_length) {
181 return Status::JwkOctetStringWrongLength(member_name, expected_length,
182 bytes.size());
183 }
184
185 out->reset(CreateBIGNUM(bytes));
186 return Status::Success();
187 }
188
189 int GetGroupDegreeInBytes(EC_KEY* ec) {
190 const EC_GROUP* group = EC_KEY_get0_group(ec);
191 return NumBitsToBytes(EC_GROUP_get_degree(group));
192 }
193
194 // Extracts the public key as affine coordinates (x,y).
195 Status GetPublicKey(EC_KEY* ec,
196 crypto::ScopedBIGNUM* x,
197 crypto::ScopedBIGNUM* y) {
198 const EC_GROUP* group = EC_KEY_get0_group(ec);
199 const EC_POINT* point = EC_KEY_get0_public_key(ec);
200
201 x->reset(BN_new());
202 y->reset(BN_new());
203
204 if (!EC_POINT_get_affine_coordinates_GFp(group, point, x->get(), y->get(),
205 NULL)) {
206 return Status::OperationError();
207 }
208
209 return Status::Success();
210 }
211
212 } // namespace
213
214 Status EcAlgorithm::GenerateKey(const blink::WebCryptoAlgorithm& algorithm,
215 bool extractable,
216 blink::WebCryptoKeyUsageMask combined_usages,
217 GenerateKeyResult* result) const {
218 blink::WebCryptoKeyUsageMask public_usages = 0;
219 blink::WebCryptoKeyUsageMask private_usages = 0;
220
221 Status status = GetUsagesForGenerateAsymmetricKey(
222 combined_usages, all_public_key_usages_, all_private_key_usages_,
223 &public_usages, &private_usages);
224 if (status.IsError())
225 return status;
226
227 const blink::WebCryptoEcKeyGenParams* params = algorithm.ecKeyGenParams();
228
229 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
230
231 // Generate an EC key pair.
232 crypto::ScopedEC_KEY ec_private_key;
233 status = CreateEC_KEY(params->namedCurve(), &ec_private_key);
234 if (status.IsError())
235 return status;
236
237 if (!EC_KEY_generate_key(ec_private_key.get()))
238 return Status::OperationError();
239
240 // Construct an EVP_PKEY for the private key.
241 crypto::ScopedEVP_PKEY private_pkey(EVP_PKEY_new());
242 if (!private_pkey ||
243 !EVP_PKEY_set1_EC_KEY(private_pkey.get(), ec_private_key.get())) {
244 return Status::OperationError();
245 }
246
247 // Construct an EVP_PKEY for just the public key.
248 crypto::ScopedEC_KEY ec_public_key;
249 crypto::ScopedEVP_PKEY public_pkey(EVP_PKEY_new());
250 status = CreateEC_KEY(params->namedCurve(), &ec_public_key);
251 if (status.IsError())
252 return status;
253 if (!EC_KEY_set_public_key(ec_public_key.get(),
254 EC_KEY_get0_public_key(ec_private_key.get()))) {
255 return Status::OperationError();
256 }
257 if (!public_pkey ||
258 !EVP_PKEY_set1_EC_KEY(public_pkey.get(), ec_public_key.get())) {
259 return Status::OperationError();
260 }
261
262 blink::WebCryptoKey public_key;
263 blink::WebCryptoKey private_key;
264
265 blink::WebCryptoKeyAlgorithm key_algorithm =
266 blink::WebCryptoKeyAlgorithm::createEc(algorithm.id(),
267 params->namedCurve());
268
269 // Note that extractable is unconditionally set to true. This is because per
270 // the WebCrypto spec generated public keys are always extractable.
271 status = CreateWebCryptoPublicKey(public_pkey.Pass(), key_algorithm, true,
272 public_usages, &public_key);
273 if (status.IsError())
274 return status;
275
276 status = CreateWebCryptoPrivateKey(private_pkey.Pass(), key_algorithm,
277 extractable, private_usages, &private_key);
278 if (status.IsError())
279 return status;
280
281 result->AssignKeyPair(public_key, private_key);
282 return Status::Success();
283 }
284
285 Status EcAlgorithm::VerifyKeyUsagesBeforeImportKey(
286 blink::WebCryptoKeyFormat format,
287 blink::WebCryptoKeyUsageMask usages) const {
288 return VerifyUsagesBeforeImportAsymmetricKey(format, all_public_key_usages_,
289 all_private_key_usages_, usages);
290 }
291
292 Status EcAlgorithm::ImportKeyPkcs8(const CryptoData& key_data,
293 const blink::WebCryptoAlgorithm& algorithm,
294 bool extractable,
295 blink::WebCryptoKeyUsageMask usages,
296 blink::WebCryptoKey* key) const {
297 crypto::ScopedEVP_PKEY private_key;
298 Status status =
299 ImportUnverifiedPkeyFromPkcs8(key_data, EVP_PKEY_EC, &private_key);
300 if (status.IsError())
301 return status;
302
303 const blink::WebCryptoEcKeyImportParams* params =
304 algorithm.ecKeyImportParams();
305
306 status = VerifyEcKeyAfterSpkiOrPkcs8Import(private_key.get(),
307 params->namedCurve());
308 if (status.IsError())
309 return status;
310
311 return CreateWebCryptoPrivateKey(private_key.Pass(),
312 blink::WebCryptoKeyAlgorithm::createEc(
313 algorithm.id(), params->namedCurve()),
314 extractable, usages, key);
315 }
316
317 Status EcAlgorithm::ImportKeySpki(const CryptoData& key_data,
318 const blink::WebCryptoAlgorithm& algorithm,
319 bool extractable,
320 blink::WebCryptoKeyUsageMask usages,
321 blink::WebCryptoKey* key) const {
322 crypto::ScopedEVP_PKEY public_key;
323 Status status =
324 ImportUnverifiedPkeyFromSpki(key_data, EVP_PKEY_EC, &public_key);
325 if (status.IsError())
326 return status;
327
328 const blink::WebCryptoEcKeyImportParams* params =
329 algorithm.ecKeyImportParams();
330
331 status =
332 VerifyEcKeyAfterSpkiOrPkcs8Import(public_key.get(), params->namedCurve());
333 if (status.IsError())
334 return status;
335
336 return CreateWebCryptoPublicKey(public_key.Pass(),
337 blink::WebCryptoKeyAlgorithm::createEc(
338 algorithm.id(), params->namedCurve()),
339 extractable, usages, key);
340 }
341
342 // The format for JWK EC keys is given by:
343 // https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-36#section-6. 2
344 Status EcAlgorithm::ImportKeyJwk(const CryptoData& key_data,
345 const blink::WebCryptoAlgorithm& algorithm,
346 bool extractable,
347 blink::WebCryptoKeyUsageMask usages,
348 blink::WebCryptoKey* key) const {
349 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
350
351 const blink::WebCryptoEcKeyImportParams* params =
352 algorithm.ecKeyImportParams();
353
354 // When importing EC keys from JWK there may be up to *three* separate curve
355 // names:
356 //
357 // (1) The one given to WebCrypto's importKey (params->namedCurve()).
358 // (2) JWK's "crv" member
359 // (3) A curve implied by JWK's "alg" member.
360 //
361 // (In the case of ECDSA, the "alg" member implicitly names a curve and hash)
362
363 JwkReader jwk;
364 Status status = jwk.Init(key_data, extractable, usages, "EC",
365 GetJwkAlgorithm(params->namedCurve()));
366 if (status.IsError())
367 return status;
368
369 // Verify that "crv" matches expected curve.
370 blink::WebCryptoNamedCurve jwk_crv = blink::WebCryptoNamedCurveP256;
371 status = ReadJwkCrv(jwk, &jwk_crv);
372 if (status.IsError())
373 return status;
374 if (jwk_crv != params->namedCurve())
375 return Status::ErrorJwkIncorrectCrv();
376
377 // Only private keys have a "d" parameter. The key may still be invalid, but
378 // tentatively decide if it is a public or private key.
379 bool is_private_key = jwk.HasMember("d");
380
381 // Now that the key type is known, verify the usages.
382 status = CheckKeyCreationUsages(
383 is_private_key ? all_private_key_usages_ : all_public_key_usages_, usages,
384 !is_private_key);
385 if (status.IsError())
386 return status;
387
388 // Create an EC_KEY.
389 crypto::ScopedEC_KEY ec;
390 status = CreateEC_KEY(params->namedCurve(), &ec);
391 if (status.IsError())
392 return status;
393
394 // JWK requires the length of x, y, d to match the group degree.
395 int degree_bytes = GetGroupDegreeInBytes(ec.get());
396
397 // Read the public key's uncompressed affine coordinates.
398 crypto::ScopedBIGNUM x;
399 status = ReadPaddedBIGNUM(jwk, "x", degree_bytes, &x);
400 if (status.IsError())
401 return status;
402
403 crypto::ScopedBIGNUM y;
404 status = ReadPaddedBIGNUM(jwk, "y", degree_bytes, &y);
405 if (status.IsError())
406 return status;
407
408 // TODO(eroman): Distinguish more accurately between a DataError and
409 // OperationError. In general if this fails it was due to the key being an
410 // invalid EC key.
411 if (!EC_KEY_set_public_key_affine_coordinates(ec.get(), x.get(), y.get()))
412 return Status::DataError();
413
414 // Extract the "d" parameters.
415 if (is_private_key) {
416 crypto::ScopedBIGNUM d;
417 status = ReadPaddedBIGNUM(jwk, "d", degree_bytes, &d);
418 if (status.IsError())
419 return status;
420
421 if (!EC_KEY_set_private_key(ec.get(), d.get()))
422 return Status::OperationError();
423 }
424
425 // Verify the key.
426 if (!EC_KEY_check_key(ec.get()))
427 return Status::ErrorEcKeyInvalid();
428
429 // Wrap the EC_KEY into an EVP_PKEY.
430 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
431 if (!pkey || !EVP_PKEY_set1_EC_KEY(pkey.get(), ec.get()))
432 return Status::OperationError();
433
434 blink::WebCryptoKeyAlgorithm key_algorithm =
435 blink::WebCryptoKeyAlgorithm::createEc(algorithm.id(),
436 params->namedCurve());
437
438 // Wrap the EVP_PKEY into a WebCryptoKey
439 if (is_private_key) {
440 return CreateWebCryptoPrivateKey(pkey.Pass(), key_algorithm, extractable,
441 usages, key);
442 }
443 return CreateWebCryptoPublicKey(pkey.Pass(), key_algorithm, extractable,
444 usages, key);
445 }
446
447 Status EcAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key,
448 std::vector<uint8_t>* buffer) const {
449 if (key.type() != blink::WebCryptoKeyTypePrivate)
450 return Status::ErrorUnexpectedKeyType();
451 *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data();
452 return Status::Success();
453 }
454
455 Status EcAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key,
456 std::vector<uint8_t>* buffer) const {
457 if (key.type() != blink::WebCryptoKeyTypePublic)
458 return Status::ErrorUnexpectedKeyType();
459 *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data();
460 return Status::Success();
461 }
462
463 // The format for JWK EC keys is given by:
464 // https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-36#section-6. 2
465 Status EcAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key,
466 std::vector<uint8_t>* buffer) const {
467 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
468
469 EVP_PKEY* pkey = AsymKeyOpenSsl::Cast(key)->key();
470
471 crypto::ScopedEC_KEY ec(EVP_PKEY_get1_EC_KEY(pkey));
472 if (!ec.get())
473 return Status::ErrorUnexpected();
474
475 // No "alg" is set for EC keys.
476 JwkWriter jwk(std::string(), key.extractable(), key.usages(), "EC");
477
478 // Set the crv
479 std::string crv;
480 Status status =
481 WebCryptoCurveToJwkCrv(key.algorithm().ecParams()->namedCurve(), &crv);
482 if (status.IsError())
483 return status;
484
485 int degree_bytes = GetGroupDegreeInBytes(ec.get());
486
487 jwk.SetString("crv", crv);
488
489 crypto::ScopedBIGNUM x;
490 crypto::ScopedBIGNUM y;
491 status = GetPublicKey(ec.get(), &x, &y);
492 if (status.IsError())
493 return status;
494
495 status = WritePaddedBIGNUM("x", x.get(), degree_bytes, &jwk);
496 if (status.IsError())
497 return status;
498
499 status = WritePaddedBIGNUM("y", y.get(), degree_bytes, &jwk);
500 if (status.IsError())
501 return status;
502
503 if (key.type() == blink::WebCryptoKeyTypePrivate) {
504 const BIGNUM* d = EC_KEY_get0_private_key(ec.get());
505 status = WritePaddedBIGNUM("d", d, degree_bytes, &jwk);
506 if (status.IsError())
507 return status;
508 }
509
510 jwk.ToJson(buffer);
511 return Status::Success();
512 }
513
514 Status EcAlgorithm::SerializeKeyForClone(
515 const blink::WebCryptoKey& key,
516 blink::WebVector<uint8_t>* key_data) const {
517 key_data->assign(AsymKeyOpenSsl::Cast(key)->serialized_key_data());
518 return Status::Success();
519 }
520
521 // TODO(eroman): Defer import to the crypto thread. http://crbug.com/430763
522 Status EcAlgorithm::DeserializeKeyForClone(
523 const blink::WebCryptoKeyAlgorithm& algorithm,
524 blink::WebCryptoKeyType type,
525 bool extractable,
526 blink::WebCryptoKeyUsageMask usages,
527 const CryptoData& key_data,
528 blink::WebCryptoKey* key) const {
529 blink::WebCryptoAlgorithm import_algorithm = CreateEcImportAlgorithm(
530 algorithm.id(), algorithm.ecParams()->namedCurve());
531
532 Status status;
533
534 switch (type) {
535 case blink::WebCryptoKeyTypePublic:
536 status =
537 ImportKeySpki(key_data, import_algorithm, extractable, usages, key);
538 break;
539 case blink::WebCryptoKeyTypePrivate:
540 status =
541 ImportKeyPkcs8(key_data, import_algorithm, extractable, usages, key);
542 break;
543 default:
544 return Status::ErrorUnexpected();
545 }
546
547 // There is some duplicated information in the serialized format used by
548 // structured clone (since the KeyAlgorithm is serialized separately from the
549 // key data). Use this extra information to further validate what was
550 // deserialized from the key data.
551
552 if (algorithm.id() != key->algorithm().id())
553 return Status::ErrorUnexpected();
554
555 if (type != key->type())
556 return Status::ErrorUnexpected();
557
558 if (algorithm.ecParams()->namedCurve() !=
559 key->algorithm().ecParams()->namedCurve()) {
560 return Status::ErrorUnexpected();
561 }
562
563 return Status::Success();
564 }
565
566 } // namespace webcrypto
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698