aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.cpp77
-rw-r--r--src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.h56
-rw-r--r--src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.cpp211
-rw-r--r--src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.h61
-rw-r--r--src/bun.js/bindings/webcrypto/CryptoAlgorithmIdentifier.h3
-rw-r--r--src/bun.js/bindings/webcrypto/CryptoKey.cpp3
-rw-r--r--src/bun.js/bindings/webcrypto/CryptoKey.h10
-rw-r--r--src/bun.js/bindings/webcrypto/CryptoKeyOKP.cpp214
-rw-r--r--src/bun.js/bindings/webcrypto/CryptoKeyOKP.h92
-rw-r--r--src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp319
-rw-r--r--src/bun.js/bindings/webcrypto/SubtleCrypto.cpp44
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;
}