Commit 2eb0433abd9 for nodejs
commit 2eb0433abd9ddc34d7a661903914f6c0eea24566
Author: Filip Skokan <panva.ip@gmail.com>
Date: Tue Sep 22 23:18:41 2026 +0200
crypto: check EC coordinate conversion results
Propagate coordinate conversion failures instead of importing an
oversized JWK coordinate as zero. Keep equivalent short and zero-padded
integer encodings accepted by the native decoder.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/66237
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Aviv Keller <me@aviv.sh>
diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc
index 52f841422c7..44b74e13fd2 100644
--- a/deps/ncrypto/ncrypto.cc
+++ b/deps/ncrypto/ncrypto.cc
@@ -5942,8 +5942,10 @@ bool ECKeyPointer::setPublicKeyRaw(const BignumPointer& x,
if (!buf) return false;
unsigned char* ptr = static_cast<unsigned char*>(buf.get());
ptr[0] = POINT_CONVERSION_UNCOMPRESSED;
- x.encodePaddedInto(ptr + 1, field_len);
- y.encodePaddedInto(ptr + 1 + field_len, field_len);
+ if (x.encodePaddedInto(ptr + 1, field_len) != field_len ||
+ y.encodePaddedInto(ptr + 1 + field_len, field_len) != field_len) {
+ return false;
+ }
auto point = ECPointPointer::New(group);
if (!point) return false;
@@ -6171,8 +6173,10 @@ bool ECKeyPointer::setPublicKeyRaw(const BignumPointer& x,
if (!buf) return false;
unsigned char* ptr = static_cast<unsigned char*>(buf.get());
ptr[0] = POINT_CONVERSION_UNCOMPRESSED;
- x.encodePaddedInto(ptr + 1, field_len);
- y.encodePaddedInto(ptr + 1 + field_len, field_len);
+ if (x.encodePaddedInto(ptr + 1, field_len) != field_len ||
+ y.encodePaddedInto(ptr + 1 + field_len, field_len) != field_len) {
+ return false;
+ }
auto point = ECPointPointer::New(group_.get());
if (!point || !point.setFromBuffer({ptr, uncompressed_len}, group_.get())) {
diff --git a/test/parallel/test-crypto-ec-jwk-coordinates.js b/test/parallel/test-crypto-ec-jwk-coordinates.js
new file mode 100644
index 00000000000..8ae3d79e6a3
--- /dev/null
+++ b/test/parallel/test-crypto-ec-jwk-coordinates.js
@@ -0,0 +1,53 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+
+const assert = require('assert');
+const { createPublicKey, subtle } = require('crypto');
+
+// This is the valid P-256 point (0, sqrt(b)). A failed conversion of an
+// oversized x coordinate must not silently replace it with zero.
+const jwk = {
+ kty: 'EC',
+ crv: 'P-256',
+ x: Buffer.alloc(32).toString('base64url'),
+ y: 'ZkhceA4vg9ckM71dhKBrtlQcKvMdrocXKL-FahdPk_Q',
+};
+
+(async () => {
+ for (const field of ['x', 'y']) {
+ const invalid = { ...jwk, [field]: Buffer.alloc(33, 1).toString('base64url') };
+ assert.throws(() => createPublicKey({ key: invalid, format: 'jwk' }), {
+ code: 'ERR_CRYPTO_INVALID_JWK',
+ });
+ for (const name of ['ECDSA', 'ECDH']) {
+ await assert.rejects(subtle.importKey(
+ 'jwk', invalid, { name, namedCurve: 'P-256' }, true,
+ name === 'ECDSA' ? ['verify'] : []), { name: 'DataError' });
+ }
+ }
+
+ // Equivalent integer encodings remain accepted by the existing decoder.
+ for (const encoded of [
+ jwk,
+ { ...jwk, x: Buffer.alloc(1).toString('base64url') },
+ {
+ ...jwk,
+ x: Buffer.alloc(33).toString('base64url'),
+ y: Buffer.concat([Buffer.alloc(1), Buffer.from(jwk.y, 'base64url')]).toString('base64url'),
+ },
+ ]) {
+ const publicKey = createPublicKey({ key: encoded, format: 'jwk' });
+ assert.deepStrictEqual(publicKey.export({ format: 'jwk' }), jwk);
+ for (const name of ['ECDSA', 'ECDH']) {
+ const key = await subtle.importKey(
+ 'jwk', encoded, { name, namedCurve: 'P-256' }, true,
+ name === 'ECDSA' ? ['verify'] : []);
+ const exported = await subtle.exportKey('jwk', key);
+ assert.strictEqual(exported.x, jwk.x);
+ assert.strictEqual(exported.y, jwk.y);
+ }
+ }
+})().then(common.mustCall());