Skip to content

Commit 359b07a

Browse files
panvaaduh95
authored andcommitted
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: #66237 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Aviv Keller <me@aviv.sh>
1 parent e4d746c commit 359b07a

2 files changed

Lines changed: 61 additions & 4 deletions

File tree

‎deps/ncrypto/ncrypto.cc‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5942,8 +5942,10 @@ bool ECKeyPointer::setPublicKeyRaw(const BignumPointer& x,
59425942
if (!buf) return false;
59435943
unsigned char* ptr = static_cast<unsigned char*>(buf.get());
59445944
ptr[0] = POINT_CONVERSION_UNCOMPRESSED;
5945-
x.encodePaddedInto(ptr + 1, field_len);
5946-
y.encodePaddedInto(ptr + 1 + field_len, field_len);
5945+
if (x.encodePaddedInto(ptr + 1, field_len) != field_len ||
5946+
y.encodePaddedInto(ptr + 1 + field_len, field_len) != field_len) {
5947+
return false;
5948+
}
59475949

59485950
auto point = ECPointPointer::New(group);
59495951
if (!point) return false;
@@ -6171,8 +6173,10 @@ bool ECKeyPointer::setPublicKeyRaw(const BignumPointer& x,
61716173
if (!buf) return false;
61726174
unsigned char* ptr = static_cast<unsigned char*>(buf.get());
61736175
ptr[0] = POINT_CONVERSION_UNCOMPRESSED;
6174-
x.encodePaddedInto(ptr + 1, field_len);
6175-
y.encodePaddedInto(ptr + 1 + field_len, field_len);
6176+
if (x.encodePaddedInto(ptr + 1, field_len) != field_len ||
6177+
y.encodePaddedInto(ptr + 1 + field_len, field_len) != field_len) {
6178+
return false;
6179+
}
61766180

61776181
auto point = ECPointPointer::New(group_.get());
61786182
if (!point || !point.setFromBuffer({ptr, uncompressed_len}, group_.get())) {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const assert = require('assert');
8+
const { createPublicKey, subtle } = require('crypto');
9+
10+
// This is the valid P-256 point (0, sqrt(b)). A failed conversion of an
11+
// oversized x coordinate must not silently replace it with zero.
12+
const jwk = {
13+
kty: 'EC',
14+
crv: 'P-256',
15+
x: Buffer.alloc(32).toString('base64url'),
16+
y: 'ZkhceA4vg9ckM71dhKBrtlQcKvMdrocXKL-FahdPk_Q',
17+
};
18+
19+
(async () => {
20+
for (const field of ['x', 'y']) {
21+
const invalid = { ...jwk, [field]: Buffer.alloc(33, 1).toString('base64url') };
22+
assert.throws(() => createPublicKey({ key: invalid, format: 'jwk' }), {
23+
code: 'ERR_CRYPTO_INVALID_JWK',
24+
});
25+
for (const name of ['ECDSA', 'ECDH']) {
26+
await assert.rejects(subtle.importKey(
27+
'jwk', invalid, { name, namedCurve: 'P-256' }, true,
28+
name === 'ECDSA' ? ['verify'] : []), { name: 'DataError' });
29+
}
30+
}
31+
32+
// Equivalent integer encodings remain accepted by the existing decoder.
33+
for (const encoded of [
34+
jwk,
35+
{ ...jwk, x: Buffer.alloc(1).toString('base64url') },
36+
{
37+
...jwk,
38+
x: Buffer.alloc(33).toString('base64url'),
39+
y: Buffer.concat([Buffer.alloc(1), Buffer.from(jwk.y, 'base64url')]).toString('base64url'),
40+
},
41+
]) {
42+
const publicKey = createPublicKey({ key: encoded, format: 'jwk' });
43+
assert.deepStrictEqual(publicKey.export({ format: 'jwk' }), jwk);
44+
for (const name of ['ECDSA', 'ECDH']) {
45+
const key = await subtle.importKey(
46+
'jwk', encoded, { name, namedCurve: 'P-256' }, true,
47+
name === 'ECDSA' ? ['verify'] : []);
48+
const exported = await subtle.exportKey('jwk', key);
49+
assert.strictEqual(exported.x, jwk.x);
50+
assert.strictEqual(exported.y, jwk.y);
51+
}
52+
}
53+
})().then(common.mustCall());

0 commit comments

Comments
 (0)