Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions src/jsc/bindings/node/crypto/JSDiffieHellmanConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ JSC_DEFINE_HOST_FUNCTION(constructDiffieHellman, (JSC::JSGlobalObject * globalOb
}

if (!generatorValue.isNumber()) {
return JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, "Second argument must be an int32"_s));
throwError(globalObject, scope, ErrorCode::ERR_INVALID_ARG_TYPE, "Second argument must be an int32"_s);
return {};
}

int32_t generator = 0;
Expand All @@ -110,7 +111,8 @@ JSC_DEFINE_HOST_FUNCTION(constructDiffieHellman, (JSC::JSGlobalObject * globalOb

dh = ncrypto::DHPointer::New(bits, generator);
if (!dh) {
return JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_ARG_VALUE, "Invalid DH parameters"_s));
throwError(globalObject, scope, ErrorCode::ERR_INVALID_ARG_VALUE, "Invalid DH parameters"_s);
return {};
}
} else {

Expand All @@ -121,12 +123,14 @@ JSC_DEFINE_HOST_FUNCTION(constructDiffieHellman, (JSC::JSGlobalObject * globalOb
RETURN_IF_EXCEPTION(scope, {});

if (keyView->byteLength() > INT32_MAX) {
return JSValue::encode(createError(globalObject, ErrorCode::ERR_OUT_OF_RANGE, "prime is too big"_s));
throwError(globalObject, scope, ErrorCode::ERR_OUT_OF_RANGE, "prime is too big"_s);
return {};
}

ncrypto::BignumPointer bn_p(reinterpret_cast<uint8_t*>(keyView->vector()), keyView->byteLength());
if (!bn_p) {
return JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_ARG_VALUE, "Invalid prime"_s));
throwError(globalObject, scope, ErrorCode::ERR_INVALID_ARG_VALUE, "Invalid prime"_s);
return {};
}
ncrypto::BignumPointer bn_g;

Expand All @@ -141,18 +145,21 @@ JSC_DEFINE_HOST_FUNCTION(constructDiffieHellman, (JSC::JSGlobalObject * globalOb
if (!bn_g.setWord(generator)) {
ERR_put_error(ERR_LIB_DH, 0, DH_R_BAD_GENERATOR, __FILE__, __LINE__);
throwCryptoError(globalObject, scope, ERR_get_error(), "Invalid generator"_s);
return {};
}
} else {
auto* generatorView = getArrayBufferOrView(globalObject, scope, generatorValue, "generator"_s, genEncodingValue);
RETURN_IF_EXCEPTION(scope, {});

if (generatorView->byteLength() > INT32_MAX) {
return JSValue::encode(createError(globalObject, ErrorCode::ERR_OUT_OF_RANGE, "generator is too big"_s));
throwError(globalObject, scope, ErrorCode::ERR_OUT_OF_RANGE, "generator is too big"_s);
return {};
}

bn_g = ncrypto::BignumPointer(reinterpret_cast<uint8_t*>(generatorView->vector()), generatorView->byteLength());
if (!bn_g) {
return JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_ARG_VALUE, "Invalid generator"_s));
throwError(globalObject, scope, ErrorCode::ERR_INVALID_ARG_VALUE, "Invalid generator"_s);
return {};
}

// A generator too wide for BN_get_word is necessarily >= 2, so only
Expand All @@ -167,7 +174,8 @@ JSC_DEFINE_HOST_FUNCTION(constructDiffieHellman, (JSC::JSGlobalObject * globalOb

dh = ncrypto::DHPointer::New(WTF::move(bn_p), WTF::move(bn_g));
if (!dh) {
return JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_ARG_VALUE, "Invalid DH parameters"_s));
throwError(globalObject, scope, ErrorCode::ERR_INVALID_ARG_VALUE, "Invalid DH parameters"_s);
return {};
}
}

Expand Down
28 changes: 28 additions & 0 deletions test/js/node/crypto/node-crypto.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,34 @@ describe("DiffieHellman", () => {
expect(() => crypto.createDiffieHellman(p, Buffer.from([0x01]))).toThrow(/bad.generator/i);
expect(() => crypto.createDiffieHellman(p, Buffer.from([0x02]))).not.toThrow();
});

it("throws (not returns) validation errors from the constructor", () => {
// toThrow() accepts a *returned* Error instance as a throw, so it cannot
// distinguish the two here; capture the control-flow outcome explicitly.
function outcome(fn) {
try {
return { threw: false, value: fn() };
} catch (e) {
return { threw: true, value: e };
}
}

// DHPointer::New rejects a 2-bit modulus; that must surface as a thrown error.
expect(outcome(() => crypto.createDiffieHellman(2))).toEqual({
threw: true,
value: expect.objectContaining({ code: "ERR_INVALID_ARG_VALUE", message: "Invalid DH parameters" }),
});
// Numeric sizeOrKey with a non-numeric generator reaches the int32-only guard.
expect(outcome(() => crypto.createDiffieHellman(1024, "abc"))).toEqual({
threw: true,
value: expect.objectContaining({ code: "ERR_INVALID_ARG_TYPE", message: "Second argument must be an int32" }),
});
// `new DiffieHellman(...)` must behave identically to the factory.
expect(outcome(() => new crypto.DiffieHellman(2))).toEqual({
threw: true,
value: expect.objectContaining({ code: "ERR_INVALID_ARG_VALUE" }),
});
});
});

describe("ECDH", () => {
Expand Down
Loading