diff options
author | 2023-02-02 02:42:58 -0800 | |
---|---|---|
committer | 2023-02-02 02:42:58 -0800 | |
commit | fe3131cf1734f20fc08187e3890dabbb970a1289 (patch) | |
tree | d55022adf443255658e123a48cd67293905bdfcd | |
parent | 2bc4858346087b987321236b2caf0331e6175d94 (diff) | |
download | bun-fe3131cf1734f20fc08187e3890dabbb970a1289.tar.gz bun-fe3131cf1734f20fc08187e3890dabbb970a1289.tar.zst bun-fe3131cf1734f20fc08187e3890dabbb970a1289.zip |
ed25519
-rw-r--r-- | src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.cpp | 77 | ||||
-rw-r--r-- | src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.h | 56 | ||||
-rw-r--r-- | src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.cpp | 211 | ||||
-rw-r--r-- | src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.h | 61 | ||||
-rw-r--r-- | src/bun.js/bindings/webcrypto/CryptoAlgorithmIdentifier.h | 3 | ||||
-rw-r--r-- | src/bun.js/bindings/webcrypto/CryptoKey.cpp | 3 | ||||
-rw-r--r-- | src/bun.js/bindings/webcrypto/CryptoKey.h | 10 | ||||
-rw-r--r-- | src/bun.js/bindings/webcrypto/CryptoKeyOKP.cpp | 214 | ||||
-rw-r--r-- | src/bun.js/bindings/webcrypto/CryptoKeyOKP.h | 92 | ||||
-rw-r--r-- | src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp | 319 | ||||
-rw-r--r-- | src/bun.js/bindings/webcrypto/SubtleCrypto.cpp | 44 |
11 files changed, 1079 insertions, 11 deletions
diff --git a/src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.cpp b/src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.cpp new file mode 100644 index 000000000..d0fb25b74 --- /dev/null +++ b/src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017 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 "CommonCryptoDERUtilities.h" + +#if ENABLE(WEB_CRYPTO) + +namespace WebCore { + +size_t bytesUsedToEncodedLength(uint8_t octet) +{ + if (octet < MaxLengthInOneByte) + return 1; + return octet - MaxLengthInOneByte + 1; +} + +size_t extraBytesNeededForEncodedLength(size_t length) +{ + if (!length) + return 0; + size_t result = 1; + while (result < sizeof(length) && length >= (1 << (result * 8))) + result += 1; + return result; +} + +void addEncodedASN1Length(Vector<uint8_t>& in, size_t length) +{ + if (length < MaxLengthInOneByte) { + in.append(length); + return; + } + + size_t extraBytes = extraBytesNeededForEncodedLength(length); + in.append(128 + extraBytes); // 128 is used to set the first bit of this byte. + + size_t lastPosition = in.size() + extraBytes - 1; + in.grow(in.size() + extraBytes); + for (size_t i = 0; i < extraBytes; i++) { + in[lastPosition - i] = length & 0xff; + length = length >> 8; + } +} + +size_t bytesNeededForEncodedLength(size_t length) +{ + if (length < MaxLengthInOneByte) + return 1; + return 1 + extraBytesNeededForEncodedLength(length); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_CRYPTO) diff --git a/src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.h b/src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.h new file mode 100644 index 000000000..2f991740f --- /dev/null +++ b/src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 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. + */ + +#pragma once + +#include <wtf/Vector.h> + +#if ENABLE(WEB_CRYPTO) + +// FIXME: <rdar://problem/31618371> +// The following constants and functions are for customized DER implementations. +// They are not intended to be used outside Crypto codes, and should be removed +// once the above bug is fixed. +namespace WebCore { + +// Per X.690 08/2015: https://www.itu.int/rec/T-REC-X.680-X.693/en +static const unsigned char BitStringMark = 0x03; +static const unsigned char IntegerMark = 0x02; +static const unsigned char OctetStringMark = 0x04; +static const unsigned char SequenceMark = 0x30; +// Version 0. Per https://tools.ietf.org/html/rfc5208#section-5 +static const unsigned char Version[] = {0x02, 0x01, 0x00}; + +static const unsigned char InitialOctet = 0x00; +static const size_t MaxLengthInOneByte = 128; + +size_t bytesUsedToEncodedLength(uint8_t); +size_t extraBytesNeededForEncodedLength(size_t); +void addEncodedASN1Length(Vector<uint8_t>&, size_t); +size_t bytesNeededForEncodedLength(size_t); + +} // namespace WebCore + +#endif // ENABLE(WEB_CRYPTO) diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.cpp new file mode 100644 index 000000000..111f57560 --- /dev/null +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.cpp @@ -0,0 +1,211 @@ +/* + * 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 "CryptoAlgorithmEd25519.h" + +#if ENABLE(WEB_CRYPTO) + +#include "CryptoKeyOKP.h" +#include <JavaScriptCore/JSCJSValueInlines.h> +#include <wtf/CrossThreadCopier.h> + +// -- BUN -- +#include <openssl/curve25519.h> + +namespace WebCore { + +static ExceptionOr<Vector<uint8_t>> signEd25519(const Vector<uint8_t>& sk, size_t len, const Vector<uint8_t>& data) +{ + uint8_t pk[32]; + uint8_t newSignature[64]; + + ED25519_sign(newSignature, data.data(), data.size(), sk.data()); + return Vector<uint8_t>(newSignature, 64); +} + +ExceptionOr<Vector<uint8_t>> CryptoAlgorithmEd25519::platformSign(const CryptoKeyOKP& key, const Vector<uint8_t>& data) +{ + return signEd25519(key.platformKey(), key.keySizeInBytes(), data); +} + +static ExceptionOr<bool> verifyEd25519(const Vector<uint8_t>& key, size_t keyLengthInBytes, const Vector<uint8_t>& signature, const Vector<uint8_t> data) +{ + if (signature.size() != keyLengthInBytes * 2) + return false; + + return ED25519_verify(data.data(), data.size(), signature.data(), key.data()) == 1; +} + +ExceptionOr<bool> CryptoAlgorithmEd25519::platformVerify(const CryptoKeyOKP& key, const Vector<uint8_t>& signature, const Vector<uint8_t>& data) +{ + return verifyEd25519(key.platformKey(), key.keySizeInBytes(), signature, data); +} + +Ref<CryptoAlgorithm> CryptoAlgorithmEd25519::create() +{ + return adoptRef(*new CryptoAlgorithmEd25519); +} + +void CryptoAlgorithmEd25519::generateKey(const CryptoAlgorithmParameters&, bool extractable, CryptoKeyUsageBitmap usages, KeyOrKeyPairCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext&) +{ + if (usages & (CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt | CryptoKeyUsageDeriveKey | CryptoKeyUsageDeriveBits | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey)) { + exceptionCallback(SyntaxError); + return; + } + + auto result = CryptoKeyOKP::generatePair(CryptoAlgorithmIdentifier::Ed25519, CryptoKeyOKP::NamedCurve::Ed25519, extractable, usages); + if (result.hasException()) { + exceptionCallback(result.releaseException().code()); + return; + } + + auto pair = result.releaseReturnValue(); + pair.publicKey->setUsagesBitmap(pair.publicKey->usagesBitmap() & CryptoKeyUsageVerify); + pair.privateKey->setUsagesBitmap(pair.privateKey->usagesBitmap() & CryptoKeyUsageSign); + callback(WTFMove(pair)); +} + +void CryptoAlgorithmEd25519::sign(const CryptoAlgorithmParameters&, Ref<CryptoKey>&& key, Vector<uint8_t>&& data, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue) +{ + if (key->type() != CryptoKeyType::Private) { + exceptionCallback(InvalidAccessError); + return; + } + dispatchOperationInWorkQueue(workQueue, context, WTFMove(callback), WTFMove(exceptionCallback), + [key = WTFMove(key), data = WTFMove(data)] { + return platformSign(downcast<CryptoKeyOKP>(key.get()), data); + }); +} + +void CryptoAlgorithmEd25519::verify(const CryptoAlgorithmParameters&, Ref<CryptoKey>&& key, Vector<uint8_t>&& signature, Vector<uint8_t>&& data, BoolCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue) +{ + if (key->type() != CryptoKeyType::Public) { + exceptionCallback(InvalidAccessError); + return; + } + dispatchOperationInWorkQueue(workQueue, context, WTFMove(callback), WTFMove(exceptionCallback), + [key = WTFMove(key), signature = WTFMove(signature), data = WTFMove(data)] { + return platformVerify(downcast<CryptoKeyOKP>(key.get()), signature, data); + }); +} + +void CryptoAlgorithmEd25519::importKey(CryptoKeyFormat format, KeyData&& data, const CryptoAlgorithmParameters&, bool extractable, CryptoKeyUsageBitmap usages, KeyCallback&& callback, ExceptionCallback&& exceptionCallback) +{ + RefPtr<CryptoKeyOKP> result; + switch (format) { + case CryptoKeyFormat::Jwk: { + JsonWebKey key = WTFMove(std::get<JsonWebKey>(data)); + if (usages && ((!key.d.isNull() && (usages ^ CryptoKeyUsageSign)) || (key.d.isNull() && (usages ^ CryptoKeyUsageVerify)))) { + exceptionCallback(SyntaxError); + return; + } + if (usages && !key.use.isNull() && key.use != "sig"_s) { + exceptionCallback(DataError); + return; + } + result = CryptoKeyOKP::importJwk(CryptoAlgorithmIdentifier::Ed25519, CryptoKeyOKP::NamedCurve::Ed25519, WTFMove(key), extractable, usages); + break; + } + case CryptoKeyFormat::Raw: + if (usages && (usages ^ CryptoKeyUsageVerify)) { + exceptionCallback(SyntaxError); + return; + } + result = CryptoKeyOKP::importRaw(CryptoAlgorithmIdentifier::Ed25519, CryptoKeyOKP::NamedCurve::Ed25519, WTFMove(std::get<Vector<uint8_t>>(data)), extractable, usages); + break; + case CryptoKeyFormat::Spki: + if (usages && (usages ^ CryptoKeyUsageVerify)) { + exceptionCallback(SyntaxError); + return; + } + result = CryptoKeyOKP::importSpki(CryptoAlgorithmIdentifier::Ed25519, CryptoKeyOKP::NamedCurve::Ed25519, WTFMove(std::get<Vector<uint8_t>>(data)), extractable, usages); + break; + case CryptoKeyFormat::Pkcs8: + if (usages && (usages ^ CryptoKeyUsageSign)) { + exceptionCallback(SyntaxError); + return; + } + result = CryptoKeyOKP::importPkcs8(CryptoAlgorithmIdentifier::Ed25519, CryptoKeyOKP::NamedCurve::Ed25519, WTFMove(std::get<Vector<uint8_t>>(data)), extractable, usages); + break; + } + if (!result) { + exceptionCallback(DataError); + return; + } + callback(*result); +} + +void CryptoAlgorithmEd25519::exportKey(CryptoKeyFormat format, Ref<CryptoKey>&& key, KeyDataCallback&& callback, ExceptionCallback&& exceptionCallback) +{ + const auto& okpKey = downcast<CryptoKeyOKP>(key.get()); + if (!okpKey.keySizeInBits()) { + exceptionCallback(OperationError); + return; + } + KeyData result; + switch (format) { + case CryptoKeyFormat::Jwk: { + auto jwk = okpKey.exportJwk(); + if (jwk.hasException()) { + exceptionCallback(jwk.releaseException().code()); + return; + } + result = jwk.releaseReturnValue(); + break; + } + case CryptoKeyFormat::Raw: { + auto raw = okpKey.exportRaw(); + if (raw.hasException()) { + exceptionCallback(raw.releaseException().code()); + return; + } + result = raw.releaseReturnValue(); + break; + } + case CryptoKeyFormat::Spki: { + auto spki = okpKey.exportSpki(); + if (spki.hasException()) { + exceptionCallback(spki.releaseException().code()); + return; + } + result = spki.releaseReturnValue(); + break; + } + case CryptoKeyFormat::Pkcs8: { + auto pkcs8 = okpKey.exportPkcs8(); + if (pkcs8.hasException()) { + exceptionCallback(pkcs8.releaseException().code()); + return; + } + result = pkcs8.releaseReturnValue(); + break; + } + } + callback(format, WTFMove(result)); +} + +} // namespace WebCore +#endif // ENABLE(WEB_CRYPTO) diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.h new file mode 100644 index 000000000..b96d57831 --- /dev/null +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.h @@ -0,0 +1,61 @@ +/* + * 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. + */ +#pragma once + +#include "CryptoAlgorithm.h" + +#if ENABLE(WEB_CRYPTO) +namespace WebCore { + +class CryptoKeyOKP; + +class CryptoAlgorithmEd25519 final : public CryptoAlgorithm { +public: + static constexpr ASCIILiteral s_name = "Ed25519"_s; + static constexpr CryptoAlgorithmIdentifier s_identifier = CryptoAlgorithmIdentifier::Ed25519; + static Ref<CryptoAlgorithm> create(); + +private: + CryptoAlgorithmEd25519() = default; + CryptoAlgorithmIdentifier identifier() const final; + + void generateKey(const CryptoAlgorithmParameters& , bool extractable, CryptoKeyUsageBitmap usages, KeyOrKeyPairCallback&& , ExceptionCallback&& , ScriptExecutionContext&); + void sign(const CryptoAlgorithmParameters&, Ref<CryptoKey>&&, Vector<uint8_t>&&, VectorCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&) final; + void verify(const CryptoAlgorithmParameters&, Ref<CryptoKey>&&, Vector<uint8_t>&& signature, Vector<uint8_t>&&, BoolCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&) final; + void importKey(CryptoKeyFormat, KeyData&&, const CryptoAlgorithmParameters&, bool extractable, CryptoKeyUsageBitmap, KeyCallback&&, ExceptionCallback&&) final; + void exportKey(CryptoKeyFormat, Ref<CryptoKey>&&, KeyDataCallback&&, ExceptionCallback&&) final; + + static ExceptionOr<Vector<uint8_t>> platformSign(const CryptoKeyOKP&, const Vector<uint8_t>&); + static ExceptionOr<bool> platformVerify(const CryptoKeyOKP&, const Vector<uint8_t>&, const Vector<uint8_t>&); +}; + +inline CryptoAlgorithmIdentifier CryptoAlgorithmEd25519::identifier() const +{ + return s_identifier; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_CRYPTO) diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmIdentifier.h b/src/bun.js/bindings/webcrypto/CryptoAlgorithmIdentifier.h index 657a7aadb..a0c07a78a 100644 --- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmIdentifier.h +++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmIdentifier.h @@ -48,7 +48,8 @@ enum class CryptoAlgorithmIdentifier { SHA_384, SHA_512, HKDF, - PBKDF2 + PBKDF2, + Ed25519 }; } // namespace WebCore diff --git a/src/bun.js/bindings/webcrypto/CryptoKey.cpp b/src/bun.js/bindings/webcrypto/CryptoKey.cpp index 4491a11fb..7902dd5d9 100644 --- a/src/bun.js/bindings/webcrypto/CryptoKey.cpp +++ b/src/bun.js/bindings/webcrypto/CryptoKey.cpp @@ -73,9 +73,6 @@ WebCoreOpaqueRoot root(CryptoKey* key) return WebCoreOpaqueRoot { key }; } - - - Vector<uint8_t> CryptoKey::randomData(size_t size) { Vector<uint8_t> result(size); diff --git a/src/bun.js/bindings/webcrypto/CryptoKey.h b/src/bun.js/bindings/webcrypto/CryptoKey.h index f91229173..066ff3de1 100644 --- a/src/bun.js/bindings/webcrypto/CryptoKey.h +++ b/src/bun.js/bindings/webcrypto/CryptoKey.h @@ -51,6 +51,7 @@ enum class CryptoKeyClass { AES, EC, HMAC, + OKP, RSA, Raw, }; @@ -94,8 +95,11 @@ WebCoreOpaqueRoot root(CryptoKey*); } // namespace WebCore #define SPECIALIZE_TYPE_TRAITS_CRYPTO_KEY(ToClassName, KeyClass) \ -SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToClassName) \ - static bool isType(const WebCore::CryptoKey& key) { return key.keyClass() == WebCore::KeyClass; } \ -SPECIALIZE_TYPE_TRAITS_END() + SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToClassName) \ + static bool isType(const WebCore::CryptoKey& key) \ + { \ + return key.keyClass() == WebCore::KeyClass; \ + } \ + SPECIALIZE_TYPE_TRAITS_END() #endif // ENABLE(WEB_CRYPTO) diff --git a/src/bun.js/bindings/webcrypto/CryptoKeyOKP.cpp b/src/bun.js/bindings/webcrypto/CryptoKeyOKP.cpp new file mode 100644 index 000000000..77d099427 --- /dev/null +++ b/src/bun.js/bindings/webcrypto/CryptoKeyOKP.cpp @@ -0,0 +1,214 @@ +/* + * 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 keySizeInBytesFromNamedCurve(CryptoKeyOKP::NamedCurve curve) +{ + switch (curve) { + case CryptoKeyOKP::NamedCurve::X25519: + case CryptoKeyOKP::NamedCurve::Ed25519: + return 32; + } + return 32; +} + +RefPtr<CryptoKeyOKP> CryptoKeyOKP::create(CryptoAlgorithmIdentifier identifier, NamedCurve curve, CryptoKeyType type, KeyMaterial&& platformKey, bool extractable, CryptoKeyUsageBitmap usages) +{ + if (platformKey.size() != keySizeInBytesFromNamedCurve(curve)) + 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(WTFMove(data)) +{ +} + +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::importJwk(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, JsonWebKey&& keyData, bool extractable, CryptoKeyUsageBitmap usages) +{ + if (!isPlatformSupportedCurve(namedCurve)) + return nullptr; + + switch (namedCurve) { + case NamedCurve::Ed25519: + if (!keyData.d.isEmpty()) { + 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 (!keyData.alg.isEmpty() && keyData.alg != "EdDSA"_s) + return nullptr; + if (usages && !keyData.use.isEmpty() && keyData.use != "sign"_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 (!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); +} + +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 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 +{ + CryptoEcKeyAlgorithm result; + result.name = CryptoAlgorithmRegistry::singleton().name(algorithmIdentifier()); + + 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) diff --git a/src/bun.js/bindings/webcrypto/CryptoKeyOKP.h b/src/bun.js/bindings/webcrypto/CryptoKeyOKP.h new file mode 100644 index 000000000..db94dbd23 --- /dev/null +++ b/src/bun.js/bindings/webcrypto/CryptoKeyOKP.h @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#pragma once + +#include "CryptoKey.h" +#include "CryptoKeyPair.h" +#include "ExceptionOr.h" + +#if ENABLE(WEB_CRYPTO) + +namespace WebCore { + +struct JsonWebKey; + +class CryptoKeyOKP final : public CryptoKey { +public: + using KeyMaterial = Vector<uint8_t>; + + enum class NamedCurve { + X25519, + Ed25519, + }; + + static RefPtr<CryptoKeyOKP> create(CryptoAlgorithmIdentifier, NamedCurve, CryptoKeyType, KeyMaterial&&, bool extractable, CryptoKeyUsageBitmap); + + WEBCORE_EXPORT static ExceptionOr<CryptoKeyPair> generatePair(CryptoAlgorithmIdentifier, NamedCurve, bool extractable, CryptoKeyUsageBitmap); + WEBCORE_EXPORT static RefPtr<CryptoKeyOKP> importRaw(CryptoAlgorithmIdentifier, NamedCurve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap); + static RefPtr<CryptoKeyOKP> importJwk(CryptoAlgorithmIdentifier, NamedCurve, JsonWebKey&&, bool extractable, CryptoKeyUsageBitmap); + static RefPtr<CryptoKeyOKP> importSpki(CryptoAlgorithmIdentifier, NamedCurve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap); + static RefPtr<CryptoKeyOKP> importPkcs8(CryptoAlgorithmIdentifier, NamedCurve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap); + + WEBCORE_EXPORT ExceptionOr<Vector<uint8_t>> exportRaw() const; + ExceptionOr<JsonWebKey> exportJwk() const; + ExceptionOr<Vector<uint8_t>> exportSpki() const; + ExceptionOr<Vector<uint8_t>> exportPkcs8() const; + + NamedCurve namedCurve() const { return m_curve; } + String namedCurveString() const; + + static bool isValidOKPAlgorithm(CryptoAlgorithmIdentifier); + + size_t keySizeInBits() const { return platformKey().size() * 8; } + size_t keySizeInBytes() const { return platformKey().size(); } + const KeyMaterial& platformKey() const { return m_data; } + +private: + CryptoKeyOKP(CryptoAlgorithmIdentifier, NamedCurve, CryptoKeyType, Vector<uint8_t>&&, bool extractable, CryptoKeyUsageBitmap); + + CryptoKeyClass keyClass() const final { return CryptoKeyClass::OKP; } + KeyAlgorithm algorithm() const final; + + String generateJwkD() const; + String generateJwkX() const; + + static bool isPlatformSupportedCurve(NamedCurve); + static std::optional<CryptoKeyPair> platformGeneratePair(CryptoAlgorithmIdentifier, NamedCurve, bool extractable, CryptoKeyUsageBitmap); + Vector<uint8_t> platformExportRaw() const; + Vector<uint8_t> platformExportSpki() const; + Vector<uint8_t> platformExportPkcs8() const; + + NamedCurve m_curve; + KeyMaterial m_data; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_CRYPTO_KEY(CryptoKeyOKP, CryptoKeyClass::OKP) + +#endif // ENABLE(WEB_CRYPTO) diff --git a/src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp b/src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp new file mode 100644 index 000000000..b020e6502 --- /dev/null +++ b/src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp @@ -0,0 +1,319 @@ +/* + * 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 "JsonWebKey.h" +// #include "Logging.h" +#include <wtf/text/Base64.h> +#include <openssl/curve25519.h> +#include "CommonCryptoDERUtilities.h" + +namespace WebCore { + +bool CryptoKeyOKP::isPlatformSupportedCurve(NamedCurve namedCurve) +{ + return namedCurve == NamedCurve::Ed25519; +} + +std::optional<CryptoKeyPair> CryptoKeyOKP::platformGeneratePair(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, bool extractable, CryptoKeyUsageBitmap usages) +{ + if (namedCurve != NamedCurve::Ed25519) + return {}; + + uint8_t public_key[X25519_PUBLIC_VALUE_LEN], private_key[X25519_PRIVATE_KEY_LEN]; + + if (identifier == CryptoAlgorithmIdentifier::Ed25519) { + ED25519_keypair(public_key, private_key); + } else { + X25519_keypair(public_key, private_key); + } + bool isPublicKeyExtractable = true; + auto publicKey = CryptoKeyOKP::create(identifier, namedCurve, CryptoKeyType::Public, Vector<uint8_t>(public_key), isPublicKeyExtractable, usages); + ASSERT(publicKey); + auto privateKey = CryptoKeyOKP::create(identifier, namedCurve, CryptoKeyType::Private, Vector<uint8_t>(private_key), extractable, usages); + ASSERT(privateKey); + return CryptoKeyPair { WTFMove(publicKey), WTFMove(privateKey) }; +} + +// Per https://www.ietf.org/rfc/rfc5280.txt +// SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING } +// AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } +// Per https://www.rfc-editor.org/rfc/rfc8410 +// id-X25519 OBJECT IDENTIFIER ::= { 1 3 101 110 } +// id-X448 OBJECT IDENTIFIER ::= { 1 3 101 111 } +// id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 } +// id-Ed448 OBJECT IDENTIFIER ::= { 1 3 101 113 } +// For all of the OIDs, the parameters MUST be absent. +RefPtr<CryptoKeyOKP> CryptoKeyOKP::importSpki(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages) +{ + // FIXME: We should use the underlying crypto library to import PKCS8 OKP keys. + + // Read SEQUENCE + size_t index = 1; + if (keyData.size() < index + 1) + return nullptr; + + // Read length and SEQUENCE + // FIXME: Check length is 5 + 1 + 1 + 1 + keyByteSize. + index += bytesUsedToEncodedLength(keyData[index]) + 1; + if (keyData.size() < index + 1) + return nullptr; + + // Read length + // FIXME: Check length is 5. + index += bytesUsedToEncodedLength(keyData[index]); + if (keyData.size() < index + 5) + return nullptr; + + // Read OID + // FIXME: spec says this is 1 3 101 11X but WPT tests expect 6 3 43 101 11X. + if (keyData[index++] != 6 || keyData[index++] != 3 || keyData[index++] != 43 || keyData[index++] != 101) + return nullptr; + + switch (namedCurve) { + case NamedCurve::X25519: + if (keyData[index++] != 110) + return nullptr; + break; + case NamedCurve::Ed25519: + if (keyData[index++] != 112) + return nullptr; + break; + }; + + // Read BIT STRING + if (keyData.size() < index + 1) + return nullptr; + if (keyData[index++] != 3) + return nullptr; + + // Read length + // FIXME: Check length is keyByteSize + 1. + index += bytesUsedToEncodedLength(keyData[index]); + + if (keyData.size() < index + 1) + return nullptr; + + // Initial octet + if (!!keyData[index]) + return nullptr; + ++index; + + return create(identifier, namedCurve, CryptoKeyType::Public, Span<const uint8_t> { keyData.data() + index, keyData.size() - index }, extractable, usages); +} + +constexpr uint8_t OKPOIDFirstByte = 6; +constexpr uint8_t OKPOIDSecondByte = 3; +constexpr uint8_t OKPOIDThirdByte = 43; +constexpr uint8_t OKPOIDFourthByte = 101; +constexpr uint8_t OKPOIDX25519Byte = 110; +constexpr uint8_t OKPOIDEd25519Byte = 112; + +static void writeOID(CryptoKeyOKP::NamedCurve namedCurve, Vector<uint8_t>& result) +{ + result.append(OKPOIDFirstByte); + result.append(OKPOIDSecondByte); + result.append(OKPOIDThirdByte); + result.append(OKPOIDFourthByte); + + switch (namedCurve) { + case CryptoKeyOKP::NamedCurve::X25519: + result.append(OKPOIDX25519Byte); + break; + case CryptoKeyOKP::NamedCurve::Ed25519: + result.append(OKPOIDEd25519Byte); + break; + }; +} + +ExceptionOr<Vector<uint8_t>> CryptoKeyOKP::exportSpki() const +{ + if (type() != CryptoKeyType::Public) + return Exception { InvalidAccessError }; + + size_t keySize = keySizeInBytes(); + + // SEQUENCE, length, SEQUENCE, length, OID, Bit String (Initial octet prepended) + size_t totalSize = 1 + 1 + 1 + 1 + 5 + 1 + 1 + 1 + keySize; + Vector<uint8_t> result; + result.reserveInitialCapacity(totalSize); + result.append(SequenceMark); + addEncodedASN1Length(result, totalSize - 2); + result.append(SequenceMark); + addEncodedASN1Length(result, 5); + + writeOID(namedCurve(), result); + + result.append(BitStringMark); + addEncodedASN1Length(result, keySize + 1); + result.append(InitialOctet); + result.append(platformKey().data(), platformKey().size()); + + ASSERT(result.size() == totalSize); + + return WTFMove(result); +} + +// Per https://www.ietf.org/rfc/rfc5280.txt +// PrivateKeyInfo ::= SEQUENCE { version INTEGER, privateKeyAlgorithm AlgorithmIdentifier, privateKey OCTET STRING } +// AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } +// Per https://www.rfc-editor.org/rfc/rfc8410 +// id-X25519 OBJECT IDENTIFIER ::= { 1 3 101 110 } +// id-X448 OBJECT IDENTIFIER ::= { 1 3 101 111 } +// id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 } +// id-Ed448 OBJECT IDENTIFIER ::= { 1 3 101 113 } +// For all of the OIDs, the parameters MUST be absent. +RefPtr<CryptoKeyOKP> CryptoKeyOKP::importPkcs8(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages) +{ + // FIXME: We should use the underlying crypto library to import PKCS8 OKP keys. + + // Read SEQUENCE + size_t index = 1; + if (keyData.size() < index + 1) + return nullptr; + + // Read length + index += bytesUsedToEncodedLength(keyData[index]); + if (keyData.size() < index + 1) + return nullptr; + + // Read version + index += 3; + if (keyData.size() < index + 1) + return nullptr; + + // Read SEQUENCE + index += bytesUsedToEncodedLength(keyData[index]); + if (keyData.size() < index + 1) + return nullptr; + + // Read length + index += bytesUsedToEncodedLength(keyData[index]); + if (keyData.size() < index + 1) + return nullptr; + + // Read OID + if (keyData[index++] != OKPOIDFirstByte || keyData[index++] != OKPOIDSecondByte || keyData[index++] != OKPOIDThirdByte || keyData[index++] != OKPOIDFourthByte) + return nullptr; + + switch (namedCurve) { + case NamedCurve::X25519: + if (keyData[index++] != OKPOIDX25519Byte) + return nullptr; + break; + case NamedCurve::Ed25519: + if (keyData[index++] != OKPOIDEd25519Byte) + return nullptr; + break; + }; + + // Read OCTET STRING + if (keyData.size() < index + 1) + return nullptr; + + if (keyData[index++] != 4) + return nullptr; + + index += bytesUsedToEncodedLength(keyData[index]); + if (keyData.size() < index + 1) + return nullptr; + + // Read OCTET STRING + if (keyData[index++] != 4) + return nullptr; + + index += bytesUsedToEncodedLength(keyData[index]); + if (keyData.size() < index + 1) + return nullptr; + + return create(identifier, namedCurve, CryptoKeyType::Private, Span<const uint8_t> { keyData.data() + index, keyData.size() - index }, extractable, usages); +} + +ExceptionOr<Vector<uint8_t>> CryptoKeyOKP::exportPkcs8() const +{ + if (type() != CryptoKeyType::Private) + return Exception { InvalidAccessError }; + + size_t keySize = keySizeInBytes(); + + // SEQUENCE, length, version SEQUENCE, length, OID, Octet String Octet String + size_t totalSize = 1 + 1 + 3 + 1 + 1 + 5 + 1 + 1 + 1 + 1 + keySize; + Vector<uint8_t> result; + result.reserveInitialCapacity(totalSize); + result.append(SequenceMark); + addEncodedASN1Length(result, totalSize - 2); + + result.append(2); + result.append(1); + result.append(0); + + result.append(SequenceMark); + addEncodedASN1Length(result, 5); + + writeOID(namedCurve(), result); + + result.append(OctetStringMark); + addEncodedASN1Length(result, keySize + 2); + result.append(OctetStringMark); + addEncodedASN1Length(result, keySize); + result.append(platformKey().data(), platformKey().size()); + + ASSERT(result.size() == totalSize); + + return WTFMove(result); +} + +String CryptoKeyOKP::generateJwkD() const +{ + ASSERT(type() == CryptoKeyType::Private); + return base64URLEncodeToString(m_data); +} + +String CryptoKeyOKP::generateJwkX() const +{ + if (type() == CryptoKeyType::Public) + return base64URLEncodeToString(m_data); + + ASSERT(type() == CryptoKeyType::Private); + + uint8_t publicKey[X25519_PRIVATE_KEY_LEN]; + + X25519_public_from_private(publicKey, m_data.data()); + + return base64URLEncodeToString(Span<const uint8_t> { publicKey, sizeof(publicKey) }); +} + +Vector<uint8_t> CryptoKeyOKP::platformExportRaw() const +{ + return m_data; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_CRYPTO) diff --git a/src/bun.js/bindings/webcrypto/SubtleCrypto.cpp b/src/bun.js/bindings/webcrypto/SubtleCrypto.cpp index d8b7d7942..2bfaa0578 100644 --- a/src/bun.js/bindings/webcrypto/SubtleCrypto.cpp +++ b/src/bun.js/bindings/webcrypto/SubtleCrypto.cpp @@ -88,6 +88,22 @@ static ExceptionOr<CryptoAlgorithmIdentifier> toHashIdentifier(JSGlobalObject& s return digestParams.returnValue()->identifier; } +static bool isRSAESPKCSWebCryptoDeprecated(JSGlobalObject& state) +{ + return true; + // auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(&state); + // auto* context = globalObject.scriptExecutionContext(); + // return context && context->settingsValues().deprecateRSAESPKCSWebCryptoEnabled; +} + +static bool isSafeCurvesEnabled(JSGlobalObject& state) +{ + return true; + // auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(&state); + // auto* context = globalObject.scriptExecutionContext(); + // return context && context->settingsValues().webCryptoSafeCurvesEnabled; +} + static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAlgorithmParameters(JSGlobalObject& state, SubtleCrypto::AlgorithmIdentifier algorithmIdentifier, Operations operation) { VM& vm = state.vm(); @@ -109,12 +125,17 @@ static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAl if (UNLIKELY(!identifier)) return Exception { NotSupportedError }; + if (*identifier == CryptoAlgorithmIdentifier::Ed25519 && !isSafeCurvesEnabled(state)) + return Exception { NotSupportedError }; + std::unique_ptr<CryptoAlgorithmParameters> result; switch (operation) { case Operations::Encrypt: case Operations::Decrypt: switch (*identifier) { case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: + if (isRSAESPKCSWebCryptoDeprecated(state)) + return Exception { NotSupportedError, "RSAES-PKCS1-v1_5 support is deprecated"_s }; result = makeUnique<CryptoAlgorithmParameters>(params); break; case CryptoAlgorithmIdentifier::RSA_OAEP: { @@ -151,6 +172,7 @@ static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAl switch (*identifier) { case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: case CryptoAlgorithmIdentifier::HMAC: + case CryptoAlgorithmIdentifier::Ed25519: result = makeUnique<CryptoAlgorithmParameters>(params); break; case CryptoAlgorithmIdentifier::ECDSA: { @@ -189,6 +211,8 @@ static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAl case Operations::GenerateKey: switch (*identifier) { case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: { + if (isRSAESPKCSWebCryptoDeprecated(state)) + return Exception { NotSupportedError, "RSAES-PKCS1-v1_5 support is deprecated"_s }; auto params = convertDictionary<CryptoAlgorithmRsaKeyGenParams>(state, value.get()); RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); result = makeUnique<CryptoAlgorithmRsaKeyGenParams>(params); @@ -233,6 +257,9 @@ static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAl result = makeUnique<CryptoAlgorithmEcKeyParams>(params); break; } + case CryptoAlgorithmIdentifier::Ed25519: + result = makeUnique<CryptoAlgorithmParameters>(params); + break; default: return Exception { NotSupportedError }; } @@ -279,6 +306,8 @@ static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAl case Operations::ImportKey: switch (*identifier) { case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: + if (isRSAESPKCSWebCryptoDeprecated(state)) + return Exception { NotSupportedError, "RSAES-PKCS1-v1_5 support is deprecated"_s }; result = makeUnique<CryptoAlgorithmParameters>(params); break; case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: @@ -298,6 +327,7 @@ static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAl case CryptoAlgorithmIdentifier::AES_GCM: case CryptoAlgorithmIdentifier::AES_CFB: case CryptoAlgorithmIdentifier::AES_KW: + case CryptoAlgorithmIdentifier::Ed25519: result = makeUnique<CryptoAlgorithmParameters>(params); break; case CryptoAlgorithmIdentifier::HMAC: { @@ -321,7 +351,11 @@ static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAl case CryptoAlgorithmIdentifier::PBKDF2: result = makeUnique<CryptoAlgorithmParameters>(params); break; - default: + case CryptoAlgorithmIdentifier::SHA_1: + case CryptoAlgorithmIdentifier::SHA_224: + case CryptoAlgorithmIdentifier::SHA_256: + case CryptoAlgorithmIdentifier::SHA_384: + case CryptoAlgorithmIdentifier::SHA_512: return Exception { NotSupportedError }; } break; @@ -482,10 +516,11 @@ static Vector<uint8_t> copyToVector(BufferSource&& data) return { data.data(), data.length() }; } -static bool isSupportedExportKey(CryptoAlgorithmIdentifier identifier) +static bool isSupportedExportKey(JSGlobalObject& state, CryptoAlgorithmIdentifier identifier) { switch (identifier) { case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: + return !isRSAESPKCSWebCryptoDeprecated(state); case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: case CryptoAlgorithmIdentifier::RSA_PSS: case CryptoAlgorithmIdentifier::RSA_OAEP: @@ -497,6 +532,7 @@ static bool isSupportedExportKey(CryptoAlgorithmIdentifier identifier) case CryptoAlgorithmIdentifier::HMAC: case CryptoAlgorithmIdentifier::ECDSA: case CryptoAlgorithmIdentifier::ECDH: + case CryptoAlgorithmIdentifier::Ed25519: return true; default: return false; @@ -932,7 +968,7 @@ void SubtleCrypto::importKey(JSC::JSGlobalObject& state, KeyFormat format, KeyDa void SubtleCrypto::exportKey(KeyFormat format, CryptoKey& key, Ref<DeferredPromise>&& promise) { - if (!isSupportedExportKey(key.algorithmIdentifier())) { + if (!isSupportedExportKey(*promise->globalObject(), key.algorithmIdentifier())) { promise->reject(Exception { NotSupportedError }); return; } @@ -1003,7 +1039,7 @@ void SubtleCrypto::wrapKey(JSC::JSGlobalObject& state, KeyFormat format, CryptoK return; } - if (!isSupportedExportKey(key.algorithmIdentifier())) { + if (!isSupportedExportKey(state, key.algorithmIdentifier())) { promise->reject(Exception { NotSupportedError }); return; } |