1'use strict';
2// Flags: --security-revert=CVE-2023-46809
3const common = require('../common');
4if (!common.hasCrypto)
5  common.skip('missing crypto');
6
7const assert = require('assert');
8const crypto = require('crypto');
9
10const constants = crypto.constants;
11
12const fixtures = require('../common/fixtures');
13
14// Test certificates
15const certPem = fixtures.readKey('rsa_cert.crt');
16const keyPem = fixtures.readKey('rsa_private.pem');
17const rsaKeySize = 2048;
18const rsaPubPem = fixtures.readKey('rsa_public.pem', 'ascii');
19const rsaKeyPem = fixtures.readKey('rsa_private.pem', 'ascii');
20const rsaKeyPemEncrypted = fixtures.readKey('rsa_private_encrypted.pem',
21                                            'ascii');
22const dsaPubPem = fixtures.readKey('dsa_public.pem', 'ascii');
23const dsaKeyPem = fixtures.readKey('dsa_private.pem', 'ascii');
24const dsaKeyPemEncrypted = fixtures.readKey('dsa_private_encrypted.pem',
25                                            'ascii');
26const rsaPkcs8KeyPem = fixtures.readKey('rsa_private_pkcs8.pem');
27const dsaPkcs8KeyPem = fixtures.readKey('dsa_private_pkcs8.pem');
28
29const ec = new TextEncoder();
30
31const openssl1DecryptError = {
32  message: 'error:06065064:digital envelope routines:EVP_DecryptFinal_ex:' +
33    'bad decrypt',
34  code: 'ERR_OSSL_EVP_BAD_DECRYPT',
35  reason: 'bad decrypt',
36  function: 'EVP_DecryptFinal_ex',
37  library: 'digital envelope routines',
38};
39
40const decryptError = common.hasOpenSSL3 ?
41  { message: 'error:1C800064:Provider routines::bad decrypt' } :
42  openssl1DecryptError;
43
44const decryptPrivateKeyError = common.hasOpenSSL3 ? {
45  message: 'error:1C800064:Provider routines::bad decrypt',
46} : openssl1DecryptError;
47
48function getBufferCopy(buf) {
49  return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
50}
51
52// Test RSA encryption/decryption
53{
54  const input = 'I AM THE WALRUS';
55  const bufferToEncrypt = Buffer.from(input);
56  const bufferPassword = Buffer.from('password');
57
58  let encryptedBuffer = crypto.publicEncrypt(rsaPubPem, bufferToEncrypt);
59
60  // Test other input types
61  let otherEncrypted;
62  {
63    const ab = getBufferCopy(ec.encode(rsaPubPem));
64    const ab2enc = getBufferCopy(bufferToEncrypt);
65
66    crypto.publicEncrypt(ab, ab2enc);
67    crypto.publicEncrypt(new Uint8Array(ab), new Uint8Array(ab2enc));
68    crypto.publicEncrypt(new DataView(ab), new DataView(ab2enc));
69    otherEncrypted = crypto.publicEncrypt({
70      key: Buffer.from(ab).toString('hex'),
71      encoding: 'hex'
72    }, Buffer.from(ab2enc).toString('hex'));
73  }
74
75  let decryptedBuffer = crypto.privateDecrypt(rsaKeyPem, encryptedBuffer);
76  const otherDecrypted = crypto.privateDecrypt(rsaKeyPem, otherEncrypted);
77  assert.strictEqual(decryptedBuffer.toString(), input);
78  assert.strictEqual(otherDecrypted.toString(), input);
79
80  decryptedBuffer = crypto.privateDecrypt(rsaPkcs8KeyPem, encryptedBuffer);
81  assert.strictEqual(decryptedBuffer.toString(), input);
82
83  let decryptedBufferWithPassword = crypto.privateDecrypt({
84    key: rsaKeyPemEncrypted,
85    passphrase: 'password'
86  }, encryptedBuffer);
87
88  const otherDecryptedBufferWithPassword = crypto.privateDecrypt({
89    key: rsaKeyPemEncrypted,
90    passphrase: ec.encode('password')
91  }, encryptedBuffer);
92
93  assert.strictEqual(
94    otherDecryptedBufferWithPassword.toString(),
95    decryptedBufferWithPassword.toString());
96
97  decryptedBufferWithPassword = crypto.privateDecrypt({
98    key: rsaKeyPemEncrypted,
99    passphrase: 'password'
100  }, encryptedBuffer);
101
102  assert.strictEqual(decryptedBufferWithPassword.toString(), input);
103
104  encryptedBuffer = crypto.publicEncrypt({
105    key: rsaKeyPemEncrypted,
106    passphrase: 'password'
107  }, bufferToEncrypt);
108
109  decryptedBufferWithPassword = crypto.privateDecrypt({
110    key: rsaKeyPemEncrypted,
111    passphrase: 'password'
112  }, encryptedBuffer);
113  assert.strictEqual(decryptedBufferWithPassword.toString(), input);
114
115  encryptedBuffer = crypto.privateEncrypt({
116    key: rsaKeyPemEncrypted,
117    passphrase: bufferPassword
118  }, bufferToEncrypt);
119
120  decryptedBufferWithPassword = crypto.publicDecrypt({
121    key: rsaKeyPemEncrypted,
122    passphrase: bufferPassword
123  }, encryptedBuffer);
124  assert.strictEqual(decryptedBufferWithPassword.toString(), input);
125
126  // Now with explicit RSA_PKCS1_PADDING.
127  encryptedBuffer = crypto.privateEncrypt({
128    padding: crypto.constants.RSA_PKCS1_PADDING,
129    key: rsaKeyPemEncrypted,
130    passphrase: bufferPassword
131  }, bufferToEncrypt);
132
133  decryptedBufferWithPassword = crypto.publicDecrypt({
134    padding: crypto.constants.RSA_PKCS1_PADDING,
135    key: rsaKeyPemEncrypted,
136    passphrase: bufferPassword
137  }, encryptedBuffer);
138  assert.strictEqual(decryptedBufferWithPassword.toString(), input);
139
140  // Omitting padding should be okay because RSA_PKCS1_PADDING is the default.
141  decryptedBufferWithPassword = crypto.publicDecrypt({
142    key: rsaKeyPemEncrypted,
143    passphrase: bufferPassword
144  }, encryptedBuffer);
145  assert.strictEqual(decryptedBufferWithPassword.toString(), input);
146
147  // Now with RSA_NO_PADDING. Plaintext needs to match key size.
148  // OpenSSL 3.x has a rsa_check_padding that will cause an error if
149  // RSA_NO_PADDING is used.
150  if (!common.hasOpenSSL3) {
151    {
152      const plaintext = 'x'.repeat(rsaKeySize / 8);
153      encryptedBuffer = crypto.privateEncrypt({
154        padding: crypto.constants.RSA_NO_PADDING,
155        key: rsaKeyPemEncrypted,
156        passphrase: bufferPassword
157      }, Buffer.from(plaintext));
158
159      decryptedBufferWithPassword = crypto.publicDecrypt({
160        padding: crypto.constants.RSA_NO_PADDING,
161        key: rsaKeyPemEncrypted,
162        passphrase: bufferPassword
163      }, encryptedBuffer);
164      assert.strictEqual(decryptedBufferWithPassword.toString(), plaintext);
165    }
166  }
167
168  encryptedBuffer = crypto.publicEncrypt(certPem, bufferToEncrypt);
169
170  decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer);
171  assert.strictEqual(decryptedBuffer.toString(), input);
172
173  encryptedBuffer = crypto.publicEncrypt(keyPem, bufferToEncrypt);
174
175  decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer);
176  assert.strictEqual(decryptedBuffer.toString(), input);
177
178  encryptedBuffer = crypto.privateEncrypt(keyPem, bufferToEncrypt);
179
180  decryptedBuffer = crypto.publicDecrypt(keyPem, encryptedBuffer);
181  assert.strictEqual(decryptedBuffer.toString(), input);
182
183  assert.throws(() => {
184    crypto.privateDecrypt({
185      key: rsaKeyPemEncrypted,
186      passphrase: 'wrong'
187    }, bufferToEncrypt);
188  }, decryptError);
189
190  assert.throws(() => {
191    crypto.publicEncrypt({
192      key: rsaKeyPemEncrypted,
193      passphrase: 'wrong'
194    }, encryptedBuffer);
195  }, decryptError);
196
197  encryptedBuffer = crypto.privateEncrypt({
198    key: rsaKeyPemEncrypted,
199    passphrase: Buffer.from('password')
200  }, bufferToEncrypt);
201
202  assert.throws(() => {
203    crypto.publicDecrypt({
204      key: rsaKeyPemEncrypted,
205      passphrase: Buffer.from('wrong')
206    }, encryptedBuffer);
207  }, decryptError);
208}
209
210function test_rsa(padding, encryptOaepHash, decryptOaepHash) {
211  const size = (padding === 'RSA_NO_PADDING') ? rsaKeySize / 8 : 32;
212  const input = Buffer.allocUnsafe(size);
213  for (let i = 0; i < input.length; i++)
214    input[i] = (i * 7 + 11) & 0xff;
215  const bufferToEncrypt = Buffer.from(input);
216
217  padding = constants[padding];
218
219  const encryptedBuffer = crypto.publicEncrypt({
220    key: rsaPubPem,
221    padding: padding,
222    oaepHash: encryptOaepHash
223  }, bufferToEncrypt);
224
225  let decryptedBuffer = crypto.privateDecrypt({
226    key: rsaKeyPem,
227    padding: padding,
228    oaepHash: decryptOaepHash
229  }, encryptedBuffer);
230  assert.deepStrictEqual(decryptedBuffer, input);
231
232  decryptedBuffer = crypto.privateDecrypt({
233    key: rsaPkcs8KeyPem,
234    padding: padding,
235    oaepHash: decryptOaepHash
236  }, encryptedBuffer);
237  assert.deepStrictEqual(decryptedBuffer, input);
238}
239
240test_rsa('RSA_NO_PADDING');
241test_rsa('RSA_PKCS1_PADDING');
242test_rsa('RSA_PKCS1_OAEP_PADDING');
243
244// Test OAEP with different hash functions.
245test_rsa('RSA_PKCS1_OAEP_PADDING', undefined, 'sha1');
246test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha1', undefined);
247test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha256');
248test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha512', 'sha512');
249assert.throws(() => {
250  test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha512');
251}, {
252  code: 'ERR_OSSL_RSA_OAEP_DECODING_ERROR'
253});
254
255// The following RSA-OAEP test cases were created using the WebCrypto API to
256// ensure compatibility when using non-SHA1 hash functions.
257{
258  const { decryptionTests } =
259      JSON.parse(fixtures.readSync('rsa-oaep-test-vectors.js', 'utf8'));
260
261  for (const { ct, oaepHash, oaepLabel } of decryptionTests) {
262    const label = oaepLabel ? Buffer.from(oaepLabel, 'hex') : undefined;
263    const copiedLabel = oaepLabel ? getBufferCopy(label) : undefined;
264
265    const decrypted = crypto.privateDecrypt({
266      key: rsaPkcs8KeyPem,
267      oaepHash,
268      oaepLabel: oaepLabel ? label : undefined
269    }, Buffer.from(ct, 'hex'));
270
271    assert.strictEqual(decrypted.toString('utf8'), 'Hello Node.js');
272
273    const otherDecrypted = crypto.privateDecrypt({
274      key: rsaPkcs8KeyPem,
275      oaepHash,
276      oaepLabel: copiedLabel
277    }, Buffer.from(ct, 'hex'));
278
279    assert.strictEqual(otherDecrypted.toString('utf8'), 'Hello Node.js');
280  }
281}
282
283// Test invalid oaepHash and oaepLabel options.
284for (const fn of [crypto.publicEncrypt, crypto.privateDecrypt]) {
285  assert.throws(() => {
286    fn({
287      key: rsaPubPem,
288      oaepHash: 'Hello world'
289    }, Buffer.alloc(10));
290  }, {
291    code: 'ERR_OSSL_EVP_INVALID_DIGEST'
292  });
293
294  for (const oaepHash of [0, false, null, Symbol(), () => {}]) {
295    assert.throws(() => {
296      fn({
297        key: rsaPubPem,
298        oaepHash
299      }, Buffer.alloc(10));
300    }, {
301      code: 'ERR_INVALID_ARG_TYPE'
302    });
303  }
304
305  for (const oaepLabel of [0, false, null, Symbol(), () => {}, {}]) {
306    assert.throws(() => {
307      fn({
308        key: rsaPubPem,
309        oaepLabel
310      }, Buffer.alloc(10));
311    }, {
312      code: 'ERR_INVALID_ARG_TYPE'
313    });
314  }
315}
316
317// Test RSA key signing/verification
318let rsaSign = crypto.createSign('SHA1');
319let rsaVerify = crypto.createVerify('SHA1');
320assert.ok(rsaSign);
321assert.ok(rsaVerify);
322
323const expectedSignature = fixtures.readKey(
324  'rsa_public_sha1_signature_signedby_rsa_private_pkcs8.sha1',
325  'hex'
326);
327
328rsaSign.update(rsaPubPem);
329let rsaSignature = rsaSign.sign(rsaKeyPem, 'hex');
330assert.strictEqual(rsaSignature, expectedSignature);
331
332rsaVerify.update(rsaPubPem);
333assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
334
335// Test RSA PKCS#8 key signing/verification
336rsaSign = crypto.createSign('SHA1');
337rsaSign.update(rsaPubPem);
338rsaSignature = rsaSign.sign(rsaPkcs8KeyPem, 'hex');
339assert.strictEqual(rsaSignature, expectedSignature);
340
341rsaVerify = crypto.createVerify('SHA1');
342rsaVerify.update(rsaPubPem);
343assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
344
345// Test RSA key signing/verification with encrypted key
346rsaSign = crypto.createSign('SHA1');
347rsaSign.update(rsaPubPem);
348const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'password' };
349rsaSignature = rsaSign.sign(signOptions, 'hex');
350assert.strictEqual(rsaSignature, expectedSignature);
351
352rsaVerify = crypto.createVerify('SHA1');
353rsaVerify.update(rsaPubPem);
354assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
355
356rsaSign = crypto.createSign('SHA1');
357rsaSign.update(rsaPubPem);
358assert.throws(() => {
359  const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'wrong' };
360  rsaSign.sign(signOptions, 'hex');
361}, decryptPrivateKeyError);
362
363//
364// Test RSA signing and verification
365//
366{
367  const privateKey = fixtures.readKey('rsa_private_b.pem');
368  const publicKey = fixtures.readKey('rsa_public_b.pem');
369
370  const input = 'I AM THE WALRUS';
371
372  const signature = fixtures.readKey(
373    'I_AM_THE_WALRUS_sha256_signature_signedby_rsa_private_b.sha256',
374    'hex'
375  );
376
377  const sign = crypto.createSign('SHA256');
378  sign.update(input);
379
380  const output = sign.sign(privateKey, 'hex');
381  assert.strictEqual(output, signature);
382
383  const verify = crypto.createVerify('SHA256');
384  verify.update(input);
385
386  assert.strictEqual(verify.verify(publicKey, signature, 'hex'), true);
387
388  // Test the legacy signature algorithm name.
389  const sign2 = crypto.createSign('RSA-SHA256');
390  sign2.update(input);
391
392  const output2 = sign2.sign(privateKey, 'hex');
393  assert.strictEqual(output2, signature);
394
395  const verify2 = crypto.createVerify('SHA256');
396  verify2.update(input);
397
398  assert.strictEqual(verify2.verify(publicKey, signature, 'hex'), true);
399}
400
401
402//
403// Test DSA signing and verification
404//
405{
406  const input = 'I AM THE WALRUS';
407
408  // DSA signatures vary across runs so there is no static string to verify
409  // against.
410  const sign = crypto.createSign('SHA1');
411  sign.update(input);
412  const signature = sign.sign(dsaKeyPem, 'hex');
413
414  const verify = crypto.createVerify('SHA1');
415  verify.update(input);
416
417  assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
418
419  // Test the legacy 'DSS1' name.
420  const sign2 = crypto.createSign('DSS1');
421  sign2.update(input);
422  const signature2 = sign2.sign(dsaKeyPem, 'hex');
423
424  const verify2 = crypto.createVerify('DSS1');
425  verify2.update(input);
426
427  assert.strictEqual(verify2.verify(dsaPubPem, signature2, 'hex'), true);
428}
429
430
431//
432// Test DSA signing and verification with PKCS#8 private key
433//
434{
435  const input = 'I AM THE WALRUS';
436
437  // DSA signatures vary across runs so there is no static string to verify
438  // against.
439  const sign = crypto.createSign('SHA1');
440  sign.update(input);
441  const signature = sign.sign(dsaPkcs8KeyPem, 'hex');
442
443  const verify = crypto.createVerify('SHA1');
444  verify.update(input);
445
446  assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
447}
448
449
450//
451// Test DSA signing and verification with encrypted key
452//
453const input = 'I AM THE WALRUS';
454
455{
456  const sign = crypto.createSign('SHA1');
457  sign.update(input);
458  assert.throws(() => {
459    sign.sign({ key: dsaKeyPemEncrypted, passphrase: 'wrong' }, 'hex');
460  }, decryptPrivateKeyError);
461}
462
463{
464  // DSA signatures vary across runs so there is no static string to verify
465  // against.
466  const sign = crypto.createSign('SHA1');
467  sign.update(input);
468  const signOptions = { key: dsaKeyPemEncrypted, passphrase: 'password' };
469  const signature = sign.sign(signOptions, 'hex');
470
471  const verify = crypto.createVerify('SHA1');
472  verify.update(input);
473
474  assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
475}
476