blob: 03315807db7f77c792454c27f741550d16ade40d [file] [log] [blame]
/*
* Copyright (C) 2014 Igalia S.L. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "CryptoKeyRSA.h"
#if ENABLE(WEB_CRYPTO)
#include "CryptoAlgorithmRegistry.h"
#include "CryptoKeyPair.h"
#include "CryptoKeyRSAComponents.h"
#include "GCryptUtilities.h"
#include "ScriptExecutionContext.h"
#include <JavaScriptCore/GenericTypedArrayViewInlines.h>
#include <JavaScriptCore/HeapInlines.h>
#include <JavaScriptCore/JSGenericTypedArrayViewInlines.h>
#include <pal/crypto/gcrypt/Utilities.h>
#include <pal/crypto/tasn1/Utilities.h>
namespace WebCore {
static size_t getRSAModulusLength(gcry_sexp_t keySexp)
{
// Retrieve the s-expression token for the public modulus N of the given RSA key.
PAL::GCrypt::Handle<gcry_sexp_t> nSexp(gcry_sexp_find_token(keySexp, "n", 0));
if (!nSexp)
return 0;
// Retrieve the MPI length for the corresponding s-expression token, in bits.
auto length = mpiLength(nSexp);
if (!length)
return 0;
return *length * 8;
}
static Vector<uint8_t> getRSAKeyParameter(gcry_sexp_t keySexp, const char* name)
{
// Retrieve the s-expression token for the specified parameter of the given RSA key.
PAL::GCrypt::Handle<gcry_sexp_t> paramSexp(gcry_sexp_find_token(keySexp, name, 0));
if (!paramSexp)
return { };
// Retrieve the MPI data for the corresponding s-expression token.
auto data = mpiData(paramSexp);
if (!data)
return { };
return WTFMove(data.value());
}
RefPtr<CryptoKeyRSA> CryptoKeyRSA::create(CryptoAlgorithmIdentifier identifier, CryptoAlgorithmIdentifier hash, bool hasHash, const CryptoKeyRSAComponents& keyData, bool extractable, CryptoKeyUsageBitmap usages)
{
// When creating a private key, we require the p and q prime information.
if (keyData.type() == CryptoKeyRSAComponents::Type::Private && !keyData.hasAdditionalPrivateKeyParameters())
return nullptr;
// But we don't currently support creating keys with any additional prime information.
if (!keyData.otherPrimeInfos().isEmpty())
return nullptr;
// Validate the key data.
{
bool valid = true;
// For both public and private keys, we need the public modulus and exponent.
valid &= !keyData.modulus().isEmpty() && !keyData.exponent().isEmpty();
// For private keys, we require the private exponent, as well as p and q prime information.
if (keyData.type() == CryptoKeyRSAComponents::Type::Private)
valid &= !keyData.privateExponent().isEmpty() && !keyData.firstPrimeInfo().primeFactor.isEmpty() && !keyData.secondPrimeInfo().primeFactor.isEmpty();
if (!valid)
return nullptr;
}
CryptoKeyType keyType;
switch (keyData.type()) {
case CryptoKeyRSAComponents::Type::Public:
keyType = CryptoKeyType::Public;
break;
case CryptoKeyRSAComponents::Type::Private:
keyType = CryptoKeyType::Private;
break;
}
// Construct the key s-expression, using the data that's available.
PAL::GCrypt::Handle<gcry_sexp_t> keySexp;
{
gcry_error_t error = GPG_ERR_NO_ERROR;
switch (keyType) {
case CryptoKeyType::Public:
error = gcry_sexp_build(&keySexp, nullptr, "(public-key(rsa(n %b)(e %b)))",
keyData.modulus().size(), keyData.modulus().data(),
keyData.exponent().size(), keyData.exponent().data());
break;
case CryptoKeyType::Private:
if (keyData.hasAdditionalPrivateKeyParameters()) {
error = gcry_sexp_build(&keySexp, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)))",
keyData.modulus().size(), keyData.modulus().data(),
keyData.exponent().size(), keyData.exponent().data(),
keyData.privateExponent().size(), keyData.privateExponent().data(),
keyData.secondPrimeInfo().primeFactor.size(), keyData.secondPrimeInfo().primeFactor.data(),
keyData.firstPrimeInfo().primeFactor.size(), keyData.firstPrimeInfo().primeFactor.data());
break;
}
error = gcry_sexp_build(&keySexp, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)))",
keyData.modulus().size(), keyData.modulus().data(),
keyData.exponent().size(), keyData.exponent().data(),
keyData.privateExponent().size(), keyData.privateExponent().data());
break;
case CryptoKeyType::Secret:
ASSERT_NOT_REACHED();
return nullptr;
}
if (error != GPG_ERR_NO_ERROR) {
PAL::GCrypt::logError(error);
return nullptr;
}
}
return adoptRef(new CryptoKeyRSA(identifier, hash, hasHash, keyType, PlatformRSAKeyContainer(keySexp.release()), extractable, usages));
}
CryptoKeyRSA::CryptoKeyRSA(CryptoAlgorithmIdentifier identifier, CryptoAlgorithmIdentifier hash, bool hasHash, CryptoKeyType type, PlatformRSAKeyContainer&& platformKey, bool extractable, CryptoKeyUsageBitmap usage)
: CryptoKey(identifier, type, extractable, usage)
, m_platformKey(WTFMove(platformKey))
, m_restrictedToSpecificHash(hasHash)
, m_hash(hash)
{
}
bool CryptoKeyRSA::isRestrictedToHash(CryptoAlgorithmIdentifier& identifier) const
{
if (!m_restrictedToSpecificHash)
return false;
identifier = m_hash;
return true;
}
size_t CryptoKeyRSA::keySizeInBits() const
{
return getRSAModulusLength(m_platformKey.get());
}
// Convert the exponent vector to a 32-bit value, if possible.
static std::optional<uint32_t> exponentVectorToUInt32(const Vector<uint8_t>& exponent)
{
if (exponent.size() > 4) {
if (std::any_of(exponent.begin(), exponent.end() - 4, [](uint8_t element) { return !!element; }))
return std::nullopt;
}
uint32_t result = 0;
for (size_t size = exponent.size(), i = std::min<size_t>(4, size); i > 0; --i) {
result <<= 8;
result += exponent[size - i];
}
return result;
}
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)
{
// libgcrypt doesn't report an error if the exponent is smaller than three or even.
auto e = exponentVectorToUInt32(publicExponent);
if (!e || *e < 3 || !(*e & 0x1)) {
failureCallback();
return;
}
// libgcrypt doesn't support generating primes of less than 16 bits.
if (modulusLength < 16) {
failureCallback();
return;
}
PAL::GCrypt::Handle<gcry_sexp_t> genkeySexp;
gcry_error_t error = gcry_sexp_build(&genkeySexp, nullptr, "(genkey(rsa(nbits %d)(rsa-use-e %d)))", modulusLength, *e);
if (error != GPG_ERR_NO_ERROR) {
PAL::GCrypt::logError(error);
failureCallback();
return;
}
PAL::GCrypt::Handle<gcry_sexp_t> keyPairSexp;
error = gcry_pk_genkey(&keyPairSexp, genkeySexp);
if (error != GPG_ERR_NO_ERROR) {
PAL::GCrypt::logError(error);
failureCallback();
return;
}
PAL::GCrypt::Handle<gcry_sexp_t> publicKeySexp(gcry_sexp_find_token(keyPairSexp, "public-key", 0));
PAL::GCrypt::Handle<gcry_sexp_t> privateKeySexp(gcry_sexp_find_token(keyPairSexp, "private-key", 0));
if (!publicKeySexp || !privateKeySexp) {
failureCallback();
return;
}
context->postTask(
[algorithm, hash, hasHash, extractable, usage, publicKeySexp = PlatformRSAKeyContainer(publicKeySexp.release()), privateKeySexp = PlatformRSAKeyContainer(privateKeySexp.release()), callback = WTFMove(callback)](auto&) mutable {
auto publicKey = CryptoKeyRSA::create(algorithm, hash, hasHash, CryptoKeyType::Public, WTFMove(publicKeySexp), true, usage);
auto privateKey = CryptoKeyRSA::create(algorithm, hash, hasHash, CryptoKeyType::Private, WTFMove(privateKeySexp), extractable, usage);
callback(CryptoKeyPair { WTFMove(publicKey), WTFMove(privateKey) });
});
}
static bool supportedAlgorithmIdentifier(const uint8_t* data, size_t size)
{
// FIXME: This is far from sufficient. Per the spec, when importing for key algorithm
// - RSASSA-PKCS1-v1_5:
// - rsaEncryption, sha{1,256,384,512}WithRSAEncryption OIDs must be supported
// - in case of sha{1,256,384,512}WithRSAEncryption OIDs the specified hash algorithm
// has to match the algorithm in the OID
// - RSA-PSS:
// - rsaEncryption, id-RSASSA-PSS OIDs must be supported
// - in case of id-RSASSA-PSS OID the parameters field of AlgorithmIdentifier has
// to be decoded as RSASSA-PSS-params ASN.1 structure, and the hashAlgorithm field
// of that structure has to contain one of id-sha{1,256,384,512} OIDs that match
// the specified hash algorithm
// - RSA-OAEP:
// - rsaEncryption, id-RSAES-OAEP OIDS must be supported
// - in case of id-RSAES-OAEP OID the parameters field of AlgorithmIdentifier has
// to be decoded as RSAES-OAEP-params ASN.1 structure, and the hashAlgorithm field
// of that structure has to contain one of id-sha{1,256,384,512} OIDs that match
// the specified hash algorithm
if (CryptoConstants::matches(data, size, CryptoConstants::s_rsaEncryptionIdentifier))
return true;
if (CryptoConstants::matches(data, size, CryptoConstants::s_RSAES_OAEPIdentifier))
return false; // Not yet supported.
if (CryptoConstants::matches(data, size, CryptoConstants::s_RSASSA_PSSIdentifier))
return false; // Not yet supported.
return false;
}
RefPtr<CryptoKeyRSA> CryptoKeyRSA::importSpki(CryptoAlgorithmIdentifier identifier, std::optional<CryptoAlgorithmIdentifier> hash, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
{
// Decode the `SubjectPublicKeyInfo` structure using the provided key data.
PAL::TASN1::Structure spki;
if (!PAL::TASN1::decodeStructure(&spki, "WebCrypto.SubjectPublicKeyInfo", keyData))
return nullptr;
// Validate `algorithm.algorithm`.
{
auto algorithm = PAL::TASN1::elementData(spki, "algorithm.algorithm");
if (!algorithm)
return nullptr;
if (!supportedAlgorithmIdentifier(algorithm->data(), algorithm->size()))
return nullptr;
}
// Decode the `RSAPublicKey` structure using the `subjectPublicKey` data.
PAL::TASN1::Structure rsaPublicKey;
{
auto subjectPublicKey = PAL::TASN1::elementData(spki, "subjectPublicKey");
if (!subjectPublicKey)
return nullptr;
if (!PAL::TASN1::decodeStructure(&rsaPublicKey, "WebCrypto.RSAPublicKey", *subjectPublicKey))
return nullptr;
}
// Retrieve the `modulus` and `publicExponent` data and embed it into the `public-key` s-expression.
PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
{
auto modulus = PAL::TASN1::elementData(rsaPublicKey, "modulus");
auto publicExponent = PAL::TASN1::elementData(rsaPublicKey, "publicExponent");
if (!modulus || !publicExponent)
return nullptr;
gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(public-key(rsa(n %b)(e %b)))",
modulus->size(), modulus->data(), publicExponent->size(), publicExponent->data());
if (error != GPG_ERR_NO_ERROR) {
PAL::GCrypt::logError(error);
return nullptr;
}
}
return adoptRef(new CryptoKeyRSA(identifier, hash.value_or(CryptoAlgorithmIdentifier::SHA_1), !!hash, CryptoKeyType::Public, PlatformRSAKeyContainer(platformKey.release()), extractable, usages));
}
RefPtr<CryptoKeyRSA> CryptoKeyRSA::importPkcs8(CryptoAlgorithmIdentifier identifier, std::optional<CryptoAlgorithmIdentifier> hash, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
{
// Decode the `PrivateKeyInfo` structure using the provided key data.
PAL::TASN1::Structure pkcs8;
if (!PAL::TASN1::decodeStructure(&pkcs8, "WebCrypto.PrivateKeyInfo", keyData))
return nullptr;
// Validate `version`.
{
auto version = PAL::TASN1::elementData(pkcs8, "version");
if (!version)
return nullptr;
if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version0))
return nullptr;
}
// Validate `privateKeyAlgorithm.algorithm`.
{
auto algorithm = PAL::TASN1::elementData(pkcs8, "privateKeyAlgorithm.algorithm");
if (!algorithm)
return nullptr;
if (!supportedAlgorithmIdentifier(algorithm->data(), algorithm->size()))
return nullptr;
}
// Decode the `RSAPrivateKey` structure using the `privateKey` data.
PAL::TASN1::Structure rsaPrivateKey;
{
auto privateKey = PAL::TASN1::elementData(pkcs8, "privateKey");
if (!privateKey)
return nullptr;
if (!PAL::TASN1::decodeStructure(&rsaPrivateKey, "WebCrypto.RSAPrivateKey", *privateKey))
return nullptr;
}
// Validate `privateKey.version`.
{
auto version = PAL::TASN1::elementData(rsaPrivateKey, "version");
if (!version)
return nullptr;
if (!CryptoConstants::matches(version->data(), version->size(), CryptoConstants::s_asn1Version0))
return nullptr;
}
// Retrieve the `modulus`, `publicExponent`, `privateExponent`, `prime1`, `prime2`,
// `exponent1`, `exponent2` and `coefficient` data and embed it into the `public-key` s-expression.
PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
{
auto modulus = PAL::TASN1::elementData(rsaPrivateKey, "modulus");
auto publicExponent = PAL::TASN1::elementData(rsaPrivateKey, "publicExponent");
auto privateExponent = PAL::TASN1::elementData(rsaPrivateKey, "privateExponent");
auto prime1 = PAL::TASN1::elementData(rsaPrivateKey, "prime1");
auto prime2 = PAL::TASN1::elementData(rsaPrivateKey, "prime2");
auto exponent1 = PAL::TASN1::elementData(rsaPrivateKey, "exponent1");
auto exponent2 = PAL::TASN1::elementData(rsaPrivateKey, "exponent2");
auto coefficient = PAL::TASN1::elementData(rsaPrivateKey, "coefficient");
if (!modulus || !publicExponent || !privateExponent
|| !prime1 || !prime2 || !exponent1 || !exponent2 || !coefficient)
return nullptr;
// libgcrypt inverts the use of p and q parameters, so we have to recalculate the `coefficient` value.
PAL::GCrypt::Handle<gcry_mpi_t> uMPI(gcry_mpi_new(0));
{
PAL::GCrypt::Handle<gcry_mpi_t> pMPI;
gcry_error_t error = gcry_mpi_scan(&pMPI, GCRYMPI_FMT_USG, prime1->data(), prime1->size(), nullptr);
if (error != GPG_ERR_NO_ERROR)
return nullptr;
PAL::GCrypt::Handle<gcry_mpi_t> qMPI;
error = gcry_mpi_scan(&qMPI, GCRYMPI_FMT_USG, prime2->data(), prime2->size(), nullptr);
if (error != GPG_ERR_NO_ERROR)
return nullptr;
gcry_mpi_invm(uMPI, qMPI, pMPI);
}
gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)(u %M)))",
modulus->size(), modulus->data(),
publicExponent->size(), publicExponent->data(),
privateExponent->size(), privateExponent->data(),
prime2->size(), prime2->data(), prime1->size(), prime1->data(), uMPI.handle());
if (error != GPG_ERR_NO_ERROR) {
PAL::GCrypt::logError(error);
return nullptr;
}
}
return adoptRef(new CryptoKeyRSA(identifier, hash.value_or(CryptoAlgorithmIdentifier::SHA_1), !!hash, CryptoKeyType::Private, PlatformRSAKeyContainer(platformKey.release()), extractable, usages));
}
ExceptionOr<Vector<uint8_t>> CryptoKeyRSA::exportSpki() const
{
if (type() != CryptoKeyType::Public)
return Exception { InvalidAccessError };
PAL::TASN1::Structure rsaPublicKey;
{
// Create the `RSAPublicKey` structure.
if (!PAL::TASN1::createStructure("WebCrypto.RSAPublicKey", &rsaPublicKey))
return Exception { OperationError };
// Retrieve the modulus and public exponent s-expressions.
PAL::GCrypt::Handle<gcry_sexp_t> modulusSexp(gcry_sexp_find_token(m_platformKey.get(), "n", 0));
PAL::GCrypt::Handle<gcry_sexp_t> publicExponentSexp(gcry_sexp_find_token(m_platformKey.get(), "e", 0));
if (!modulusSexp || !publicExponentSexp)
return Exception { OperationError };
// Retrieve MPI data for the modulus and public exponent components.
auto modulus = mpiSignedData(modulusSexp);
auto publicExponent = mpiSignedData(publicExponentSexp);
if (!modulus || !publicExponent)
return Exception { OperationError };
// Write out the modulus data under `modulus`.
if (!PAL::TASN1::writeElement(rsaPublicKey, "modulus", modulus->data(), modulus->size()))
return Exception { OperationError };
// Write out the public exponent data under `publicExponent`.
if (!PAL::TASN1::writeElement(rsaPublicKey, "publicExponent", publicExponent->data(), publicExponent->size()))
return Exception { OperationError };
}
PAL::TASN1::Structure spki;
{
// Create the `SubjectPublicKeyInfo` structure.
if (!PAL::TASN1::createStructure("WebCrypto.SubjectPublicKeyInfo", &spki))
return Exception { OperationError };
// Write out the id-rsaEncryption identifier under `algorithm.algorithm`.
// FIXME: In case the key algorithm is:
// - RSA-PSS:
// - this should write out id-RSASSA-PSS, along with setting `algorithm.parameters`
// to a RSASSA-PSS-params structure
// - RSA-OAEP:
// - this should write out id-RSAES-OAEP, along with setting `algorithm.parameters`
// to a RSAES-OAEP-params structure
if (!PAL::TASN1::writeElement(spki, "algorithm.algorithm", CryptoConstants::s_rsaEncryptionIdentifier.data(), 1))
return Exception { OperationError };
// Write out the null value under `algorithm.parameters`.
if (!PAL::TASN1::writeElement(spki, "algorithm.parameters", CryptoConstants::s_asn1NullValue.data(), CryptoConstants::s_asn1NullValue.size()))
return Exception { OperationError };
// Write out the `RSAPublicKey` data under `subjectPublicKey`. Because this is a
// bit string parameter, the data size has to be multiplied by 8.
{
auto data = PAL::TASN1::encodedData(rsaPublicKey, "");
if (!data || !PAL::TASN1::writeElement(spki, "subjectPublicKey", data->data(), data->size() * 8))
return Exception { OperationError };
}
}
// Retrieve the encoded `SubjectPublicKeyInfo` data and return it.
auto result = PAL::TASN1::encodedData(spki, "");
if (!result)
return Exception { OperationError };
return WTFMove(result.value());
}
ExceptionOr<Vector<uint8_t>> CryptoKeyRSA::exportPkcs8() const
{
if (type() != CryptoKeyType::Private)
return Exception { InvalidAccessError };
PAL::TASN1::Structure rsaPrivateKey;
{
// Create the `RSAPrivateKey` structure.
if (!PAL::TASN1::createStructure("WebCrypto.RSAPrivateKey", &rsaPrivateKey))
return Exception { OperationError };
// Write out '0' under `version`.
if (!PAL::TASN1::writeElement(rsaPrivateKey, "version", "0", 0))
return Exception { OperationError };
// Retrieve the `n`, `e`, `d`, `q` and `p` s-expression tokens. libgcrypt swaps the usage of
// the p and q primes internally, so we adjust the lookup accordingly.
PAL::GCrypt::Handle<gcry_sexp_t> nSexp(gcry_sexp_find_token(m_platformKey.get(), "n", 0));
PAL::GCrypt::Handle<gcry_sexp_t> eSexp(gcry_sexp_find_token(m_platformKey.get(), "e", 0));
PAL::GCrypt::Handle<gcry_sexp_t> dSexp(gcry_sexp_find_token(m_platformKey.get(), "d", 0));
PAL::GCrypt::Handle<gcry_sexp_t> pSexp(gcry_sexp_find_token(m_platformKey.get(), "q", 0));
PAL::GCrypt::Handle<gcry_sexp_t> qSexp(gcry_sexp_find_token(m_platformKey.get(), "p", 0));
if (!nSexp || !eSexp || !dSexp || !pSexp || !qSexp)
return Exception { OperationError };
// Write the MPI data of retrieved s-expression tokens under `modulus`, `publicExponent`,
// `privateExponent`, `prime1` and `prime2`.
{
auto modulus = mpiSignedData(nSexp);
auto publicExponent = mpiSignedData(eSexp);
auto privateExponent = mpiSignedData(dSexp);
auto prime1 = mpiSignedData(pSexp);
auto prime2 = mpiSignedData(qSexp);
if (!modulus || !publicExponent || !privateExponent || !prime1 || !prime2)
return Exception { OperationError };
if (!PAL::TASN1::writeElement(rsaPrivateKey, "modulus", modulus->data(), modulus->size())
|| !PAL::TASN1::writeElement(rsaPrivateKey, "publicExponent", publicExponent->data(), publicExponent->size())
|| !PAL::TASN1::writeElement(rsaPrivateKey, "privateExponent", privateExponent->data(), privateExponent->size())
|| !PAL::TASN1::writeElement(rsaPrivateKey, "prime1", prime1->data(), prime1->size())
|| !PAL::TASN1::writeElement(rsaPrivateKey, "prime2", prime2->data(), prime2->size()))
return Exception { OperationError };
}
// Manually compute the MPI values for the `exponent1`, `exponent2` and `coefficient`
// parameters. Again note the swapped usage of the `p` and `q` s-expression parameters.
{
PAL::GCrypt::Handle<gcry_mpi_t> dMPI(gcry_sexp_nth_mpi(dSexp, 1, GCRYMPI_FMT_USG));
PAL::GCrypt::Handle<gcry_mpi_t> pMPI(gcry_sexp_nth_mpi(pSexp, 1, GCRYMPI_FMT_USG));
PAL::GCrypt::Handle<gcry_mpi_t> qMPI(gcry_sexp_nth_mpi(qSexp, 1, GCRYMPI_FMT_USG));
if (!dMPI || !pMPI || !qMPI)
return Exception { OperationError };
// `exponent1`
{
PAL::GCrypt::Handle<gcry_mpi_t> dpMPI(gcry_mpi_set_ui(nullptr, 0));
PAL::GCrypt::Handle<gcry_mpi_t> pm1MPI(gcry_mpi_set(nullptr, pMPI));
gcry_mpi_sub_ui(pm1MPI, pm1MPI, 1);
gcry_mpi_mod(dpMPI, dMPI, pm1MPI);
auto dp = mpiSignedData(dpMPI);
if (!dp || !PAL::TASN1::writeElement(rsaPrivateKey, "exponent1", dp->data(), dp->size()))
return Exception { OperationError };
}
// `exponent2`
{
PAL::GCrypt::Handle<gcry_mpi_t> dqMPI(gcry_mpi_set_ui(nullptr, 0));
PAL::GCrypt::Handle<gcry_mpi_t> qm1MPI(gcry_mpi_set(nullptr, qMPI));
gcry_mpi_sub_ui(qm1MPI, qm1MPI, 1);
gcry_mpi_mod(dqMPI, dMPI, qm1MPI);
auto dq = mpiSignedData(dqMPI);
if (!dq || !PAL::TASN1::writeElement(rsaPrivateKey, "exponent2", dq->data(), dq->size()))
return Exception { OperationError };
}
// `coefficient`
{
PAL::GCrypt::Handle<gcry_mpi_t> qiMPI(gcry_mpi_set_ui(nullptr, 0));
gcry_mpi_invm(qiMPI, qMPI, pMPI);
auto qi = mpiSignedData(qiMPI);
if (!qi || !PAL::TASN1::writeElement(rsaPrivateKey, "coefficient", qi->data(), qi->size()))
return Exception { OperationError };
}
}
// Eliminate the optional `otherPrimeInfos` element.
// FIXME: this should be supported in the future, if there is such information available.
if (!PAL::TASN1::writeElement(rsaPrivateKey, "otherPrimeInfos", nullptr, 0))
return Exception { OperationError };
}
PAL::TASN1::Structure pkcs8;
{
// Create the `PrivateKeyInfo` structure.
if (!PAL::TASN1::createStructure("WebCrypto.PrivateKeyInfo", &pkcs8))
return Exception { OperationError };
// Write out '0' under `version`.
if (!PAL::TASN1::writeElement(pkcs8, "version", "0", 0))
return Exception { OperationError };
// Write out the id-rsaEncryption identifier under `algorithm.algorithm`.
// FIXME: In case the key algorithm is:
// - RSA-PSS:
// - this should write out id-RSASSA-PSS, along with setting `algorithm.parameters`
// to a RSASSA-PSS-params structure
// - RSA-OAEP:
// - this should write out id-RSAES-OAEP, along with setting `algorithm.parameters`
// to a RSAES-OAEP-params structure
if (!PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.algorithm", "1.2.840.113549.1.1.1", 1))
return Exception { OperationError };
// Write out a null value under `algorithm.parameters`.
if (!PAL::TASN1::writeElement(pkcs8, "privateKeyAlgorithm.parameters", CryptoConstants::s_asn1NullValue.data(), CryptoConstants::s_asn1NullValue.size()))
return Exception { OperationError };
// Write out the `RSAPrivateKey` data under `privateKey`.
{
auto data = PAL::TASN1::encodedData(rsaPrivateKey, "");
if (!data || !PAL::TASN1::writeElement(pkcs8, "privateKey", data->data(), data->size()))
return Exception { OperationError };
}
// Eliminate the optional `attributes` element.
if (!PAL::TASN1::writeElement(pkcs8, "attributes", nullptr, 0))
return Exception { OperationError };
}
// Retrieve the encoded `PrivateKeyInfo` data and return it.
auto result = PAL::TASN1::encodedData(pkcs8, "");
if (!result)
return Exception { OperationError };
return WTFMove(result.value());
}
auto CryptoKeyRSA::algorithm() const -> KeyAlgorithm
{
auto modulusLength = getRSAModulusLength(m_platformKey.get());
auto publicExponent = getRSAKeyParameter(m_platformKey.get(), "e");
if (m_restrictedToSpecificHash) {
CryptoRsaHashedKeyAlgorithm result;
result.name = CryptoAlgorithmRegistry::singleton().name(algorithmIdentifier());
result.modulusLength = modulusLength;
result.publicExponent = Uint8Array::tryCreate(publicExponent.data(), publicExponent.size());
result.hash.name = CryptoAlgorithmRegistry::singleton().name(m_hash);
return result;
}
CryptoRsaKeyAlgorithm result;
result.name = CryptoAlgorithmRegistry::singleton().name(algorithmIdentifier());
result.modulusLength = modulusLength;
result.publicExponent = Uint8Array::tryCreate(publicExponent.data(), publicExponent.size());
return result;
}
std::unique_ptr<CryptoKeyRSAComponents> CryptoKeyRSA::exportData() const
{
switch (type()) {
case CryptoKeyType::Public:
return CryptoKeyRSAComponents::createPublic(getRSAKeyParameter(m_platformKey.get(), "n"), getRSAKeyParameter(m_platformKey.get(), "e"));
case CryptoKeyType::Private: {
auto parameterMPI =
[](gcry_sexp_t sexp, const char* name) -> gcry_mpi_t {
PAL::GCrypt::Handle<gcry_sexp_t> paramSexp(gcry_sexp_find_token(sexp, name, 0));
if (!paramSexp)
return nullptr;
return gcry_sexp_nth_mpi(paramSexp, 1, GCRYMPI_FMT_USG);
};
PAL::GCrypt::Handle<gcry_mpi_t> dMPI(parameterMPI(m_platformKey.get(), "d"));
// libgcrypt internally uses p and q such that p < q, while usually it's q < p.
// Switch the two primes here and continue with assuming the latter.
PAL::GCrypt::Handle<gcry_mpi_t> pMPI(parameterMPI(m_platformKey.get(), "q"));
PAL::GCrypt::Handle<gcry_mpi_t> qMPI(parameterMPI(m_platformKey.get(), "p"));
if (!dMPI || !pMPI || !qMPI)
return nullptr;
CryptoKeyRSAComponents::PrimeInfo firstPrimeInfo;
if (auto data = mpiData(pMPI))
firstPrimeInfo.primeFactor = WTFMove(data.value());
CryptoKeyRSAComponents::PrimeInfo secondPrimeInfo;
if (auto data = mpiData(qMPI))
secondPrimeInfo.primeFactor = WTFMove(data.value());
// dp -- d mod (p - 1)
{
PAL::GCrypt::Handle<gcry_mpi_t> dpMPI(gcry_mpi_new(0));
PAL::GCrypt::Handle<gcry_mpi_t> pm1MPI(gcry_mpi_new(0));
gcry_mpi_sub_ui(pm1MPI, pMPI, 1);
gcry_mpi_mod(dpMPI, dMPI, pm1MPI);
if (auto data = mpiData(dpMPI))
firstPrimeInfo.factorCRTExponent = WTFMove(data.value());
}
// dq -- d mod (q - 1)
{
PAL::GCrypt::Handle<gcry_mpi_t> dqMPI(gcry_mpi_new(0));
PAL::GCrypt::Handle<gcry_mpi_t> qm1MPI(gcry_mpi_new(0));
gcry_mpi_sub_ui(qm1MPI, qMPI, 1);
gcry_mpi_mod(dqMPI, dMPI, qm1MPI);
if (auto data = mpiData(dqMPI))
secondPrimeInfo.factorCRTExponent = WTFMove(data.value());
}
// qi -- q^(-1) mod p
{
PAL::GCrypt::Handle<gcry_mpi_t> qiMPI(gcry_mpi_new(0));
gcry_mpi_invm(qiMPI, qMPI, pMPI);
if (auto data = mpiData(qiMPI))
secondPrimeInfo.factorCRTCoefficient = WTFMove(data.value());
}
Vector<uint8_t> privateExponent;
if (auto data = mpiData(dMPI))
privateExponent = WTFMove(data.value());
return CryptoKeyRSAComponents::createPrivateWithAdditionalData(
getRSAKeyParameter(m_platformKey.get(), "n"), getRSAKeyParameter(m_platformKey.get(), "e"), WTFMove(privateExponent),
WTFMove(firstPrimeInfo), WTFMove(secondPrimeInfo), Vector<CryptoKeyRSAComponents::PrimeInfo> { });
}
default:
ASSERT_NOT_REACHED();
return nullptr;
}
}
} // namespace WebCore
#endif // ENABLE(WEB_CRYPTO)