Skip to content

Commit

Permalink
fix(crypto) oneshot Sign and Verify (oven-sh#7256)
Browse files Browse the repository at this point in the history
* WIP

* native oneshot sign

* add native verify

* fallback rsa to non-native

* WIP der dsaEncoding

* pass encoding

* RSA-PSS padding and saltLength

* oopies

* improve RSA-PSS support

* accepts hash identifiers like nodejs and add options.hashAlgorithm support

* fix string check

* tests

* define hash for ECDSA

* fix compilation
  • Loading branch information
cirospaciari authored and ryoppippi committed Feb 1, 2024
1 parent b562b69 commit 0cdfb1b
Show file tree
Hide file tree
Showing 24 changed files with 1,076 additions and 203 deletions.
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

0 comments on commit 0cdfb1b

Please sign in to comment.