Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(crypto) oneshot Sign and Verify #7256

Merged
merged 14 commits into from
Nov 25, 2023
595 changes: 592 additions & 3 deletions src/bun.js/bindings/KeyObject.cpp

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/bun.js/bindings/KeyObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ JSC::EncodedJSValue KeyObject__createPublicKey(JSC::JSGlobalObject* lexicalGloba
JSC::EncodedJSValue KeyObject__createPrivateKey(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC::EncodedJSValue KeyObject__generateKeySync(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC::EncodedJSValue KeyObject__generateKeyPairSync(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC::EncodedJSValue KeyObject__Sign(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame);
JSC::EncodedJSValue KeyObject__Verify(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame);

}
3 changes: 3 additions & 0 deletions src/bun.js/bindings/ZigGlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1754,6 +1754,9 @@ JSC_DEFINE_HOST_FUNCTION(functionLazyLoad,

obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "generateKeyPairSync"_s)), JSC::JSFunction::create(vm, globalObject, 2, "generateKeyPairSync"_s, KeyObject__generateKeyPairSync, ImplementationVisibility::Public, NoIntrinsic), 0);

obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "sign"_s)), JSC::JSFunction::create(vm, globalObject, 3, "sign"_s, KeyObject__Sign, ImplementationVisibility::Public, NoIntrinsic), 0);
obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "verify"_s)), JSC::JSFunction::create(vm, globalObject, 4, "verify"_s, KeyObject__Verify, ImplementationVisibility::Public, NoIntrinsic), 0);

return JSValue::encode(obj);
}

Expand Down
4 changes: 2 additions & 2 deletions src/bun.js/bindings/webcrypto/CryptoAlgorithmECDSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class CryptoAlgorithmECDSA final : public CryptoAlgorithm {
static constexpr CryptoAlgorithmIdentifier s_identifier = CryptoAlgorithmIdentifier::ECDSA;
static Ref<CryptoAlgorithm> create();

static ExceptionOr<Vector<uint8_t>> platformSign(const CryptoAlgorithmEcdsaParams&, const CryptoKeyEC&, const Vector<uint8_t>&);
static ExceptionOr<bool> platformVerify(const CryptoAlgorithmEcdsaParams&, const CryptoKeyEC&, const Vector<uint8_t>&, const Vector<uint8_t>&);
private:
CryptoAlgorithmECDSA() = default;
CryptoAlgorithmIdentifier identifier() const final;
Expand All @@ -50,8 +52,6 @@ class CryptoAlgorithmECDSA final : public CryptoAlgorithm {
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 CryptoAlgorithmEcdsaParams&, const CryptoKeyEC&, const Vector<uint8_t>&);
static ExceptionOr<bool> platformVerify(const CryptoAlgorithmEcdsaParams&, const CryptoKeyEC&, const Vector<uint8_t>&, const Vector<uint8_t>&);
};

} // namespace WebCore
Expand Down
106 changes: 70 additions & 36 deletions src/bun.js/bindings/webcrypto/CryptoAlgorithmECDSAOpenSSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,46 +56,80 @@ ExceptionOr<Vector<uint8_t>> CryptoAlgorithmECDSA::platformSign(const CryptoAlgo
if (!sig)
return Exception { OperationError };

const BIGNUM* r;
const BIGNUM* s;
ECDSA_SIG_get0(sig.get(), &r, &s);

// Concatenate r and s, expanding r and s to keySizeInBytes.
Vector<uint8_t> signature = convertToBytesExpand(r, keySizeInBytes);
signature.appendVector(convertToBytesExpand(s, keySizeInBytes));

return signature;
if (parameters.encoding == CryptoAlgorithmECDSAEncoding::DER) {
int derSigLength = i2d_ECDSA_SIG(sig.get(), nullptr);
if (derSigLength <= 0)
return Exception { OperationError };
Vector<uint8_t> signature(derSigLength);
uint8_t* p = signature.data();
if(i2d_ECDSA_SIG(sig.get(), &p) != derSigLength)
return Exception { OperationError };
return signature;
} else {

const BIGNUM* r;
const BIGNUM* s;
ECDSA_SIG_get0(sig.get(), &r, &s);

// Concatenate r and s, expanding r and s to keySizeInBytes.
Vector<uint8_t> signature = convertToBytesExpand(r, keySizeInBytes);
signature.appendVector(convertToBytesExpand(s, keySizeInBytes));
return signature;
}
}

