2 * Copyright (C) 2017 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "CryptoKeyEC.h"
29 #if ENABLE(SUBTLE_CRYPTO)
31 #include "CryptoKeyPair.h"
32 #include "GCryptUtilities.h"
33 #include "JsonWebKey.h"
34 #include <pal/crypto/gcrypt/Handle.h>
35 #include <pal/crypto/gcrypt/Utilities.h>
36 #include <pal/crypto/tasn1/Utilities.h>
37 #include <wtf/text/Base64.h>
41 static const char* curveName(CryptoKeyEC::NamedCurve curve)
44 case CryptoKeyEC::NamedCurve::P256:
46 case CryptoKeyEC::NamedCurve::P384:
54 static const uint8_t* curveIdentifier(CryptoKeyEC::NamedCurve curve)
57 case CryptoKeyEC::NamedCurve::P256:
58 return CryptoConstants::s_secp256r1Identifier.data();
59 case CryptoKeyEC::NamedCurve::P384:
60 return CryptoConstants::s_secp384r1Identifier.data();
67 static size_t curveSize(CryptoKeyEC::NamedCurve curve)
70 case CryptoKeyEC::NamedCurve::P256:
72 case CryptoKeyEC::NamedCurve::P384:
80 static unsigned curveUncompressedFieldElementSize(CryptoKeyEC::NamedCurve curve)
83 case CryptoKeyEC::NamedCurve::P256:
85 case CryptoKeyEC::NamedCurve::P384:
93 static unsigned curveUncompressedPointSize(CryptoKeyEC::NamedCurve curve)
95 return 2 * curveUncompressedFieldElementSize(curve) + 1;
98 CryptoKeyEC::~CryptoKeyEC()
101 PAL::GCrypt::HandleDeleter<gcry_sexp_t>()(m_platformKey);
104 size_t CryptoKeyEC::keySizeInBits() const
106 size_t size = curveSize(m_curve);
107 ASSERT(size == gcry_pk_get_nbits(m_platformKey));
111 std::optional<CryptoKeyPair> CryptoKeyEC::platformGeneratePair(CryptoAlgorithmIdentifier identifier, NamedCurve curve, bool extractable, CryptoKeyUsageBitmap usages)
113 PAL::GCrypt::Handle<gcry_sexp_t> genkeySexp;
114 gcry_error_t error = gcry_sexp_build(&genkeySexp, nullptr, "(genkey(ecc(curve %s)))", curveName(curve));
115 if (error != GPG_ERR_NO_ERROR) {
116 PAL::GCrypt::logError(error);
120 PAL::GCrypt::Handle<gcry_sexp_t> keyPairSexp;
121 error = gcry_pk_genkey(&keyPairSexp, genkeySexp);
122 if (error != GPG_ERR_NO_ERROR) {
123 PAL::GCrypt::logError(error);
127 PAL::GCrypt::Handle<gcry_sexp_t> publicKeySexp(gcry_sexp_find_token(keyPairSexp, "public-key", 0));
128 PAL::GCrypt::Handle<gcry_sexp_t> privateKeySexp(gcry_sexp_find_token(keyPairSexp, "private-key", 0));
129 if (!publicKeySexp || !privateKeySexp)
132 auto publicKey = CryptoKeyEC::create(identifier, curve, CryptoKeyType::Public, publicKeySexp.release(), true, usages);
133 auto privateKey = CryptoKeyEC::create(identifier, curve, CryptoKeyType::Private, privateKeySexp.release(), extractable, usages);
134 return CryptoKeyPair { WTFMove(publicKey), WTFMove(privateKey) };
137 RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportRaw(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
139 if (keyData.size() != curveUncompressedPointSize(curve))
142 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
143 gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(public-key(ecc(curve %s)(q %b)))",
144 curveName(curve), keyData.size(), keyData.data());
145 if (error != GPG_ERR_NO_ERROR) {
146 PAL::GCrypt::logError(error);
150 return create(identifier, curve, CryptoKeyType::Public, platformKey.release(), extractable, usages);
153 RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportJWKPublic(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& x, Vector<uint8_t>&& y, bool extractable, CryptoKeyUsageBitmap usages)
155 unsigned uncompressedFieldElementSize = curveUncompressedFieldElementSize(curve);
156 if (x.size() != uncompressedFieldElementSize || y.size() != uncompressedFieldElementSize)
159 // Construct the Vector that represents the EC point in uncompressed format.
161 q.reserveInitialCapacity(curveUncompressedPointSize(curve));
162 q.append(CryptoConstants::s_ecUncompressedFormatLeadingByte.data(), CryptoConstants::s_ecUncompressedFormatLeadingByte.size());
166 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
167 gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(public-key(ecc(curve %s)(q %b)))",
168 curveName(curve), q.size(), q.data());
169 if (error != GPG_ERR_NO_ERROR) {
170 PAL::GCrypt::logError(error);
174 return create(identifier, curve, CryptoKeyType::Public, platformKey.release(), extractable, usages);
177 RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportJWKPrivate(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& x, Vector<uint8_t>&& y, Vector<uint8_t>&& d, bool extractable, CryptoKeyUsageBitmap usages)
179 unsigned uncompressedFieldElementSize = curveUncompressedFieldElementSize(curve);
180 if (x.size() != uncompressedFieldElementSize || y.size() != uncompressedFieldElementSize || d.size() != uncompressedFieldElementSize)
183 // Construct the Vector that represents the EC point in uncompressed format.
185 q.reserveInitialCapacity(curveUncompressedPointSize(curve));
186 q.append(CryptoConstants::s_ecUncompressedFormatLeadingByte.data(), CryptoConstants::s_ecUncompressedFormatLeadingByte.size());
190 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
191 gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(private-key(ecc(curve %s)(q %b)(d %b)))",
192 curveName(curve), q.size(), q.data(), d.size(), d.data());
193 if (error != GPG_ERR_NO_ERROR) {
194 PAL::GCrypt::logError(error);
198 return create(identifier, curve, CryptoKeyType::Private, platformKey.release(), extractable, usages);
201 static bool supportedAlgorithmIdentifier(CryptoAlgorithmIdentifier keyIdentifier, const Vector<uint8_t>& identifier)
203 auto* data = identifier.data();
204 auto size = identifier.size();
206 switch (keyIdentifier) {
207 case CryptoAlgorithmIdentifier::ECDSA:
208 // ECDSA only supports id-ecPublicKey algorithms for imported keys.
209 if (CryptoConstants::matches(data, size, CryptoConstants::s_ecPublicKeyIdentifier))
212 case CryptoAlgorithmIdentifier::ECDH:
213 // ECDH supports both id-ecPublicKey and ic-ecDH algorithms for imported keys.
214 if (CryptoConstants::matches(data, size, CryptoConstants::s_ecPublicKeyIdentifier))
216 if (CryptoConstants::matches(data, size, CryptoConstants::s_ecDHIdentifier))
220 ASSERT_NOT_REACHED();
227 static std::optional<CryptoKeyEC::NamedCurve> curveForIdentifier(const Vector<uint8_t>& identifier)
229 auto* data = identifier.data();
230 auto size = identifier.size();
232 if (CryptoConstants::matches(data, size, CryptoConstants::s_secp256r1Identifier))
233 return CryptoKeyEC::NamedCurve::P256;
234 if (CryptoConstants::matches(data, size, CryptoConstants::s_secp384r1Identifier))
235 return CryptoKeyEC::NamedCurve::P384;
236 if (CryptoConstants::matches(data, size, CryptoConstants::s_secp521r1Identifier))
237 return std::nullopt; // Not yet supported.
242 RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportSpki(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
244 // Decode the `SubjectPublicKeyInfo` structure using the provided key data.
245 PAL::TASN1::Structure spki;
246 if (!PAL::TASN1::decodeStructure(&spki, "WebCrypto.SubjectPublicKeyInfo", keyData))
249 // Validate `algorithm.algorithm`.
251 auto algorithm = PAL::TASN1::elementData(spki, "algorithm.algorithm");
255 if (!supportedAlgorithmIdentifier(identifier, *algorithm))
259 // Validate `algorithm.parameters` and therein embedded `ECParameters`.
261 auto parameters = PAL::TASN1::elementData(spki, "algorithm.parameters");
265 // Decode the `ECParameters` structure using the `algorithm.parameters` data.
266 PAL::TASN1::Structure ecParameters;
267 if (!PAL::TASN1::decodeStructure(&ecParameters, "WebCrypto.ECParameters", *parameters))
270 auto namedCurve = PAL::TASN1::elementData(ecParameters, "namedCurve");
274 auto parameterCurve = curveForIdentifier(*namedCurve);
275 if (!parameterCurve || *parameterCurve != curve)
279 // Retrieve the `subjectPublicKey` data and embed it into the `public-key` s-expression.
280 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
282 auto subjectPublicKey = PAL::TASN1::elementData(spki, "subjectPublicKey");
283 if (!subjectPublicKey)
286 // Bail if the `subjectPublicKey` data size doesn't match the size of an uncompressed point
287 // for this curve, or if the first byte in the `subjectPublicKey` data isn't 0x04, as required
288 // for an uncompressed EC point encoded in an octet string.
289 if (subjectPublicKey->size() != curveUncompressedPointSize(curve)
290 || !CryptoConstants::matches(subjectPublicKey->data(), 1, CryptoConstants::s_ecUncompressedFormatLeadingByte))
293 // Convert X and Y coordinate data into MPIs.
294 unsigned coordinateSize = curveUncompressedFieldElementSize(curve);
295 PAL::GCrypt::Handle<gcry_mpi_t> xMPI, yMPI;
297 gcry_error_t error = gcry_mpi_scan(&xMPI, GCRYMPI_FMT_USG, &subjectPublicKey->at(1), coordinateSize, nullptr);
298 if (error != GPG_ERR_NO_ERROR) {
299 PAL::GCrypt::logError(error);
303 error = gcry_mpi_scan(&yMPI, GCRYMPI_FMT_USG, &subjectPublicKey->at(1 + coordinateSize), coordinateSize, nullptr);
304 if (error != GPG_ERR_NO_ERROR) {
305 PAL::GCrypt::logError(error);
310 // Construct an MPI point from the X and Y coordinates and using 1 as the Z coordinate.
311 // This always allocates the gcry_mpi_point_t object.
312 PAL::GCrypt::Handle<gcry_mpi_point_t> point(gcry_mpi_point_set(nullptr, xMPI, yMPI, GCRYMPI_CONST_ONE));
314 // Create an EC context for the specified curve.
315 PAL::GCrypt::Handle<gcry_ctx_t> context;
316 gcry_error_t error = gcry_mpi_ec_new(&context, nullptr, curveName(curve));
317 if (error != GPG_ERR_NO_ERROR) {
318 PAL::GCrypt::logError(error);
322 // Bail if the constructed MPI point is not on the specified EC curve.
323 if (!gcry_mpi_ec_curve_point(point, context))
326 error = gcry_sexp_build(&platformKey, nullptr, "(public-key(ecc(curve %s)(q %b)))",
327 curveName(curve), subjectPublicKey->size(), subjectPublicKey->data());
328 if (error != GPG_ERR_NO_ERROR) {
329 PAL::GCrypt::logError(error);
334 // Finally create a new CryptoKeyEC object, transferring to it ownership of the `public-key` s-expression.
335 return create(identifier, curve, CryptoKeyType::Public, platformKey.release(), extractable, usages);
338 RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportPkcs8(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
340 // Decode the `PrivateKeyInfo` structure using the provided key data.
341 PAL::TASN1::Structure pkcs8;
342 if (!PAL::TASN1::decodeStructure(&pkcs8, "WebCrypto.PrivateKeyInfo", keyData))
345 // Validate `version`.
347 auto version = PAL::TASN1::elementData(pkcs8, "version");
351 if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version0))
355 // Validate `privateKeyAlgorithm.algorithm`.
357 auto algorithm = PAL::TASN1::elementData(pkcs8, "privateKeyAlgorithm.algorithm");
361 if (!supportedAlgorithmIdentifier(identifier, *algorithm))
365 // Validate `privateKeyAlgorithm.parameters` and therein embedded `ECParameters`.
367 auto parameters = PAL::TASN1::elementData(pkcs8, "privateKeyAlgorithm.parameters");
371 PAL::TASN1::Structure ecParameters;
372 if (!PAL::TASN1::decodeStructure(&ecParameters, "WebCrypto.ECParameters", *parameters))
375 auto namedCurve = PAL::TASN1::elementData(ecParameters, "namedCurve");
379 auto parameterCurve = curveForIdentifier(*namedCurve);
380 if (!parameterCurve || *parameterCurve != curve)
384 // Decode the `ECPrivateKey` structure using the `privateKey` data.
385 PAL::TASN1::Structure ecPrivateKey;
387 auto privateKey = PAL::TASN1::elementData(pkcs8, "privateKey");
391 if (!PAL::TASN1::decodeStructure(&ecPrivateKey, "WebCrypto.ECPrivateKey", *privateKey))
395 // Validate `privateKey.version`.
397 auto version = PAL::TASN1::elementData(ecPrivateKey, "version");
401 if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version1))
405 // Validate `privateKey.parameters.namedCurve`, if any.
407 auto namedCurve = PAL::TASN1::elementData(ecPrivateKey, "parameters.namedCurve");
409 auto parameterCurve = curveForIdentifier(*namedCurve);
410 if (!parameterCurve || *parameterCurve != curve)
415 // Validate `privateKey.publicKey`, if any, and scan the data into an MPI.
416 PAL::GCrypt::Handle<gcry_mpi_t> publicKeyMPI;
418 auto publicKey = PAL::TASN1::elementData(ecPrivateKey, "publicKey");
420 if (publicKey->size() != curveUncompressedPointSize(curve)
421 || !CryptoConstants::matches(publicKey->data(), 1, CryptoConstants::s_ecUncompressedFormatLeadingByte))
424 gcry_error_t error = gcry_mpi_scan(&publicKeyMPI, GCRYMPI_FMT_USG, publicKey->data(), publicKey->size(), nullptr);
425 if (error != GPG_ERR_NO_ERROR) {
426 PAL::GCrypt::logError(error);
432 // Retrieve the `privateKey.privateKey` data and embed it into the `private-key` s-expression.
433 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
435 auto privateKey = PAL::TASN1::elementData(ecPrivateKey, "privateKey");
439 // Validate the size of `privateKey`, making sure it fits the size of the specified EC curve.
440 if (privateKey->size() * 8 != curveSize(curve))
443 // Construct the `private-key` expression that will also be used for the EC context.
444 gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(private-key(ecc(curve %s)(d %b)))",
445 curveName(curve), privateKey->size(), privateKey->data());
446 if (error != GPG_ERR_NO_ERROR) {
447 PAL::GCrypt::logError(error);
451 // Create an EC context for the specified curve.
452 PAL::GCrypt::Handle<gcry_ctx_t> context;
453 error = gcry_mpi_ec_new(&context, platformKey, nullptr);
454 if (error != GPG_ERR_NO_ERROR) {
455 PAL::GCrypt::logError(error);
459 // Set the 'q' value on the EC context if public key data was provided through the import.
461 error = gcry_mpi_ec_set_mpi("q", publicKeyMPI, context);
462 if (error != GPG_ERR_NO_ERROR) {
463 PAL::GCrypt::logError(error);
468 // Retrieve the `q` point. If the public key was provided through the PKCS#8 import, that
469 // key value will be retrieved as an gcry_mpi_point_t. Otherwise, the `q` point value will
470 // be computed on-the-fly by libgcrypt for the specified elliptic curve.
471 PAL::GCrypt::Handle<gcry_mpi_point_t> point(gcry_mpi_ec_get_point("q", context, 1));
475 // Bail if the retrieved `q` MPI point is not on the specified EC curve.
476 if (!gcry_mpi_ec_curve_point(point, context))
480 return create(identifier, curve, CryptoKeyType::Private, platformKey.release(), extractable, usages);
483 Vector<uint8_t> CryptoKeyEC::platformExportRaw() const
485 PAL::GCrypt::Handle<gcry_ctx_t> context;
486 gcry_error_t error = gcry_mpi_ec_new(&context, m_platformKey, nullptr);
487 if (error != GPG_ERR_NO_ERROR) {
488 PAL::GCrypt::logError(error);
492 PAL::GCrypt::Handle<gcry_mpi_t> qMPI(gcry_mpi_ec_get_mpi("q", context, 0));
496 auto q = mpiData(qMPI);
497 if (!q || q->size() != curveUncompressedPointSize(m_curve))
500 return WTFMove(q.value());
503 void CryptoKeyEC::platformAddFieldElements(JsonWebKey& jwk) const
505 PAL::GCrypt::Handle<gcry_ctx_t> context;
506 gcry_error_t error = gcry_mpi_ec_new(&context, m_platformKey, nullptr);
507 if (error != GPG_ERR_NO_ERROR) {
508 PAL::GCrypt::logError(error);
512 unsigned uncompressedFieldElementSize = curveUncompressedFieldElementSize(m_curve);
514 PAL::GCrypt::Handle<gcry_mpi_t> qMPI(gcry_mpi_ec_get_mpi("q", context, 0));
516 auto q = mpiData(qMPI);
517 if (q && q->size() == curveUncompressedPointSize(m_curve)) {
519 a.append(q->data() + 1, uncompressedFieldElementSize);
520 jwk.x = base64URLEncode(a);
523 b.append(q->data() + 1 + uncompressedFieldElementSize, uncompressedFieldElementSize);
524 jwk.y = base64URLEncode(b);
528 if (type() == Type::Private) {
529 PAL::GCrypt::Handle<gcry_mpi_t> dMPI(gcry_mpi_ec_get_mpi("d", context, 0));
531 auto d = mpiData(dMPI);
532 if (d && d->size() == uncompressedFieldElementSize)
533 jwk.d = base64URLEncode(*d);
538 Vector<uint8_t> CryptoKeyEC::platformExportSpki() const
540 PAL::TASN1::Structure ecParameters;
542 // Create the `ECParameters` structure.
543 if (!PAL::TASN1::createStructure("WebCrypto.ECParameters", &ecParameters))
546 // Select the `namedCurve` object identifier as the target `ECParameters` choice.
547 if (!PAL::TASN1::writeElement(ecParameters, "", "namedCurve", 1))
550 // Write out the EC curve identifier under `namedCurve`.
551 if (!PAL::TASN1::writeElement(ecParameters, "namedCurve", curveIdentifier(m_curve), 1))
555 PAL::TASN1::Structure spki;
557 // Create the `SubjectPublicKeyInfo` structure.
558 if (!PAL::TASN1::createStructure("WebCrypto.SubjectPublicKeyInfo", &spki))
561 // Write out the id-ecPublicKey identifier under `algorithm.algorithm`.
562 // FIXME: Per specification this should write out id-ecDH when the ECDH algorithm
563 // is specified for this CryptoKeyEC object, but not even the W3C tests expect that.
564 if (!PAL::TASN1::writeElement(spki, "algorithm.algorithm", CryptoConstants::s_ecPublicKeyIdentifier.data(), 1))
567 // Write out the `ECParameters` data under `algorithm.parameters`.
569 auto data = PAL::TASN1::encodedData(ecParameters, "");
570 if (!data || !PAL::TASN1::writeElement(spki, "algorithm.parameters", data->data(), data->size()))
574 // Retrieve the `q` s-expression, which should contain the public key data.
575 PAL::GCrypt::Handle<gcry_sexp_t> qSexp(gcry_sexp_find_token(m_platformKey, "q", 0));
579 // Retrieve the `q` data, which should be in the uncompressed point format.
580 // Validate the data size and the first byte (which should be 0x04).
581 auto qData = mpiData(qSexp);
582 if (!qData || qData->size() != curveUncompressedPointSize(m_curve)
583 || !CryptoConstants::matches(qData->data(), 1, CryptoConstants::s_ecUncompressedFormatLeadingByte))
586 // Write out the public key data under `subjectPublicKey`. Because this is a
587 // bit string parameter, the data size has to be multiplied by 8.
588 if (!PAL::TASN1::writeElement(spki, "subjectPublicKey", qData->data(), qData->size() * 8))
592 // Retrieve the encoded `SubjectPublicKeyInfo` data and return it.
593 auto result = PAL::TASN1::encodedData(spki, "");
597 return WTFMove(result.value());
600 Vector<uint8_t> CryptoKeyEC::platformExportPkcs8() const
602 PAL::TASN1::Structure ecParameters;
604 // Create the `ECParameters` structure.
605 if (!PAL::TASN1::createStructure("WebCrypto.ECParameters", &ecParameters))
608 // Select the `namedCurve` object identifier as the target `ECParameters` choice.
609 if (!PAL::TASN1::writeElement(ecParameters, "", "namedCurve", 1))
612 // Write out the EC curve identifier under `namedCurve`.
613 if (!PAL::TASN1::writeElement(ecParameters, "namedCurve", curveIdentifier(m_curve), 1))
617 PAL::TASN1::Structure ecPrivateKey;
619 // Create the `ECPrivateKey` structure.
620 if (!PAL::TASN1::createStructure("WebCrypto.ECPrivateKey", &ecPrivateKey))
623 // Write out '1' under `version`.
624 if (!PAL::TASN1::writeElement(ecPrivateKey, "version", "1", 0))
627 // Construct the EC context that we'll use to retrieve private and public key data.
628 PAL::GCrypt::Handle<gcry_ctx_t> context;
629 gcry_error_t error = gcry_mpi_ec_new(&context, m_platformKey, nullptr);
630 if (error != GPG_ERR_NO_ERROR)
634 // Retrieve the `d` MPI that holds the private key data.
635 PAL::GCrypt::Handle<gcry_mpi_t> dMPI(gcry_mpi_ec_get_mpi("d", context, 0));
639 // Retrieve the MPI data and write it out under `privateKey`.
640 auto data = mpiData(dMPI);
641 if (!data || !PAL::TASN1::writeElement(ecPrivateKey, "privateKey", data->data(), data->size()))
645 // Eliminate the optional `parameters` element.
646 if (!PAL::TASN1::writeElement(ecPrivateKey, "parameters", nullptr, 0))
650 // Retrieve the `q` MPI that holds the public key data.
651 PAL::GCrypt::Handle<gcry_mpi_t> qMPI(gcry_mpi_ec_get_mpi("q", context, 0));
655 // Retrieve the MPI data and write it out under `publicKey`. Because this is a
656 // bit string parameter, the data size has to be multiplied by 8.
657 auto data = mpiData(qMPI);
658 if (!data || !PAL::TASN1::writeElement(ecPrivateKey, "publicKey", data->data(), data->size() * 8))
663 PAL::TASN1::Structure pkcs8;
665 // Create the `PrivateKeyInfo` structure.
666 if (!PAL::TASN1::createStructure("WebCrypto.PrivateKeyInfo", &pkcs8))
669 // Write out '0' under `version`.
670 if (!PAL::TASN1::writeElement(pkcs8, "version", "0", 0))
673 // Write out the id-ecPublicKey identifier under `privateKeyAlgorithm.algorithm`.
674 // FIXME: Per specification this should write out id-ecDH when the ECDH algorithm
675 // is specified for this CryptoKeyEC object, but not even the W3C tests expect that.
676 if (!PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.algorithm", CryptoConstants::s_ecPublicKeyIdentifier.data(), 1))
679 // Write out the `ECParameters` data under `privateKeyAlgorithm.parameters`.
681 auto data = PAL::TASN1::encodedData(ecParameters, "");
682 if (!data || !PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.parameters", data->data(), data->size()))
686 // Write out the `ECPrivateKey` data under `privateKey`.
688 auto data = PAL::TASN1::encodedData(ecPrivateKey, "");
689 if (!data || !PAL::TASN1::writeElement(pkcs8, "privateKey", data->data(), data->size()))
693 // Eliminate the optional `attributes` element.
694 if (!PAL::TASN1::writeElement(pkcs8, "attributes", nullptr, 0))
698 // Retrieve the encoded `PrivateKeyInfo` data and return it.
699 auto result = PAL::TASN1::encodedData(pkcs8, "");
703 return WTFMove(result.value());
706 } // namespace WebCore
708 #endif // ENABLE(SUBTLE_CRYPTO)