• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2const common = require('../common');
3if (!common.hasCrypto)
4  common.skip('missing crypto');
5
6const assert = require('assert');
7
8const crypto = require('crypto');
9
10// Tests for CVE-2022-21449
11// https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/
12// Dubbed "Psychic Signatures", these signatures bypassed the ECDSA signature
13// verification implementation in Java in 15, 16, 17, and 18. OpenSSL is not
14// (and was not) vulnerable so these are a precaution.
15
16const vectors = {
17  'ieee-p1363': [
18    Buffer.from('0000000000000000000000000000000000000000000000000000000000000000' +
19      '0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
20    Buffer.from('ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551' +
21      'ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551', 'hex'),
22  ],
23  'der': [
24    Buffer.from('3046022100' +
25      '0000000000000000000000000000000000000000000000000000000000000000' +
26      '022100' +
27      '0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
28    Buffer.from('3046022100' +
29      'ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551' +
30      '022100' +
31      'ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551', 'hex'),
32  ],
33};
34
35const keyPair = crypto.generateKeyPairSync('ec', {
36  namedCurve: 'P-256',
37  publicKeyEncoding: {
38    format: 'der',
39    type: 'spki'
40  },
41});
42
43const data = Buffer.from('Hello!');
44
45for (const [encoding, signatures] of Object.entries(vectors)) {
46  for (const signature of signatures) {
47    const key = {
48      key: keyPair.publicKey,
49      format: 'der',
50      type: 'spki',
51      dsaEncoding: encoding,
52    };
53
54    // one-shot sync
55    assert.strictEqual(
56      crypto.verify(
57        'sha256',
58        data,
59        key,
60        signature,
61      ),
62      false,
63    );
64
65    // one-shot async
66    crypto.verify(
67      'sha256',
68      data,
69      key,
70      signature,
71      common.mustSucceed((verified) => assert.strictEqual(verified, false)),
72    );
73
74    // stream
75    assert.strictEqual(
76      crypto.createVerify('sha256')
77        .update(data)
78        .verify(key, signature),
79      false,
80    );
81
82    // webcrypto
83    crypto.webcrypto.subtle.importKey(
84      'spki',
85      keyPair.publicKey,
86      { name: 'ECDSA', namedCurve: 'P-256' },
87      false,
88      ['verify'],
89    ).then((publicKey) => {
90      return crypto.webcrypto.subtle.verify(
91        { name: 'ECDSA', hash: 'SHA-256' },
92        publicKey,
93        signature,
94        data,
95      );
96    }).then(common.mustCall((verified) => {
97      assert.strictEqual(verified, false);
98    }));
99  }
100}
101