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