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
10 changes: 10 additions & 0 deletions src/jsc/bindings/node/crypto/JSCipherConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,16 @@
}
}

// OpenSSL 3 caps GCM IVs at 1024 bits (GCM_IV_MAX_SIZE). BoringSSL has no
// such cap, so enforce it here to match Node.js and avoid unbounded GHASH work.
if (cipher.isGcmMode()) {
ASSERT(ivView);

if (ivView->byteLength() > 128) {
return ERR::CRYPTO_INVALID_IV(scope, globalObject);
}
}

Check warning on line 200 in src/jsc/bindings/node/crypto/JSCipherConstructor.cpp

View check run for this annotation

Claude / Claude Code Review

getCipherInfo still accepts GCM IV lengths >128, now inconsistent with createCipheriv

Nit: `crypto.getCipherInfo` has the same missing GCM IV cap — the empty `else if (cipher.isGcmMode()) {}` branch at `src/jsc/bindings/node/crypto/node_crypto_binding.cpp:289` still accepts any `ivLength`, so after this PR `getCipherInfo('aes-128-gcm', { ivLength: 200 })` reports 200 as valid while `createCipheriv` rejects it (Node returns `undefined`). Same bug class at a sibling site; a one-liner `if (length < 1 || length > 128) return JSValue::encode(jsUndefined());` in that branch would keep
Comment thread
robobun marked this conversation as resolved.

CipherCtxPointer ctx = CipherCtxPointer::New();

if (cipher.isWrapMode()) {
Expand Down
13 changes: 13 additions & 0 deletions test/js/bun/crypto/cipheriv-decipheriv.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,19 @@ it("only zero-sized iv or null should be accepted in ECB mode", () => {
it("should allow only valid iv lengths in GCM mode", () => {
expect(sampleEncryptDecryptGCM("aes-256-gcm", randomBytes(32), randomBytes(1))).toBe(true);
expect(sampleEncryptDecryptGCM("aes-256-gcm", randomBytes(32), randomBytes(96))).toBe(true);
expect(sampleEncryptDecryptGCM("aes-256-gcm", randomBytes(32), randomBytes(128))).toBe(true);
});

it("should reject GCM IVs longer than 128 bytes", () => {
// Node.js (OpenSSL 3) caps GCM IV length at 1024 bits / 128 bytes.
const invalidIV = { code: "ERR_CRYPTO_INVALID_IV" };
for (const algo of ["aes-128-gcm", "aes-192-gcm", "aes-256-gcm"] as const) {
const key = randomBytes(algo === "aes-128-gcm" ? 16 : algo === "aes-192-gcm" ? 24 : 32);
expect(() => createCipheriv(algo, key, Buffer.alloc(128))).not.toThrow();
expect(() => createCipheriv(algo, key, Buffer.alloc(129))).toThrow(expect.objectContaining(invalidIV));
expect(() => createCipheriv(algo, key, Buffer.alloc(4096))).toThrow(expect.objectContaining(invalidIV));
expect(() => createDecipheriv(algo, key, Buffer.alloc(129))).toThrow(expect.objectContaining(invalidIV));
}
});

const referencePlaintext = "Out of the mountain of despair, a stone of hope.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,9 @@ assert.throws(

// But all other IV lengths should be accepted.
const minIvLength = hasOpenSSL3 ? 8 : 1;
const maxIvLength = hasOpenSSL3 ? 64 : 256;
// Bun: BoringSSL has no upper bound, but Bun enforces the OpenSSL 3 cap of
// 128 bytes (1024 bits) for Node.js compatibility.
const maxIvLength = hasOpenSSL3 ? 64 : 129;
for (let n = minIvLength; n < maxIvLength; n += 1) {
if (isFipsEnabled && n < 12) continue;
crypto.createCipheriv('aes-128-gcm', Buffer.alloc(16), Buffer.alloc(n));
Expand Down
Loading