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 | |
38 | namespace WebCore { |
39 | |
40 | static 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 | |
55 | static 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 | |
70 | static 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 | |
85 | static 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 | |
100 | static unsigned curveUncompressedPointSize(CryptoKeyEC::NamedCurve curve) |
101 | { |
102 | return 2 * curveUncompressedFieldElementSize(curve) + 1; |
103 | } |
104 | |
105 | size_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 | |
112 | bool CryptoKeyEC::platformSupportedCurve(NamedCurve curve) |
113 | { |
114 | return curve == NamedCurve::P256 || curve == NamedCurve::P384 || curve == NamedCurve::P521; |
115 | } |
116 | |
117 | Optional<CryptoKeyPair> CryptoKeyEC::platformGeneratePair(CryptoAlgorithmIdentifier identifier, NamedCurve curve, bool , 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 | |
143 | RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportRaw(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& keyData, bool , 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 | |
159 | RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportJWKPublic(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& x, Vector<uint8_t>&& y, bool , 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 | |
183 | RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportJWKPrivate(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& x, Vector<uint8_t>&& y, Vector<uint8_t>&& d, bool , 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 | |
207 | static 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 | |
233 | static 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 | |
248 | RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportSpki(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& keyData, bool , 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 | |
344 | RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportPkcs8(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& keyData, bool , 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 | |
489 | Vector<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 | |
509 | bool 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 | |
554 | Vector<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 | |
616 | Vector<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 | |