2 * Copyright (C) 2014 Igalia S.L. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "CryptoKeyRSA.h"
29 #if ENABLE(SUBTLE_CRYPTO)
31 #include "CryptoAlgorithmRegistry.h"
32 #include "CryptoKeyDataRSAComponents.h"
33 #include "CryptoKeyPair.h"
34 #include "GCryptUtilities.h"
35 #include "ScriptExecutionContext.h"
36 #include <pal/crypto/gcrypt/Handle.h>
37 #include <pal/crypto/gcrypt/Utilities.h>
38 #include <pal/crypto/tasn1/Utilities.h>
42 static size_t getRSAModulusLength(gcry_sexp_t keySexp)
44 // Retrieve the s-expression token for the public modulus N of the given RSA key.
45 PAL::GCrypt::Handle<gcry_sexp_t> nSexp(gcry_sexp_find_token(keySexp, "n", 0));
49 // Retrieve the MPI length for the corresponding s-expression token, in bits.
50 auto length = mpiLength(nSexp);
57 static Vector<uint8_t> getRSAKeyParameter(gcry_sexp_t keySexp, const char* name)
59 // Retrieve the s-expression token for the specified parameter of the given RSA key.
60 PAL::GCrypt::Handle<gcry_sexp_t> paramSexp(gcry_sexp_find_token(keySexp, name, 0));
64 // Retrieve the MPI data for the corresponding s-expression token.
65 auto data = mpiData(paramSexp);
69 return WTFMove(data.value());
72 RefPtr<CryptoKeyRSA> CryptoKeyRSA::create(CryptoAlgorithmIdentifier identifier, CryptoAlgorithmIdentifier hash, bool hasHash, const CryptoKeyDataRSAComponents& keyData, bool extractable, CryptoKeyUsageBitmap usages)
74 // When creating a private key, we require the p and q prime information.
75 if (keyData.type() == CryptoKeyDataRSAComponents::Type::Private && !keyData.hasAdditionalPrivateKeyParameters())
78 // But we don't currently support creating keys with any additional prime information.
79 if (!keyData.otherPrimeInfos().isEmpty())
82 // Validate the key data.
86 // For both public and private keys, we need the public modulus and exponent.
87 valid &= !keyData.modulus().isEmpty() && !keyData.exponent().isEmpty();
89 // For private keys, we require the private exponent, as well as p and q prime information.
90 if (keyData.type() == CryptoKeyDataRSAComponents::Type::Private)
91 valid &= !keyData.privateExponent().isEmpty() && !keyData.firstPrimeInfo().primeFactor.isEmpty() && !keyData.secondPrimeInfo().primeFactor.isEmpty();
97 CryptoKeyType keyType;
98 switch (keyData.type()) {
99 case CryptoKeyDataRSAComponents::Type::Public:
100 keyType = CryptoKeyType::Public;
102 case CryptoKeyDataRSAComponents::Type::Private:
103 keyType = CryptoKeyType::Private;
107 // Construct the key s-expression, using the data that's available.
108 PAL::GCrypt::Handle<gcry_sexp_t> keySexp;
110 gcry_error_t error = GPG_ERR_NO_ERROR;
113 case CryptoKeyType::Public:
114 error = gcry_sexp_build(&keySexp, nullptr, "(public-key(rsa(n %b)(e %b)))",
115 keyData.modulus().size(), keyData.modulus().data(),
116 keyData.exponent().size(), keyData.exponent().data());
118 case CryptoKeyType::Private:
119 if (keyData.hasAdditionalPrivateKeyParameters()) {
120 error = gcry_sexp_build(&keySexp, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)))",
121 keyData.modulus().size(), keyData.modulus().data(),
122 keyData.exponent().size(), keyData.exponent().data(),
123 keyData.privateExponent().size(), keyData.privateExponent().data(),
124 keyData.secondPrimeInfo().primeFactor.size(), keyData.secondPrimeInfo().primeFactor.data(),
125 keyData.firstPrimeInfo().primeFactor.size(), keyData.firstPrimeInfo().primeFactor.data());
129 error = gcry_sexp_build(&keySexp, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)))",
130 keyData.modulus().size(), keyData.modulus().data(),
131 keyData.exponent().size(), keyData.exponent().data(),
132 keyData.privateExponent().size(), keyData.privateExponent().data());
134 case CryptoKeyType::Secret:
135 ASSERT_NOT_REACHED();
139 if (error != GPG_ERR_NO_ERROR) {
140 PAL::GCrypt::logError(error);
145 return adoptRef(new CryptoKeyRSA(identifier, hash, hasHash, keyType, keySexp.release(), extractable, usages));
148 CryptoKeyRSA::CryptoKeyRSA(CryptoAlgorithmIdentifier identifier, CryptoAlgorithmIdentifier hash, bool hasHash, CryptoKeyType type, PlatformRSAKey platformKey, bool extractable, CryptoKeyUsageBitmap usage)
149 : CryptoKey(identifier, type, extractable, usage)
150 , m_platformKey(platformKey)
151 , m_restrictedToSpecificHash(hasHash)
156 CryptoKeyRSA::~CryptoKeyRSA()
159 PAL::GCrypt::HandleDeleter<gcry_sexp_t>()(m_platformKey);
162 bool CryptoKeyRSA::isRestrictedToHash(CryptoAlgorithmIdentifier& identifier) const
164 if (!m_restrictedToSpecificHash)
171 size_t CryptoKeyRSA::keySizeInBits() const
173 return getRSAModulusLength(m_platformKey);
176 // Convert the exponent vector to a 32-bit value, if possible.
177 static std::optional<uint32_t> exponentVectorToUInt32(const Vector<uint8_t>& exponent)
179 if (exponent.size() > 4) {
180 if (std::any_of(exponent.begin(), exponent.end() - 4, [](uint8_t element) { return !!element; }))
185 for (size_t size = exponent.size(), i = std::min<size_t>(4, size); i > 0; --i) {
187 result += exponent[size - i];
193 void 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)
195 // libgcrypt doesn't report an error if the exponent is smaller than three or even.
196 auto e = exponentVectorToUInt32(publicExponent);
197 if (!e || *e < 3 || !(*e & 0x1)) {
202 // libgcrypt doesn't support generating primes of less than 16 bits.
203 if (modulusLength < 16) {
208 PAL::GCrypt::Handle<gcry_sexp_t> genkeySexp;
209 gcry_error_t error = gcry_sexp_build(&genkeySexp, nullptr, "(genkey(rsa(nbits %d)(rsa-use-e %d)))", modulusLength, *e);
210 if (error != GPG_ERR_NO_ERROR) {
211 PAL::GCrypt::logError(error);
216 PAL::GCrypt::Handle<gcry_sexp_t> keyPairSexp;
217 error = gcry_pk_genkey(&keyPairSexp, genkeySexp);
218 if (error != GPG_ERR_NO_ERROR) {
219 PAL::GCrypt::logError(error);
224 PAL::GCrypt::Handle<gcry_sexp_t> publicKeySexp(gcry_sexp_find_token(keyPairSexp, "public-key", 0));
225 PAL::GCrypt::Handle<gcry_sexp_t> privateKeySexp(gcry_sexp_find_token(keyPairSexp, "private-key", 0));
226 if (!publicKeySexp || !privateKeySexp) {
233 [algorithm, hash, hasHash, extractable, usage, publicKeySexp = publicKeySexp.release(), privateKeySexp = privateKeySexp.release(), callback = WTFMove(callback)](ScriptExecutionContext& context) mutable {
234 auto publicKey = CryptoKeyRSA::create(algorithm, hash, hasHash, CryptoKeyType::Public, publicKeySexp, true, usage);
235 auto privateKey = CryptoKeyRSA::create(algorithm, hash, hasHash, CryptoKeyType::Private, privateKeySexp, extractable, usage);
237 callback(CryptoKeyPair { WTFMove(publicKey), WTFMove(privateKey) });
242 static bool supportedAlgorithmIdentifier(const uint8_t* data, size_t size)
244 // FIXME: This is far from sufficient. Per the spec, when importing for key algorithm
245 // - RSASSA-PKCS1-v1_5:
246 // - rsaEncryption, sha{1,256,384,512}WithRSAEncryption OIDs must be supported
247 // - in case of sha{1,256,384,512}WithRSAEncryption OIDs the specified hash algorithm
248 // has to match the algorithm in the OID
250 // - rsaEncryption, id-RSASSA-PSS OIDs must be supported
251 // - in case of id-RSASSA-PSS OID the parameters field of AlgorithmIdentifier has
252 // to be decoded as RSASSA-PSS-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
256 // - rsaEncryption, id-RSAES-OAEP OIDS must be supported
257 // - in case of id-RSAES-OAEP OID the parameters field of AlgorithmIdentifier has
258 // to be decoded as RSAES-OAEP-params ASN.1 structure, and the hashAlgorithm field
259 // of that structure has to contain one of id-sha{1,256,384,512} OIDs that match
260 // the specified hash algorithm
262 if (CryptoConstants::matches(data, size, CryptoConstants::s_rsaEncryptionIdentifier))
264 if (CryptoConstants::matches(data, size, CryptoConstants::s_RSAES_OAEPIdentifier))
265 return false; // Not yet supported.
266 if (CryptoConstants::matches(data, size, CryptoConstants::s_RSASSA_PSSIdentifier))
267 return false; // Not yet supported.
271 RefPtr<CryptoKeyRSA> CryptoKeyRSA::importSpki(CryptoAlgorithmIdentifier identifier, std::optional<CryptoAlgorithmIdentifier> hash, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
273 // Decode the `SubjectPublicKeyInfo` structure using the provided key data.
274 PAL::TASN1::Structure spki;
275 if (!PAL::TASN1::decodeStructure(&spki, "WebCrypto.SubjectPublicKeyInfo", keyData))
278 // Validate `algorithm.algorithm`.
280 auto algorithm = PAL::TASN1::elementData(spki, "algorithm.algorithm");
284 if (!supportedAlgorithmIdentifier(algorithm->data(), algorithm->size()))
288 // Decode the `RSAPublicKey` structure using the `subjectPublicKey` data.
289 PAL::TASN1::Structure rsaPublicKey;
291 auto subjectPublicKey = PAL::TASN1::elementData(spki, "subjectPublicKey");
292 if (!subjectPublicKey)
295 if (!PAL::TASN1::decodeStructure(&rsaPublicKey, "WebCrypto.RSAPublicKey", *subjectPublicKey))
299 // Retrieve the `modulus` and `publicExponent` data and embed it into the `public-key` s-expression.
300 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
302 auto modulus = PAL::TASN1::elementData(rsaPublicKey, "modulus");
303 auto publicExponent = PAL::TASN1::elementData(rsaPublicKey, "publicExponent");
304 if (!modulus || !publicExponent)
307 gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(public-key(rsa(n %b)(e %b)))",
308 modulus->size(), modulus->data(), publicExponent->size(), publicExponent->data());
309 if (error != GPG_ERR_NO_ERROR) {
310 PAL::GCrypt::logError(error);
315 return adoptRef(new CryptoKeyRSA(identifier, hash.value_or(CryptoAlgorithmIdentifier::SHA_1), !!hash, CryptoKeyType::Public, platformKey.release(), extractable, usages));
318 RefPtr<CryptoKeyRSA> CryptoKeyRSA::importPkcs8(CryptoAlgorithmIdentifier identifier, std::optional<CryptoAlgorithmIdentifier> hash, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
320 // Decode the `PrivateKeyInfo` structure using the provided key data.
321 PAL::TASN1::Structure pkcs8;
322 if (!PAL::TASN1::decodeStructure(&pkcs8, "WebCrypto.PrivateKeyInfo", keyData))
325 // Validate `version`.
327 auto version = PAL::TASN1::elementData(pkcs8, "version");
331 if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version0))
335 // Validate `privateKeyAlgorithm.algorithm`.
337 auto algorithm = PAL::TASN1::elementData(pkcs8, "privateKeyAlgorithm.algorithm");
341 if (!supportedAlgorithmIdentifier(algorithm->data(), algorithm->size()))
345 // Decode the `RSAPrivateKey` structure using the `privateKey` data.
346 PAL::TASN1::Structure rsaPrivateKey;
348 auto privateKey = PAL::TASN1::elementData(pkcs8, "privateKey");
352 if (!PAL::TASN1::decodeStructure(&rsaPrivateKey, "WebCrypto.RSAPrivateKey", *privateKey))
356 // Validate `privateKey.version`.
358 auto version = PAL::TASN1::elementData(rsaPrivateKey, "version");
362 if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version0))
366 // Retrieve the `modulus`, `publicExponent`, `privateExponent`, `prime1`, `prime2`,
367 // `exponent1`, `exponent2` and `coefficient` data and embed it into the `public-key` s-expression.
368 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
370 auto modulus = PAL::TASN1::elementData(rsaPrivateKey, "modulus");
371 auto publicExponent = PAL::TASN1::elementData(rsaPrivateKey, "publicExponent");
372 auto privateExponent = PAL::TASN1::elementData(rsaPrivateKey, "privateExponent");
373 auto prime1 = PAL::TASN1::elementData(rsaPrivateKey, "prime1");
374 auto prime2 = PAL::TASN1::elementData(rsaPrivateKey, "prime2");
375 auto exponent1 = PAL::TASN1::elementData(rsaPrivateKey, "exponent1");
376 auto exponent2 = PAL::TASN1::elementData(rsaPrivateKey, "exponent2");
377 auto coefficient = PAL::TASN1::elementData(rsaPrivateKey, "coefficient");
379 if (!modulus || !publicExponent || !privateExponent
380 || !prime1 || !prime2 || !exponent1 || !exponent2 || !coefficient)
383 // libgcrypt inverts the use of p and q parameters, so we have to recalculate the `coefficient` value.
384 PAL::GCrypt::Handle<gcry_mpi_t> uMPI(gcry_mpi_new(0));
386 PAL::GCrypt::Handle<gcry_mpi_t> pMPI;
387 gcry_error_t error = gcry_mpi_scan(&pMPI, GCRYMPI_FMT_USG, prime1->data(), prime1->size(), nullptr);
388 if (error != GPG_ERR_NO_ERROR)
391 PAL::GCrypt::Handle<gcry_mpi_t> qMPI;
392 error = gcry_mpi_scan(&qMPI, GCRYMPI_FMT_USG, prime2->data(), prime2->size(), nullptr);
393 if (error != GPG_ERR_NO_ERROR)
396 gcry_mpi_invm(uMPI, qMPI, pMPI);
399 gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)(u %M)))",
400 modulus->size(), modulus->data(),
401 publicExponent->size(), publicExponent->data(),
402 privateExponent->size(), privateExponent->data(),
403 prime2->size(), prime2->data(), prime1->size(), prime1->data(), uMPI.handle());
404 if (error != GPG_ERR_NO_ERROR) {
405 PAL::GCrypt::logError(error);
410 return adoptRef(new CryptoKeyRSA(identifier, hash.value_or(CryptoAlgorithmIdentifier::SHA_1), !!hash, CryptoKeyType::Private, platformKey.release(), extractable, usages));
413 ExceptionOr<Vector<uint8_t>> CryptoKeyRSA::exportSpki() const
415 if (type() != CryptoKeyType::Public)
416 return Exception { InvalidAccessError };
418 PAL::TASN1::Structure rsaPublicKey;
420 // Create the `RSAPublicKey` structure.
421 if (!PAL::TASN1::createStructure("WebCrypto.RSAPublicKey", &rsaPublicKey))
422 return Exception { OperationError };
424 // Retrieve the modulus and public exponent s-expressions.
425 PAL::GCrypt::Handle<gcry_sexp_t> modulusSexp(gcry_sexp_find_token(m_platformKey, "n", 0));
426 PAL::GCrypt::Handle<gcry_sexp_t> publicExponentSexp(gcry_sexp_find_token(m_platformKey, "e", 0));
427 if (!modulusSexp || !publicExponentSexp)
428 return Exception { OperationError };
430 // Retrieve MPI data for the modulus and public exponent components.
431 auto modulus = mpiSignedData(modulusSexp);
432 auto publicExponent = mpiSignedData(publicExponentSexp);
433 if (!modulus || !publicExponent)
434 return Exception { OperationError };
436 // Write out the modulus data under `modulus`.
437 if (!PAL::TASN1::writeElement(rsaPublicKey, "modulus", modulus->data(), modulus->size()))
438 return Exception { OperationError };
440 // Write out the public exponent data under `publicExponent`.
441 if (!PAL::TASN1::writeElement(rsaPublicKey, "publicExponent", publicExponent->data(), publicExponent->size()))
442 return Exception { OperationError };
445 PAL::TASN1::Structure spki;
447 // Create the `SubjectPublicKeyInfo` structure.
448 if (!PAL::TASN1::createStructure("WebCrypto.SubjectPublicKeyInfo", &spki))
449 return Exception { OperationError };
451 // Write out the id-rsaEncryption identifier under `algorithm.algorithm`.
452 // FIXME: In case the key algorithm is:
454 // - this should write out id-RSASSA-PSS, along with setting `algorithm.parameters`
455 // to a RSASSA-PSS-params structure
457 // - this should write out id-RSAES-OAEP, along with setting `algorithm.parameters`
458 // to a RSAES-OAEP-params structure
459 if (!PAL::TASN1::writeElement(spki, "algorithm.algorithm", CryptoConstants::s_rsaEncryptionIdentifier.data(), 1))
460 return Exception { OperationError };
462 // Write out the null value under `algorithm.parameters`.
463 if (!PAL::TASN1::writeElement(spki, "algorithm.parameters", CryptoConstants::s_asn1NullValue.data(), CryptoConstants::s_asn1NullValue.size()))
464 return Exception { OperationError };
466 // Write out the `RSAPublicKey` data under `subjectPublicKey`. Because this is a
467 // bit string parameter, the data size has to be multiplied by 8.
469 auto data = PAL::TASN1::encodedData(rsaPublicKey, "");
470 if (!data || !PAL::TASN1::writeElement(spki, "subjectPublicKey", data->data(), data->size() * 8))
471 return Exception { OperationError };
475 // Retrieve the encoded `SubjectPublicKeyInfo` data and return it.
476 auto result = PAL::TASN1::encodedData(spki, "");
478 return Exception { OperationError };
480 return WTFMove(result.value());
483 ExceptionOr<Vector<uint8_t>> CryptoKeyRSA::exportPkcs8() const
485 if (type() != CryptoKeyType::Private)
486 return Exception { InvalidAccessError };
488 PAL::TASN1::Structure rsaPrivateKey;
490 // Create the `RSAPrivateKey` structure.
491 if (!PAL::TASN1::createStructure("WebCrypto.RSAPrivateKey", &rsaPrivateKey))
492 return Exception { OperationError };
494 // Write out '0' under `version`.
495 if (!PAL::TASN1::writeElement(rsaPrivateKey, "version", "0", 0))
496 return Exception { OperationError };
498 // Retrieve the `n`, `e`, `d`, `q` and `p` s-expression tokens. libgcrypt swaps the usage of
499 // the p and q primes internally, so we adjust the lookup accordingly.
500 PAL::GCrypt::Handle<gcry_sexp_t> nSexp(gcry_sexp_find_token(m_platformKey, "n", 0));
501 PAL::GCrypt::Handle<gcry_sexp_t> eSexp(gcry_sexp_find_token(m_platformKey, "e", 0));
502 PAL::GCrypt::Handle<gcry_sexp_t> dSexp(gcry_sexp_find_token(m_platformKey, "d", 0));
503 PAL::GCrypt::Handle<gcry_sexp_t> pSexp(gcry_sexp_find_token(m_platformKey, "q", 0));
504 PAL::GCrypt::Handle<gcry_sexp_t> qSexp(gcry_sexp_find_token(m_platformKey, "p", 0));
505 if (!nSexp || !eSexp || !dSexp || !pSexp || !qSexp)
506 return Exception { OperationError };
508 // Write the MPI data of retrieved s-expression tokens under `modulus`, `publicExponent`,
509 // `privateExponent`, `prime1` and `prime2`.
511 auto modulus = mpiSignedData(nSexp);
512 auto publicExponent = mpiSignedData(eSexp);
513 auto privateExponent = mpiSignedData(dSexp);
514 auto prime1 = mpiSignedData(pSexp);
515 auto prime2 = mpiSignedData(qSexp);
516 if (!modulus || !publicExponent || !privateExponent || !prime1 || !prime2)
517 return Exception { OperationError };
519 if (!PAL::TASN1::writeElement(rsaPrivateKey, "modulus", modulus->data(), modulus->size())
520 || !PAL::TASN1::writeElement(rsaPrivateKey, "publicExponent", publicExponent->data(), publicExponent->size())
521 || !PAL::TASN1::writeElement(rsaPrivateKey, "privateExponent", privateExponent->data(), privateExponent->size())
522 || !PAL::TASN1::writeElement(rsaPrivateKey, "prime1", prime1->data(), prime1->size())
523 || !PAL::TASN1::writeElement(rsaPrivateKey, "prime2", prime2->data(), prime2->size()))
524 return Exception { OperationError };
527 // Manually compute the MPI values for the `exponent1`, `exponent2` and `coefficient`
528 // parameters. Again note the swapped usage of the `p` and `q` s-expression parameters.
530 PAL::GCrypt::Handle<gcry_mpi_t> dMPI(gcry_sexp_nth_mpi(dSexp, 1, GCRYMPI_FMT_USG));
531 PAL::GCrypt::Handle<gcry_mpi_t> pMPI(gcry_sexp_nth_mpi(pSexp, 1, GCRYMPI_FMT_USG));
532 PAL::GCrypt::Handle<gcry_mpi_t> qMPI(gcry_sexp_nth_mpi(qSexp, 1, GCRYMPI_FMT_USG));
533 if (!dMPI || !pMPI || !qMPI)
534 return Exception { OperationError };
538 PAL::GCrypt::Handle<gcry_mpi_t> dpMPI(gcry_mpi_set_ui(nullptr, 0));
539 PAL::GCrypt::Handle<gcry_mpi_t> pm1MPI(gcry_mpi_set(nullptr, pMPI));
540 gcry_mpi_sub_ui(pm1MPI, pm1MPI, 1);
541 gcry_mpi_mod(dpMPI, dMPI, pm1MPI);
543 auto dp = mpiSignedData(dpMPI);
544 if (!dp || !PAL::TASN1::writeElement(rsaPrivateKey, "exponent1", dp->data(), dp->size()))
545 return Exception { OperationError };
550 PAL::GCrypt::Handle<gcry_mpi_t> dqMPI(gcry_mpi_set_ui(nullptr, 0));
551 PAL::GCrypt::Handle<gcry_mpi_t> qm1MPI(gcry_mpi_set(nullptr, qMPI));
552 gcry_mpi_sub_ui(qm1MPI, qm1MPI, 1);
553 gcry_mpi_mod(dqMPI, dMPI, qm1MPI);
555 auto dq = mpiSignedData(dqMPI);
556 if (!dq || !PAL::TASN1::writeElement(rsaPrivateKey, "exponent2", dq->data(), dq->size()))
557 return Exception { OperationError };
562 PAL::GCrypt::Handle<gcry_mpi_t> qiMPI(gcry_mpi_set_ui(nullptr, 0));
563 gcry_mpi_invm(qiMPI, qMPI, pMPI);
565 auto qi = mpiSignedData(qiMPI);
566 if (!qi || !PAL::TASN1::writeElement(rsaPrivateKey, "coefficient", qi->data(), qi->size()))
567 return Exception { OperationError };
571 // Eliminate the optional `otherPrimeInfos` element.
572 // FIXME: this should be supported in the future, if there is such information available.
573 if (!PAL::TASN1::writeElement(rsaPrivateKey, "otherPrimeInfos", nullptr, 0))
574 return Exception { OperationError };
577 PAL::TASN1::Structure pkcs8;
579 // Create the `PrivateKeyInfo` structure.
580 if (!PAL::TASN1::createStructure("WebCrypto.PrivateKeyInfo", &pkcs8))
581 return Exception { OperationError };
583 // Write out '0' under `version`.
584 if (!PAL::TASN1::writeElement(pkcs8, "version", "0", 0))
585 return Exception { OperationError };
587 // Write out the id-rsaEncryption identifier under `algorithm.algorithm`.
588 // FIXME: In case the key algorithm is:
590 // - this should write out id-RSASSA-PSS, along with setting `algorithm.parameters`
591 // to a RSASSA-PSS-params structure
593 // - this should write out id-RSAES-OAEP, along with setting `algorithm.parameters`
594 // to a RSAES-OAEP-params structure
595 if (!PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.algorithm", "1.2.840.113549.1.1.1", 1))
596 return Exception { OperationError };
598 // Write out a null value under `algorithm.parameters`.
599 if (!PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.parameters", CryptoConstants::s_asn1NullValue.data(), CryptoConstants::s_asn1NullValue.size()))
600 return Exception { OperationError };
602 // Write out the `RSAPrivateKey` data under `privateKey`.
604 auto data = PAL::TASN1::encodedData(rsaPrivateKey, "");
605 if (!data || !PAL::TASN1::writeElement(pkcs8, "privateKey", data->data(), data->size()))
606 return Exception { OperationError };
609 // Eliminate the optional `attributes` element.
610 if (!PAL::TASN1::writeElement(pkcs8, "attributes", nullptr, 0))
611 return Exception { OperationError };
614 // Retrieve the encoded `PrivateKeyInfo` data and return it.
615 auto result = PAL::TASN1::encodedData(pkcs8, "");
617 return Exception { OperationError };
619 return WTFMove(result.value());
622 std::unique_ptr<KeyAlgorithm> CryptoKeyRSA::buildAlgorithm() const
624 String name = CryptoAlgorithmRegistry::singleton().name(algorithmIdentifier());
625 size_t modulusLength = getRSAModulusLength(m_platformKey);
626 Vector<uint8_t> publicExponent = getRSAKeyParameter(m_platformKey, "e");
628 if (m_restrictedToSpecificHash)
629 return std::make_unique<RsaHashedKeyAlgorithm>(name, modulusLength, WTFMove(publicExponent), CryptoAlgorithmRegistry::singleton().name(m_hash));
630 return std::make_unique<RsaKeyAlgorithm>(name, modulusLength, WTFMove(publicExponent));
633 std::unique_ptr<CryptoKeyData> CryptoKeyRSA::exportData() const
635 ASSERT(extractable());
638 case CryptoKeyType::Public:
639 return CryptoKeyDataRSAComponents::createPublic(getRSAKeyParameter(m_platformKey, "n"), getRSAKeyParameter(m_platformKey, "e"));
640 case CryptoKeyType::Private: {
642 [](gcry_sexp_t sexp, const char* name) -> gcry_mpi_t {
643 PAL::GCrypt::Handle<gcry_sexp_t> paramSexp(gcry_sexp_find_token(sexp, name, 0));
646 return gcry_sexp_nth_mpi(paramSexp, 1, GCRYMPI_FMT_USG);
649 PAL::GCrypt::Handle<gcry_mpi_t> dMPI(parameterMPI(m_platformKey, "d"));
650 // libgcrypt internally uses p and q such that p < q, while usually it's q < p.
651 // Switch the two primes here and continue with assuming the latter.
652 PAL::GCrypt::Handle<gcry_mpi_t> pMPI(parameterMPI(m_platformKey, "q"));
653 PAL::GCrypt::Handle<gcry_mpi_t> qMPI(parameterMPI(m_platformKey, "p"));
654 if (!dMPI || !pMPI || !qMPI)
657 CryptoKeyDataRSAComponents::PrimeInfo firstPrimeInfo;
658 if (auto data = mpiData(pMPI))
659 firstPrimeInfo.primeFactor = WTFMove(data.value());
661 CryptoKeyDataRSAComponents::PrimeInfo secondPrimeInfo;
662 if (auto data = mpiData(qMPI))
663 secondPrimeInfo.primeFactor = WTFMove(data.value());
665 // dp -- d mod (p - 1)
667 PAL::GCrypt::Handle<gcry_mpi_t> dpMPI(gcry_mpi_new(0));
668 PAL::GCrypt::Handle<gcry_mpi_t> pm1MPI(gcry_mpi_new(0));
669 gcry_mpi_sub_ui(pm1MPI, pMPI, 1);
670 gcry_mpi_mod(dpMPI, dMPI, pm1MPI);
672 if (auto data = mpiData(dpMPI))
673 firstPrimeInfo.factorCRTExponent = WTFMove(data.value());
676 // dq -- d mod (q - 1)
678 PAL::GCrypt::Handle<gcry_mpi_t> dqMPI(gcry_mpi_new(0));
679 PAL::GCrypt::Handle<gcry_mpi_t> qm1MPI(gcry_mpi_new(0));
680 gcry_mpi_sub_ui(qm1MPI, qMPI, 1);
681 gcry_mpi_mod(dqMPI, dMPI, qm1MPI);
683 if (auto data = mpiData(dqMPI))
684 secondPrimeInfo.factorCRTExponent = WTFMove(data.value());
687 // qi -- q^(-1) mod p
689 PAL::GCrypt::Handle<gcry_mpi_t> qiMPI(gcry_mpi_new(0));
690 gcry_mpi_invm(qiMPI, qMPI, pMPI);
692 if (auto data = mpiData(qiMPI))
693 secondPrimeInfo.factorCRTCoefficient = WTFMove(data.value());
696 Vector<uint8_t> privateExponent;
697 if (auto data = mpiData(dMPI))
698 privateExponent = WTFMove(data.value());
700 return CryptoKeyDataRSAComponents::createPrivateWithAdditionalData(
701 getRSAKeyParameter(m_platformKey, "n"), getRSAKeyParameter(m_platformKey, "e"), WTFMove(privateExponent),
702 WTFMove(firstPrimeInfo), WTFMove(secondPrimeInfo), Vector<CryptoKeyDataRSAComponents::PrimeInfo> { });
705 ASSERT_NOT_REACHED();
710 } // namespace WebCore
712 #endif // ENABLE(SUBTLE_CRYPTO)