aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Derrick Farris <mr.dcfarris@gmail.com> 2023-02-08 16:57:13 -0600
committerGravatar GitHub <noreply@github.com> 2023-02-08 14:57:13 -0800
commitc8243d19f0a06c7a04a5d611bd14c5fb52b46c42 (patch)
tree76ce2650cfc2f4dbd6e08a471fe626b06de0b898
parent7db87fe0cbac2d4eeba1c8a3d530897e1f16e78f (diff)
downloadbun-c8243d19f0a06c7a04a5d611bd14c5fb52b46c42.tar.gz
bun-c8243d19f0a06c7a04a5d611bd14c5fb52b46c42.tar.zst
bun-c8243d19f0a06c7a04a5d611bd14c5fb52b46c42.zip
fix: import and export ed25519 (#2004)
* fix(webcrypto): allow import and export ed25519 * fix(webcrypto): copy exportkey * fix(webcrypto): fix use after stack free
-rw-r--r--src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.cpp1
-rw-r--r--src/bun.js/bindings/webcrypto/CryptoKeyOKP.cpp51
-rw-r--r--src/bun.js/bindings/webcrypto/CryptoKeyOKP.h12
-rw-r--r--src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp47
-rw-r--r--src/bun.js/builtins/cpp/ProcessObjectInternalsBuiltins.cpp48
5 files changed, 100 insertions, 59 deletions
diff --git a/src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.cpp b/src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.cpp
index 111f57560..7003222bd 100644
--- a/src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.cpp
+++ b/src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.cpp
@@ -39,7 +39,6 @@ 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());
diff --git a/src/bun.js/bindings/webcrypto/CryptoKeyOKP.cpp b/src/bun.js/bindings/webcrypto/CryptoKeyOKP.cpp
index 58b46cbc4..a4138d8c9 100644
--- a/src/bun.js/bindings/webcrypto/CryptoKeyOKP.cpp
+++ b/src/bun.js/bindings/webcrypto/CryptoKeyOKP.cpp
@@ -37,28 +37,65 @@ namespace WebCore {
static const ASCIILiteral X25519 { "X25519"_s };
static const ASCIILiteral Ed25519 { "Ed25519"_s };
-static constexpr size_t keySizeInBytesFromNamedCurve(CryptoKeyOKP::NamedCurve curve, CryptoKeyType type)
+static constexpr size_t internalKeySizeInBytesFromNamedCurve(CryptoKeyOKP::NamedCurve curve, CryptoKeyType type)
{
switch (curve) {
case CryptoKeyOKP::NamedCurve::X25519:
return 32;
case CryptoKeyOKP::NamedCurve::Ed25519:
return type == CryptoKeyType::Private ? 64 : 32;
+ default:
+ return -1;
+ }
+}
+
+static constexpr size_t externalKeySizeInBytesFromNamedCurve(CryptoKeyOKP::NamedCurve curve)
+{
+ switch (curve) {
+ case CryptoKeyOKP::NamedCurve::X25519:
+ case CryptoKeyOKP::NamedCurve::Ed25519:
+ return 32;
+ default:
+ return -1;
}
- return 32;
}
RefPtr<CryptoKeyOKP> CryptoKeyOKP::create(CryptoAlgorithmIdentifier identifier, NamedCurve curve, CryptoKeyType type, KeyMaterial&& platformKey, bool extractable, CryptoKeyUsageBitmap usages)
{
- if (platformKey.size() != keySizeInBytesFromNamedCurve(curve, type))
+ auto bytesExpectedInternal = internalKeySizeInBytesFromNamedCurve(curve, type);
+ if (bytesExpectedInternal == -1)
return nullptr;
+
+ if (platformKey.size() != bytesExpectedInternal) {
+ if (type != CryptoKeyType::Private || curve != NamedCurve::Ed25519)
+ return nullptr;
+
+ auto bytesExpectedExternal = externalKeySizeInBytesFromNamedCurve(curve);
+ if (bytesExpectedExternal == -1)
+ return nullptr;
+
+ // We need to match the internal format when importing a private key
+ // Import format only consists of 32 bytes of private key
+ // Internal format is private key + public key suffix
+ if (platformKey.size() == bytesExpectedExternal) {
+ auto&& privateKey = ed25519PrivateFromSeed(WTFMove(platformKey));
+ if (!privateKey.data())
+ return nullptr;
+
+ return adoptRef(*new CryptoKeyOKP(identifier, curve, type, WTFMove(privateKey), extractable, usages));
+ }
+
+ return nullptr;
+ }
+
return adoptRef(*new CryptoKeyOKP(identifier, curve, type, WTFMove(platformKey), extractable, usages));
}
CryptoKeyOKP::CryptoKeyOKP(CryptoAlgorithmIdentifier identifier, NamedCurve curve, CryptoKeyType type, KeyMaterial&& data, bool extractable, CryptoKeyUsageBitmap usages)
: CryptoKey(identifier, type, extractable, usages)
, m_curve(curve)
- , m_data(WTFMove(data))
+ , m_data(data)
+ , m_exportKey(curve == NamedCurve::Ed25519 && type == CryptoKeyType::Private ? std::optional<Vector<uint8_t>>(Vector<uint8_t>(data.data(), 32)) : std::nullopt)
{
}
@@ -102,7 +139,7 @@ RefPtr<CryptoKeyOKP> CryptoKeyOKP::importJwk(CryptoAlgorithmIdentifier identifie
return nullptr;
if (!keyData.alg.isEmpty() && keyData.alg != "EdDSA"_s)
return nullptr;
- if (usages && !keyData.use.isEmpty() && keyData.use != "sign"_s)
+ if (usages && !keyData.use.isEmpty() && keyData.use != "sig"_s)
return nullptr;
if (keyData.key_ops && ((keyData.usages & usages) != usages))
return nullptr;
@@ -138,10 +175,10 @@ ExceptionOr<Vector<uint8_t>> CryptoKeyOKP::exportRaw() const
if (type() != CryptoKey::Type::Public)
return Exception { InvalidAccessError };
- auto result = platformExportRaw();
+ auto&& result = platformExportRaw();
if (result.isEmpty())
return Exception { OperationError };
- return result;
+ return WTFMove(result);
}
ExceptionOr<JsonWebKey> CryptoKeyOKP::exportJwk() const
diff --git a/src/bun.js/bindings/webcrypto/CryptoKeyOKP.h b/src/bun.js/bindings/webcrypto/CryptoKeyOKP.h
index 40642fe68..cc1fe2c73 100644
--- a/src/bun.js/bindings/webcrypto/CryptoKeyOKP.h
+++ b/src/bun.js/bindings/webcrypto/CryptoKeyOKP.h
@@ -59,13 +59,21 @@ public:
NamedCurve namedCurve() const { return m_curve; }
String namedCurveString() const;
+ bool isEd25519PrivateKey() { return namedCurve() == NamedCurve::Ed25519 && type() == CryptoKeyType::Private; };
static bool isValidOKPAlgorithm(CryptoAlgorithmIdentifier);
+ static KeyMaterial ed25519PublicFromPrivate(const KeyMaterial& privateKey);
+ static KeyMaterial x25519PublicFromPrivate(const KeyMaterial& privateKey);
+ static KeyMaterial ed25519PrivateFromSeed(KeyMaterial&& seed);
size_t keySizeInBits() const { return platformKey().size() * 8; }
size_t keySizeInBytes() const { return platformKey().size(); }
const KeyMaterial& platformKey() const { return m_data; }
+ size_t exportKeySizeInBits() const { return exportKey().size() * 8; }
+ size_t exportKeySizeInBytes() const { return exportKey().size(); }
+ const KeyMaterial& exportKey() const { return !m_exportKey ? m_data : *m_exportKey; };
+
private:
CryptoKeyOKP(CryptoAlgorithmIdentifier, NamedCurve, CryptoKeyType, Vector<uint8_t>&&, bool extractable, CryptoKeyUsageBitmap);
@@ -75,9 +83,6 @@ private:
String generateJwkD() const;
String generateJwkX() const;
- String getEd25519PublicFromPrivate() const;
- String getX25519PublicFromPrivate() const;
-
static bool isPlatformSupportedCurve(NamedCurve);
static std::optional<CryptoKeyPair> platformGeneratePair(CryptoAlgorithmIdentifier, NamedCurve, bool extractable, CryptoKeyUsageBitmap);
Vector<uint8_t> platformExportRaw() const;
@@ -86,6 +91,7 @@ private:
NamedCurve m_curve;
KeyMaterial m_data;
+ std::optional<KeyMaterial> m_exportKey;
};
} // namespace WebCore
diff --git a/src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp b/src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp
index 0372443fc..8c6a25f19 100644
--- a/src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp
+++ b/src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp
@@ -262,7 +262,7 @@ ExceptionOr<Vector<uint8_t>> CryptoKeyOKP::exportPkcs8() const
if (type() != CryptoKeyType::Private)
return Exception { InvalidAccessError };
- size_t keySize = keySizeInBytes();
+ size_t keySize = exportKeySizeInBytes();
// SEQUENCE, length, version SEQUENCE, length, OID, Octet String Octet String
size_t totalSize = 1 + 1 + 3 + 1 + 1 + 5 + 1 + 1 + 1 + 1 + keySize;
@@ -284,7 +284,7 @@ ExceptionOr<Vector<uint8_t>> CryptoKeyOKP::exportPkcs8() const
addEncodedASN1Length(result, keySize + 2);
result.append(OctetStringMark);
addEncodedASN1Length(result, keySize);
- result.append(platformKey().data(), platformKey().size());
+ result.append(exportKey().data(), exportKey().size());
ASSERT(result.size() == totalSize);
@@ -294,26 +294,40 @@ ExceptionOr<Vector<uint8_t>> CryptoKeyOKP::exportPkcs8() const
String CryptoKeyOKP::generateJwkD() const
{
ASSERT(type() == CryptoKeyType::Private);
+ if (namedCurve() == NamedCurve::Ed25519) {
+ ASSERT(m_exportKey);
+ return base64URLEncodeToString(*m_exportKey);
+ }
return base64URLEncodeToString(m_data);
}
-String CryptoKeyOKP::getEd25519PublicFromPrivate() const
+CryptoKeyOKP::KeyMaterial CryptoKeyOKP::ed25519PublicFromPrivate(const KeyMaterial& seed)
{
- uint8_t publicKey[ED25519_PUBLIC_KEY_LEN];
+ auto publicKey = KeyMaterial(ED25519_PUBLIC_KEY_LEN);
uint8_t privateKey[ED25519_PRIVATE_KEY_LEN];
- ED25519_keypair_from_seed(publicKey, privateKey, m_data.data());
+ ED25519_keypair_from_seed(publicKey.data(), privateKey, seed.data());
- return base64URLEncodeToString(Span<const uint8_t> { publicKey, sizeof(publicKey) });
+ return WTFMove(publicKey);
}
-String CryptoKeyOKP::getX25519PublicFromPrivate() const
+CryptoKeyOKP::KeyMaterial CryptoKeyOKP::x25519PublicFromPrivate(const KeyMaterial& privateKey)
{
- uint8_t publicKey[X25519_PUBLIC_VALUE_LEN];
+ auto publicKey = KeyMaterial(X25519_PUBLIC_VALUE_LEN);
- X25519_public_from_private(publicKey, m_data.data());
+ X25519_public_from_private(publicKey.data(), privateKey.data());
- return base64URLEncodeToString(Span<const uint8_t> { publicKey, sizeof(publicKey) });
+ return WTFMove(publicKey);
+}
+
+CryptoKeyOKP::KeyMaterial CryptoKeyOKP::ed25519PrivateFromSeed(KeyMaterial&& seed)
+{
+ uint8_t publicKey[ED25519_PUBLIC_KEY_LEN];
+ auto privateKey = KeyMaterial(ED25519_PRIVATE_KEY_LEN);
+
+ ED25519_keypair_from_seed(publicKey, privateKey.data(), seed.data());
+
+ return WTFMove(privateKey);
}
String CryptoKeyOKP::generateJwkX() const
@@ -324,15 +338,20 @@ String CryptoKeyOKP::generateJwkX() const
ASSERT(type() == CryptoKeyType::Private);
if (namedCurve() == NamedCurve::Ed25519)
- return getEd25519PublicFromPrivate();
+ return base64URLEncodeToString(WTFMove(ed25519PublicFromPrivate(const_cast<KeyMaterial&>(m_data))));
ASSERT(namedCurve() == NamedCurve::X25519);
- return getX25519PublicFromPrivate();
+ return base64URLEncodeToString(WTFMove(x25519PublicFromPrivate(const_cast<KeyMaterial&>(m_data))));
}
-Vector<uint8_t> CryptoKeyOKP::platformExportRaw() const
+CryptoKeyOKP::KeyMaterial CryptoKeyOKP::platformExportRaw() const
{
- return m_data;
+ if (namedCurve() == NamedCurve::Ed25519 && type() == CryptoKeyType::Private) {
+ ASSERT(m_exportKey);
+ const auto& exportKey = *m_exportKey;
+ return WTFMove(Vector<uint8_t>(exportKey.data(), exportKey.size()));
+ }
+ return WTFMove(KeyMaterial(m_data.data(), m_data.size()));
}
} // namespace WebCore
diff --git a/src/bun.js/builtins/cpp/ProcessObjectInternalsBuiltins.cpp b/src/bun.js/builtins/cpp/ProcessObjectInternalsBuiltins.cpp
index c5ffdff09..574d18f20 100644
--- a/src/bun.js/builtins/cpp/ProcessObjectInternalsBuiltins.cpp
+++ b/src/bun.js/builtins/cpp/ProcessObjectInternalsBuiltins.cpp
@@ -51,12 +51,12 @@ namespace WebCore {
const JSC::ConstructAbility s_processObjectInternalsGetStdioWriteStreamCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
const JSC::ConstructorKind s_processObjectInternalsGetStdioWriteStreamCodeConstructorKind = JSC::ConstructorKind::None;
const JSC::ImplementationVisibility s_processObjectInternalsGetStdioWriteStreamCodeImplementationVisibility = JSC::ImplementationVisibility::Public;
-const int s_processObjectInternalsGetStdioWriteStreamCodeLength = 9968;
+const int s_processObjectInternalsGetStdioWriteStreamCodeLength = 9767;
static const JSC::Intrinsic s_processObjectInternalsGetStdioWriteStreamCodeIntrinsic = JSC::NoIntrinsic;
const char* const s_processObjectInternalsGetStdioWriteStreamCode =
"(function (fd_, rawRequire) {\n" \
" var module = { path: \"node:process\", require: rawRequire };\n" \
- " var require = (path) => module.require(path);\n" \
+ " var require = path => module.require(path);\n" \
"\n" \
" function createStdioWriteStream(fd_) {\n" \
" var { Duplex, eos, destroy } = require(\"node:stream\");\n" \
@@ -103,20 +103,11 @@ const char* const s_processObjectInternalsGetStdioWriteStreamCode =
" _destroy(err, callback) {\n" \
" if (!err && this.#onClose !== null) {\n" \
" var AbortError = class AbortError extends Error {\n" \
- " constructor(\n" \
- " message = \"The operation was aborted\",\n" \
- " options = void 0,\n" \
- " ) {\n" \
+ " constructor(message = \"The operation was aborted\", options = void 0) {\n" \
" if (options !== void 0 && typeof options !== \"object\") {\n" \
- " throw new Error(\n" \
- " `Invalid AbortError options:\\n" \
+ " throw new Error(`Invalid AbortError options:\\n" \
"\\n" \
- "${JSON.stringify(\n" \
- " options,\n" \
- " null,\n" \
- " 2,\n" \
- " )}`,\n" \
- " );\n" \
+ "${JSON.stringify(options, null, 2)}`);\n" \
" }\n" \
" super(message, options);\n" \
" this.code = \"ABORT_ERR\";\n" \
@@ -158,7 +149,7 @@ const char* const s_processObjectInternalsGetStdioWriteStreamCode =
" }\n" \
" });\n" \
"\n" \
- " eos(stream, (err) => {\n" \
+ " eos(stream, err => {\n" \
" this.#writable = false;\n" \
" if (err) {\n" \
" destroy(stream, err);\n" \
@@ -197,7 +188,7 @@ const char* const s_processObjectInternalsGetStdioWriteStreamCode =
" this.push(null);\n" \
" });\n" \
"\n" \
- " eos(readStream, (err) => {\n" \
+ " eos(readStream, err => {\n" \
" this.#readable = false;\n" \
" if (err) {\n" \
" destroy(readStream, err);\n" \
@@ -230,12 +221,7 @@ const char* const s_processObjectInternalsGetStdioWriteStreamCode =
" if (!encoding) return true;\n" \
"\n" \
" var normalied = encoding.toLowerCase();\n" \
- " return (\n" \
- " normalied === \"utf8\" ||\n" \
- " normalied === \"utf-8\" ||\n" \
- " normalied === \"buffer\" ||\n" \
- " normalied === \"binary\"\n" \
- " );\n" \
+ " return normalied === \"utf8\" || normalied === \"utf-8\" || normalied === \"buffer\" || normalied === \"binary\";\n" \
" }\n" \
"\n" \
" var FastStdioWriteStream = class StdioWriteStream extends EventEmitter {\n" \
@@ -389,7 +375,7 @@ const char* const s_processObjectInternalsGetStdioWriteStreamCode =
" this.#performCallback(callback);\n" \
" this.emit(\"drain\");\n" \
" },\n" \
- " (err) => this.#performCallback(callback, err),\n" \
+ " err => this.#performCallback(callback, err),\n" \
" );\n" \
" return false;\n" \
" }\n" \
@@ -472,12 +458,12 @@ const char* const s_processObjectInternalsGetStdioWriteStreamCode =
const JSC::ConstructAbility s_processObjectInternalsGetStdinStreamCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
const JSC::ConstructorKind s_processObjectInternalsGetStdinStreamCodeConstructorKind = JSC::ConstructorKind::None;
const JSC::ImplementationVisibility s_processObjectInternalsGetStdinStreamCodeImplementationVisibility = JSC::ImplementationVisibility::Public;
-const int s_processObjectInternalsGetStdinStreamCodeLength = 4415;
+const int s_processObjectInternalsGetStdinStreamCodeLength = 4305;
static const JSC::Intrinsic s_processObjectInternalsGetStdinStreamCodeIntrinsic = JSC::NoIntrinsic;
const char* const s_processObjectInternalsGetStdinStreamCode =
"(function (fd_, rawRequire, Bun) {\n" \
" var module = { path: \"node:process\", require: rawRequire };\n" \
- " var require = (path) => module.require(path);\n" \
+ " var require = path => module.require(path);\n" \
"\n" \
" var { Duplex, eos, destroy } = require(\"node:stream\");\n" \
"\n" \
@@ -526,15 +512,9 @@ const char* const s_processObjectInternalsGetStdinStreamCode =
" var AbortError = class AbortError extends Error {\n" \
" constructor(message = \"The operation was aborted\", options = void 0) {\n" \
" if (options !== void 0 && typeof options !== \"object\") {\n" \
- " throw new Error(\n" \
- " `Invalid AbortError options:\\n" \
+ " throw new Error(`Invalid AbortError options:\\n" \
"\\n" \
- "${JSON.stringify(\n" \
- " options,\n" \
- " null,\n" \
- " 2,\n" \
- " )}`,\n" \
- " );\n" \
+ "${JSON.stringify(options, null, 2)}`);\n" \
" }\n" \
" super(message, options);\n" \
" this.code = \"ABORT_ERR\";\n" \
@@ -652,7 +632,7 @@ const char* const s_processObjectInternalsGetStdinStreamCode =
" }\n" \
" });\n" \
"\n" \
- " eos(writeStream, (err) => {\n" \
+ " eos(writeStream, err => {\n" \
" this.#writable = false;\n" \
" if (err) {\n" \
" destroy(writeStream, err);\n" \