1/*
2 * Copyright (C) 2014 Igalia S.L. 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 "CryptoKeyRSA.h"
28
29#if ENABLE(WEB_CRYPTO)
30
31#include "CryptoAlgorithmRegistry.h"
32#include "CryptoKeyPair.h"
33#include "CryptoKeyRSAComponents.h"
34#include "GCryptUtilities.h"
35#include "ScriptExecutionContext.h"
36#include <JavaScriptCore/GenericTypedArrayViewInlines.h>
37#include <JavaScriptCore/HeapInlines.h>
38#include <JavaScriptCore/JSGenericTypedArrayViewInlines.h>
39#include <pal/crypto/gcrypt/Utilities.h>
40#include <pal/crypto/tasn1/Utilities.h>
41
42namespace WebCore {
43
44static size_t getRSAModulusLength(gcry_sexp_t keySexp)
45{
46 // Retrieve the s-expression token for the public modulus N of the given RSA key.
47 PAL::GCrypt::Handle<gcry_sexp_t> nSexp(gcry_sexp_find_token(keySexp, "n", 0));
48 if (!nSexp)
49 return 0;
50
51 // Retrieve the MPI length for the corresponding s-expression token, in bits.
52 auto length = mpiLength(nSexp);
53 if (!length)
54 return 0;
55
56 return *length * 8;
57}
58
59static Vector<uint8_t> getRSAKeyParameter(gcry_sexp_t keySexp, const char* name)
60{
61 // Retrieve the s-expression token for the specified parameter of the given RSA key.
62 PAL::GCrypt::Handle<gcry_sexp_t> paramSexp(gcry_sexp_find_token(keySexp, name, 0));
63 if (!paramSexp)
64 return { };
65
66 // Retrieve the MPI data for the corresponding s-expression token.
67 auto data = mpiData(paramSexp);
68 if (!data)
69 return { };
70
71 return WTFMove(data.value());
72}
73
74RefPtr<CryptoKeyRSA> CryptoKeyRSA::create(CryptoAlgorithmIdentifier identifier, CryptoAlgorithmIdentifier hash, bool hasHash, const CryptoKeyRSAComponents& keyData, bool extractable, CryptoKeyUsageBitmap usages)
75{
76 // When creating a private key, we require the p and q prime information.
77 if (keyData.type() == CryptoKeyRSAComponents::Type::Private && !keyData.hasAdditionalPrivateKeyParameters())
78 return nullptr;
79
80 // But we don't currently support creating keys with any additional prime information.
81 if (!keyData.otherPrimeInfos().isEmpty())
82 return nullptr;
83
84 // Validate the key data.
85 {
86 bool valid = true;
87
88 // For both public and private keys, we need the public modulus and exponent.
89 valid &= !keyData.modulus().isEmpty() && !keyData.exponent().isEmpty();
90
91 // For private keys, we require the private exponent, as well as p and q prime information.
92 if (keyData.type() == CryptoKeyRSAComponents::Type::Private)
93 valid &= !keyData.privateExponent().isEmpty() && !keyData.firstPrimeInfo().primeFactor.isEmpty() && !keyData.secondPrimeInfo().primeFactor.isEmpty();
94
95 if (!valid)
96 return nullptr;
97 }
98
99 CryptoKeyType keyType;
100 switch (keyData.type()) {
101 case CryptoKeyRSAComponents::Type::Public:
102 keyType = CryptoKeyType::Public;
103 break;
104 case CryptoKeyRSAComponents::Type::Private:
105 keyType = CryptoKeyType::Private;
106 break;
107 }
108
109 // Construct the key s-expression, using the data that's available.
110 PAL::GCrypt::Handle<gcry_sexp_t> keySexp;
111 {
112 gcry_error_t error = GPG_ERR_NO_ERROR;
113
114 switch (keyType) {
115 case CryptoKeyType::Public:
116 error = gcry_sexp_build(&keySexp, nullptr, "(public-key(rsa(n %b)(e %b)))",
117 keyData.modulus().size(), keyData.modulus().data(),
118 keyData.exponent().size(), keyData.exponent().data());
119 break;
120 case CryptoKeyType::Private:
121 if (keyData.hasAdditionalPrivateKeyParameters()) {
122 error = gcry_sexp_build(&keySexp, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)))",
123 keyData.modulus().size(), keyData.modulus().data(),
124 keyData.exponent().size(), keyData.exponent().data(),
125 keyData.privateExponent().size(), keyData.privateExponent().data(),
126 keyData.secondPrimeInfo().primeFactor.size(), keyData.secondPrimeInfo().primeFactor.data(),
127 keyData.firstPrimeInfo().primeFactor.size(), keyData.firstPrimeInfo().primeFactor.data());
128 break;
129 }
130
131 error = gcry_sexp_build(&keySexp, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)))",
132 keyData.modulus().size(), keyData.modulus().data(),
133 keyData.exponent().size(), keyData.exponent().data(),
134 keyData.privateExponent().size(), keyData.privateExponent().data());
135 break;
136 case CryptoKeyType::Secret:
137 ASSERT_NOT_REACHED();
138 return nullptr;
139 }
140
141 if (error != GPG_ERR_NO_ERROR) {
142 PAL::GCrypt::logError(error);
143 return nullptr;
144 }
145 }
146
147 return adoptRef(new CryptoKeyRSA(identifier, hash, hasHash, keyType, PlatformRSAKeyContainer(keySexp.release()), extractable, usages));
148}
149
150CryptoKeyRSA::CryptoKeyRSA(CryptoAlgorithmIdentifier identifier, CryptoAlgorithmIdentifier hash, bool hasHash, CryptoKeyType type, PlatformRSAKeyContainer&& platformKey, bool extractable, CryptoKeyUsageBitmap usage)
151 : CryptoKey(identifier, type, extractable, usage)
152 , m_platformKey(WTFMove(platformKey))
153 , m_restrictedToSpecificHash(hasHash)
154 , m_hash(hash)
155{
156}
157
158bool CryptoKeyRSA::isRestrictedToHash(CryptoAlgorithmIdentifier& identifier) const
159{
160 if (!m_restrictedToSpecificHash)
161 return false;
162
163 identifier = m_hash;
164 return true;
165}
166
167size_t CryptoKeyRSA::keySizeInBits() const
168{
169 return getRSAModulusLength(m_platformKey.get());
170}
171
172// Convert the exponent vector to a 32-bit value, if possible.
173static Optional<uint32_t> exponentVectorToUInt32(const Vector<uint8_t>& exponent)
174{
175 if (exponent.size() > 4) {
176 if (std::any_of(exponent.begin(), exponent.end() - 4, [](uint8_t element) { return !!element; }))
177 return WTF::nullopt;
178 }
179
180 uint32_t result = 0;
181 for (size_t size = exponent.size(), i = std::min<size_t>(4, size); i > 0; --i) {
182 result <<= 8;
183 result += exponent[size - i];
184 }
185
186 return result;
187}
188
189void CryptoKeyRSA::generatePair(CryptoAlgorithmIdentifier algorithm, CryptoAlgorithmIdentifier hash, bool hasHash, unsigned modulusLength, const Vector<uint8_t>& publicExponent, bool extractable, CryptoKeyUsageBitmap usage, KeyPairCallback&& callback, VoidCallback&& failureCallback, ScriptExecutionContext* context)
190{
191 // libgcrypt doesn't report an error if the exponent is smaller than three or even.
192 auto e = exponentVectorToUInt32(publicExponent);
193 if (!e || *e < 3 || !(*e & 0x1)) {
194 failureCallback();
195 return;
196 }
197
198 // libgcrypt doesn't support generating primes of less than 16 bits.
199 if (modulusLength < 16) {
200 failureCallback();
201 return;
202 }
203
204 PAL::GCrypt::Handle<gcry_sexp_t> genkeySexp;
205 gcry_error_t error = gcry_sexp_build(&genkeySexp, nullptr, "(genkey(rsa(nbits %d)(rsa-use-e %d)))", modulusLength, *e);
206 if (error != GPG_ERR_NO_ERROR) {
207 PAL::GCrypt::logError(error);
208 failureCallback();
209 return;
210 }
211
212 PAL::GCrypt::Handle<gcry_sexp_t> keyPairSexp;
213 error = gcry_pk_genkey(&keyPairSexp, genkeySexp);
214 if (error != GPG_ERR_NO_ERROR) {
215 PAL::GCrypt::logError(error);
216 failureCallback();
217 return;
218 }
219
220 PAL::GCrypt::Handle<gcry_sexp_t> publicKeySexp(gcry_sexp_find_token(keyPairSexp, "public-key", 0));
221 PAL::GCrypt::Handle<gcry_sexp_t> privateKeySexp(gcry_sexp_find_token(keyPairSexp, "private-key", 0));
222 if (!publicKeySexp || !privateKeySexp) {
223 failureCallback();
224 return;
225 }
226
227 context->postTask(
228 [algorithm, hash, hasHash, extractable, usage, publicKeySexp = PlatformRSAKeyContainer(publicKeySexp.release()), privateKeySexp = PlatformRSAKeyContainer(privateKeySexp.release()), callback = WTFMove(callback)](auto&) mutable {
229 auto publicKey = CryptoKeyRSA::create(algorithm, hash, hasHash, CryptoKeyType::Public, WTFMove(publicKeySexp), true, usage);
230 auto privateKey = CryptoKeyRSA::create(algorithm, hash, hasHash, CryptoKeyType::Private, WTFMove(privateKeySexp), extractable, usage);
231
232 callback(CryptoKeyPair { WTFMove(publicKey), WTFMove(privateKey) });
233 });
234}
235
236static bool supportedAlgorithmIdentifier(const uint8_t* data, size_t size)
237{
238 // FIXME: This is far from sufficient. Per the spec, when importing for key algorithm
239 // - RSASSA-PKCS1-v1_5:
240 // - rsaEncryption, sha{1,256,384,512}WithRSAEncryption OIDs must be supported
241 // - in case of sha{1,256,384,512}WithRSAEncryption OIDs the specified hash algorithm
242 // has to match the algorithm in the OID
243 // - RSA-PSS:
244 // - rsaEncryption, id-RSASSA-PSS OIDs must be supported
245 // - in case of id-RSASSA-PSS OID the parameters field of AlgorithmIdentifier has
246 // to be decoded as RSASSA-PSS-params ASN.1 structure, and the hashAlgorithm field
247 // of that structure has to contain one of id-sha{1,256,384,512} OIDs that match
248 // the specified hash algorithm
249 // - RSA-OAEP:
250 // - rsaEncryption, id-RSAES-OAEP OIDS must be supported
251 // - in case of id-RSAES-OAEP OID the parameters field of AlgorithmIdentifier has
252 // to be decoded as RSAES-OAEP-params ASN.1 structure, and the hashAlgorithm field
253 // of that structure has to contain one of id-sha{1,256,384,512} OIDs that match
254 // the specified hash algorithm
255
256 if (CryptoConstants::matches(data, size, CryptoConstants::s_rsaEncryptionIdentifier))
257 return true;
258 if (CryptoConstants::matches(data, size, CryptoConstants::s_RSAES_OAEPIdentifier))
259 return false; // Not yet supported.
260 if (CryptoConstants::matches(data, size, CryptoConstants::s_RSASSA_PSSIdentifier))
261 return false; // Not yet supported.
262 return false;
263}
264
265RefPtr<CryptoKeyRSA> CryptoKeyRSA::importSpki(CryptoAlgorithmIdentifier identifier, Optional<CryptoAlgorithmIdentifier> hash, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
266{
267 // Decode the `SubjectPublicKeyInfo` structure using the provided key data.
268 PAL::TASN1::Structure spki;
269 if (!PAL::TASN1::decodeStructure(&spki, "WebCrypto.SubjectPublicKeyInfo", keyData))
270 return nullptr;
271
272 // Validate `algorithm.algorithm`.
273 {
274 auto algorithm = PAL::TASN1::elementData(spki, "algorithm.algorithm");
275 if (!algorithm)
276 return nullptr;
277
278 if (!supportedAlgorithmIdentifier(algorithm->data(), algorithm->size()))
279 return nullptr;
280 }
281
282 // Decode the `RSAPublicKey` structure using the `subjectPublicKey` data.
283 PAL::TASN1::Structure rsaPublicKey;
284 {
285 auto subjectPublicKey = PAL::TASN1::elementData(spki, "subjectPublicKey");
286 if (!subjectPublicKey)
287 return nullptr;
288
289 if (!PAL::TASN1::decodeStructure(&rsaPublicKey, "WebCrypto.RSAPublicKey", *subjectPublicKey))
290 return nullptr;
291 }
292
293 // Retrieve the `modulus` and `publicExponent` data and embed it into the `public-key` s-expression.
294 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
295 {
296 auto modulus = PAL::TASN1::elementData(rsaPublicKey, "modulus");
297 auto publicExponent = PAL::TASN1::elementData(rsaPublicKey, "publicExponent");
298 if (!modulus || !publicExponent)
299 return nullptr;
300
301 gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(public-key(rsa(n %b)(e %b)))",
302 modulus->size(), modulus->data(), publicExponent->size(), publicExponent->data());
303 if (error != GPG_ERR_NO_ERROR) {
304 PAL::GCrypt::logError(error);
305 return nullptr;
306 }
307 }
308
309 return adoptRef(new CryptoKeyRSA(identifier, hash.valueOr(CryptoAlgorithmIdentifier::SHA_1), !!hash, CryptoKeyType::Public, PlatformRSAKeyContainer(platformKey.release()), extractable, usages));
310}
311
312RefPtr<CryptoKeyRSA> CryptoKeyRSA::importPkcs8(CryptoAlgorithmIdentifier identifier, Optional<CryptoAlgorithmIdentifier> hash, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
313{
314 // Decode the `PrivateKeyInfo` structure using the provided key data.
315 PAL::TASN1::Structure pkcs8;
316 if (!PAL::TASN1::decodeStructure(&pkcs8, "WebCrypto.PrivateKeyInfo", keyData))
317 return nullptr;
318
319 // Validate `version`.
320 {
321 auto version = PAL::TASN1::elementData(pkcs8, "version");
322 if (!version)
323 return nullptr;
324
325 if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version0))
326 return nullptr;
327 }
328
329 // Validate `privateKeyAlgorithm.algorithm`.
330 {
331 auto algorithm = PAL::TASN1::elementData(pkcs8, "privateKeyAlgorithm.algorithm");
332 if (!algorithm)
333 return nullptr;
334
335 if (!supportedAlgorithmIdentifier(algorithm->data(), algorithm->size()))
336 return nullptr;
337 }
338
339 // Decode the `RSAPrivateKey` structure using the `privateKey` data.
340 PAL::TASN1::Structure rsaPrivateKey;
341 {
342 auto privateKey = PAL::TASN1::elementData(pkcs8, "privateKey");
343 if (!privateKey)
344 return nullptr;
345
346 if (!PAL::TASN1::decodeStructure(&rsaPrivateKey, "WebCrypto.RSAPrivateKey", *privateKey))
347 return nullptr;
348 }
349
350 // Validate `privateKey.version`.
351 {
352 auto version = PAL::TASN1::elementData(rsaPrivateKey, "version");
353 if (!version)
354 return nullptr;
355
356 if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version0))
357 return nullptr;
358 }
359
360 // Retrieve the `modulus`, `publicExponent`, `privateExponent`, `prime1`, `prime2`,
361 // `exponent1`, `exponent2` and `coefficient` data and embed it into the `public-key` s-expression.
362 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
363 {
364 auto modulus = PAL::TASN1::elementData(rsaPrivateKey, "modulus");
365 auto publicExponent = PAL::TASN1::elementData(rsaPrivateKey, "publicExponent");
366 auto privateExponent = PAL::TASN1::elementData(rsaPrivateKey, "privateExponent");
367 auto prime1 = PAL::TASN1::elementData(rsaPrivateKey, "prime1");
368 auto prime2 = PAL::TASN1::elementData(rsaPrivateKey, "prime2");
369 auto exponent1 = PAL::TASN1::elementData(rsaPrivateKey, "exponent1");
370 auto exponent2 = PAL::TASN1::elementData(rsaPrivateKey, "exponent2");
371 auto coefficient = PAL::TASN1::elementData(rsaPrivateKey, "coefficient");
372
373 if (!modulus || !publicExponent || !privateExponent
374 || !prime1 || !prime2 || !exponent1 || !exponent2 || !coefficient)
375 return nullptr;
376
377 // libgcrypt inverts the use of p and q parameters, so we have to recalculate the `coefficient` value.
378 PAL::GCrypt::Handle<gcry_mpi_t> uMPI(gcry_mpi_new(0));
379 {
380 PAL::GCrypt::Handle<gcry_mpi_t> pMPI;
381 gcry_error_t error = gcry_mpi_scan(&pMPI, GCRYMPI_FMT_USG, prime1->data(), prime1->size(), nullptr);
382 if (error != GPG_ERR_NO_ERROR)
383 return nullptr;
384
385 PAL::GCrypt::Handle<gcry_mpi_t> qMPI;
386 error = gcry_mpi_scan(&qMPI, GCRYMPI_FMT_USG, prime2->data(), prime2->size(), nullptr);
387 if (error != GPG_ERR_NO_ERROR)
388 return nullptr;
389
390 gcry_mpi_invm(uMPI, qMPI, pMPI);
391 }
392
393 gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)(u %M)))",
394 modulus->size(), modulus->data(),
395 publicExponent->size(), publicExponent->data(),
396 privateExponent->size(), privateExponent->data(),
397 prime2->size(), prime2->data(), prime1->size(), prime1->data(), uMPI.handle());
398 if (error != GPG_ERR_NO_ERROR) {
399 PAL::GCrypt::logError(error);
400 return nullptr;
401 }
402 }
403
404 return adoptRef(new CryptoKeyRSA(identifier, hash.valueOr(CryptoAlgorithmIdentifier::SHA_1), !!hash, CryptoKeyType::Private, PlatformRSAKeyContainer(platformKey.release()), extractable, usages));
405}
406
407ExceptionOr<Vector<uint8_t>> CryptoKeyRSA::exportSpki() const
408{
409 if (type() != CryptoKeyType::Public)
410 return Exception { InvalidAccessError };
411
412 PAL::TASN1::Structure rsaPublicKey;
413 {
414 // Create the `RSAPublicKey` structure.
415 if (!PAL::TASN1::createStructure("WebCrypto.RSAPublicKey", &rsaPublicKey))
416 return Exception { OperationError };
417
418 // Retrieve the modulus and public exponent s-expressions.
419 PAL::GCrypt::Handle<gcry_sexp_t> modulusSexp(gcry_sexp_find_token(m_platformKey.get(), "n", 0));
420 PAL::GCrypt::Handle<gcry_sexp_t> publicExponentSexp(gcry_sexp_find_token(m_platformKey.get(), "e", 0));
421 if (!modulusSexp || !publicExponentSexp)
422 return Exception { OperationError };
423
424 // Retrieve MPI data for the modulus and public exponent components.
425 auto modulus = mpiSignedData(modulusSexp);
426 auto publicExponent = mpiSignedData(publicExponentSexp);
427 if (!modulus || !publicExponent)
428 return Exception { OperationError };
429
430 // Write out the modulus data under `modulus`.
431 if (!PAL::TASN1::writeElement(rsaPublicKey, "modulus", modulus->data(), modulus->size()))
432 return Exception { OperationError };
433
434 // Write out the public exponent data under `publicExponent`.
435 if (!PAL::TASN1::writeElement(rsaPublicKey, "publicExponent", publicExponent->data(), publicExponent->size()))
436 return Exception { OperationError };
437 }
438
439 PAL::TASN1::Structure spki;
440 {
441 // Create the `SubjectPublicKeyInfo` structure.
442 if (!PAL::TASN1::createStructure("WebCrypto.SubjectPublicKeyInfo", &spki))
443 return Exception { OperationError };
444
445 // Write out the id-rsaEncryption identifier under `algorithm.algorithm`.
446 // FIXME: In case the key algorithm is:
447 // - RSA-PSS:
448 // - this should write out id-RSASSA-PSS, along with setting `algorithm.parameters`
449 // to a RSASSA-PSS-params structure
450 // - RSA-OAEP:
451 // - this should write out id-RSAES-OAEP, along with setting `algorithm.parameters`
452 // to a RSAES-OAEP-params structure
453 if (!PAL::TASN1::writeElement(spki, "algorithm.algorithm", CryptoConstants::s_rsaEncryptionIdentifier.data(), 1))
454 return Exception { OperationError };
455
456 // Write out the null value under `algorithm.parameters`.
457 if (!PAL::TASN1::writeElement(spki, "algorithm.parameters", CryptoConstants::s_asn1NullValue.data(), CryptoConstants::s_asn1NullValue.size()))
458 return Exception { OperationError };
459
460 // Write out the `RSAPublicKey` data under `subjectPublicKey`. Because this is a
461 // bit string parameter, the data size has to be multiplied by 8.
462 {
463 auto data = PAL::TASN1::encodedData(rsaPublicKey, "");
464 if (!data || !PAL::TASN1::writeElement(spki, "subjectPublicKey", data->data(), data->size() * 8))
465 return Exception { OperationError };
466 }
467 }
468
469 // Retrieve the encoded `SubjectPublicKeyInfo` data and return it.
470 auto result = PAL::TASN1::encodedData(spki, "");
471 if (!result)
472 return Exception { OperationError };
473
474 return WTFMove(result.value());
475}
476
477ExceptionOr<Vector<uint8_t>> CryptoKeyRSA::exportPkcs8() const
478{
479 if (type() != CryptoKeyType::Private)
480 return Exception { InvalidAccessError };
481
482 PAL::TASN1::Structure rsaPrivateKey;
483 {
484 // Create the `RSAPrivateKey` structure.
485 if (!PAL::TASN1::createStructure("WebCrypto.RSAPrivateKey", &rsaPrivateKey))
486 return Exception { OperationError };
487
488 // Write out '0' under `version`.
489 if (!PAL::TASN1::writeElement(rsaPrivateKey, "version", "0", 0))
490 return Exception { OperationError };
491
492 // Retrieve the `n`, `e`, `d`, `q` and `p` s-expression tokens. libgcrypt swaps the usage of
493 // the p and q primes internally, so we adjust the lookup accordingly.
494 PAL::GCrypt::Handle<gcry_sexp_t> nSexp(gcry_sexp_find_token(m_platformKey.get(), "n", 0));
495 PAL::GCrypt::Handle<gcry_sexp_t> eSexp(gcry_sexp_find_token(m_platformKey.get(), "e", 0));
496 PAL::GCrypt::Handle<gcry_sexp_t> dSexp(gcry_sexp_find_token(m_platformKey.get(), "d", 0));
497 PAL::GCrypt::Handle<gcry_sexp_t> pSexp(gcry_sexp_find_token(m_platformKey.get(), "q", 0));
498 PAL::GCrypt::Handle<gcry_sexp_t> qSexp(gcry_sexp_find_token(m_platformKey.get(), "p", 0));
499 if (!nSexp || !eSexp || !dSexp || !pSexp || !qSexp)
500 return Exception { OperationError };
501
502 // Write the MPI data of retrieved s-expression tokens under `modulus`, `publicExponent`,
503 // `privateExponent`, `prime1` and `prime2`.
504 {
505 auto modulus = mpiSignedData(nSexp);
506 auto publicExponent = mpiSignedData(eSexp);
507 auto privateExponent = mpiSignedData(dSexp);
508 auto prime1 = mpiSignedData(pSexp);
509 auto prime2 = mpiSignedData(qSexp);
510 if (!modulus || !publicExponent || !privateExponent || !prime1 || !prime2)
511 return Exception { OperationError };
512
513 if (!PAL::TASN1::writeElement(rsaPrivateKey, "modulus", modulus->data(), modulus->size())
514 || !PAL::TASN1::writeElement(rsaPrivateKey, "publicExponent", publicExponent->data(), publicExponent->size())
515 || !PAL::TASN1::writeElement(rsaPrivateKey, "privateExponent", privateExponent->data(), privateExponent->size())
516 || !PAL::TASN1::writeElement(rsaPrivateKey, "prime1", prime1->data(), prime1->size())
517 || !PAL::TASN1::writeElement(rsaPrivateKey, "prime2", prime2->data(), prime2->size()))
518 return Exception { OperationError };
519 }
520
521 // Manually compute the MPI values for the `exponent1`, `exponent2` and `coefficient`
522 // parameters. Again note the swapped usage of the `p` and `q` s-expression parameters.
523 {
524 PAL::GCrypt::Handle<gcry_mpi_t> dMPI(gcry_sexp_nth_mpi(dSexp, 1, GCRYMPI_FMT_USG));
525 PAL::GCrypt::Handle<gcry_mpi_t> pMPI(gcry_sexp_nth_mpi(pSexp, 1, GCRYMPI_FMT_USG));
526 PAL::GCrypt::Handle<gcry_mpi_t> qMPI(gcry_sexp_nth_mpi(qSexp, 1, GCRYMPI_FMT_USG));
527 if (!dMPI || !pMPI || !qMPI)
528 return Exception { OperationError };
529
530 // `exponent1`
531 {
532 PAL::GCrypt::Handle<gcry_mpi_t> dpMPI(gcry_mpi_set_ui(nullptr, 0));
533 PAL::GCrypt::Handle<gcry_mpi_t> pm1MPI(gcry_mpi_set(nullptr, pMPI));
534 gcry_mpi_sub_ui(pm1MPI, pm1MPI, 1);
535 gcry_mpi_mod(dpMPI, dMPI, pm1MPI);
536
537 auto dp = mpiSignedData(dpMPI);
538 if (!dp || !PAL::TASN1::writeElement(rsaPrivateKey, "exponent1", dp->data(), dp->size()))
539 return Exception { OperationError };
540 }
541
542 // `exponent2`
543 {
544 PAL::GCrypt::Handle<gcry_mpi_t> dqMPI(gcry_mpi_set_ui(nullptr, 0));
545 PAL::GCrypt::Handle<gcry_mpi_t> qm1MPI(gcry_mpi_set(nullptr, qMPI));
546 gcry_mpi_sub_ui(qm1MPI, qm1MPI, 1);
547 gcry_mpi_mod(dqMPI, dMPI, qm1MPI);
548
549 auto dq = mpiSignedData(dqMPI);
550 if (!dq || !PAL::TASN1::writeElement(rsaPrivateKey, "exponent2", dq->data(), dq->size()))
551 return Exception { OperationError };
552 }
553
554 // `coefficient`
555 {
556 PAL::GCrypt::Handle<gcry_mpi_t> qiMPI(gcry_mpi_set_ui(nullptr, 0));
557 gcry_mpi_invm(qiMPI, qMPI, pMPI);
558
559 auto qi = mpiSignedData(qiMPI);
560 if (!qi || !PAL::TASN1::writeElement(rsaPrivateKey, "coefficient", qi->data(), qi->size()))
561 return Exception { OperationError };
562 }
563 }
564
565 // Eliminate the optional `otherPrimeInfos` element.
566 // FIXME: this should be supported in the future, if there is such information available.
567 if (!PAL::TASN1::writeElement(rsaPrivateKey, "otherPrimeInfos", nullptr, 0))
568 return Exception { OperationError };
569 }
570
571 PAL::TASN1::Structure pkcs8;
572 {
573 // Create the `PrivateKeyInfo` structure.
574 if (!PAL::TASN1::createStructure("WebCrypto.PrivateKeyInfo", &pkcs8))
575 return Exception { OperationError };
576
577 // Write out '0' under `version`.
578 if (!PAL::TASN1::writeElement(pkcs8, "version", "0", 0))
579 return Exception { OperationError };
580
581 // Write out the id-rsaEncryption identifier under `algorithm.algorithm`.
582 // FIXME: In case the key algorithm is:
583 // - RSA-PSS:
584 // - this should write out id-RSASSA-PSS, along with setting `algorithm.parameters`
585 // to a RSASSA-PSS-params structure
586 // - RSA-OAEP:
587 // - this should write out id-RSAES-OAEP, along with setting `algorithm.parameters`
588 // to a RSAES-OAEP-params structure
589 if (!PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.algorithm", "1.2.840.113549.1.1.1", 1))
590 return Exception { OperationError };
591
592 // Write out a null value under `algorithm.parameters`.
593 if (!PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.parameters", CryptoConstants::s_asn1NullValue.data(), CryptoConstants::s_asn1NullValue.size()))
594 return Exception { OperationError };
595
596 // Write out the `RSAPrivateKey` data under `privateKey`.
597 {
598 auto data = PAL::TASN1::encodedData(rsaPrivateKey, "");
599 if (!data || !PAL::TASN1::writeElement(pkcs8, "privateKey", data->data(), data->size()))
600 return Exception { OperationError };
601 }
602
603 // Eliminate the optional `attributes` element.
604 if (!PAL::TASN1::writeElement(pkcs8, "attributes", nullptr, 0))
605 return Exception { OperationError };
606 }
607
608 // Retrieve the encoded `PrivateKeyInfo` data and return it.
609 auto result = PAL::TASN1::encodedData(pkcs8, "");
610 if (!result)
611 return Exception { OperationError };
612
613 return WTFMove(result.value());
614}
615
616auto CryptoKeyRSA::algorithm() const -> KeyAlgorithm
617{
618 auto modulusLength = getRSAModulusLength(m_platformKey.get());
619 auto publicExponent = getRSAKeyParameter(m_platformKey.get(), "e");
620
621 if (m_restrictedToSpecificHash) {
622 CryptoRsaHashedKeyAlgorithm result;
623 result.name = CryptoAlgorithmRegistry::singleton().name(algorithmIdentifier());
624 result.modulusLength = modulusLength;
625 result.publicExponent = Uint8Array::tryCreate(publicExponent.data(), publicExponent.size());
626 result.hash.name = CryptoAlgorithmRegistry::singleton().name(m_hash);
627 return result;
628 }
629
630 CryptoRsaKeyAlgorithm result;
631 result.name = CryptoAlgorithmRegistry::singleton().name(algorithmIdentifier());
632 result.modulusLength = modulusLength;
633 result.publicExponent = Uint8Array::tryCreate(publicExponent.data(), publicExponent.size());
634 return result;
635}
636
637std::unique_ptr<CryptoKeyRSAComponents> CryptoKeyRSA::exportData() const
638{
639 switch (type()) {
640 case CryptoKeyType::Public:
641 return CryptoKeyRSAComponents::createPublic(getRSAKeyParameter(m_platformKey.get(), "n"), getRSAKeyParameter(m_platformKey.get(), "e"));
642 case CryptoKeyType::Private: {
643 auto parameterMPI =
644 [](gcry_sexp_t sexp, const char* name) -> gcry_mpi_t {
645 PAL::GCrypt::Handle<gcry_sexp_t> paramSexp(gcry_sexp_find_token(sexp, name, 0));
646 if (!paramSexp)
647 return nullptr;
648 return gcry_sexp_nth_mpi(paramSexp, 1, GCRYMPI_FMT_USG);
649 };
650
651 PAL::GCrypt::Handle<gcry_mpi_t> dMPI(parameterMPI(m_platformKey.get(), "d"));
652 // libgcrypt internally uses p and q such that p < q, while usually it's q < p.
653 // Switch the two primes here and continue with assuming the latter.
654 PAL::GCrypt::Handle<gcry_mpi_t> pMPI(parameterMPI(m_platformKey.get(), "q"));
655 PAL::GCrypt::Handle<gcry_mpi_t> qMPI(parameterMPI(m_platformKey.get(), "p"));
656 if (!dMPI || !pMPI || !qMPI)
657 return nullptr;
658
659 CryptoKeyRSAComponents::PrimeInfo firstPrimeInfo;
660 if (auto data = mpiData(pMPI))
661 firstPrimeInfo.primeFactor = WTFMove(data.value());
662
663 CryptoKeyRSAComponents::PrimeInfo secondPrimeInfo;
664 if (auto data = mpiData(qMPI))
665 secondPrimeInfo.primeFactor = WTFMove(data.value());
666
667 // dp -- d mod (p - 1)
668 {
669 PAL::GCrypt::Handle<gcry_mpi_t> dpMPI(gcry_mpi_new(0));
670 PAL::GCrypt::Handle<gcry_mpi_t> pm1MPI(gcry_mpi_new(0));
671 gcry_mpi_sub_ui(pm1MPI, pMPI, 1);
672 gcry_mpi_mod(dpMPI, dMPI, pm1MPI);
673
674 if (auto data = mpiData(dpMPI))
675 firstPrimeInfo.factorCRTExponent = WTFMove(data.value());
676 }
677
678 // dq -- d mod (q - 1)
679 {
680 PAL::GCrypt::Handle<gcry_mpi_t> dqMPI(gcry_mpi_new(0));
681 PAL::GCrypt::Handle<gcry_mpi_t> qm1MPI(gcry_mpi_new(0));
682 gcry_mpi_sub_ui(qm1MPI, qMPI, 1);
683 gcry_mpi_mod(dqMPI, dMPI, qm1MPI);
684
685 if (auto data = mpiData(dqMPI))
686 secondPrimeInfo.factorCRTExponent = WTFMove(data.value());
687 }
688
689 // qi -- q^(-1) mod p
690 {
691 PAL::GCrypt::Handle<gcry_mpi_t> qiMPI(gcry_mpi_new(0));
692 gcry_mpi_invm(qiMPI, qMPI, pMPI);
693
694 if (auto data = mpiData(qiMPI))
695 secondPrimeInfo.factorCRTCoefficient = WTFMove(data.value());
696 }
697
698 Vector<uint8_t> privateExponent;
699 if (auto data = mpiData(dMPI))
700 privateExponent = WTFMove(data.value());
701
702 return CryptoKeyRSAComponents::createPrivateWithAdditionalData(
703 getRSAKeyParameter(m_platformKey.get(), "n"), getRSAKeyParameter(m_platformKey.get(), "e"), WTFMove(privateExponent),
704 WTFMove(firstPrimeInfo), WTFMove(secondPrimeInfo), Vector<CryptoKeyRSAComponents::PrimeInfo> { });
705 }
706 default:
707 ASSERT_NOT_REACHED();
708 return nullptr;
709 }
710}
711
712} // namespace WebCore
713
714#endif // ENABLE(WEB_CRYPTO)
715