1/*
2 * Copyright (C) 2017-2019 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(WEB_CRYPTO)
30
31#include "CryptoKeyPair.h"
32#include "GCryptUtilities.h"
33#include "JsonWebKey.h"
34#include <pal/crypto/gcrypt/Utilities.h>
35#include <pal/crypto/tasn1/Utilities.h>
36#include <wtf/text/Base64.h>
37
38namespace WebCore {
39
40static const char* curveName(CryptoKeyEC::NamedCurve curve)
41{
42 switch (curve) {
43 case CryptoKeyEC::NamedCurve::P256:
44 return "NIST P-256";
45 case CryptoKeyEC::NamedCurve::P384:
46 return "NIST P-384";
47 case CryptoKeyEC::NamedCurve::P521:
48 return "NIST P-521";
49 }
50
51 ASSERT_NOT_REACHED();
52 return nullptr;
53}
54
55static const uint8_t* curveIdentifier(CryptoKeyEC::NamedCurve curve)
56{
57 switch (curve) {
58 case CryptoKeyEC::NamedCurve::P256:
59 return CryptoConstants::s_secp256r1Identifier.data();
60 case CryptoKeyEC::NamedCurve::P384:
61 return CryptoConstants::s_secp384r1Identifier.data();
62 case CryptoKeyEC::NamedCurve::P521:
63 return CryptoConstants::s_secp521r1Identifier.data();
64 }
65
66 ASSERT_NOT_REACHED();
67 return nullptr;
68}
69
70static size_t curveSize(CryptoKeyEC::NamedCurve curve)
71{
72 switch (curve) {
73 case CryptoKeyEC::NamedCurve::P256:
74 return 256;
75 case CryptoKeyEC::NamedCurve::P384:
76 return 384;
77 case CryptoKeyEC::NamedCurve::P521:
78 return 521;
79 }
80
81 ASSERT_NOT_REACHED();
82 return 0;
83}
84
85static unsigned curveUncompressedFieldElementSize(CryptoKeyEC::NamedCurve curve)
86{
87 switch (curve) {
88 case CryptoKeyEC::NamedCurve::P256:
89 return 32;
90 case CryptoKeyEC::NamedCurve::P384:
91 return 48;
92 case CryptoKeyEC::NamedCurve::P521:
93 return 66;
94 }
95
96 ASSERT_NOT_REACHED();
97 return 0;
98}
99
100static unsigned curveUncompressedPointSize(CryptoKeyEC::NamedCurve curve)
101{
102 return 2 * curveUncompressedFieldElementSize(curve) + 1;
103}
104
105size_t CryptoKeyEC::keySizeInBits() const
106{
107 size_t size = curveSize(m_curve);
108 ASSERT(size == gcry_pk_get_nbits(m_platformKey.get()));
109 return size;
110}
111
112bool CryptoKeyEC::platformSupportedCurve(NamedCurve curve)
113{
114 return curve == NamedCurve::P256 || curve == NamedCurve::P384 || curve == NamedCurve::P521;
115}
116
117Optional<CryptoKeyPair> CryptoKeyEC::platformGeneratePair(CryptoAlgorithmIdentifier identifier, NamedCurve curve, bool extractable, CryptoKeyUsageBitmap usages)
118{
119 PAL::GCrypt::Handle<gcry_sexp_t> genkeySexp;
120 gcry_error_t error = gcry_sexp_build(&genkeySexp, nullptr, "(genkey(ecc(curve %s)))", curveName(curve));
121 if (error != GPG_ERR_NO_ERROR) {
122 PAL::GCrypt::logError(error);
123 return WTF::nullopt;
124 }
125
126 PAL::GCrypt::Handle<gcry_sexp_t> keyPairSexp;
127 error = gcry_pk_genkey(&keyPairSexp, genkeySexp);
128 if (error != GPG_ERR_NO_ERROR) {
129 PAL::GCrypt::logError(error);
130 return WTF::nullopt;
131 }
132
133 PAL::GCrypt::Handle<gcry_sexp_t> publicKeySexp(gcry_sexp_find_token(keyPairSexp, "public-key", 0));
134 PAL::GCrypt::Handle<gcry_sexp_t> privateKeySexp(gcry_sexp_find_token(keyPairSexp, "private-key", 0));
135 if (!publicKeySexp || !privateKeySexp)
136 return WTF::nullopt;
137
138 auto publicKey = CryptoKeyEC::create(identifier, curve, CryptoKeyType::Public, PlatformECKeyContainer(publicKeySexp.release()), true, usages);
139 auto privateKey = CryptoKeyEC::create(identifier, curve, CryptoKeyType::Private, PlatformECKeyContainer(privateKeySexp.release()), extractable, usages);
140 return CryptoKeyPair { WTFMove(publicKey), WTFMove(privateKey) };
141}
142
143RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportRaw(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
144{
145 if (keyData.size() != curveUncompressedPointSize(curve))
146 return nullptr;
147
148 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
149 gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(public-key(ecc(curve %s)(q %b)))",
150 curveName(curve), keyData.size(), keyData.data());
151 if (error != GPG_ERR_NO_ERROR) {
152 PAL::GCrypt::logError(error);
153 return nullptr;
154 }
155
156 return create(identifier, curve, CryptoKeyType::Public, PlatformECKeyContainer(platformKey.release()), extractable, usages);
157}
158
159RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportJWKPublic(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& x, Vector<uint8_t>&& y, bool extractable, CryptoKeyUsageBitmap usages)
160{
161 unsigned uncompressedFieldElementSize = curveUncompressedFieldElementSize(curve);
162 if (x.size() != uncompressedFieldElementSize || y.size() != uncompressedFieldElementSize)
163 return nullptr;
164
165 // Construct the Vector that represents the EC point in uncompressed format.
166 Vector<uint8_t> q;
167 q.reserveInitialCapacity(curveUncompressedPointSize(curve));
168 q.append(CryptoConstants::s_ecUncompressedFormatLeadingByte.data(), CryptoConstants::s_ecUncompressedFormatLeadingByte.size());
169 q.appendVector(x);
170 q.appendVector(y);
171
172 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
173 gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(public-key(ecc(curve %s)(q %b)))",
174 curveName(curve), q.size(), q.data());
175 if (error != GPG_ERR_NO_ERROR) {
176 PAL::GCrypt::logError(error);
177 return nullptr;
178 }
179
180 return create(identifier, curve, CryptoKeyType::Public, PlatformECKeyContainer(platformKey.release()), extractable, usages);
181}
182
183RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportJWKPrivate(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& x, Vector<uint8_t>&& y, Vector<uint8_t>&& d, bool extractable, CryptoKeyUsageBitmap usages)
184{
185 unsigned uncompressedFieldElementSize = curveUncompressedFieldElementSize(curve);
186 if (x.size() != uncompressedFieldElementSize || y.size() != uncompressedFieldElementSize || d.size() != uncompressedFieldElementSize)
187 return nullptr;
188
189 // Construct the Vector that represents the EC point in uncompressed format.
190 Vector<uint8_t> q;
191 q.reserveInitialCapacity(curveUncompressedPointSize(curve));
192 q.append(CryptoConstants::s_ecUncompressedFormatLeadingByte.data(), CryptoConstants::s_ecUncompressedFormatLeadingByte.size());
193 q.appendVector(x);
194 q.appendVector(y);
195
196 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
197 gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(private-key(ecc(curve %s)(q %b)(d %b)))",
198 curveName(curve), q.size(), q.data(), d.size(), d.data());
199 if (error != GPG_ERR_NO_ERROR) {
200 PAL::GCrypt::logError(error);
201 return nullptr;
202 }
203
204 return create(identifier, curve, CryptoKeyType::Private, PlatformECKeyContainer(platformKey.release()), extractable, usages);
205}
206
207static bool supportedAlgorithmIdentifier(CryptoAlgorithmIdentifier keyIdentifier, const Vector<uint8_t>& identifier)
208{
209 auto* data = identifier.data();
210 auto size = identifier.size();
211
212 switch (keyIdentifier) {
213 case CryptoAlgorithmIdentifier::ECDSA:
214 // ECDSA only supports id-ecPublicKey algorithms for imported keys.
215 if (CryptoConstants::matches(data, size, CryptoConstants::s_ecPublicKeyIdentifier))
216 return true;
217 return false;
218 case CryptoAlgorithmIdentifier::ECDH:
219 // ECDH supports both id-ecPublicKey and ic-ecDH algorithms for imported keys.
220 if (CryptoConstants::matches(data, size, CryptoConstants::s_ecPublicKeyIdentifier))
221 return true;
222 if (CryptoConstants::matches(data, size, CryptoConstants::s_ecDHIdentifier))
223 return true;
224 return false;
225 default:
226 ASSERT_NOT_REACHED();
227 break;
228 }
229
230 return false;
231}
232
233static Optional<CryptoKeyEC::NamedCurve> curveForIdentifier(const Vector<uint8_t>& identifier)
234{
235 auto* data = identifier.data();
236 auto size = identifier.size();
237
238 if (CryptoConstants::matches(data, size, CryptoConstants::s_secp256r1Identifier))
239 return CryptoKeyEC::NamedCurve::P256;
240 if (CryptoConstants::matches(data, size, CryptoConstants::s_secp384r1Identifier))
241 return CryptoKeyEC::NamedCurve::P384;
242 if (CryptoConstants::matches(data, size, CryptoConstants::s_secp521r1Identifier))
243 return CryptoKeyEC::NamedCurve::P521;
244
245 return WTF::nullopt;
246}
247
248RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportSpki(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
249{
250 // Decode the `SubjectPublicKeyInfo` structure using the provided key data.
251 PAL::TASN1::Structure spki;
252 if (!PAL::TASN1::decodeStructure(&spki, "WebCrypto.SubjectPublicKeyInfo", keyData))
253 return nullptr;
254
255 // Validate `algorithm.algorithm`.
256 {
257 auto algorithm = PAL::TASN1::elementData(spki, "algorithm.algorithm");
258 if (!algorithm)
259 return nullptr;
260
261 if (!supportedAlgorithmIdentifier(identifier, *algorithm))
262 return nullptr;
263 }
264
265 // Validate `algorithm.parameters` and therein embedded `ECParameters`.
266 {
267 auto parameters = PAL::TASN1::elementData(spki, "algorithm.parameters");
268 if (!parameters)
269 return nullptr;
270
271 // Decode the `ECParameters` structure using the `algorithm.parameters` data.
272 PAL::TASN1::Structure ecParameters;
273 if (!PAL::TASN1::decodeStructure(&ecParameters, "WebCrypto.ECParameters", *parameters))
274 return nullptr;
275
276 auto namedCurve = PAL::TASN1::elementData(ecParameters, "namedCurve");
277 if (!namedCurve)
278 return nullptr;
279
280 auto parameterCurve = curveForIdentifier(*namedCurve);
281 if (!parameterCurve || *parameterCurve != curve)
282 return nullptr;
283 }
284
285 // Retrieve the `subjectPublicKey` data and embed it into the `public-key` s-expression.
286 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
287 {
288 auto subjectPublicKey = PAL::TASN1::elementData(spki, "subjectPublicKey");
289 if (!subjectPublicKey)
290 return nullptr;
291
292 // Bail if the `subjectPublicKey` data size doesn't match the size of an uncompressed point
293 // for this curve, or if the first byte in the `subjectPublicKey` data isn't 0x04, as required
294 // for an uncompressed EC point encoded in an octet string.
295 if (subjectPublicKey->size() != curveUncompressedPointSize(curve)
296 || !CryptoConstants::matches(subjectPublicKey->data(), 1, CryptoConstants::s_ecUncompressedFormatLeadingByte))
297 return nullptr;
298
299 // Convert X and Y coordinate data into MPIs.
300 unsigned coordinateSize = curveUncompressedFieldElementSize(curve);
301 PAL::GCrypt::Handle<gcry_mpi_t> xMPI, yMPI;
302 {
303 gcry_error_t error = gcry_mpi_scan(&xMPI, GCRYMPI_FMT_USG, &subjectPublicKey->at(1), coordinateSize, nullptr);
304 if (error != GPG_ERR_NO_ERROR) {
305 PAL::GCrypt::logError(error);
306 return nullptr;
307 }
308
309 error = gcry_mpi_scan(&yMPI, GCRYMPI_FMT_USG, &subjectPublicKey->at(1 + coordinateSize), coordinateSize, nullptr);
310 if (error != GPG_ERR_NO_ERROR) {
311 PAL::GCrypt::logError(error);
312 return nullptr;
313 }
314 }
315
316 // Construct an MPI point from the X and Y coordinates and using 1 as the Z coordinate.
317 // This always allocates the gcry_mpi_point_t object.
318 PAL::GCrypt::Handle<gcry_mpi_point_t> point(gcry_mpi_point_set(nullptr, xMPI, yMPI, GCRYMPI_CONST_ONE));
319
320 // Create an EC context for the specified curve.
321 PAL::GCrypt::Handle<gcry_ctx_t> context;
322 gcry_error_t error = gcry_mpi_ec_new(&context, nullptr, curveName(curve));
323 if (error != GPG_ERR_NO_ERROR) {
324 PAL::GCrypt::logError(error);
325 return nullptr;
326 }
327
328 // Bail if the constructed MPI point is not on the specified EC curve.
329 if (!gcry_mpi_ec_curve_point(point, context))
330 return nullptr;
331
332 error = gcry_sexp_build(&platformKey, nullptr, "(public-key(ecc(curve %s)(q %b)))",
333 curveName(curve), subjectPublicKey->size(), subjectPublicKey->data());
334 if (error != GPG_ERR_NO_ERROR) {
335 PAL::GCrypt::logError(error);
336 return nullptr;
337 }
338 }
339
340 // Finally create a new CryptoKeyEC object, transferring to it ownership of the `public-key` s-expression.
341 return create(identifier, curve, CryptoKeyType::Public, PlatformECKeyContainer(platformKey.release()), extractable, usages);
342}
343
344RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportPkcs8(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
345{
346 // Decode the `PrivateKeyInfo` structure using the provided key data.
347 PAL::TASN1::Structure pkcs8;
348 if (!PAL::TASN1::decodeStructure(&pkcs8, "WebCrypto.PrivateKeyInfo", keyData))
349 return nullptr;
350
351 // Validate `version`.
352 {
353 auto version = PAL::TASN1::elementData(pkcs8, "version");
354 if (!version)
355 return nullptr;
356
357 if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version0))
358 return nullptr;
359 }
360
361 // Validate `privateKeyAlgorithm.algorithm`.
362 {
363 auto algorithm = PAL::TASN1::elementData(pkcs8, "privateKeyAlgorithm.algorithm");
364 if (!algorithm)
365 return nullptr;
366
367 if (!supportedAlgorithmIdentifier(identifier, *algorithm))
368 return nullptr;
369 }
370
371 // Validate `privateKeyAlgorithm.parameters` and therein embedded `ECParameters`.
372 {
373 auto parameters = PAL::TASN1::elementData(pkcs8, "privateKeyAlgorithm.parameters");
374 if (!parameters)
375 return nullptr;
376
377 PAL::TASN1::Structure ecParameters;
378 if (!PAL::TASN1::decodeStructure(&ecParameters, "WebCrypto.ECParameters", *parameters))
379 return nullptr;
380
381 auto namedCurve = PAL::TASN1::elementData(ecParameters, "namedCurve");
382 if (!namedCurve)
383 return nullptr;
384
385 auto parameterCurve = curveForIdentifier(*namedCurve);
386 if (!parameterCurve || *parameterCurve != curve)
387 return nullptr;
388 }
389
390 // Decode the `ECPrivateKey` structure using the `privateKey` data.
391 PAL::TASN1::Structure ecPrivateKey;
392 {
393 auto privateKey = PAL::TASN1::elementData(pkcs8, "privateKey");
394 if (!privateKey)
395 return nullptr;
396
397 if (!PAL::TASN1::decodeStructure(&ecPrivateKey, "WebCrypto.ECPrivateKey", *privateKey))
398 return nullptr;
399 }
400
401 // Validate `privateKey.version`.
402 {
403 auto version = PAL::TASN1::elementData(ecPrivateKey, "version");
404 if (!version)
405 return nullptr;
406
407 if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version1))
408 return nullptr;
409 }
410
411 // Validate `privateKey.parameters.namedCurve`, if any.
412 {
413 auto namedCurve = PAL::TASN1::elementData(ecPrivateKey, "parameters.namedCurve");
414 if (namedCurve) {
415 auto parameterCurve = curveForIdentifier(*namedCurve);
416 if (!parameterCurve || *parameterCurve != curve)
417 return nullptr;
418 }
419 }
420
421 // Validate `privateKey.publicKey`, if any, and scan the data into an MPI.
422 PAL::GCrypt::Handle<gcry_mpi_t> publicKeyMPI;
423 {
424 auto publicKey = PAL::TASN1::elementData(ecPrivateKey, "publicKey");
425 if (publicKey) {
426 if (publicKey->size() != curveUncompressedPointSize(curve)
427 || !CryptoConstants::matches(publicKey->data(), 1, CryptoConstants::s_ecUncompressedFormatLeadingByte))
428 return nullptr;
429
430 gcry_error_t error = gcry_mpi_scan(&publicKeyMPI, GCRYMPI_FMT_USG, publicKey->data(), publicKey->size(), nullptr);
431 if (error != GPG_ERR_NO_ERROR) {
432 PAL::GCrypt::logError(error);
433 return nullptr;
434 }
435 }
436 }
437
438 // Retrieve the `privateKey.privateKey` data and embed it into the `private-key` s-expression.
439 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
440 {
441 auto privateKey = PAL::TASN1::elementData(ecPrivateKey, "privateKey");
442 if (!privateKey)
443 return nullptr;
444
445 // Validate the size of `privateKey`, making sure it fits the byte-size of the specified EC curve.
446 if (privateKey->size() != (curveSize(curve) + 7) / 8)
447 return nullptr;
448
449 // Construct the `private-key` expression that will also be used for the EC context.
450 gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(private-key(ecc(curve %s)(d %b)))",
451 curveName(curve), privateKey->size(), privateKey->data());
452 if (error != GPG_ERR_NO_ERROR) {
453 PAL::GCrypt::logError(error);
454 return nullptr;
455 }
456
457 // Create an EC context for the specified curve.
458 PAL::GCrypt::Handle<gcry_ctx_t> context;
459 error = gcry_mpi_ec_new(&context, platformKey, nullptr);
460 if (error != GPG_ERR_NO_ERROR) {
461 PAL::GCrypt::logError(error);
462 return nullptr;
463 }
464
465 // Set the 'q' value on the EC context if public key data was provided through the import.
466 if (publicKeyMPI) {
467 error = gcry_mpi_ec_set_mpi("q", publicKeyMPI, context);
468 if (error != GPG_ERR_NO_ERROR) {
469 PAL::GCrypt::logError(error);
470 return nullptr;
471 }
472 }
473
474 // Retrieve the `q` point. If the public key was provided through the PKCS#8 import, that
475 // key value will be retrieved as an gcry_mpi_point_t. Otherwise, the `q` point value will
476 // be computed on-the-fly by libgcrypt for the specified elliptic curve.
477 PAL::GCrypt::Handle<gcry_mpi_point_t> point(gcry_mpi_ec_get_point("q", context, 1));
478 if (!point)
479 return nullptr;
480
481 // Bail if the retrieved `q` MPI point is not on the specified EC curve.
482 if (!gcry_mpi_ec_curve_point(point, context))
483 return nullptr;
484 }
485
486 return create(identifier, curve, CryptoKeyType::Private, PlatformECKeyContainer(platformKey.release()), extractable, usages);
487}
488
489Vector<uint8_t> CryptoKeyEC::platformExportRaw() const
490{
491 PAL::GCrypt::Handle<gcry_ctx_t> context;
492 gcry_error_t error = gcry_mpi_ec_new(&context, m_platformKey.get(), nullptr);
493 if (error != GPG_ERR_NO_ERROR) {
494 PAL::GCrypt::logError(error);
495 return { };
496 }
497
498 PAL::GCrypt::Handle<gcry_mpi_t> qMPI(gcry_mpi_ec_get_mpi("q", context, 0));
499 if (!qMPI)
500 return { };
501
502 auto q = mpiData(qMPI);
503 if (!q || q->size() != curveUncompressedPointSize(m_curve))
504 return { };
505
506 return WTFMove(q.value());
507}
508
509bool CryptoKeyEC::platformAddFieldElements(JsonWebKey& jwk) const
510{
511 PAL::GCrypt::Handle<gcry_ctx_t> context;
512 gcry_error_t error = gcry_mpi_ec_new(&context, m_platformKey.get(), nullptr);
513 if (error != GPG_ERR_NO_ERROR) {
514 PAL::GCrypt::logError(error);
515 return false;
516 }
517
518 unsigned uncompressedFieldElementSize = curveUncompressedFieldElementSize(m_curve);
519
520 PAL::GCrypt::Handle<gcry_mpi_t> qMPI(gcry_mpi_ec_get_mpi("q", context, 0));
521 if (qMPI) {
522 auto q = mpiData(qMPI);
523 if (q && q->size() == curveUncompressedPointSize(m_curve)) {
524 Vector<uint8_t> a;
525 a.append(q->data() + 1, uncompressedFieldElementSize);
526 jwk.x = base64URLEncode(a);
527
528 Vector<uint8_t> b;
529 b.append(q->data() + 1 + uncompressedFieldElementSize, uncompressedFieldElementSize);
530 jwk.y = base64URLEncode(b);
531 }
532 }
533
534 if (type() == Type::Private) {
535 PAL::GCrypt::Handle<gcry_mpi_t> dMPI(gcry_mpi_ec_get_mpi("d", context, 0));
536 if (dMPI) {
537 auto d = mpiData(dMPI);
538 if (d && d->size() <= uncompressedFieldElementSize) {
539 // Zero-pad the private key data up to the field element size, if necessary.
540 if (d->size() < uncompressedFieldElementSize) {
541 Vector<uint8_t> paddedData(uncompressedFieldElementSize - d->size(), 0);
542 paddedData.appendVector(*d);
543 *d = WTFMove(paddedData);
544 }
545
546 jwk.d = base64URLEncode(*d);
547 }
548 }
549 }
550
551 return true;
552}
553
554Vector<uint8_t> CryptoKeyEC::platformExportSpki() const
555{
556 PAL::TASN1::Structure ecParameters;
557 {
558 // Create the `ECParameters` structure.
559 if (!PAL::TASN1::createStructure("WebCrypto.ECParameters", &ecParameters))
560 return { };
561
562 // Select the `namedCurve` object identifier as the target `ECParameters` choice.
563 if (!PAL::TASN1::writeElement(ecParameters, "", "namedCurve", 1))
564 return { };
565
566 // Write out the EC curve identifier under `namedCurve`.
567 if (!PAL::TASN1::writeElement(ecParameters, "namedCurve", curveIdentifier(m_curve), 1))
568 return { };
569 }
570
571 PAL::TASN1::Structure spki;
572 {
573 // Create the `SubjectPublicKeyInfo` structure.
574 if (!PAL::TASN1::createStructure("WebCrypto.SubjectPublicKeyInfo", &spki))
575 return { };
576
577 // Write out the id-ecPublicKey identifier under `algorithm.algorithm`.
578 // FIXME: Per specification this should write out id-ecDH when the ECDH algorithm
579 // is specified for this CryptoKeyEC object, but not even the W3C tests expect that.
580 if (!PAL::TASN1::writeElement(spki, "algorithm.algorithm", CryptoConstants::s_ecPublicKeyIdentifier.data(), 1))
581 return { };
582
583 // Write out the `ECParameters` data under `algorithm.parameters`.
584 {
585 auto data = PAL::TASN1::encodedData(ecParameters, "");
586 if (!data || !PAL::TASN1::writeElement(spki, "algorithm.parameters", data->data(), data->size()))
587 return { };
588 }
589
590 // Retrieve the `q` s-expression, which should contain the public key data.
591 PAL::GCrypt::Handle<gcry_sexp_t> qSexp(gcry_sexp_find_token(m_platformKey.get(), "q", 0));
592 if (!qSexp)
593 return { };
594
595 // Retrieve the `q` data, which should be in the uncompressed point format.
596 // Validate the data size and the first byte (which should be 0x04).
597 auto qData = mpiData(qSexp);
598 if (!qData || qData->size() != curveUncompressedPointSize(m_curve)
599 || !CryptoConstants::matches(qData->data(), 1, CryptoConstants::s_ecUncompressedFormatLeadingByte))
600 return { };
601
602 // Write out the public key data under `subjectPublicKey`. Because this is a
603 // bit string parameter, the data size has to be multiplied by 8.
604 if (!PAL::TASN1::writeElement(spki, "subjectPublicKey", qData->data(), qData->size() * 8))
605 return { };
606 }
607
608 // Retrieve the encoded `SubjectPublicKeyInfo` data and return it.
609 auto result = PAL::TASN1::encodedData(spki, "");
610 if (!result)
611 return { };
612
613 return WTFMove(result.value());
614}
615
616Vector<uint8_t> CryptoKeyEC::platformExportPkcs8() const
617{
618 PAL::TASN1::Structure ecParameters;
619 {
620 // Create the `ECParameters` structure.
621 if (!PAL::TASN1::createStructure("WebCrypto.ECParameters", &ecParameters))
622 return { };
623
624 // Select the `namedCurve` object identifier as the target `ECParameters` choice.
625 if (!PAL::TASN1::writeElement(ecParameters, "", "namedCurve", 1))
626 return { };
627
628 // Write out the EC curve identifier under `namedCurve`.
629 if (!PAL::TASN1::writeElement(ecParameters, "namedCurve", curveIdentifier(m_curve), 1))
630 return { };
631 }
632
633 PAL::TASN1::Structure ecPrivateKey;
634 {
635 // Create the `ECPrivateKey` structure.
636 if (!PAL::TASN1::createStructure("WebCrypto.ECPrivateKey", &ecPrivateKey))
637 return { };
638
639 // Write out '1' under `version`.
640 if (!PAL::TASN1::writeElement(ecPrivateKey, "version", "1", 0))
641 return { };
642
643 // Construct the EC context that we'll use to retrieve private and public key data.
644 PAL::GCrypt::Handle<gcry_ctx_t> context;
645 gcry_error_t error = gcry_mpi_ec_new(&context, m_platformKey.get(), nullptr);
646 if (error != GPG_ERR_NO_ERROR)
647 return { };
648
649 {
650 // Retrieve the `d` MPI that holds the private key data.
651 PAL::GCrypt::Handle<gcry_mpi_t> dMPI(gcry_mpi_ec_get_mpi("d", context, 0));
652 if (!dMPI)
653 return { };
654
655 unsigned uncompressedFieldElementSize = curveUncompressedFieldElementSize(m_curve);
656
657 // Retrieve the `d` MPI data.
658 auto data = mpiData(dMPI);
659 if (!data || data->size() > uncompressedFieldElementSize)
660 return { };
661
662 // Zero-pad the private key data up to the field element size, if necessary.
663 if (data->size() < uncompressedFieldElementSize) {
664 Vector<uint8_t> paddedData(uncompressedFieldElementSize - data->size(), 0);
665 paddedData.appendVector(*data);
666 *data = WTFMove(paddedData);
667 }
668
669 // Write out the data under `privateKey`.
670 if (!PAL::TASN1::writeElement(ecPrivateKey, "privateKey", data->data(), data->size()))
671 return { };
672 }
673
674 // Eliminate the optional `parameters` element.
675 if (!PAL::TASN1::writeElement(ecPrivateKey, "parameters", nullptr, 0))
676 return { };
677
678 {
679 // Retrieve the `q` MPI that holds the public key data.
680 PAL::GCrypt::Handle<gcry_mpi_t> qMPI(gcry_mpi_ec_get_mpi("q", context, 0));
681 if (!qMPI)
682 return { };
683
684 // Retrieve the MPI data and write it out under `publicKey`. Because this is a
685 // bit string parameter, the data size has to be multiplied by 8.
686 auto data = mpiData(qMPI);
687 if (!data || !PAL::TASN1::writeElement(ecPrivateKey, "publicKey", data->data(), data->size() * 8))
688 return { };
689 }
690 }
691
692 PAL::TASN1::Structure pkcs8;
693 {
694 // Create the `PrivateKeyInfo` structure.
695 if (!PAL::TASN1::createStructure("WebCrypto.PrivateKeyInfo", &pkcs8))
696 return { };
697
698 // Write out '0' under `version`.
699 if (!PAL::TASN1::writeElement(pkcs8, "version", "0", 0))
700 return { };
701
702 // Write out the id-ecPublicKey identifier under `privateKeyAlgorithm.algorithm`.
703 // FIXME: Per specification this should write out id-ecDH when the ECDH algorithm
704 // is specified for this CryptoKeyEC object, but not even the W3C tests expect that.
705 if (!PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.algorithm", CryptoConstants::s_ecPublicKeyIdentifier.data(), 1))
706 return { };
707
708 // Write out the `ECParameters` data under `privateKeyAlgorithm.parameters`.
709 {
710 auto data = PAL::TASN1::encodedData(ecParameters, "");
711 if (!data || !PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.parameters", data->data(), data->size()))
712 return { };
713 }
714
715 // Write out the `ECPrivateKey` data under `privateKey`.
716 {
717 auto data = PAL::TASN1::encodedData(ecPrivateKey, "");
718 if (!data || !PAL::TASN1::writeElement(pkcs8, "privateKey", data->data(), data->size()))
719 return { };
720 }
721
722 // Eliminate the optional `attributes` element.
723 if (!PAL::TASN1::writeElement(pkcs8, "attributes", nullptr, 0))
724 return { };
725 }
726
727 // Retrieve the encoded `PrivateKeyInfo` data and return it.
728 auto result = PAL::TASN1::encodedData(pkcs8, "");
729 if (!result)
730 return { };
731
732 return WTFMove(result.value());
733}
734
735} // namespace WebCore
736
737#endif // ENABLE(WEB_CRYPTO)
738