• 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');
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  let decryptedBuffer = crypto.privateDecrypt({
225    key: rsaKeyPem,
226    padding: padding,
227    oaepHash: decryptOaepHash
228  }, encryptedBuffer);
229  assert.deepStrictEqual(decryptedBuffer, input);
230
231  decryptedBuffer = crypto.privateDecrypt({
232    key: rsaPkcs8KeyPem,
233    padding: padding,
234    oaepHash: decryptOaepHash
235  }, encryptedBuffer);
236  assert.deepStrictEqual(decryptedBuffer, input);
237}
238
239test_rsa('RSA_NO_PADDING');
240test_rsa('RSA_PKCS1_PADDING');
241test_rsa('RSA_PKCS1_OAEP_PADDING');
242
243// Test OAEP with different hash functions.
244test_rsa('RSA_PKCS1_OAEP_PADDING', undefined, 'sha1');
245test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha1', undefined);
246test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha256');
247test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha512', 'sha512');
248assert.throws(() => {
249  test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha512');
250}, {
251  code: 'ERR_OSSL_RSA_OAEP_DECODING_ERROR'
252});
253
254// The following RSA-OAEP test cases were created using the WebCrypto API to
255// ensure compatibility when using non-SHA1 hash functions.
256{
257  const { decryptionTests } =
258      JSON.parse(fixtures.readSync('rsa-oaep-test-vectors.js', 'utf8'));
259
260  for (const { ct, oaepHash, oaepLabel } of decryptionTests) {
261    const label = oaepLabel ? Buffer.from(oaepLabel, 'hex') : undefined;
262    const copiedLabel = oaepLabel ? getBufferCopy(label) : undefined;
263
264    const decrypted = crypto.privateDecrypt({
265      key: rsaPkcs8KeyPem,
266      oaepHash,
267      oaepLabel: oaepLabel ? label : undefined
268    }, Buffer.from(ct, 'hex'));
269
270    assert.strictEqual(decrypted.toString('utf8'), 'Hello Node.js');
271
272    const otherDecrypted = crypto.privateDecrypt({
273      key: rsaPkcs8KeyPem,
274      oaepHash,
275      oaepLabel: copiedLabel
276    }, Buffer.from(ct, 'hex'));
277
278    assert.strictEqual(otherDecrypted.toString('utf8'), 'Hello Node.js');
279  }
280}
281
282// Test invalid oaepHash and oaepLabel options.
283for (const fn of [crypto.publicEncrypt, crypto.privateDecrypt]) {
284  assert.throws(() => {
285    fn({
286      key: rsaPubPem,
287      oaepHash: 'Hello world'
288    }, Buffer.alloc(10));
289  }, {
290    code: 'ERR_OSSL_EVP_INVALID_DIGEST'
291  });
292
293  for (const oaepHash of [0, false, null, Symbol(), () => {}]) {
294    assert.throws(() => {
295      fn({
296        key: rsaPubPem,
297        oaepHash
298      }, Buffer.alloc(10));
299    }, {
300      code: 'ERR_INVALID_ARG_TYPE'
301    });
302  }
303
304  for (const oaepLabel of [0, false, null, Symbol(), () => {}, {}]) {
305    assert.throws(() => {
306      fn({
307        key: rsaPubPem,
308        oaepLabel
309      }, Buffer.alloc(10));
310    }, {
311      code: 'ERR_INVALID_ARG_TYPE'
312    });
313  }
314}
315
316// Test RSA key signing/verification
317let rsaSign = crypto.createSign('SHA1');
318let rsaVerify = crypto.createVerify('SHA1');
319assert.ok(rsaSign);
320assert.ok(rsaVerify);
321
322const expectedSignature = fixtures.readKey(
323  'rsa_public_sha1_signature_signedby_rsa_private_pkcs8.sha1',
324  'hex'
325);
326
327rsaSign.update(rsaPubPem);
328let rsaSignature = rsaSign.sign(rsaKeyPem, 'hex');
329assert.strictEqual(rsaSignature, expectedSignature);
330
331rsaVerify.update(rsaPubPem);
332assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
333
334// Test RSA PKCS#8 key signing/verification
335rsaSign = crypto.createSign('SHA1');
336rsaSign.update(rsaPubPem);
337rsaSignature = rsaSign.sign(rsaPkcs8KeyPem, 'hex');
338assert.strictEqual(rsaSignature, expectedSignature);
339
340rsaVerify = crypto.createVerify('SHA1');
341rsaVerify.update(rsaPubPem);
342assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
343
344// Test RSA key signing/verification with encrypted key
345rsaSign = crypto.createSign('SHA1');
346rsaSign.update(rsaPubPem);
347const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'password' };
348rsaSignature = rsaSign.sign(signOptions, 'hex');
349assert.strictEqual(rsaSignature, expectedSignature);
350
351rsaVerify = crypto.createVerify('SHA1');
352rsaVerify.update(rsaPubPem);
353assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
354
355rsaSign = crypto.createSign('SHA1');
356rsaSign.update(rsaPubPem);
357assert.throws(() => {
358  const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'wrong' };
359  rsaSign.sign(signOptions, 'hex');
360}, decryptPrivateKeyError);
361
362//
363// Test RSA signing and verification
364//
365{
366  const privateKey = fixtures.readKey('rsa_private_b.pem');
367  const publicKey = fixtures.readKey('rsa_public_b.pem');
368
369  const input = 'I AM THE WALRUS';
370
371  const signature = fixtures.readKey(
372    'I_AM_THE_WALRUS_sha256_signature_signedby_rsa_private_b.sha256',
373    'hex'
374  );
375
376  const sign = crypto.createSign('SHA256');
377  sign.update(input);
378
379  const output = sign.sign(privateKey, 'hex');
380  assert.strictEqual(output, signature);
381
382  const verify = crypto.createVerify('SHA256');
383  verify.update(input);
384
385  assert.strictEqual(verify.verify(publicKey, signature, 'hex'), true);
386
387  // Test the legacy signature algorithm name.
388  const sign2 = crypto.createSign('RSA-SHA256');
389  sign2.update(input);
390
391  const output2 = sign2.sign(privateKey, 'hex');
392  assert.strictEqual(output2, signature);
393
394  const verify2 = crypto.createVerify('SHA256');
395  verify2.update(input);
396
397  assert.strictEqual(verify2.verify(publicKey, signature, 'hex'), true);
398}
399
400
401//
402// Test DSA signing and verification
403//
404{
405  const input = 'I AM THE WALRUS';
406
407  // DSA signatures vary across runs so there is no static string to verify
408  // against.
409  const sign = crypto.createSign('SHA1');
410  sign.update(input);
411  const signature = sign.sign(dsaKeyPem, 'hex');
412
413  const verify = crypto.createVerify('SHA1');
414  verify.update(input);
415
416  assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
417
418  // Test the legacy 'DSS1' name.
419  const sign2 = crypto.createSign('DSS1');
420  sign2.update(input);
421  const signature2 = sign2.sign(dsaKeyPem, 'hex');
422
423  const verify2 = crypto.createVerify('DSS1');
424  verify2.update(input);
425
426  assert.strictEqual(verify2.verify(dsaPubPem, signature2, 'hex'), true);
427}
428
429
430//
431// Test DSA signing and verification with PKCS#8 private key
432//
433{
434  const input = 'I AM THE WALRUS';
435
436  // DSA signatures vary across runs so there is no static string to verify
437  // against.
438  const sign = crypto.createSign('SHA1');
439  sign.update(input);
440  const signature = sign.sign(dsaPkcs8KeyPem, 'hex');
441
442  const verify = crypto.createVerify('SHA1');
443  verify.update(input);
444
445  assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
446}
447
448
449//
450// Test DSA signing and verification with encrypted key
451//
452const input = 'I AM THE WALRUS';
453
454{
455  const sign = crypto.createSign('SHA1');
456  sign.update(input);
457  assert.throws(() => {
458    sign.sign({ key: dsaKeyPemEncrypted, passphrase: 'wrong' }, 'hex');
459  }, decryptPrivateKeyError);
460}
461
462{
463  // DSA signatures vary across runs so there is no static string to verify
464  // against.
465  const sign = crypto.createSign('SHA1');
466  sign.update(input);
467  const signOptions = { key: dsaKeyPemEncrypted, passphrase: 'password' };
468  const signature = sign.sign(signOptions, 'hex');
469
470  const verify = crypto.createVerify('SHA1');
471  verify.update(input);
472
473  assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
474}
475