Files
bun.sh/src/bun.js/bindings/webcrypto/CryptoKeyOKP.cpp
Ciro Spaciari 35109160ca feat(KeyObject) (#5940)
* oops

* createSecretKey but weird error

* use the right prototype, do not add a function called export lol

* HMAC JWT export + base64 fix

* Fix Equals, Fix Get KeySize, add complete export RSA

* fix RSA export

* add EC exports

* X25519 and ED25519 export + fixes

* fix default exports

* better asymmetricKeyType

* fix private exports

* fix symmetricKeySize

* createPublicKey validations + refactor

* jwt + der fixes

* oopsies

* add PEM into createPublicKey

* cleanup

* WIP

* bunch of fixes

* public from private + private OKP

* encrypted keys fixes

* oops

* fix clear tls error, add some support to jwk and other formats on publicEncrypt/publicDecrypt

* more fixes and tests working

* more fixes more tests

* more clear hmac errors

* more tests and fixes

* add generateKeyPair

* more tests passing, some skips

* fix EC key from private

* fix OKP JWK

* nodejs ignores ext and key_ops on KeyObject.exports

* add EC sign verify test

* some fixes

* add crypto.generateKeyPairSync(type, options)

* more fixes and more tests

* fix hmac tests

* jsonwebtoken tests

* oops

* oops2

* generated files

* revert package.json

* vm tests

* todos instead of failues

* toBunString -> toString

* undo simdutf

* improvements

* unlikely

* cleanup

* cleanup 2

* oops

* move _generateKeyPairSync checks to native
2023-10-07 15:22:45 -07:00

262 lines
9.3 KiB
C++

/*
* Copyright (C) 2023 Apple Inc. 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 "CryptoKeyOKP.h"
#if ENABLE(WEB_CRYPTO)
#include "CryptoAlgorithmRegistry.h"
#include "JsonWebKey.h"
#include <wtf/text/Base64.h>
namespace WebCore {
static const ASCIILiteral X25519 { "X25519"_s };
static const ASCIILiteral Ed25519 { "Ed25519"_s };
static constexpr size_t internalKeySizeInBytesFromNamedCurve(CryptoKeyOKP::NamedCurve curve, CryptoKeyType type)
{
switch (curve) {
case CryptoKeyOKP::NamedCurve::X25519:
return 32;
case CryptoKeyOKP::NamedCurve::Ed25519:
return type == CryptoKeyType::Private ? 64 : 32;
default:
return -1;
}
}
static constexpr size_t externalKeySizeInBytesFromNamedCurve(CryptoKeyOKP::NamedCurve curve)
{
switch (curve) {
case CryptoKeyOKP::NamedCurve::X25519:
case CryptoKeyOKP::NamedCurve::Ed25519:
return 32;
default:
return -1;
}
}
RefPtr<CryptoKeyOKP> CryptoKeyOKP::create(CryptoAlgorithmIdentifier identifier, NamedCurve curve, CryptoKeyType type, KeyMaterial&& platformKey, bool extractable, CryptoKeyUsageBitmap usages)
{
auto bytesExpectedInternal = internalKeySizeInBytesFromNamedCurve(curve, type);
if (bytesExpectedInternal == -1)
return nullptr;
if (platformKey.size() != bytesExpectedInternal) {
if (type != CryptoKeyType::Private || curve != NamedCurve::Ed25519)
return nullptr;
auto bytesExpectedExternal = externalKeySizeInBytesFromNamedCurve(curve);
if (bytesExpectedExternal == -1)
return nullptr;
// We need to match the internal format when importing a private key
// Import format only consists of 32 bytes of private key
// Internal format is private key + public key suffix
if (platformKey.size() == bytesExpectedExternal) {
auto&& privateKey = ed25519PrivateFromSeed(WTFMove(platformKey));
if (!privateKey.data())
return nullptr;
return adoptRef(*new CryptoKeyOKP(identifier, curve, type, WTFMove(privateKey), extractable, usages));
}
return nullptr;
}
return adoptRef(*new CryptoKeyOKP(identifier, curve, type, WTFMove(platformKey), extractable, usages));
}
CryptoKeyOKP::CryptoKeyOKP(CryptoAlgorithmIdentifier identifier, NamedCurve curve, CryptoKeyType type, KeyMaterial&& data, bool extractable, CryptoKeyUsageBitmap usages)
: CryptoKey(identifier, type, extractable, usages)
, m_curve(curve)
, m_data(data)
, m_exportKey(curve == NamedCurve::Ed25519 && type == CryptoKeyType::Private ? std::optional<Vector<uint8_t>>(Vector<uint8_t>(data.data(), 32)) : std::nullopt)
{
}
ExceptionOr<CryptoKeyPair> CryptoKeyOKP::generatePair(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, bool extractable, CryptoKeyUsageBitmap usages)
{
if (!isPlatformSupportedCurve(namedCurve))
return Exception { NotSupportedError };
auto result = platformGeneratePair(identifier, namedCurve, extractable, usages);
if (!result)
return Exception { OperationError };
return WTFMove(*result);
}
RefPtr<CryptoKeyOKP> CryptoKeyOKP::importRaw(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
{
if (!isPlatformSupportedCurve(namedCurve))
return nullptr;
return create(identifier, namedCurve, usages & CryptoKeyUsageSign ? CryptoKeyType::Private : CryptoKeyType::Public, WTFMove(keyData), extractable, usages);
}
RefPtr<CryptoKeyOKP> CryptoKeyOKP::importJwkInternal(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, JsonWebKey&& keyData, bool extractable, CryptoKeyUsageBitmap usages, bool onlyPublic) {
if (!isPlatformSupportedCurve(namedCurve))
return nullptr;
switch (namedCurve) {
case NamedCurve::Ed25519:
if (!keyData.d.isEmpty() && !onlyPublic) {
if (usages & (CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt | CryptoKeyUsageVerify | CryptoKeyUsageDeriveKey | CryptoKeyUsageDeriveBits | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey))
return nullptr;
} else {
if (usages & (CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt | CryptoKeyUsageSign | CryptoKeyUsageDeriveKey | CryptoKeyUsageDeriveBits | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey))
return nullptr;
}
if (keyData.kty != "OKP"_s)
return nullptr;
if (keyData.crv != "Ed25519"_s)
return nullptr;
if (usages && !keyData.use.isEmpty() && keyData.use != "sig"_s)
return nullptr;
if (keyData.key_ops && ((keyData.usages & usages) != usages))
return nullptr;
if (keyData.ext && !keyData.ext.value() && extractable)
return nullptr;
break;
case NamedCurve::X25519:
if (keyData.crv != "X25519"_s)
return nullptr;
// FIXME: Add further checks.
break;
}
if(!onlyPublic){
if (!keyData.d.isNull()) {
// FIXME: Validate keyData.x is paired with keyData.d
auto d = base64URLDecode(keyData.d);
if (!d)
return nullptr;
return create(identifier, namedCurve, CryptoKeyType::Private, WTFMove(*d), extractable, usages);
}
}
if (keyData.x.isNull())
return nullptr;
auto x = base64URLDecode(keyData.x);
if (!x)
return nullptr;
return create(identifier, namedCurve, CryptoKeyType::Public, WTFMove(*x), extractable, usages);
}
RefPtr<CryptoKeyOKP> CryptoKeyOKP::importPublicJwk(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, JsonWebKey&& keyData, bool extractable, CryptoKeyUsageBitmap usages) {
return importJwkInternal(identifier, namedCurve, WTFMove(keyData), extractable, usages, true);
}
RefPtr<CryptoKeyOKP> CryptoKeyOKP::importJwk(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, JsonWebKey&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
{
return importJwkInternal(identifier, namedCurve, WTFMove(keyData), extractable, usages, false);
}
ExceptionOr<Vector<uint8_t>> CryptoKeyOKP::exportRaw() const
{
if (type() != CryptoKey::Type::Public)
return Exception { InvalidAccessError };
auto&& result = platformExportRaw();
if (result.isEmpty())
return Exception { OperationError };
return WTFMove(result);
}
ExceptionOr<JsonWebKey> CryptoKeyOKP::exportJwk() const
{
JsonWebKey result;
result.kty = "OKP"_s;
switch (m_curve) {
case NamedCurve::X25519:
result.crv = X25519;
break;
case NamedCurve::Ed25519:
result.crv = Ed25519;
break;
}
result.key_ops = usages();
result.ext = extractable();
switch (type()) {
case CryptoKeyType::Private:
result.d = generateJwkD();
result.x = generateJwkX();
break;
case CryptoKeyType::Public:
result.x = generateJwkX();
break;
case CryptoKeyType::Secret:
return Exception { OperationError };
}
return result;
}
String CryptoKeyOKP::namedCurveString() const
{
switch (m_curve) {
case NamedCurve::X25519:
return X25519;
case NamedCurve::Ed25519:
return Ed25519;
}
ASSERT_NOT_REACHED();
return emptyString();
}
bool CryptoKeyOKP::isValidOKPAlgorithm(CryptoAlgorithmIdentifier algorithm)
{
return algorithm == CryptoAlgorithmIdentifier::Ed25519;
}
auto CryptoKeyOKP::algorithm() const -> KeyAlgorithm
{
CryptoKeyAlgorithm result;
// FIXME: This should be set to the actual algorithm name in the case of X25519
result.name = CryptoAlgorithmRegistry::singleton().name(algorithmIdentifier());
// This is commented out because the spec doesn't define the namedCurve field for OKP keys
// switch (m_curve) {
// case NamedCurve::X25519:
// result.namedCurve = X25519;
// break;
// case NamedCurve::Ed25519:
// result.namedCurve = Ed25519;
// break;
// }
return result;
}
} // namespace WebCore
#endif // ENABLE(WEB_CRYPTO)