Unreviewed. Removing redundant NotImplemented.h header inclusions
[WebKit-https.git] / Source / WebCore / crypto / gcrypt / CryptoKeyECGCrypt.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #include "config.h"
27 #include "CryptoKeyEC.h"
28
29 #if ENABLE(SUBTLE_CRYPTO)
30
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>
38
39 namespace WebCore {
40
41 static const char* curveName(CryptoKeyEC::NamedCurve curve)
42 {
43     switch (curve) {
44     case CryptoKeyEC::NamedCurve::P256:
45         return "NIST P-256";
46     case CryptoKeyEC::NamedCurve::P384:
47         return "NIST P-384";
48     }
49
50     ASSERT_NOT_REACHED();
51     return nullptr;
52 }
53
54 static const uint8_t* curveIdentifier(CryptoKeyEC::NamedCurve curve)
55 {
56     switch (curve) {
57     case CryptoKeyEC::NamedCurve::P256:
58         return CryptoConstants::s_secp256r1Identifier.data();
59     case CryptoKeyEC::NamedCurve::P384:
60         return CryptoConstants::s_secp384r1Identifier.data();
61     }
62
63     ASSERT_NOT_REACHED();
64     return nullptr;
65 }
66
67 static size_t curveSize(CryptoKeyEC::NamedCurve curve)
68 {
69     switch (curve) {
70     case CryptoKeyEC::NamedCurve::P256:
71         return 256;
72     case CryptoKeyEC::NamedCurve::P384:
73         return 384;
74     }
75
76     ASSERT_NOT_REACHED();
77     return 0;
78 }
79
80 static unsigned curveUncompressedFieldElementSize(CryptoKeyEC::NamedCurve curve)
81 {
82     switch (curve) {
83     case CryptoKeyEC::NamedCurve::P256:
84         return 32;
85     case CryptoKeyEC::NamedCurve::P384:
86         return 48;
87     }
88
89     ASSERT_NOT_REACHED();
90     return 0;
91 }
92
93 static unsigned curveUncompressedPointSize(CryptoKeyEC::NamedCurve curve)
94 {
95     return 2 * curveUncompressedFieldElementSize(curve) + 1;
96 }
97
98 CryptoKeyEC::~CryptoKeyEC()
99 {
100     if (m_platformKey)
101         PAL::GCrypt::HandleDeleter<gcry_sexp_t>()(m_platformKey);
102 }
103
104 size_t CryptoKeyEC::keySizeInBits() const
105 {
106     size_t size = curveSize(m_curve);
107     ASSERT(size == gcry_pk_get_nbits(m_platformKey));
108     return size;
109 }
110
111 std::optional<CryptoKeyPair> CryptoKeyEC::platformGeneratePair(CryptoAlgorithmIdentifier identifier, NamedCurve curve, bool extractable, CryptoKeyUsageBitmap usages)
112 {
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);
117         return std::nullopt;
118     }
119
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);
124         return std::nullopt;
125     }
126
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)
130         return std::nullopt;
131
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) };
135 }
136
137 RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportRaw(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
138 {
139     if (keyData.size() != curveUncompressedPointSize(curve))
140         return nullptr;
141
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);
147         return nullptr;
148     }
149
150     return create(identifier, curve, CryptoKeyType::Public, platformKey.release(), extractable, usages);
151 }
152
153 RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportJWKPublic(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& x, Vector<uint8_t>&& y, bool extractable, CryptoKeyUsageBitmap usages)
154 {
155     unsigned uncompressedFieldElementSize = curveUncompressedFieldElementSize(curve);
156     if (x.size() != uncompressedFieldElementSize || y.size() != uncompressedFieldElementSize)
157         return nullptr;
158
159     // Construct the Vector that represents the EC point in uncompressed format.
160     Vector<uint8_t> q;
161     q.reserveInitialCapacity(curveUncompressedPointSize(curve));
162     q.append(CryptoConstants::s_ecUncompressedFormatLeadingByte.data(), CryptoConstants::s_ecUncompressedFormatLeadingByte.size());
163     q.appendVector(x);
164     q.appendVector(y);
165
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);
171         return nullptr;
172     }
173
174     return create(identifier, curve, CryptoKeyType::Public, platformKey.release(), extractable, usages);
175 }
176
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)
178 {
179     unsigned uncompressedFieldElementSize = curveUncompressedFieldElementSize(curve);
180     if (x.size() != uncompressedFieldElementSize || y.size() != uncompressedFieldElementSize || d.size() != uncompressedFieldElementSize)
181         return nullptr;
182
183     // Construct the Vector that represents the EC point in uncompressed format.
184     Vector<uint8_t> q;
185     q.reserveInitialCapacity(curveUncompressedPointSize(curve));
186     q.append(CryptoConstants::s_ecUncompressedFormatLeadingByte.data(), CryptoConstants::s_ecUncompressedFormatLeadingByte.size());
187     q.appendVector(x);
188     q.appendVector(y);
189
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);
195         return nullptr;
196     }
197
198     return create(identifier, curve, CryptoKeyType::Private, platformKey.release(), extractable, usages);
199 }
200
201 static bool supportedAlgorithmIdentifier(CryptoAlgorithmIdentifier keyIdentifier, const Vector<uint8_t>& identifier)
202 {
203     auto* data = identifier.data();
204     auto size = identifier.size();
205
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))
210             return true;
211         return false;
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))
215             return true;
216         if (CryptoConstants::matches(data, size, CryptoConstants::s_ecDHIdentifier))
217             return true;
218         return false;
219     default:
220         ASSERT_NOT_REACHED();
221         break;
222     }
223
224     return false;
225 }
226
227 static std::optional<CryptoKeyEC::NamedCurve> curveForIdentifier(const Vector<uint8_t>& identifier)
228 {
229     auto* data = identifier.data();
230     auto size = identifier.size();
231
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.
238
239     return std::nullopt;
240 }
241
242 RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportSpki(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
243 {
244     // Decode the `SubjectPublicKeyInfo` structure using the provided key data.
245     PAL::TASN1::Structure spki;
246     if (!PAL::TASN1::decodeStructure(&spki, "WebCrypto.SubjectPublicKeyInfo", keyData))
247         return nullptr;
248
249     // Validate `algorithm.algorithm`.
250     {
251         auto algorithm = PAL::TASN1::elementData(spki, "algorithm.algorithm");
252         if (!algorithm)
253             return nullptr;
254
255         if (!supportedAlgorithmIdentifier(identifier, *algorithm))
256             return nullptr;
257     }
258
259     // Validate `algorithm.parameters` and therein embedded `ECParameters`.
260     {
261         auto parameters = PAL::TASN1::elementData(spki, "algorithm.parameters");
262         if (!parameters)
263             return nullptr;
264
265         // Decode the `ECParameters` structure using the `algorithm.parameters` data.
266         PAL::TASN1::Structure ecParameters;
267         if (!PAL::TASN1::decodeStructure(&ecParameters, "WebCrypto.ECParameters", *parameters))
268             return nullptr;
269
270         auto namedCurve = PAL::TASN1::elementData(ecParameters, "namedCurve");
271         if (!namedCurve)
272             return nullptr;
273
274         auto parameterCurve = curveForIdentifier(*namedCurve);
275         if (!parameterCurve || *parameterCurve != curve)
276             return nullptr;
277     }
278
279     // Retrieve the `subjectPublicKey` data and embed it into the `public-key` s-expression.
280     PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
281     {
282         auto subjectPublicKey = PAL::TASN1::elementData(spki, "subjectPublicKey");
283         if (!subjectPublicKey)
284             return nullptr;
285
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))
291             return nullptr;
292
293         // Convert X and Y coordinate data into MPIs.
294         unsigned coordinateSize = curveUncompressedFieldElementSize(curve);
295         PAL::GCrypt::Handle<gcry_mpi_t> xMPI, yMPI;
296         {
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);
300                 return nullptr;
301             }
302
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);
306                 return nullptr;
307             }
308         }
309
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));
313
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);
319             return nullptr;
320         }
321
322         // Bail if the constructed MPI point is not on the specified EC curve.
323         if (!gcry_mpi_ec_curve_point(point, context))
324             return nullptr;
325
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);
330             return nullptr;
331         }
332     }
333
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);
336 }
337
338 RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportPkcs8(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
339 {
340     // Decode the `PrivateKeyInfo` structure using the provided key data.
341     PAL::TASN1::Structure pkcs8;
342     if (!PAL::TASN1::decodeStructure(&pkcs8, "WebCrypto.PrivateKeyInfo", keyData))
343         return nullptr;
344
345     // Validate `version`.
346     {
347         auto version = PAL::TASN1::elementData(pkcs8, "version");
348         if (!version)
349             return nullptr;
350
351         if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version0))
352             return nullptr;
353     }
354
355     // Validate `privateKeyAlgorithm.algorithm`.
356     {
357         auto algorithm = PAL::TASN1::elementData(pkcs8, "privateKeyAlgorithm.algorithm");
358         if (!algorithm)
359             return nullptr;
360
361         if (!supportedAlgorithmIdentifier(identifier, *algorithm))
362             return nullptr;
363     }
364
365     // Validate `privateKeyAlgorithm.parameters` and therein embedded `ECParameters`.
366     {
367         auto parameters = PAL::TASN1::elementData(pkcs8, "privateKeyAlgorithm.parameters");
368         if (!parameters)
369             return nullptr;
370
371         PAL::TASN1::Structure ecParameters;
372         if (!PAL::TASN1::decodeStructure(&ecParameters, "WebCrypto.ECParameters", *parameters))
373             return nullptr;
374
375         auto namedCurve = PAL::TASN1::elementData(ecParameters, "namedCurve");
376         if (!namedCurve)
377             return nullptr;
378
379         auto parameterCurve = curveForIdentifier(*namedCurve);
380         if (!parameterCurve || *parameterCurve != curve)
381             return nullptr;
382     }
383
384     // Decode the `ECPrivateKey` structure using the `privateKey` data.
385     PAL::TASN1::Structure ecPrivateKey;
386     {
387         auto privateKey = PAL::TASN1::elementData(pkcs8, "privateKey");
388         if (!privateKey)
389             return nullptr;
390
391         if (!PAL::TASN1::decodeStructure(&ecPrivateKey, "WebCrypto.ECPrivateKey", *privateKey))
392             return nullptr;
393     }
394
395     // Validate `privateKey.version`.
396     {
397         auto version = PAL::TASN1::elementData(ecPrivateKey, "version");
398         if (!version)
399             return nullptr;
400
401         if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version1))
402             return nullptr;
403     }
404
405     // Validate `privateKey.parameters.namedCurve`, if any.
406     {
407         auto namedCurve = PAL::TASN1::elementData(ecPrivateKey, "parameters.namedCurve");
408         if (namedCurve) {
409             auto parameterCurve = curveForIdentifier(*namedCurve);
410             if (!parameterCurve || *parameterCurve != curve)
411                 return nullptr;
412         }
413     }
414
415     // Validate `privateKey.publicKey`, if any, and scan the data into an MPI.
416     PAL::GCrypt::Handle<gcry_mpi_t> publicKeyMPI;
417     {
418         auto publicKey = PAL::TASN1::elementData(ecPrivateKey, "publicKey");
419         if (publicKey) {
420             if (publicKey->size() != curveUncompressedPointSize(curve)
421                 || !CryptoConstants::matches(publicKey->data(), 1, CryptoConstants::s_ecUncompressedFormatLeadingByte))
422                 return nullptr;
423
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);
427                 return nullptr;
428             }
429         }
430     }
431
432     // Retrieve the `privateKey.privateKey` data and embed it into the `private-key` s-expression.
433     PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
434     {
435         auto privateKey = PAL::TASN1::elementData(ecPrivateKey, "privateKey");
436         if (!privateKey)
437             return nullptr;
438
439         // Validate the size of `privateKey`, making sure it fits the size of the specified EC curve.
440         if (privateKey->size() * 8 != curveSize(curve))
441             return nullptr;
442
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);
448             return nullptr;
449         }
450
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);
456             return nullptr;
457         }
458
459         // Set the 'q' value on the EC context if public key data was provided through the import.
460         if (publicKeyMPI) {
461             error = gcry_mpi_ec_set_mpi("q", publicKeyMPI, context);
462             if (error != GPG_ERR_NO_ERROR) {
463                 PAL::GCrypt::logError(error);
464                 return nullptr;
465             }
466         }
467
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));
472         if (!point)
473             return nullptr;
474
475         // Bail if the retrieved `q` MPI point is not on the specified EC curve.
476         if (!gcry_mpi_ec_curve_point(point, context))
477             return nullptr;
478     }
479
480     return create(identifier, curve, CryptoKeyType::Private, platformKey.release(), extractable, usages);
481 }
482
483 Vector<uint8_t> CryptoKeyEC::platformExportRaw() const
484 {
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);
489         return { };
490     }
491
492     PAL::GCrypt::Handle<gcry_mpi_t> qMPI(gcry_mpi_ec_get_mpi("q", context, 0));
493     if (!qMPI)
494         return { };
495
496     auto q = mpiData(qMPI);
497     if (!q || q->size() != curveUncompressedPointSize(m_curve))
498         return { };
499
500     return WTFMove(q.value());
501 }
502
503 void CryptoKeyEC::platformAddFieldElements(JsonWebKey& jwk) const
504 {
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);
509         return;
510     }
511
512     unsigned uncompressedFieldElementSize = curveUncompressedFieldElementSize(m_curve);
513
514     PAL::GCrypt::Handle<gcry_mpi_t> qMPI(gcry_mpi_ec_get_mpi("q", context, 0));
515     if (qMPI) {
516         auto q = mpiData(qMPI);
517         if (q && q->size() == curveUncompressedPointSize(m_curve)) {
518             Vector<uint8_t> a;
519             a.append(q->data() + 1, uncompressedFieldElementSize);
520             jwk.x = base64URLEncode(a);
521
522             Vector<uint8_t> b;
523             b.append(q->data() + 1 + uncompressedFieldElementSize, uncompressedFieldElementSize);
524             jwk.y = base64URLEncode(b);
525         }
526     }
527
528     if (type() == Type::Private) {
529         PAL::GCrypt::Handle<gcry_mpi_t> dMPI(gcry_mpi_ec_get_mpi("d", context, 0));
530         if (dMPI) {
531             auto d = mpiData(dMPI);
532             if (d && d->size() == uncompressedFieldElementSize)
533                 jwk.d = base64URLEncode(*d);
534         }
535     }
536 }
537
538 Vector<uint8_t> CryptoKeyEC::platformExportSpki() const
539 {
540     PAL::TASN1::Structure ecParameters;
541     {
542         // Create the `ECParameters` structure.
543         if (!PAL::TASN1::createStructure("WebCrypto.ECParameters", &ecParameters))
544             return { };
545
546         // Select the `namedCurve` object identifier as the target `ECParameters` choice.
547         if (!PAL::TASN1::writeElement(ecParameters, "", "namedCurve", 1))
548             return { };
549
550         // Write out the EC curve identifier under `namedCurve`.
551         if (!PAL::TASN1::writeElement(ecParameters, "namedCurve", curveIdentifier(m_curve), 1))
552             return { };
553     }
554
555     PAL::TASN1::Structure spki;
556     {
557         // Create the `SubjectPublicKeyInfo` structure.
558         if (!PAL::TASN1::createStructure("WebCrypto.SubjectPublicKeyInfo", &spki))
559             return { };
560
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))
565             return { };
566
567         // Write out the `ECParameters` data under `algorithm.parameters`.
568         {
569             auto data = PAL::TASN1::encodedData(ecParameters, "");
570             if (!data || !PAL::TASN1::writeElement(spki, "algorithm.parameters", data->data(), data->size()))
571                 return { };
572         }
573
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));
576         if (!qSexp)
577             return { };
578
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))
584             return { };
585
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))
589             return { };
590     }
591
592     // Retrieve the encoded `SubjectPublicKeyInfo` data and return it.
593     auto result = PAL::TASN1::encodedData(spki, "");
594     if (!result)
595         return { };
596
597     return WTFMove(result.value());
598 }
599
600 Vector<uint8_t> CryptoKeyEC::platformExportPkcs8() const
601 {
602     PAL::TASN1::Structure ecParameters;
603     {
604         // Create the `ECParameters` structure.
605         if (!PAL::TASN1::createStructure("WebCrypto.ECParameters", &ecParameters))
606             return { };
607
608         // Select the `namedCurve` object identifier as the target `ECParameters` choice.
609         if (!PAL::TASN1::writeElement(ecParameters, "", "namedCurve", 1))
610             return { };
611
612         // Write out the EC curve identifier under `namedCurve`.
613         if (!PAL::TASN1::writeElement(ecParameters, "namedCurve", curveIdentifier(m_curve), 1))
614             return { };
615     }
616
617     PAL::TASN1::Structure ecPrivateKey;
618     {
619         // Create the `ECPrivateKey` structure.
620         if (!PAL::TASN1::createStructure("WebCrypto.ECPrivateKey", &ecPrivateKey))
621             return { };
622
623         // Write out '1' under `version`.
624         if (!PAL::TASN1::writeElement(ecPrivateKey, "version", "1", 0))
625             return { };
626
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)
631             return { };
632
633         {
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));
636             if (!dMPI)
637                 return { };
638
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()))
642                 return { };
643         }
644
645         // Eliminate the optional `parameters` element.
646         if (!PAL::TASN1::writeElement(ecPrivateKey, "parameters", nullptr, 0))
647             return { };
648
649         {
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));
652             if (!qMPI)
653                 return { };
654
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))
659                 return { };
660         }
661     }
662
663     PAL::TASN1::Structure pkcs8;
664     {
665         // Create the `PrivateKeyInfo` structure.
666         if (!PAL::TASN1::createStructure("WebCrypto.PrivateKeyInfo", &pkcs8))
667             return { };
668
669         // Write out '0' under `version`.
670         if (!PAL::TASN1::writeElement(pkcs8, "version", "0", 0))
671             return { };
672
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))
677             return { };
678
679         // Write out the `ECParameters` data under `privateKeyAlgorithm.parameters`.
680         {
681             auto data = PAL::TASN1::encodedData(ecParameters, "");
682             if (!data || !PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.parameters", data->data(), data->size()))
683                 return { };
684         }
685
686         // Write out the `ECPrivateKey` data under `privateKey`.
687         {
688             auto data = PAL::TASN1::encodedData(ecPrivateKey, "");
689             if (!data || !PAL::TASN1::writeElement(pkcs8, "privateKey", data->data(), data->size()))
690                 return { };
691         }
692
693         // Eliminate the optional `attributes` element.
694         if (!PAL::TASN1::writeElement(pkcs8, "attributes", nullptr, 0))
695             return { };
696     }
697
698     // Retrieve the encoded `PrivateKeyInfo` data and return it.
699     auto result = PAL::TASN1::encodedData(pkcs8, "");
700     if (!result)
701         return { };
702
703     return WTFMove(result.value());
704 }
705
706 } // namespace WebCore
707
708 #endif // ENABLE(SUBTLE_CRYPTO)