mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 10:58:56 +00:00
[windows] fix crypto (#8797)
* fix password * refactor --------- Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
This commit is contained in:
@@ -128,15 +128,49 @@ struct AsymmetricKeyValueWithDER {
|
||||
long der_len;
|
||||
};
|
||||
|
||||
struct PrivateKeyPassphrase {
|
||||
class KeyPassphrase {
|
||||
private:
|
||||
WTF::CString pass_holder;
|
||||
|
||||
public:
|
||||
char* passphrase;
|
||||
size_t passphrase_len;
|
||||
bool hasPassphrase;
|
||||
KeyPassphrase(JSValue passphraseJSValue, JSC::JSGlobalObject* globalObject, JSC::ThrowScope& scope)
|
||||
{
|
||||
this->passphrase = nullptr;
|
||||
this->passphrase_len = 0;
|
||||
if (passphraseJSValue.isUndefinedOrNull() || passphraseJSValue.isEmpty()) {
|
||||
this->hasPassphrase = false;
|
||||
return;
|
||||
}
|
||||
this->hasPassphrase = true;
|
||||
if (passphraseJSValue.isString()) {
|
||||
auto passphrase_wtfstr = passphraseJSValue.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, );
|
||||
if (!passphrase_wtfstr.isNull()) {
|
||||
if (auto pass = passphrase_wtfstr.tryGetUTF8()) {
|
||||
if (pass.has_value()) {
|
||||
auto value = pass.value();
|
||||
this->passphrase = const_cast<char*>(value.data());
|
||||
this->passphrase_len = value.length();
|
||||
pass_holder = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (auto* passphraseBuffer = jsDynamicCast<JSUint8Array*>(passphraseJSValue)) {
|
||||
this->passphrase = (char*)passphraseBuffer->vector();
|
||||
this->passphrase_len = passphraseBuffer->byteLength();
|
||||
} else {
|
||||
JSC::throwTypeError(globalObject, scope, "passphrase must be a Buffer or String"_s);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int PasswordCallback(char* buf, int size, int rwflag, void* u)
|
||||
{
|
||||
auto result = static_cast<PrivateKeyPassphrase*>(u);
|
||||
if (result != nullptr && size > 0 && result->passphrase != nullptr) {
|
||||
auto result = static_cast<KeyPassphrase*>(u);
|
||||
if (result->hasPassphrase && result != nullptr && size > 0 && result->passphrase != nullptr) {
|
||||
size_t buflen = static_cast<size_t>(size);
|
||||
size_t len = result->passphrase_len;
|
||||
if (buflen < len)
|
||||
@@ -367,31 +401,8 @@ JSC::EncodedJSValue KeyObject__createPrivateKey(JSC::JSGlobalObject* globalObjec
|
||||
}
|
||||
|
||||
JSValue passphraseJSValue = options->getIfPropertyExists(globalObject, PropertyName(Identifier::fromString(vm, "passphrase"_s)));
|
||||
PrivateKeyPassphrase passphrase = { nullptr, 0 };
|
||||
|
||||
auto hasPassphrase = !passphraseJSValue.isUndefinedOrNull() && !passphraseJSValue.isEmpty();
|
||||
|
||||
if (hasPassphrase) {
|
||||
if (passphraseJSValue.isString()) {
|
||||
auto passphrase_wtfstr = passphraseJSValue.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, encodedJSValue());
|
||||
if (!passphrase_wtfstr.isNull()) {
|
||||
if (auto pass = passphrase_wtfstr.tryGetUTF8()) {
|
||||
if (pass.has_value()) {
|
||||
auto value = pass.value();
|
||||
passphrase.passphrase = const_cast<char*>(value.data());
|
||||
passphrase.passphrase_len = value.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (auto* passphraseBuffer = jsDynamicCast<JSUint8Array*>(passphraseJSValue)) {
|
||||
passphrase.passphrase = (char*)passphraseBuffer->vector();
|
||||
passphrase.passphrase_len = passphraseBuffer->byteLength();
|
||||
} else {
|
||||
JSC::throwTypeError(globalObject, scope, "passphrase must be a Buffer or String"_s);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
}
|
||||
KeyPassphrase passphrase(passphraseJSValue, globalObject, scope);
|
||||
RETURN_IF_EXCEPTION(scope, encodedJSValue());
|
||||
|
||||
if (format == "pem"_s) {
|
||||
auto bio = BIOPtr(BIO_new_mem_buf(const_cast<char*>((char*)data), byteLength));
|
||||
@@ -952,32 +963,8 @@ JSC::EncodedJSValue KeyObject__createPublicKey(JSC::JSGlobalObject* globalObject
|
||||
// maybe is a private pem
|
||||
auto bio = BIOPtr(BIO_new_mem_buf(const_cast<char*>((char*)data), byteLength));
|
||||
JSValue passphraseJSValue = options->getIfPropertyExists(globalObject, PropertyName(Identifier::fromString(vm, "passphrase"_s)));
|
||||
PrivateKeyPassphrase passphrase = { nullptr, 0 };
|
||||
|
||||
auto hasPassphrase = !passphraseJSValue.isUndefinedOrNull() && !passphraseJSValue.isEmpty();
|
||||
|
||||
if (hasPassphrase) {
|
||||
if (passphraseJSValue.isString()) {
|
||||
auto passphrase_wtfstr = passphraseJSValue.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, encodedJSValue());
|
||||
if (!passphrase_wtfstr.isNull()) {
|
||||
if (auto pass = passphrase_wtfstr.tryGetUTF8()) {
|
||||
if (pass.has_value()) {
|
||||
auto value = pass.value();
|
||||
passphrase.passphrase = const_cast<char*>(value.data());
|
||||
passphrase.passphrase_len = value.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (auto* passphraseBuffer = jsDynamicCast<JSUint8Array*>(passphraseJSValue)) {
|
||||
passphrase.passphrase = (char*)passphraseBuffer->vector();
|
||||
passphrase.passphrase_len = passphraseBuffer->byteLength();
|
||||
} else {
|
||||
JSC::throwTypeError(globalObject, scope, "passphrase must be a Buffer or String"_s);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
}
|
||||
|
||||
KeyPassphrase passphrase(passphraseJSValue, globalObject, scope);
|
||||
RETURN_IF_EXCEPTION(scope, encodedJSValue());
|
||||
auto pkey = EvpPKeyPtr(PEM_read_bio_PrivateKey(bio.get(), nullptr, PasswordCallback, &passphrase));
|
||||
if (!pkey) {
|
||||
throwException(globalObject, scope, createTypeError(globalObject, "Invalid PEM data"_s));
|
||||
@@ -1740,7 +1727,8 @@ JSC::EncodedJSValue KeyObject__Exports(JSC::JSGlobalObject* globalObject, JSC::C
|
||||
JSValue formatJSValue = options->getIfPropertyExists(globalObject, PropertyName(Identifier::fromString(vm, "format"_s)));
|
||||
JSValue typeJSValue = options->getIfPropertyExists(globalObject, PropertyName(Identifier::fromString(vm, "type"_s)));
|
||||
JSValue passphraseJSValue = options->getIfPropertyExists(globalObject, PropertyName(Identifier::fromString(vm, "passphrase"_s)));
|
||||
auto hasPassphrase = !passphraseJSValue.isUndefinedOrNull() && !passphraseJSValue.isEmpty();
|
||||
KeyPassphrase passphrase(passphraseJSValue, globalObject, scope);
|
||||
RETURN_IF_EXCEPTION(scope, encodedJSValue());
|
||||
if (formatJSValue.isUndefinedOrNull() || formatJSValue.isEmpty()) {
|
||||
JSC::throwTypeError(globalObject, scope, "format is expected to be a string"_s);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
@@ -1748,7 +1736,7 @@ JSC::EncodedJSValue KeyObject__Exports(JSC::JSGlobalObject* globalObject, JSC::C
|
||||
|
||||
auto string = formatJSValue.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, encodedJSValue());
|
||||
if (string == "jwk"_s && hasPassphrase) {
|
||||
if (string == "jwk"_s && passphrase.hasPassphrase) {
|
||||
JSC::throwTypeError(globalObject, scope, "encryption is not supported for jwk format"_s);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
@@ -1879,45 +1867,23 @@ JSC::EncodedJSValue KeyObject__Exports(JSC::JSGlobalObject* globalObject, JSC::C
|
||||
}
|
||||
}
|
||||
}
|
||||
void* passphrase = nullptr;
|
||||
size_t passphrase_len = 0;
|
||||
if (hasPassphrase) {
|
||||
if (passphrase.hasPassphrase) {
|
||||
if (!cipher) {
|
||||
JSC::throwTypeError(globalObject, scope, "cipher is required when passphrase is specified"_s);
|
||||
BIO_free(bio);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
if (passphraseJSValue.isString()) {
|
||||
auto passphrase_wtfstr = passphraseJSValue.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, encodedJSValue());
|
||||
if (!passphrase_wtfstr.isNull()) {
|
||||
if (auto pass = passphrase_wtfstr.tryGetUTF8()) {
|
||||
if (pass.has_value()) {
|
||||
auto value = pass.value();
|
||||
passphrase = const_cast<char*>(value.data());
|
||||
passphrase_len = value.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (auto* passphraseBuffer = jsDynamicCast<JSUint8Array*>(passphraseJSValue)) {
|
||||
passphrase = passphraseBuffer->vector();
|
||||
passphrase_len = passphraseBuffer->byteLength();
|
||||
} else {
|
||||
JSC::throwTypeError(globalObject, scope, "passphrase must be a Buffer or String"_s);
|
||||
BIO_free(bio);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
}
|
||||
|
||||
if (string == "pem"_s) {
|
||||
if (type == "pkcs1"_s) {
|
||||
if (PEM_write_bio_RSAPrivateKey(bio, rsa_ptr, cipher, (unsigned char*)passphrase, passphrase_len, nullptr, nullptr) != 1) {
|
||||
if (PEM_write_bio_RSAPrivateKey(bio, rsa_ptr, cipher, (unsigned char*)passphrase.passphrase, passphrase.passphrase_len, nullptr, nullptr) != 1) {
|
||||
JSC::throwTypeError(globalObject, scope, "Failed to write private key"_s);
|
||||
BIO_free(bio);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
} else if (type == "pkcs8"_s) {
|
||||
if (PEM_write_bio_PKCS8PrivateKey(bio, rsaKey, cipher, (char*)passphrase, passphrase_len, nullptr, nullptr) != 1) {
|
||||
if (PEM_write_bio_PKCS8PrivateKey(bio, rsaKey, cipher, passphrase.passphrase, passphrase.passphrase_len, nullptr, nullptr) != 1) {
|
||||
JSC::throwTypeError(globalObject, scope, "Failed to write private key"_s);
|
||||
BIO_free(bio);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
@@ -1935,7 +1901,7 @@ JSC::EncodedJSValue KeyObject__Exports(JSC::JSGlobalObject* globalObject, JSC::C
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
} else if (type == "pkcs8"_s) {
|
||||
if (i2d_PKCS8PrivateKey_bio(bio, rsaKey, cipher, (char*)passphrase, passphrase_len, nullptr, nullptr) != 1) {
|
||||
if (i2d_PKCS8PrivateKey_bio(bio, rsaKey, cipher, passphrase.passphrase, passphrase.passphrase_len, nullptr, nullptr) != 1) {
|
||||
JSC::throwTypeError(globalObject, scope, "Failed to write private key"_s);
|
||||
BIO_free(bio);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
@@ -2024,7 +1990,6 @@ JSC::EncodedJSValue KeyObject__Exports(JSC::JSGlobalObject* globalObject, JSC::C
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
} else {
|
||||
JSValue passphraseJSValue = options->getIfPropertyExists(globalObject, PropertyName(Identifier::fromString(vm, "passphrase"_s)));
|
||||
JSValue cipherJSValue = options->getIfPropertyExists(globalObject, PropertyName(Identifier::fromString(vm, "cipher"_s)));
|
||||
|
||||
const EVP_CIPHER* cipher = nullptr;
|
||||
@@ -2046,47 +2011,25 @@ JSC::EncodedJSValue KeyObject__Exports(JSC::JSGlobalObject* globalObject, JSC::C
|
||||
}
|
||||
}
|
||||
}
|
||||
void* passphrase = nullptr;
|
||||
size_t passphrase_len = 0;
|
||||
auto hasPassphrase = !passphraseJSValue.isUndefinedOrNull() && !passphraseJSValue.isEmpty();
|
||||
|
||||
if (hasPassphrase) {
|
||||
if (passphrase.hasPassphrase) {
|
||||
|
||||
if (!cipher) {
|
||||
JSC::throwTypeError(globalObject, scope, "cipher is required when passphrase is specified"_s);
|
||||
BIO_free(bio);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
if (passphraseJSValue.isString()) {
|
||||
auto passphrase_wtfstr = passphraseJSValue.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, encodedJSValue());
|
||||
if (!passphrase_wtfstr.isNull()) {
|
||||
if (auto pass = passphrase_wtfstr.tryGetUTF8()) {
|
||||
if (pass.has_value()) {
|
||||
auto value = pass.value();
|
||||
passphrase = const_cast<char*>(value.data());
|
||||
passphrase_len = value.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (auto* passphraseBuffer = jsDynamicCast<JSUint8Array*>(passphraseJSValue)) {
|
||||
passphrase = passphraseBuffer->vector();
|
||||
passphrase_len = passphraseBuffer->byteLength();
|
||||
} else {
|
||||
JSC::throwTypeError(globalObject, scope, "passphrase must be a Buffer or String"_s);
|
||||
BIO_free(bio);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
}
|
||||
|
||||
if (string == "pem"_s) {
|
||||
if (type == "sec1"_s) {
|
||||
if (PEM_write_bio_ECPrivateKey(bio, ec_ptr, cipher, (unsigned char*)passphrase, passphrase_len, nullptr, nullptr) != 1) {
|
||||
if (PEM_write_bio_ECPrivateKey(bio, ec_ptr, cipher, (unsigned char*)passphrase.passphrase, passphrase.passphrase_len, nullptr, nullptr) != 1) {
|
||||
JSC::throwTypeError(globalObject, scope, "Failed to write private key"_s);
|
||||
BIO_free(bio);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
} else if (type == "pkcs8"_s) {
|
||||
if (PEM_write_bio_PKCS8PrivateKey(bio, ecKey, cipher, (char*)passphrase, passphrase_len, nullptr, nullptr) != 1) {
|
||||
if (PEM_write_bio_PKCS8PrivateKey(bio, ecKey, cipher, passphrase.passphrase, passphrase.passphrase_len, nullptr, nullptr) != 1) {
|
||||
JSC::throwTypeError(globalObject, scope, "Failed to write private key"_s);
|
||||
BIO_free(bio);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
@@ -2104,7 +2047,7 @@ JSC::EncodedJSValue KeyObject__Exports(JSC::JSGlobalObject* globalObject, JSC::C
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
} else if (type == "pkcs8"_s) {
|
||||
if (i2d_PKCS8PrivateKey_bio(bio, ecKey, cipher, (char*)passphrase, passphrase_len, nullptr, nullptr) != 1) {
|
||||
if (i2d_PKCS8PrivateKey_bio(bio, ecKey, cipher, passphrase.passphrase, passphrase.passphrase_len, nullptr, nullptr) != 1) {
|
||||
JSC::throwTypeError(globalObject, scope, "Failed to write private key"_s);
|
||||
BIO_free(bio);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
@@ -2164,7 +2107,6 @@ JSC::EncodedJSValue KeyObject__Exports(JSC::JSGlobalObject* globalObject, JSC::C
|
||||
// TODO: CHECK THIS WHEN X488 AND ED448 ARE ADDED
|
||||
if (okpKey.type() == CryptoKeyType::Private) {
|
||||
evpKey = EVP_PKEY_new_raw_private_key(okpKey.namedCurve() == CryptoKeyOKP::NamedCurve::X25519 ? EVP_PKEY_X25519 : EVP_PKEY_ED25519, nullptr, keyData.data(), keyData.size());
|
||||
JSValue passphraseJSValue = options->getIfPropertyExists(globalObject, PropertyName(Identifier::fromString(vm, "passphrase"_s)));
|
||||
JSValue cipherJSValue = options->getIfPropertyExists(globalObject, PropertyName(Identifier::fromString(vm, "cipher"_s)));
|
||||
|
||||
const EVP_CIPHER* cipher = nullptr;
|
||||
@@ -2187,41 +2129,18 @@ JSC::EncodedJSValue KeyObject__Exports(JSC::JSGlobalObject* globalObject, JSC::C
|
||||
}
|
||||
}
|
||||
}
|
||||
void* passphrase = nullptr;
|
||||
size_t passphrase_len = 0;
|
||||
auto hasPassphrase = !passphraseJSValue.isUndefinedOrNull() && !passphraseJSValue.isEmpty();
|
||||
|
||||
if (hasPassphrase) {
|
||||
if (passphrase.hasPassphrase) {
|
||||
if (!cipher) {
|
||||
JSC::throwTypeError(globalObject, scope, "cipher is required when passphrase is specified"_s);
|
||||
BIO_free(bio);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
if (passphraseJSValue.isString()) {
|
||||
auto passphrase_wtfstr = passphraseJSValue.toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, encodedJSValue());
|
||||
if (!passphrase_wtfstr.isNull()) {
|
||||
if (auto pass = passphrase_wtfstr.tryGetUTF8()) {
|
||||
if (pass.has_value()) {
|
||||
auto value = pass.value();
|
||||
passphrase = const_cast<char*>(value.data());
|
||||
passphrase_len = value.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (auto* passphraseBuffer = jsDynamicCast<JSUint8Array*>(passphraseJSValue)) {
|
||||
passphrase = passphraseBuffer->vector();
|
||||
passphrase_len = passphraseBuffer->byteLength();
|
||||
} else {
|
||||
JSC::throwTypeError(globalObject, scope, "passphrase must be a Buffer or String"_s);
|
||||
BIO_free(bio);
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
}
|
||||
|
||||
if (string == "pem"_s) {
|
||||
if (type == "pkcs8"_s) {
|
||||
if (PEM_write_bio_PKCS8PrivateKey(bio, evpKey, cipher, (char*)passphrase, passphrase_len, nullptr, nullptr) != 1) {
|
||||
if (PEM_write_bio_PKCS8PrivateKey(bio, evpKey, cipher, passphrase.passphrase, passphrase.passphrase_len, nullptr, nullptr) != 1) {
|
||||
JSC::throwTypeError(globalObject, scope, "Failed to write private key"_s);
|
||||
BIO_free(bio);
|
||||
EVP_PKEY_free(evpKey);
|
||||
@@ -2235,7 +2154,7 @@ JSC::EncodedJSValue KeyObject__Exports(JSC::JSGlobalObject* globalObject, JSC::C
|
||||
}
|
||||
} else if (string == "der"_s) {
|
||||
if (type == "pkcs8"_s) {
|
||||
if (i2d_PKCS8PrivateKey_bio(bio, evpKey, cipher, (char*)passphrase, passphrase_len, nullptr, nullptr) != 1) {
|
||||
if (i2d_PKCS8PrivateKey_bio(bio, evpKey, cipher, passphrase.passphrase, passphrase.passphrase_len, nullptr, nullptr) != 1) {
|
||||
JSC::throwTypeError(globalObject, scope, "Failed to write private key"_s);
|
||||
BIO_free(bio);
|
||||
EVP_PKEY_free(evpKey);
|
||||
|
||||
Reference in New Issue
Block a user