From a6aa86cc0583ce182f020d9ddf45bb013fc21a02 Mon Sep 17 00:00:00 2001 From: Nikita Skovoroda Date: Mon, 20 Apr 2026 16:50:03 +0400 Subject: [PATCH] crypto: fix unsigned conversion of 4-byte RSA publicExponent `bigIntArrayToUnsignedInt` used the signed `<<` operator, so when the most significant byte of a 4-byte input had its top bit set (e.g. `[0x80, 0x00, 0x00, 0x01]`) the result was a negative Int32 instead of the intended unsigned 32-bit value. This caused any RSA `publicExponent` exactly 4 bytes long with the top bit set to be parsed incorrectly. Coerce the final value with `>>> 0` and add a unit test. Assisted-by: Claude Co-Authored-By: Claude Co-Authored-By: DeepView Autofix <276251120+deepview-autofix@users.noreply.github.com> Co-Authored-By: Nikita Skovoroda Signed-off-by: Nikita Skovoroda --- lib/internal/crypto/util.js | 2 +- test/parallel/test-webcrypto-util.js | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js index 70e1027946190a..74ab59d3f9ec86 100644 --- a/lib/internal/crypto/util.js +++ b/lib/internal/crypto/util.js @@ -696,7 +696,7 @@ function bigIntArrayToUnsignedInt(input) { result |= input[n] << 8 * n_reversed; } - return result; + return result >>> 0; } function bigIntArrayToUnsignedBigInt(input) { diff --git a/test/parallel/test-webcrypto-util.js b/test/parallel/test-webcrypto-util.js index 89d8575e20ddbd..a1f79f21e51e49 100644 --- a/test/parallel/test-webcrypto-util.js +++ b/test/parallel/test-webcrypto-util.js @@ -8,9 +8,28 @@ if (!common.hasCrypto) const assert = require('assert'); const { + bigIntArrayToUnsignedInt, normalizeAlgorithm, } = require('internal/crypto/util'); +// bigIntArrayToUnsignedInt must return an unsigned 32-bit value even when +// the most significant byte has its top bit set. Otherwise the signed `<<` +// operator yields a negative Int32 for inputs like [0x80, 0x00, 0x00, 0x01]. +{ + assert.strictEqual( + bigIntArrayToUnsignedInt(new Uint8Array([0x80, 0x00, 0x00, 0x01])), + 0x80000001); + assert.strictEqual( + bigIntArrayToUnsignedInt(new Uint8Array([0xff, 0xff, 0xff, 0xff])), + 0xffffffff); + assert.strictEqual( + bigIntArrayToUnsignedInt(new Uint8Array([1, 0, 1])), + 65537); + assert.strictEqual( + bigIntArrayToUnsignedInt(new Uint8Array([1, 0, 0, 0, 0])), + undefined); +} + { // Check that normalizeAlgorithm does not mutate object inputs. const algorithm = { name: 'ECDSA', hash: 'SHA-256' };