ExceptionOr<bool> CryptoAlgorithmECDSA::platformVerify(const CryptoAlgorithmEcdsaParams& parameters, const CryptoKeyEC& key, const Vector<uint8_t>& signature, const Vector<uint8_t>& data)
{
size_t keySizeInBytes = (key.keySizeInBits() + 7) / 8;

// Bail if the signature size isn't double the key size (i.e. concatenated r and s components).
if (signature.size() != keySizeInBytes * 2)
return false;

auto sig = ECDSASigPtr(ECDSA_SIG_new());
auto r = BN_bin2bn(signature.data(), keySizeInBytes, nullptr);
auto s = BN_bin2bn(signature.data() + keySizeInBytes, keySizeInBytes, nullptr);

if (!ECDSA_SIG_set0(sig.get(), r, s))
return Exception { OperationError };

const EVP_MD* md = digestAlgorithm(parameters.hashIdentifier);
if (!md)
return Exception { NotSupportedError };

std::optional<Vector<uint8_t>> digest = calculateDigest(md, data);
if (!digest)
return Exception { OperationError };

EC_KEY* ecKey = EVP_PKEY_get0_EC_KEY(key.platformKey());
if (!ecKey)
return Exception { OperationError };

int ret = ECDSA_do_verify(digest->data(), digest->size(), sig.get(), ecKey);
return ret == 1;
if (parameters.encoding == CryptoAlgorithmECDSAEncoding::DER) {
const uint8_t* p = signature.data();

auto sig = ECDSASigPtr(d2i_ECDSA_SIG(nullptr, &p, signature.size()));
if (!sig)
return Exception { OperationError };

const EVP_MD* md = digestAlgorithm(parameters.hashIdentifier);
if (!md)
return Exception { NotSupportedError };

std::optional<Vector<uint8_t>> digest = calculateDigest(md, data);
if (!digest)
return Exception { OperationError };

EC_KEY* ecKey = EVP_PKEY_get0_EC_KEY(key.platformKey());
if (!ecKey)
return Exception { OperationError };

int ret = ECDSA_do_verify(digest->data(), digest->size(), sig.get(), ecKey);
return ret == 1;
} else {
size_t keySizeInBytes = (key.keySizeInBits() + 7) / 8;

// Bail if the signature size isn't double the key size (i.e. concatenated r and s components).
if (signature.size() != keySizeInBytes * 2)
return false;

auto sig = ECDSASigPtr(ECDSA_SIG_new());
auto r = BN_bin2bn(signature.data(), keySizeInBytes, nullptr);
auto s = BN_bin2bn(signature.data() + keySizeInBytes, keySizeInBytes, nullptr);

if (!ECDSA_SIG_set0(sig.get(), r, s))
return Exception { OperationError };

const EVP_MD* md = digestAlgorithm(parameters.hashIdentifier);
if (!md)
return Exception { NotSupportedError };

std::optional<Vector<uint8_t>> digest = calculateDigest(md, data);
if (!digest)
return Exception { OperationError };

EC_KEY* ecKey = EVP_PKEY_get0_EC_KEY(key.platformKey());
if (!ecKey)
return Exception { OperationError };

int ret = ECDSA_do_verify(digest->data(), digest->size(), sig.get(), ecKey);
return ret == 1;
}
}

} // namespace WebCore
Expand Down
8 changes: 7 additions & 1 deletion src/bun.js/bindings/webcrypto/CryptoAlgorithmEcdsaParams.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,17 @@

