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 "NotImplemented.h"
36 #include "ScriptExecutionContext.h"
37 #include <pal/crypto/gcrypt/Handle.h>
38 #include <pal/crypto/gcrypt/Utilities.h>
39 #include <pal/crypto/tasn1/Utilities.h>
43 static size_t getRSAModulusLength(gcry_sexp_t keySexp)
45 // Retrieve the s-expression token for the public modulus N of the given RSA key.
46 PAL::GCrypt::Handle<gcry_sexp_t> nSexp(gcry_sexp_find_token(keySexp, "n", 0));
50 // Retrieve the MPI length for the corresponding s-expression token, in bits.
51 auto length = mpiLength(nSexp);
58 static Vector<uint8_t> getRSAKeyParameter(gcry_sexp_t keySexp, const char* name)
60 // Retrieve the s-expression token for the specified parameter of the given RSA key.
61 PAL::GCrypt::Handle<gcry_sexp_t> paramSexp(gcry_sexp_find_token(keySexp, name, 0));
65 // Retrieve the MPI data for the corresponding s-expression token.
66 auto data = mpiData(paramSexp);
70 return WTFMove(data.value());
73 RefPtr<CryptoKeyRSA> CryptoKeyRSA::create(CryptoAlgorithmIdentifier identifier, CryptoAlgorithmIdentifier hash, bool hasHash, const CryptoKeyDataRSAComponents& keyData, bool extractable, CryptoKeyUsageBitmap usages)
75 // When creating a private key, we require the p and q prime information.
76 if (keyData.type() == CryptoKeyDataRSAComponents::Type::Private && !keyData.hasAdditionalPrivateKeyParameters())
79 // But we don't currently support creating keys with any additional prime information.
80 if (!keyData.otherPrimeInfos().isEmpty())
83 // Validate the key data.
87 // For both public and private keys, we need the public modulus and exponent.
88 valid &= !keyData.modulus().isEmpty() && !keyData.exponent().isEmpty();
90 // For private keys, we require the private exponent, as well as p and q prime information.
91 if (keyData.type() == CryptoKeyDataRSAComponents::Type::Private)
92 valid &= !keyData.privateExponent().isEmpty() && !keyData.firstPrimeInfo().primeFactor.isEmpty() && !keyData.secondPrimeInfo().primeFactor.isEmpty();
98 CryptoKeyType keyType;
99 switch (keyData.type()) {
100 case CryptoKeyDataRSAComponents::Type::Public:
101 keyType = CryptoKeyType::Public;
103 case CryptoKeyDataRSAComponents::Type::Private:
104 keyType = CryptoKeyType::Private;
108 // Construct the key s-expression, using the data that's available.
109 PAL::GCrypt::Handle<gcry_sexp_t> keySexp;
111 gcry_error_t error = GPG_ERR_NO_ERROR;
114 case CryptoKeyType::Public:
115 error = gcry_sexp_build(&keySexp, nullptr, "(public-key(rsa(n %b)(e %b)))",
116 keyData.modulus().size(), keyData.modulus().data(),
117 keyData.exponent().size(), keyData.exponent().data());
119 case CryptoKeyType::Private:
120 if (keyData.hasAdditionalPrivateKeyParameters()) {
121 error = gcry_sexp_build(&keySexp, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)))",
122 keyData.modulus().size(), keyData.modulus().data(),
123 keyData.exponent().size(), keyData.exponent().data(),
124 keyData.privateExponent().size(), keyData.privateExponent().data(),
125 keyData.secondPrimeInfo().primeFactor.size(), keyData.secondPrimeInfo().primeFactor.data(),
126 keyData.firstPrimeInfo().primeFactor.size(), keyData.firstPrimeInfo().primeFactor.data());
130 error = gcry_sexp_build(&keySexp, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)))",
131 keyData.modulus().size(), keyData.modulus().data(),
132 keyData.exponent().size(), keyData.exponent().data(),
133 keyData.privateExponent().size(), keyData.privateExponent().data());
135 case CryptoKeyType::Secret:
136 ASSERT_NOT_REACHED();
140 if (error != GPG_ERR_NO_ERROR) {
141 PAL::GCrypt::logError(error);
146 return adoptRef(new CryptoKeyRSA(identifier, hash, hasHash, keyType, keySexp.release(), extractable, usages));
149 CryptoKeyRSA::CryptoKeyRSA(CryptoAlgorithmIdentifier identifier, CryptoAlgorithmIdentifier hash, bool hasHash, CryptoKeyType type, PlatformRSAKey platformKey, bool extractable, CryptoKeyUsageBitmap usage)
150 : CryptoKey(identifier, type, extractable, usage)
151 , m_platformKey(platformKey)
152 , m_restrictedToSpecificHash(hasHash)
157 CryptoKeyRSA::~CryptoKeyRSA()
160 PAL::GCrypt::HandleDeleter<gcry_sexp_t>()(m_platformKey);
163 bool CryptoKeyRSA::isRestrictedToHash(CryptoAlgorithmIdentifier& identifier) const
165 if (!m_restrictedToSpecificHash)
172 size_t CryptoKeyRSA::keySizeInBits() const
174 return getRSAModulusLength(m_platformKey);
177 // Convert the exponent vector to a 32-bit value, if possible.
178 static std::optional<uint32_t> exponentVectorToUInt32(const Vector<uint8_t>& exponent)
180 if (exponent.size() > 4) {
181 if (std::any_of(exponent.begin(), exponent.end() - 4, [](uint8_t element) { return !!element; }))
186 for (size_t size = exponent.size(), i = std::min<size_t>(4, size); i > 0; --i) {
188 result += exponent[size - i];
194 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)
196 // libgcrypt doesn't report an error if the exponent is smaller than three or even.
197 auto e = exponentVectorToUInt32(publicExponent);
198 if (!e || *e < 3 || !(*e & 0x1)) {
203 // libgcrypt doesn't support generating primes of less than 16 bits.
204 if (modulusLength < 16) {
209 PAL::GCrypt::Handle<gcry_sexp_t> genkeySexp;
210 gcry_error_t error = gcry_sexp_build(&genkeySexp, nullptr, "(genkey(rsa(nbits %d)(rsa-use-e %d)))", modulusLength, *e);
211 if (error != GPG_ERR_NO_ERROR) {
212 PAL::GCrypt::logError(error);
217 PAL::GCrypt::Handle<gcry_sexp_t> keyPairSexp;
218 error = gcry_pk_genkey(&keyPairSexp, genkeySexp);
219 if (error != GPG_ERR_NO_ERROR) {
220 PAL::GCrypt::logError(error);
225 PAL::GCrypt::Handle<gcry_sexp_t> publicKeySexp(gcry_sexp_find_token(keyPairSexp, "public-key", 0));
226 PAL::GCrypt::Handle<gcry_sexp_t> privateKeySexp(gcry_sexp_find_token(keyPairSexp, "private-key", 0));
227 if (!publicKeySexp || !privateKeySexp) {
234 [algorithm, hash, hasHash, extractable, usage, publicKeySexp = publicKeySexp.release(), privateKeySexp = privateKeySexp.release(), callback = WTFMove(callback)](ScriptExecutionContext& context) mutable {
235 auto publicKey = CryptoKeyRSA::create(algorithm, hash, hasHash, CryptoKeyType::Public, publicKeySexp, true, usage);
236 auto privateKey = CryptoKeyRSA::create(algorithm, hash, hasHash, CryptoKeyType::Private, privateKeySexp, extractable, usage);
238 callback(CryptoKeyPair { WTFMove(publicKey), WTFMove(privateKey) });
243 static bool supportedAlgorithmIdentifier(const uint8_t* data, size_t size)
245 // FIXME: This is far from sufficient. Per the spec, when importing for key algorithm
246 // - RSASSA-PKCS1-v1_5:
247 // - rsaEncryption, sha{1,256,384,512}WithRSAEncryption OIDs must be supported
248 // - in case of sha{1,256,384,512}WithRSAEncryption OIDs the specified hash algorithm
249 // has to match the algorithm in the OID
251 // - rsaEncryption, id-RSASSA-PSS OIDs must be supported
252 // - in case of id-RSASSA-PSS OID the parameters field of AlgorithmIdentifier has
253 // to be decoded as RSASSA-PSS-params ASN.1 structure, and the hashAlgorithm field
254 // of that structure has to contain one of id-sha{1,256,384,512} OIDs that match
255 // the specified hash algorithm
257 // - rsaEncryption, id-RSAES-OAEP OIDS must be supported
258 // - in case of id-RSAES-OAEP OID the parameters field of AlgorithmIdentifier has
259 // to be decoded as RSAES-OAEP-params ASN.1 structure, and the hashAlgorithm field
260 // of that structure has to contain one of id-sha{1,256,384,512} OIDs that match
261 // the specified hash algorithm
263 if (CryptoConstants::matches(data, size, CryptoConstants::s_rsaEncryptionIdentifier))
265 if (CryptoConstants::matches(data, size, CryptoConstants::s_RSAES_OAEPIdentifier))
266 return false; // Not yet supported.
267 if (CryptoConstants::matches(data, size, CryptoConstants::s_RSASSA_PSSIdentifier))
268 return false; // Not yet supported.
272 RefPtr<CryptoKeyRSA> CryptoKeyRSA::importSpki(CryptoAlgorithmIdentifier identifier, std::optional<CryptoAlgorithmIdentifier> hash, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
274 // Decode the `SubjectPublicKeyInfo` structure using the provided key data.
275 PAL::TASN1::Structure spki;
276 if (!PAL::TASN1::decodeStructure(&spki, "WebCrypto.SubjectPublicKeyInfo", keyData))
279 // Validate `algorithm.algorithm`.
281 auto algorithm = PAL::TASN1::elementData(spki, "algorithm.algorithm");
285 if (!supportedAlgorithmIdentifier(algorithm->data(), algorithm->size()))
289 // Decode the `RSAPublicKey` structure using the `subjectPublicKey` data.
290 PAL::TASN1::Structure rsaPublicKey;
292 auto subjectPublicKey = PAL::TASN1::elementData(spki, "subjectPublicKey");
293 if (!subjectPublicKey)
296 if (!PAL::TASN1::decodeStructure(&rsaPublicKey, "WebCrypto.RSAPublicKey", *subjectPublicKey))
300 // Retrieve the `modulus` and `publicExponent` data and embed it into the `public-key` s-expression.
301 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
303 auto modulus = PAL::TASN1::elementData(rsaPublicKey, "modulus");
304 auto publicExponent = PAL::TASN1::elementData(rsaPublicKey, "publicExponent");
305 if (!modulus || !publicExponent)
308 gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(public-key(rsa(n %b)(e %b)))",
309 modulus->size(), modulus->data(), publicExponent->size(), publicExponent->data());
310 if (error != GPG_ERR_NO_ERROR) {
311 PAL::GCrypt::logError(error);
316 return adoptRef(new CryptoKeyRSA(identifier, hash.value_or(CryptoAlgorithmIdentifier::SHA_1), !!hash, CryptoKeyType::Public, platformKey.release(), extractable, usages));
319 RefPtr<CryptoKeyRSA> CryptoKeyRSA::importPkcs8(CryptoAlgorithmIdentifier identifier, std::optional<CryptoAlgorithmIdentifier> hash, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
321 // Decode the `PrivateKeyInfo` structure using the provided key data.
322 PAL::TASN1::Structure pkcs8;
323 if (!PAL::TASN1::decodeStructure(&pkcs8, "WebCrypto.PrivateKeyInfo", keyData))
326 // Validate `version`.
328 auto version = PAL::TASN1::elementData(pkcs8, "version");
332 if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version0))
336 // Validate `privateKeyAlgorithm.algorithm`.
338 auto algorithm = PAL::TASN1::elementData(pkcs8, "privateKeyAlgorithm.algorithm");
342 if (!supportedAlgorithmIdentifier(algorithm->data(), algorithm->size()))
346 // Decode the `RSAPrivateKey` structure using the `privateKey` data.
347 PAL::TASN1::Structure rsaPrivateKey;
349 auto privateKey = PAL::TASN1::elementData(pkcs8, "privateKey");
353 if (!PAL::TASN1::decodeStructure(&rsaPrivateKey, "WebCrypto.RSAPrivateKey", *privateKey))
357 // Validate `privateKey.version`.
359 auto version = PAL::TASN1::elementData(rsaPrivateKey, "version");
363 if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version0))
367 // Retrieve the `modulus`, `publicExponent`, `privateExponent`, `prime1`, `prime2`,
368 // `exponent1`, `exponent2` and `coefficient` data and embed it into the `public-key` s-expression.
369 PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
371 auto modulus = PAL::TASN1::elementData(rsaPrivateKey, "modulus");
372 auto publicExponent = PAL::TASN1::elementData(rsaPrivateKey, "publicExponent");
373 auto privateExponent = PAL::TASN1::elementData(rsaPrivateKey, "privateExponent");
374 auto prime1 = PAL::TASN1::elementData(rsaPrivateKey, "prime1");
375 auto prime2 = PAL::TASN1::elementData(rsaPrivateKey, "prime2");
376 auto exponent1 = PAL::TASN1::elementData(rsaPrivateKey, "exponent1");
377 auto exponent2 = PAL::TASN1::elementData(rsaPrivateKey, "exponent2");
378 auto coefficient = PAL::TASN1::elementData(rsaPrivateKey, "coefficient");
380 if (!modulus || !publicExponent || !privateExponent
381 || !prime1 || !prime2 || !exponent1 || !exponent2 || !coefficient)
384 // libgcrypt inverts the use of p and q parameters, so we have to recalculate the `coefficient` value.
385 PAL::GCrypt::Handle<gcry_mpi_t> uMPI(gcry_mpi_new(0));
387 PAL::GCrypt::Handle<gcry_mpi_t> pMPI;
388 gcry_error_t error = gcry_mpi_scan(&pMPI, GCRYMPI_FMT_USG, prime1->data(), prime1->size(), nullptr);
389 if (error != GPG_ERR_NO_ERROR)
392 PAL::GCrypt::Handle<gcry_mpi_t> qMPI;
393 error = gcry_mpi_scan(&qMPI, GCRYMPI_FMT_USG, prime2->data(), prime2->size(), nullptr);
394 if (error != GPG_ERR_NO_ERROR)
397 gcry_mpi_invm(uMPI, qMPI, pMPI);
400 gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)(u %M)))",
401 modulus->size(), modulus->data(),
402 publicExponent->size(), publicExponent->data(),
403 privateExponent->size(), privateExponent->data(),
404 prime2->size(), prime2->data(), prime1->size(), prime1->data(), uMPI.handle());
405 if (error != GPG_ERR_NO_ERROR) {
406 PAL::GCrypt::logError(error);
411 return adoptRef(new CryptoKeyRSA(identifier, hash.value_or(CryptoAlgorithmIdentifier::SHA_1), !!hash, CryptoKeyType::Private, platformKey.release(), extractable, usages));
414 ExceptionOr<Vector<uint8_t>> CryptoKeyRSA::exportSpki() const
416 if (type() != CryptoKeyType::Public)
417 return Exception { InvalidAccessError };
419 PAL::TASN1::Structure rsaPublicKey;
421 // Create the `RSAPublicKey` structure.
422 if (!PAL::TASN1::createStructure("WebCrypto.RSAPublicKey", &rsaPublicKey))
423 return Exception { OperationError };
425 // Retrieve the modulus and public exponent s-expressions.
426 PAL::GCrypt::Handle<gcry_sexp_t> modulusSexp(gcry_sexp_find_token(m_platformKey, "n", 0));
427 PAL::GCrypt::Handle<gcry_sexp_t> publicExponentSexp(gcry_sexp_find_token(m_platformKey, "e", 0));
428 if (!modulusSexp || !publicExponentSexp)
429 return Exception { OperationError };
431 // Retrieve MPI data for the modulus and public exponent components.
432 auto modulus = mpiSignedData(modulusSexp);
433 auto publicExponent = mpiSignedData(publicExponentSexp);
434 if (!modulus || !publicExponent)
435 return Exception { OperationError };
437 // Write out the modulus data under `modulus`.
438 if (!PAL::TASN1::writeElement(rsaPublicKey, "modulus", modulus->data(), modulus->size()))
439 return Exception { OperationError };
441 // Write out the public exponent data under `publicExponent`.
442 if (!PAL::TASN1::writeElement(rsaPublicKey, "publicExponent", publicExponent->data(), publicExponent->size()))
443 return Exception { OperationError };
446 PAL::TASN1::Structure spki;
448 // Create the `SubjectPublicKeyInfo` structure.
449 if (!PAL::TASN1::createStructure("WebCrypto.SubjectPublicKeyInfo", &spki))
450 return Exception { OperationError };
452 // Write out the id-rsaEncryption identifier under `algorithm.algorithm`.
453 // FIXME: In case the key algorithm is:
455 // - this should write out id-RSASSA-PSS, along with setting `algorithm.parameters`
456 // to a RSASSA-PSS-params structure
458 // - this should write out id-RSAES-OAEP, along with setting `algorithm.parameters`
459 // to a RSAES-OAEP-params structure
460 if (!PAL::TASN1::writeElement(spki, "algorithm.algorithm", CryptoConstants::s_rsaEncryptionIdentifier.data(), 1))
461 return Exception { OperationError };
463 // Write out the null value under `algorithm.parameters`.
464 if (!PAL::TASN1::writeElement(spki, "algorithm.parameters", CryptoConstants::s_asn1NullValue.data(), CryptoConstants::s_asn1NullValue.size()))
465 return Exception { OperationError };
467 // Write out the `RSAPublicKey` data under `subjectPublicKey`. Because this is a
468 // bit string parameter, the data size has to be multiplied by 8.
470 auto data = PAL::TASN1::encodedData(rsaPublicKey, "");
471 if (!data || !PAL::TASN1::writeElement(spki, "subjectPublicKey", data->data(), data->size() * 8))
472 return Exception { OperationError };
476 // Retrieve the encoded `SubjectPublicKeyInfo` data and return it.
477 auto result = PAL::TASN1::encodedData(spki, "");
479 return Exception { OperationError };
481 return WTFMove(result.value());
484 ExceptionOr<Vector<uint8_t>> CryptoKeyRSA::exportPkcs8() const
486 if (type() != CryptoKeyType::Private)
487 return Exception { InvalidAccessError };
489 PAL::TASN1::Structure rsaPrivateKey;
491 // Create the `RSAPrivateKey` structure.
492 if (!PAL::TASN1::createStructure("WebCrypto.RSAPrivateKey", &rsaPrivateKey))
493 return Exception { OperationError };
495 // Write out '0' under `version`.
496 if (!PAL::TASN1::writeElement(rsaPrivateKey, "version", "0", 0))
497 return Exception { OperationError };
499 // Retrieve the `n`, `e`, `d`, `q` and `p` s-expression tokens. libgcrypt swaps the usage of
500 // the p and q primes internally, so we adjust the lookup accordingly.
501 PAL::GCrypt::Handle<gcry_sexp_t> nSexp(gcry_sexp_find_token(m_platformKey, "n", 0));
502 PAL::GCrypt::Handle<gcry_sexp_t> eSexp(gcry_sexp_find_token(m_platformKey, "e", 0));
503 PAL::GCrypt::Handle<gcry_sexp_t> dSexp(gcry_sexp_find_token(m_platformKey, "d", 0));
504 PAL::GCrypt::Handle<gcry_sexp_t> pSexp(gcry_sexp_find_token(m_platformKey, "q", 0));
505 PAL::GCrypt::Handle<gcry_sexp_t> qSexp(gcry_sexp_find_token(m_platformKey, "p", 0));
506 if (!nSexp || !eSexp || !dSexp || !pSexp || !qSexp)
507 return Exception { OperationError };
509 // Write the MPI data of retrieved s-expression tokens under `modulus`, `publicExponent`,
510 // `privateExponent`, `prime1` and `prime2`.
512 auto modulus = mpiSignedData(nSexp);
513 auto publicExponent = mpiSignedData(eSexp);
514 auto privateExponent = mpiSignedData(dSexp);
515 auto prime1 = mpiSignedData(pSexp);
516 auto prime2 = mpiSignedData(qSexp);
517 if (!modulus || !publicExponent || !privateExponent || !prime1 || !prime2)
518 return Exception { OperationError };
520 if (!PAL::TASN1::writeElement(rsaPrivateKey, "modulus", modulus->data(), modulus->size())
521 || !PAL::TASN1::writeElement(rsaPrivateKey, "publicExponent", publicExponent->data(), publicExponent->size())
522 || !PAL::TASN1::writeElement(rsaPrivateKey, "privateExponent", privateExponent->data(), privateExponent->size())
523 || !PAL::TASN1::writeElement(rsaPrivateKey, "prime1", prime1->data(), prime1->size())
524 || !PAL::TASN1::writeElement(rsaPrivateKey, "prime2", prime2->data(), prime2->size()))
525 return Exception { OperationError };
528 // Manually compute the MPI values for the `exponent1`, `exponent2` and `coefficient`
529 // parameters. Again note the swapped usage of the `p` and `q` s-expression parameters.
531 PAL::GCrypt::Handle<gcry_mpi_t> dMPI(gcry_sexp_nth_mpi(dSexp, 1, GCRYMPI_FMT_USG));
532 PAL::GCrypt::Handle<gcry_mpi_t> pMPI(gcry_sexp_nth_mpi(pSexp, 1, GCRYMPI_FMT_USG));
533 PAL::GCrypt::Handle<gcry_mpi_t> qMPI(gcry_sexp_nth_mpi(qSexp, 1, GCRYMPI_FMT_USG));
534 if (!dMPI || !pMPI || !qMPI)
535 return Exception { OperationError };
539 PAL::GCrypt::Handle<gcry_mpi_t> dpMPI(gcry_mpi_set_ui(nullptr, 0));
540 PAL::GCrypt::Handle<gcry_mpi_t> pm1MPI(gcry_mpi_set(nullptr, pMPI));
541 gcry_mpi_sub_ui(pm1MPI, pm1MPI, 1);
542 gcry_mpi_mod(dpMPI, dMPI, pm1MPI);
544 auto dp = mpiSignedData(dpMPI);
545 if (!dp || !PAL::TASN1::writeElement(rsaPrivateKey, "exponent1", dp->data(), dp->size()))
546 return Exception { OperationError };
551 PAL::GCrypt::Handle<gcry_mpi_t> dqMPI(gcry_mpi_set_ui(nullptr, 0));
552 PAL::GCrypt::Handle<gcry_mpi_t> qm1MPI(gcry_mpi_set(nullptr, qMPI));
553 gcry_mpi_sub_ui(qm1MPI, qm1MPI, 1);
554 gcry_mpi_mod(dqMPI, dMPI, qm1MPI);
556 auto dq = mpiSignedData(dqMPI);
557 if (!dq || !PAL::TASN1::writeElement(rsaPrivateKey, "exponent2", dq->data(), dq->size()))
558 return Exception { OperationError };
563 PAL::GCrypt::Handle<gcry_mpi_t> qiMPI(gcry_mpi_set_ui(nullptr, 0));
564 gcry_mpi_invm(qiMPI, qMPI, pMPI);
566 auto qi = mpiSignedData(qiMPI);
567 if (!qi || !PAL::TASN1::writeElement(rsaPrivateKey, "coefficient", qi->data(), qi->size()))
568 return Exception { OperationError };
572 // Eliminate the optional `otherPrimeInfos` element.
573 // FIXME: this should be supported in the future, if there is such information available.
574 if (!PAL::TASN1::writeElement(rsaPrivateKey, "otherPrimeInfos", nullptr, 0))
575 return Exception { OperationError };
578 PAL::TASN1::Structure pkcs8;
580 // Create the `PrivateKeyInfo` structure.
581 if (!PAL::TASN1::createStructure("WebCrypto.PrivateKeyInfo", &pkcs8))
582 return Exception { OperationError };
584 // Write out '0' under `version`.
585 if (!PAL::TASN1::writeElement(pkcs8, "version", "0", 0))
586 return Exception { OperationError };
588 // Write out the id-rsaEncryption identifier under `algorithm.algorithm`.
589 // FIXME: In case the key algorithm is:
591 // - this should write out id-RSASSA-PSS, along with setting `algorithm.parameters`
592 // to a RSASSA-PSS-params structure
594 // - this should write out id-RSAES-OAEP, along with setting `algorithm.parameters`
595 // to a RSAES-OAEP-params structure
596 if (!PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.algorithm", "1.2.840.113549.1.1.1", 1))
597 return Exception { OperationError };
599 // Write out a null value under `algorithm.parameters`.
600 if (!PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.parameters", CryptoConstants::s_asn1NullValue.data(), CryptoConstants::s_asn1NullValue.size()))
601 return Exception { OperationError };
603 // Write out the `RSAPrivateKey` data under `privateKey`.
605 auto data = PAL::TASN1::encodedData(rsaPrivateKey, "");
606 if (!data || !PAL::TASN1::writeElement(pkcs8, "privateKey", data->data(), data->size()))
607 return Exception { OperationError };
610 // Eliminate the optional `attributes` element.
611 if (!PAL::TASN1::writeElement(pkcs8, "attributes", nullptr, 0))
612 return Exception { OperationError };
615 // Retrieve the encoded `PrivateKeyInfo` data and return it.
616 auto result = PAL::TASN1::encodedData(pkcs8, "");
618 return Exception { OperationError };
620 return WTFMove(result.value());
623 std::unique_ptr<KeyAlgorithm> CryptoKeyRSA::buildAlgorithm() const
625 String name = CryptoAlgorithmRegistry::singleton().name(algorithmIdentifier());
626 size_t modulusLength = getRSAModulusLength(m_platformKey);
627 Vector<uint8_t> publicExponent = getRSAKeyParameter(m_platformKey, "e");
629 if (m_restrictedToSpecificHash)
630 return std::make_unique<RsaHashedKeyAlgorithm>(name, modulusLength, WTFMove(publicExponent), CryptoAlgorithmRegistry::singleton().name(m_hash));
631 return std::make_unique<RsaKeyAlgorithm>(name, modulusLength, WTFMove(publicExponent));
634 std::unique_ptr<CryptoKeyData> CryptoKeyRSA::exportData() const
636 ASSERT(extractable());
639 case CryptoKeyType::Public:
640 return CryptoKeyDataRSAComponents::createPublic(getRSAKeyParameter(m_platformKey, "n"), getRSAKeyParameter(m_platformKey, "e"));
641 case CryptoKeyType::Private: {
643 [](gcry_sexp_t sexp, const char* name) -> gcry_mpi_t {
644 PAL::GCrypt::Handle<gcry_sexp_t> paramSexp(gcry_sexp_find_token(sexp, name, 0));
647 return gcry_sexp_nth_mpi(paramSexp, 1, GCRYMPI_FMT_USG);
650 PAL::GCrypt::Handle<gcry_mpi_t> dMPI(parameterMPI(m_platformKey, "d"));
651 // libgcrypt internally uses p and q such that p < q, while usually it's q < p.
652 // Switch the two primes here and continue with assuming the latter.
653 PAL::GCrypt::Handle<gcry_mpi_t> pMPI(parameterMPI(m_platformKey, "q"));
654 PAL::GCrypt::Handle<gcry_mpi_t> qMPI(parameterMPI(m_platformKey, "p"));
655 if (!dMPI || !pMPI || !qMPI)
658 CryptoKeyDataRSAComponents::PrimeInfo firstPrimeInfo;
659 if (auto data = mpiData(pMPI))
660 firstPrimeInfo.primeFactor = WTFMove(data.value());
662 CryptoKeyDataRSAComponents::PrimeInfo secondPrimeInfo;
663 if (auto data = mpiData(qMPI))
664 secondPrimeInfo.primeFactor = WTFMove(data.value());
666 // dp -- d mod (p - 1)
668 PAL::GCrypt::Handle<gcry_mpi_t> dpMPI(gcry_mpi_new(0));
669 PAL::GCrypt::Handle<gcry_mpi_t> pm1MPI(gcry_mpi_new(0));
670 gcry_mpi_sub_ui(pm1MPI, pMPI, 1);
671 gcry_mpi_mod(dpMPI, dMPI, pm1MPI);
673 if (auto data = mpiData(dpMPI))
674 firstPrimeInfo.factorCRTExponent = WTFMove(data.value());
677 // dq -- d mod (q - 1)
679 PAL::GCrypt::Handle<gcry_mpi_t> dqMPI(gcry_mpi_new(0));
680 PAL::GCrypt::Handle<gcry_mpi_t> qm1MPI(gcry_mpi_new(0));
681 gcry_mpi_sub_ui(qm1MPI, qMPI, 1);
682 gcry_mpi_mod(dqMPI, dMPI, qm1MPI);
684 if (auto data = mpiData(dqMPI))
685 secondPrimeInfo.factorCRTExponent = WTFMove(data.value());
688 // qi -- q^(-1) mod p
690 PAL::GCrypt::Handle<gcry_mpi_t> qiMPI(gcry_mpi_new(0));
691 gcry_mpi_invm(qiMPI, qMPI, pMPI);
693 if (auto data = mpiData(qiMPI))
694 secondPrimeInfo.factorCRTCoefficient = WTFMove(data.value());
697 Vector<uint8_t> privateExponent;
698 if (auto data = mpiData(dMPI))
699 privateExponent = WTFMove(data.value());
701 return CryptoKeyDataRSAComponents::createPrivateWithAdditionalData(
702 getRSAKeyParameter(m_platformKey, "n"), getRSAKeyParameter(m_platformKey, "e"), WTFMove(privateExponent),
703 WTFMove(firstPrimeInfo), WTFMove(secondPrimeInfo), Vector<CryptoKeyDataRSAComponents::PrimeInfo> { });
706 ASSERT_NOT_REACHED();
711 } // namespace WebCore
713 #endif // ENABLE(SUBTLE_CRYPTO)