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