blob: 1d965c689ca83e82932d8a92aa8a35890609e506 [file] [log] [blame]
/*
* Copyright (C) 2021 Sony Interactive Entertainment Inc.
*
* 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 "OpenSSLUtilities.h"
#include <JavaScriptCore/TypedArrayInlines.h>
#include <openssl/X509.h>
#include <openssl/evp.h>
namespace WebCore {
static size_t getRSAModulusLength(RSA* rsa)
{
if (!rsa)
return 0;
return BN_num_bytes(rsa->n) * 8;
}
RefPtr<CryptoKeyRSA> CryptoKeyRSA::create(CryptoAlgorithmIdentifier identifier, CryptoAlgorithmIdentifier hash, bool hasHash, const CryptoKeyRSAComponents& keyData, bool extractable, CryptoKeyUsageBitmap usages)
{
CryptoKeyType keyType;
switch (keyData.type()) {
case CryptoKeyRSAComponents::Type::Public:
keyType = CryptoKeyType::Public;
break;
case CryptoKeyRSAComponents::Type::Private:
keyType = CryptoKeyType::Private;
break;
default:
return nullptr;
}
// When creating a private key, we require the p and q prime information.
if (keyType == CryptoKeyType::Private && !keyData.hasAdditionalPrivateKeyParameters())
return nullptr;
// But we don't currently support creating keys with any additional prime information.
if (!keyData.otherPrimeInfos().isEmpty())
return nullptr;
// For both public and private keys, we need the public modulus and exponent.
if (keyData.modulus().isEmpty() || keyData.exponent().isEmpty())
return nullptr;
// For private keys, we require the private exponent, as well as p and q prime information.
if (keyType == CryptoKeyType::Private) {
if (keyData.privateExponent().isEmpty() || keyData.firstPrimeInfo().primeFactor.isEmpty() || keyData.secondPrimeInfo().primeFactor.isEmpty())
return nullptr;
}
auto rsa = RSAPtr(RSA_new());
if (!rsa)
return nullptr;
rsa->n = convertToBigNumber(rsa->n, keyData.modulus());
rsa->e = convertToBigNumber(rsa->e, keyData.exponent());
if (!rsa->n || !rsa->e)
return nullptr;
if (keyType == CryptoKeyType::Private) {
rsa->d = convertToBigNumber(rsa->d, keyData.privateExponent());
rsa->p = convertToBigNumber(rsa->p, keyData.firstPrimeInfo().primeFactor);
rsa->q = convertToBigNumber(rsa->q, keyData.secondPrimeInfo().primeFactor);
if (!rsa->d || !rsa->p || !rsa->q)
return nullptr;
// We set dmp1, dmpq1, and iqmp member of the RSA struct if the keyData has corresponding data.
// dmp1 -- d mod (p - 1)
if (!keyData.firstPrimeInfo().factorCRTExponent.isEmpty())
rsa->dmp1 = convertToBigNumber(rsa->dmp1, keyData.firstPrimeInfo().factorCRTExponent);
// dmq1 -- d mod (q - 1)
if (!keyData.secondPrimeInfo().factorCRTExponent.isEmpty())
rsa->dmq1 = convertToBigNumber(rsa->dmq1, keyData.secondPrimeInfo().factorCRTExponent);
// iqmp -- q^(-1) mod p
if (!keyData.secondPrimeInfo().factorCRTCoefficient.isEmpty())
rsa->iqmp = convertToBigNumber(rsa->iqmp, keyData.secondPrimeInfo().factorCRTCoefficient);
}
auto pkey = EvpPKeyPtr(EVP_PKEY_new());
if (!pkey)
return nullptr;
if (EVP_PKEY_set1_RSA(pkey.get(), rsa.get()) != 1)
return nullptr;
return adoptRef(new CryptoKeyRSA(identifier, hash, hasHash, keyType, WTFMove(pkey), extractable, usages));
}
CryptoKeyRSA::CryptoKeyRSA(CryptoAlgorithmIdentifier identifier, CryptoAlgorithmIdentifier hash, bool hasHash, CryptoKeyType type, PlatformRSAKeyContainer&& platformKey, bool extractable, CryptoKeyUsageBitmap usages)
: CryptoKey(identifier, type, extractable, usages)
, 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
{
RSA* rsa = EVP_PKEY_get0_RSA(m_platformKey.get());
if (!rsa)
return 0;
return getRSAModulusLength(rsa);
}
// 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 usages, KeyPairCallback&& callback, VoidCallback&& failureCallback, ScriptExecutionContext*)
{
// OpenSSL 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;
}
auto exponent = BIGNUMPtr(convertToBigNumber(nullptr, publicExponent));
auto privateRSA = RSAPtr(RSA_new());
if (!exponent || RSA_generate_key_ex(privateRSA.get(), modulusLength, exponent.get(), nullptr) <= 0) {
failureCallback();
return;
}
auto publicRSA = RSAPtr(RSAPublicKey_dup(privateRSA.get()));
if (!publicRSA) {
failureCallback();
return;
}
auto privatePKey = EvpPKeyPtr(EVP_PKEY_new());
if (EVP_PKEY_set1_RSA(privatePKey.get(), privateRSA.get()) <= 0) {
failureCallback();
return;
}
auto publicPKey = EvpPKeyPtr(EVP_PKEY_new());
if (EVP_PKEY_set1_RSA(publicPKey.get(), publicRSA.get()) <= 0) {
failureCallback();
return;
}
auto publicKey = CryptoKeyRSA::create(algorithm, hash, hasHash, CryptoKeyType::Public, WTFMove(publicPKey), true, usages);
auto privateKey = CryptoKeyRSA::create(algorithm, hash, hasHash, CryptoKeyType::Private, WTFMove(privatePKey), extractable, usages);
callback(CryptoKeyPair { WTFMove(publicKey), WTFMove(privateKey) });
}
RefPtr<CryptoKeyRSA> CryptoKeyRSA::importSpki(CryptoAlgorithmIdentifier identifier, std::optional<CryptoAlgorithmIdentifier> hash, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
{
// We need a local pointer variable to pass to d2i (DER to internal) functions().
const uint8_t* ptr = keyData.data();
// We use d2i_PUBKEY() to import a public key.
auto pkey = EvpPKeyPtr(d2i_PUBKEY(nullptr, &ptr, keyData.size()));
if (!pkey || EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA)
return nullptr;
return adoptRef(new CryptoKeyRSA(identifier, hash.value_or(CryptoAlgorithmIdentifier::SHA_1), !!hash, CryptoKeyType::Public, WTFMove(pkey), extractable, usages));
}
RefPtr<CryptoKeyRSA> CryptoKeyRSA::importPkcs8(CryptoAlgorithmIdentifier identifier, std::optional<CryptoAlgorithmIdentifier> hash, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
{
// We need a local pointer variable to pass to d2i (DER to internal) functions().
const uint8_t* ptr = keyData.data();
// We use d2i_PKCS8_PRIV_KEY_INFO() to import a private key.
auto p8inf = PKCS8PrivKeyInfoPtr(d2i_PKCS8_PRIV_KEY_INFO(nullptr, &ptr, keyData.size()));
if (!p8inf)
return nullptr;
auto pkey = EvpPKeyPtr(EVP_PKCS82PKEY(p8inf.get()));
if (!pkey || EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA)
return nullptr;
return adoptRef(new CryptoKeyRSA(identifier, hash.value_or(CryptoAlgorithmIdentifier::SHA_1), !!hash, CryptoKeyType::Private, WTFMove(pkey), extractable, usages));
}
ExceptionOr<Vector<uint8_t>> CryptoKeyRSA::exportSpki() const
{
if (type() != CryptoKeyType::Public)
return Exception { InvalidAccessError };
int len = i2d_PUBKEY(platformKey(), nullptr);
if (len < 0)
return Exception { OperationError };
Vector<uint8_t> keyData(len);
auto ptr = keyData.data();
if (i2d_PUBKEY(platformKey(), &ptr) < 0)
return Exception { OperationError };
return keyData;
}
ExceptionOr<Vector<uint8_t>> CryptoKeyRSA::exportPkcs8() const
{
if (type() != CryptoKeyType::Private)
return Exception { InvalidAccessError };
auto p8inf = PKCS8PrivKeyInfoPtr(EVP_PKEY2PKCS8(platformKey()));
if (!p8inf)
return Exception { OperationError };
int len = i2d_PKCS8_PRIV_KEY_INFO(p8inf.get(), nullptr);
if (len < 0)
return Exception { OperationError };
Vector<uint8_t> keyData(len);
auto ptr = keyData.data();
if (i2d_PKCS8_PRIV_KEY_INFO(p8inf.get(), &ptr) < 0)
return Exception { OperationError };
return keyData;
}
auto CryptoKeyRSA::algorithm() const -> KeyAlgorithm
{
RSA* rsa = EVP_PKEY_get0_RSA(platformKey());
auto modulusLength = rsa ? BN_num_bytes(rsa->n) * 8 : 0;
auto publicExponent = rsa ? convertToBytes(rsa->e) : Vector<uint8_t> { };
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
{
RSA* rsa = EVP_PKEY_get0_RSA(platformKey());
if (!rsa)
return nullptr;
switch (type()) {
case CryptoKeyType::Public:
// We need the public modulus and exponent for the public key.
if (!rsa->n || !rsa->e)
return nullptr;
return CryptoKeyRSAComponents::createPublic(convertToBytes(rsa->n), convertToBytes(rsa->e));
case CryptoKeyType::Private: {
// We need the public modulus, exponent, and private exponent, as well as p and q prime information.
if (!rsa->n || !rsa->e || !rsa->d || !rsa->p || !rsa->q)
return nullptr;
CryptoKeyRSAComponents::PrimeInfo firstPrimeInfo;
firstPrimeInfo.primeFactor = convertToBytes(rsa->p);
CryptoKeyRSAComponents::PrimeInfo secondPrimeInfo;
secondPrimeInfo.primeFactor = convertToBytes(rsa->q);
auto context = BNCtxPtr(BN_CTX_new());
// dmp1 -- d mod (p - 1)
if (rsa->dmp1)
firstPrimeInfo.factorCRTExponent = convertToBytes(rsa->dmp1);
else {
auto dmp1 = BIGNUMPtr(BN_new());
auto pm1 = BIGNUMPtr(BN_dup(rsa->p));
if (BN_sub_word(pm1.get(), 1) == 1 && BN_mod(dmp1.get(), rsa->d, pm1.get(), context.get()) == 1)
firstPrimeInfo.factorCRTExponent = convertToBytes(dmp1.get());
}
// dmq1 -- d mod (q - 1)
if (rsa->dmq1)
secondPrimeInfo.factorCRTExponent = convertToBytes(rsa->dmq1);
else {
auto dmq1 = BIGNUMPtr(BN_new());
auto qm1 = BIGNUMPtr(BN_dup(rsa->q));
if (BN_sub_word(qm1.get(), 1) == 1 && BN_mod(dmq1.get(), rsa->d, qm1.get(), context.get()) == 1)
secondPrimeInfo.factorCRTExponent = convertToBytes(dmq1.get());
}
// iqmp -- q^(-1) mod p
if (rsa->iqmp)
secondPrimeInfo.factorCRTCoefficient = convertToBytes(rsa->iqmp);
else {
auto iqmp = BIGNUMPtr(BN_mod_inverse(nullptr, rsa->q, rsa->p, context.get()));
if (iqmp)
secondPrimeInfo.factorCRTCoefficient = convertToBytes(iqmp.get());
}
return CryptoKeyRSAComponents::createPrivateWithAdditionalData(
convertToBytes(rsa->n), convertToBytes(rsa->e), convertToBytes(rsa->d),
WTFMove(firstPrimeInfo), WTFMove(secondPrimeInfo), Vector<CryptoKeyRSAComponents::PrimeInfo> { });
}
default:
ASSERT_NOT_REACHED();
return nullptr;
}
}
} // namespace WebCore
#endif // ENABLE(WEB_CRYPTO)