namespace WebCore {

enum CryptoAlgorithmECDSAEncoding {
IeeeP1363,
DER,
};
class CryptoAlgorithmEcdsaParams final : public CryptoAlgorithmParameters {
public:
// FIXME: Consider merging hash and hashIdentifier.
std::variant<JSC::Strong<JSC::JSObject>, String> hash;
CryptoAlgorithmIdentifier hashIdentifier;
// WebCrypto default is IeeeP1363.
CryptoAlgorithmECDSAEncoding encoding { CryptoAlgorithmECDSAEncoding::IeeeP1363 };

Class parametersClass() const final { return Class::EcdsaParams; }

Expand All @@ -47,7 +53,7 @@ class CryptoAlgorithmEcdsaParams final : public CryptoAlgorithmParameters {
CryptoAlgorithmEcdsaParams result;
result.identifier = identifier;
result.hashIdentifier = hashIdentifier;

result.encoding = encoding;
return result;
}
};
Expand Down
5 changes: 2 additions & 3 deletions src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class CryptoAlgorithmEd25519 final : public CryptoAlgorithm {
static constexpr CryptoAlgorithmIdentifier s_identifier = CryptoAlgorithmIdentifier::Ed25519;
static Ref<CryptoAlgorithm> create();

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>&);
private:
CryptoAlgorithmEd25519() = default;
CryptoAlgorithmIdentifier identifier() const final;
Expand All @@ -46,9 +48,6 @@ class CryptoAlgorithmEd25519 final : public CryptoAlgorithm {
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
Expand Down
3 changes: 3 additions & 0 deletions src/bun.js/bindings/webcrypto/CryptoAlgorithmHMAC.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ class CryptoAlgorithmHMAC final : public CryptoAlgorithm {

// Operations can be performed directly.
static ExceptionOr<Vector<uint8_t>> platformSign(const CryptoKeyHMAC&, const Vector<uint8_t>&);
static ExceptionOr<Vector<uint8_t>> platformSignWithAlgorithm(const CryptoKeyHMAC&, CryptoAlgorithmIdentifier, const Vector<uint8_t>&);
static ExceptionOr<bool> platformVerify(const CryptoKeyHMAC&, const Vector<uint8_t>&, const Vector<uint8_t>&);
static ExceptionOr<bool> platformVerifyWithAlgorithm(const CryptoKeyHMAC&, CryptoAlgorithmIdentifier, const Vector<uint8_t>&, const Vector<uint8_t>&);


private:
CryptoAlgorithmHMAC() = default;
Expand Down
24 changes: 24 additions & 0 deletions src/bun.js/bindings/webcrypto/CryptoAlgorithmHMACOpenSSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ static std::optional<Vector<uint8_t>> calculateSignature(const EVP_MD* algorithm
return cipherText;
}

ExceptionOr<Vector<uint8_t>> CryptoAlgorithmHMAC::platformSignWithAlgorithm(const CryptoKeyHMAC& key, CryptoAlgorithmIdentifier algorithmIdentifier, const Vector<uint8_t>& data) {

auto algorithm = digestAlgorithm(algorithmIdentifier);
if (!algorithm)
return Exception { OperationError };

auto result = calculateSignature(algorithm, key.key(), data.data(), data.size());
if (!result)
return Exception { OperationError };
return WTFMove(*result);
}

ExceptionOr<Vector<uint8_t>> CryptoAlgorithmHMAC::platformSign(const CryptoKeyHMAC& key, const Vector<uint8_t>& data)
{
auto algorithm = digestAlgorithm(key.hashAlgorithmIdentifier());
Expand All @@ -71,6 +83,18 @@ ExceptionOr<Vector<uint8_t>> CryptoAlgorithmHMAC::platformSign(const CryptoKeyHM
return WTFMove(*result);
}

ExceptionOr<bool> CryptoAlgorithmHMAC::platformVerifyWithAlgorithm(const CryptoKeyHMAC& key, CryptoAlgorithmIdentifier algorithmIdentifier, const Vector<uint8_t>& signature, const Vector<uint8_t>& data) {

auto algorithm = digestAlgorithm(algorithmIdentifier);
if (!algorithm)
return Exception { OperationError };

auto expectedSignature = calculateSignature(algorithm, key.key(), data.data(), data.size());
if (!expectedSignature)
return Exception { OperationError };
// Using a constant time comparison to prevent timing attacks.
return signature.size() == expectedSignature->size() && !constantTimeMemcmp(expectedSignature->data(), signature.data(), expectedSignature->size());
}
ExceptionOr<bool> CryptoAlgorithmHMAC::platformVerify(const CryptoKeyHMAC& key, const Vector<uint8_t>& signature, const Vector<uint8_t>& data)
{
auto algorithm = digestAlgorithm(key.hashAlgorithmIdentifier());
Expand Down
10 changes: 7 additions & 3 deletions src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ class CryptoAlgorithmRSASSA_PKCS1_v1_5 final : public CryptoAlgorithm {
static constexpr CryptoAlgorithmIdentifier s_identifier = CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5;
static Ref<CryptoAlgorithm> create();

static ExceptionOr<Vector<uint8_t>> platformSign(const CryptoKeyRSA&, const Vector<uint8_t>&);
static ExceptionOr<Vector<uint8_t>> platformSignWithAlgorithm(const CryptoKeyRSA&, CryptoAlgorithmIdentifier, const Vector<uint8_t>&);

static ExceptionOr<bool> platformVerify(const CryptoKeyRSA&, const Vector<uint8_t>&, const Vector<uint8_t>&);
static ExceptionOr<bool> platformVerifyWithAlgorithm(const CryptoKeyRSA&, CryptoAlgorithmIdentifier, const Vector<uint8_t>&, const Vector<uint8_t>&);


private:
CryptoAlgorithmRSASSA_PKCS1_v1_5() = default;
CryptoAlgorithmIdentifier identifier() const final;
Expand All @@ -48,9 +55,6 @@ class CryptoAlgorithmRSASSA_PKCS1_v1_5 final : public CryptoAlgorithm {
void generateKey(const CryptoAlgorithmParameters&, bool extractable, CryptoKeyUsageBitmap, KeyOrKeyPairCallback&&, ExceptionCallback&&, ScriptExecutionContext&) 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 CryptoKeyRSA&, const Vector<uint8_t>&);
static ExceptionOr<bool> platformVerify(const CryptoKeyRSA&, const Vector<uint8_t>&, const Vector<uint8_t>&);
};

} // namespace WebCore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,8 @@

namespace WebCore {

ExceptionOr<Vector<uint8_t>> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformSign(const CryptoKeyRSA& key, const Vector<uint8_t>& data)
{
const EVP_MD* md = digestAlgorithm(key.hashAlgorithmIdentifier());
if (!md)
return Exception { NotSupportedError };

static ExceptionOr<Vector<uint8_t>> signWithEVP_MD(const CryptoKeyRSA& key, const EVP_MD* md, const Vector<uint8_t>& data) {

std::optional<Vector<uint8_t>> digest = calculateDigest(md, data);
if (!digest)
Expand Down Expand Up @@ -68,12 +65,25 @@ ExceptionOr<Vector<uint8_t>> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformSign(cons
return signature;
}

ExceptionOr<bool> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformVerify(const CryptoKeyRSA& key, const Vector<uint8_t>& signature, const Vector<uint8_t>& data)
ExceptionOr<Vector<uint8_t>> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformSignWithAlgorithm(const CryptoKeyRSA& key, CryptoAlgorithmIdentifier algorithm, const Vector<uint8_t>& data) {

const EVP_MD* md = digestAlgorithm(algorithm);
if (!md)
return Exception { NotSupportedError };

return signWithEVP_MD(key, md, data);
}
ExceptionOr<Vector<uint8_t>> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformSign(const CryptoKeyRSA& key, const Vector<uint8_t>& data)
{
const EVP_MD* md = digestAlgorithm(key.hashAlgorithmIdentifier());
if (!md)
return Exception { NotSupportedError };

return signWithEVP_MD(key, md, data);
}


static ExceptionOr<bool> verifyWithEVP_MD(const CryptoKeyRSA& key, const EVP_MD* md, const Vector<uint8_t>& signature, const Vector<uint8_t>& data) {
std::optional<Vector<uint8_t>> digest = calculateDigest(md, data);
if (!digest)
return Exception { OperationError };
Expand All @@ -96,6 +106,24 @@ ExceptionOr<bool> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformVerify(const CryptoK
return ret == 1;
}

ExceptionOr<bool> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformVerifyWithAlgorithm(const CryptoKeyRSA& key, CryptoAlgorithmIdentifier algorithm, const Vector<uint8_t>& signature, const Vector<uint8_t>& data) {
const EVP_MD* md = digestAlgorithm(algorithm);
if (!md)
return Exception { NotSupportedError };

return verifyWithEVP_MD(key, md, signature, data);
}


ExceptionOr<bool> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformVerify(const CryptoKeyRSA& key, const Vector<uint8_t>& signature, const Vector<uint8_t>& data)
{
const EVP_MD* md = digestAlgorithm(key.hashAlgorithmIdentifier());
if (!md)
return Exception { NotSupportedError };

return verifyWithEVP_MD(key, md, signature, data);
}

} // namespace WebCore

#endif // ENABLE(WEB_CRYPTO)
9 changes: 7 additions & 2 deletions src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSS.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ class CryptoAlgorithmRSA_PSS final : public CryptoAlgorithm {
static constexpr CryptoAlgorithmIdentifier s_identifier = CryptoAlgorithmIdentifier::RSA_PSS;
static Ref<CryptoAlgorithm> create();

static ExceptionOr<Vector<uint8_t>> platformSign(const CryptoAlgorithmRsaPssParams&, const CryptoKeyRSA&, const Vector<uint8_t>&);
static ExceptionOr<Vector<uint8_t>> platformSignWithAlgorithm(const CryptoAlgorithmRsaPssParams&, CryptoAlgorithmIdentifier, const CryptoKeyRSA&, const Vector<uint8_t>&);

static ExceptionOr<bool> platformVerify(const CryptoAlgorithmRsaPssParams&, const CryptoKeyRSA&, const Vector<uint8_t>&, const Vector<uint8_t>&);
static ExceptionOr<bool> platformVerifyWithAlgorithm(const CryptoAlgorithmRsaPssParams&, CryptoAlgorithmIdentifier, const CryptoKeyRSA&, const Vector<uint8_t>&, const Vector<uint8_t>&);


private:
CryptoAlgorithmRSA_PSS() = default;
CryptoAlgorithmIdentifier identifier() const final;
Expand All @@ -50,8 +57,6 @@ class CryptoAlgorithmRSA_PSS final : public CryptoAlgorithm {
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 CryptoAlgorithmRsaPssParams&, const CryptoKeyRSA&, const Vector<uint8_t>&);
static ExceptionOr<bool> platformVerify(const CryptoAlgorithmRsaPssParams&, const CryptoKeyRSA&, const Vector<uint8_t>&, const Vector<uint8_t>&);
};

} // namespace WebCore
Expand Down
Loading
Loading