• 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 decryptError = {
29  message: 'error:06065064:digital envelope routines:EVP_DecryptFinal_ex:' +
30    'bad decrypt',
31  code: 'ERR_OSSL_EVP_BAD_DECRYPT',
32  reason: 'bad decrypt',
33  function: 'EVP_DecryptFinal_ex',
34  library: 'digital envelope routines',
35};
36
37// Test RSA encryption/decryption
38{
39  const input = 'I AM THE WALRUS';
40  const bufferToEncrypt = Buffer.from(input);
41  const bufferPassword = Buffer.from('password');
42
43  let encryptedBuffer = crypto.publicEncrypt(rsaPubPem, bufferToEncrypt);
44
45  let decryptedBuffer = crypto.privateDecrypt(rsaKeyPem, encryptedBuffer);
46  assert.strictEqual(decryptedBuffer.toString(), input);
47
48  decryptedBuffer = crypto.privateDecrypt(rsaPkcs8KeyPem, encryptedBuffer);
49  assert.strictEqual(decryptedBuffer.toString(), input);
50
51  let decryptedBufferWithPassword = crypto.privateDecrypt({
52    key: rsaKeyPemEncrypted,
53    passphrase: 'password'
54  }, encryptedBuffer);
55  assert.strictEqual(decryptedBufferWithPassword.toString(), input);
56
57  encryptedBuffer = crypto.publicEncrypt({
58    key: rsaKeyPemEncrypted,
59    passphrase: 'password'
60  }, bufferToEncrypt);
61
62  decryptedBufferWithPassword = crypto.privateDecrypt({
63    key: rsaKeyPemEncrypted,
64    passphrase: 'password'
65  }, encryptedBuffer);
66  assert.strictEqual(decryptedBufferWithPassword.toString(), input);
67
68  encryptedBuffer = crypto.privateEncrypt({
69    key: rsaKeyPemEncrypted,
70    passphrase: bufferPassword
71  }, bufferToEncrypt);
72
73  decryptedBufferWithPassword = crypto.publicDecrypt({
74    key: rsaKeyPemEncrypted,
75    passphrase: bufferPassword
76  }, encryptedBuffer);
77  assert.strictEqual(decryptedBufferWithPassword.toString(), input);
78
79  // Now with explicit RSA_PKCS1_PADDING.
80  encryptedBuffer = crypto.privateEncrypt({
81    padding: crypto.constants.RSA_PKCS1_PADDING,
82    key: rsaKeyPemEncrypted,
83    passphrase: bufferPassword
84  }, bufferToEncrypt);
85
86  decryptedBufferWithPassword = crypto.publicDecrypt({
87    padding: crypto.constants.RSA_PKCS1_PADDING,
88    key: rsaKeyPemEncrypted,
89    passphrase: bufferPassword
90  }, encryptedBuffer);
91  assert.strictEqual(decryptedBufferWithPassword.toString(), input);
92
93  // Omitting padding should be okay because RSA_PKCS1_PADDING is the default.
94  decryptedBufferWithPassword = crypto.publicDecrypt({
95    key: rsaKeyPemEncrypted,
96    passphrase: bufferPassword
97  }, encryptedBuffer);
98  assert.strictEqual(decryptedBufferWithPassword.toString(), input);
99
100  // Now with RSA_NO_PADDING. Plaintext needs to match key size.
101  const plaintext = 'x'.repeat(rsaKeySize / 8);
102  encryptedBuffer = crypto.privateEncrypt({
103    padding: crypto.constants.RSA_NO_PADDING,
104    key: rsaKeyPemEncrypted,
105    passphrase: bufferPassword
106  }, Buffer.from(plaintext));
107
108  decryptedBufferWithPassword = crypto.publicDecrypt({
109    padding: crypto.constants.RSA_NO_PADDING,
110    key: rsaKeyPemEncrypted,
111    passphrase: bufferPassword
112  }, encryptedBuffer);
113  assert.strictEqual(decryptedBufferWithPassword.toString(), plaintext);
114
115  encryptedBuffer = crypto.publicEncrypt(certPem, bufferToEncrypt);
116
117  decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer);
118  assert.strictEqual(decryptedBuffer.toString(), input);
119
120  encryptedBuffer = crypto.publicEncrypt(keyPem, bufferToEncrypt);
121
122  decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer);
123  assert.strictEqual(decryptedBuffer.toString(), input);
124
125  encryptedBuffer = crypto.privateEncrypt(keyPem, bufferToEncrypt);
126
127  decryptedBuffer = crypto.publicDecrypt(keyPem, encryptedBuffer);
128  assert.strictEqual(decryptedBuffer.toString(), input);
129
130  assert.throws(() => {
131    crypto.privateDecrypt({
132      key: rsaKeyPemEncrypted,
133      passphrase: 'wrong'
134    }, bufferToEncrypt);
135  }, decryptError);
136
137  assert.throws(() => {
138    crypto.publicEncrypt({
139      key: rsaKeyPemEncrypted,
140      passphrase: 'wrong'
141    }, encryptedBuffer);
142  }, decryptError);
143
144  encryptedBuffer = crypto.privateEncrypt({
145    key: rsaKeyPemEncrypted,
146    passphrase: Buffer.from('password')
147  }, bufferToEncrypt);
148
149  assert.throws(() => {
150    crypto.publicDecrypt({
151      key: rsaKeyPemEncrypted,
152      passphrase: Buffer.from('wrong')
153    }, encryptedBuffer);
154  }, decryptError);
155}
156
157function test_rsa(padding, encryptOaepHash, decryptOaepHash) {
158  const size = (padding === 'RSA_NO_PADDING') ? rsaKeySize / 8 : 32;
159  const input = Buffer.allocUnsafe(size);
160  for (let i = 0; i < input.length; i++)
161    input[i] = (i * 7 + 11) & 0xff;
162  const bufferToEncrypt = Buffer.from(input);
163
164  padding = constants[padding];
165
166  const encryptedBuffer = crypto.publicEncrypt({
167    key: rsaPubPem,
168    padding: padding,
169    oaepHash: encryptOaepHash
170  }, bufferToEncrypt);
171
172  let decryptedBuffer = crypto.privateDecrypt({
173    key: rsaKeyPem,
174    padding: padding,
175    oaepHash: decryptOaepHash
176  }, encryptedBuffer);
177  assert.deepStrictEqual(decryptedBuffer, input);
178
179  decryptedBuffer = crypto.privateDecrypt({
180    key: rsaPkcs8KeyPem,
181    padding: padding,
182    oaepHash: decryptOaepHash
183  }, encryptedBuffer);
184  assert.deepStrictEqual(decryptedBuffer, input);
185}
186
187test_rsa('RSA_NO_PADDING');
188test_rsa('RSA_PKCS1_PADDING');
189test_rsa('RSA_PKCS1_OAEP_PADDING');
190
191// Test OAEP with different hash functions.
192test_rsa('RSA_PKCS1_OAEP_PADDING', undefined, 'sha1');
193test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha1', undefined);
194test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha256');
195test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha512', 'sha512');
196assert.throws(() => {
197  test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha512');
198}, {
199  code: 'ERR_OSSL_RSA_OAEP_DECODING_ERROR'
200});
201
202// The following RSA-OAEP test cases were created using the WebCrypto API to
203// ensure compatibility when using non-SHA1 hash functions.
204{
205  const { decryptionTests } =
206      JSON.parse(fixtures.readSync('rsa-oaep-test-vectors.js', 'utf8'));
207
208  for (const { ct, oaepHash, oaepLabel } of decryptionTests) {
209    const decrypted = crypto.privateDecrypt({
210      key: rsaPkcs8KeyPem,
211      oaepHash,
212      oaepLabel: oaepLabel ? Buffer.from(oaepLabel, 'hex') : undefined
213    }, Buffer.from(ct, 'hex'));
214
215    assert.strictEqual(decrypted.toString('utf8'), 'Hello Node.js');
216  }
217}
218
219// Test invalid oaepHash and oaepLabel options.
220for (const fn of [crypto.publicEncrypt, crypto.privateDecrypt]) {
221  assert.throws(() => {
222    fn({
223      key: rsaPubPem,
224      oaepHash: 'Hello world'
225    }, Buffer.alloc(10));
226  }, {
227    code: 'ERR_OSSL_EVP_INVALID_DIGEST'
228  });
229
230  for (const oaepHash of [0, false, null, Symbol(), () => {}]) {
231    assert.throws(() => {
232      fn({
233        key: rsaPubPem,
234        oaepHash
235      }, Buffer.alloc(10));
236    }, {
237      code: 'ERR_INVALID_ARG_TYPE'
238    });
239  }
240
241  for (const oaepLabel of [0, false, null, Symbol(), () => {}, {}, 'foo']) {
242    assert.throws(() => {
243      fn({
244        key: rsaPubPem,
245        oaepLabel
246      }, Buffer.alloc(10));
247    }, {
248      code: 'ERR_INVALID_ARG_TYPE'
249    });
250  }
251}
252
253// Test RSA key signing/verification
254let rsaSign = crypto.createSign('SHA1');
255let rsaVerify = crypto.createVerify('SHA1');
256assert.ok(rsaSign);
257assert.ok(rsaVerify);
258
259const expectedSignature = fixtures.readKey(
260  'rsa_public_sha1_signature_signedby_rsa_private_pkcs8.sha1',
261  'hex'
262);
263
264rsaSign.update(rsaPubPem);
265let rsaSignature = rsaSign.sign(rsaKeyPem, 'hex');
266assert.strictEqual(rsaSignature, expectedSignature);
267
268rsaVerify.update(rsaPubPem);
269assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
270
271// Test RSA PKCS#8 key signing/verification
272rsaSign = crypto.createSign('SHA1');
273rsaSign.update(rsaPubPem);
274rsaSignature = rsaSign.sign(rsaPkcs8KeyPem, 'hex');
275assert.strictEqual(rsaSignature, expectedSignature);
276
277rsaVerify = crypto.createVerify('SHA1');
278rsaVerify.update(rsaPubPem);
279assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
280
281// Test RSA key signing/verification with encrypted key
282rsaSign = crypto.createSign('SHA1');
283rsaSign.update(rsaPubPem);
284const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'password' };
285rsaSignature = rsaSign.sign(signOptions, 'hex');
286assert.strictEqual(rsaSignature, expectedSignature);
287
288rsaVerify = crypto.createVerify('SHA1');
289rsaVerify.update(rsaPubPem);
290assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true);
291
292rsaSign = crypto.createSign('SHA1');
293rsaSign.update(rsaPubPem);
294assert.throws(() => {
295  const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'wrong' };
296  rsaSign.sign(signOptions, 'hex');
297}, decryptError);
298
299//
300// Test RSA signing and verification
301//
302{
303  const privateKey = fixtures.readKey('rsa_private_b.pem');
304  const publicKey = fixtures.readKey('rsa_public_b.pem');
305
306  const input = 'I AM THE WALRUS';
307
308  const signature = fixtures.readKey(
309    'I_AM_THE_WALRUS_sha256_signature_signedby_rsa_private_b.sha256',
310    'hex'
311  );
312
313  const sign = crypto.createSign('SHA256');
314  sign.update(input);
315
316  const output = sign.sign(privateKey, 'hex');
317  assert.strictEqual(output, signature);
318
319  const verify = crypto.createVerify('SHA256');
320  verify.update(input);
321
322  assert.strictEqual(verify.verify(publicKey, signature, 'hex'), true);
323
324  // Test the legacy signature algorithm name.
325  const sign2 = crypto.createSign('RSA-SHA256');
326  sign2.update(input);
327
328  const output2 = sign2.sign(privateKey, 'hex');
329  assert.strictEqual(output2, signature);
330
331  const verify2 = crypto.createVerify('SHA256');
332  verify2.update(input);
333
334  assert.strictEqual(verify2.verify(publicKey, signature, 'hex'), true);
335}
336
337
338//
339// Test DSA signing and verification
340//
341{
342  const input = 'I AM THE WALRUS';
343
344  // DSA signatures vary across runs so there is no static string to verify
345  // against.
346  const sign = crypto.createSign('SHA1');
347  sign.update(input);
348  const signature = sign.sign(dsaKeyPem, 'hex');
349
350  const verify = crypto.createVerify('SHA1');
351  verify.update(input);
352
353  assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
354
355  // Test the legacy 'DSS1' name.
356  const sign2 = crypto.createSign('DSS1');
357  sign2.update(input);
358  const signature2 = sign2.sign(dsaKeyPem, 'hex');
359
360  const verify2 = crypto.createVerify('DSS1');
361  verify2.update(input);
362
363  assert.strictEqual(verify2.verify(dsaPubPem, signature2, 'hex'), true);
364}
365
366
367//
368// Test DSA signing and verification with PKCS#8 private key
369//
370{
371  const input = 'I AM THE WALRUS';
372
373  // DSA signatures vary across runs so there is no static string to verify
374  // against.
375  const sign = crypto.createSign('SHA1');
376  sign.update(input);
377  const signature = sign.sign(dsaPkcs8KeyPem, 'hex');
378
379  const verify = crypto.createVerify('SHA1');
380  verify.update(input);
381
382  assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
383}
384
385
386//
387// Test DSA signing and verification with encrypted key
388//
389const input = 'I AM THE WALRUS';
390
391{
392  const sign = crypto.createSign('SHA1');
393  sign.update(input);
394  assert.throws(() => {
395    sign.sign({ key: dsaKeyPemEncrypted, passphrase: 'wrong' }, 'hex');
396  }, decryptError);
397}
398
399{
400  // DSA signatures vary across runs so there is no static string to verify
401  // against.
402  const sign = crypto.createSign('SHA1');
403  sign.update(input);
404  const signOptions = { key: dsaKeyPemEncrypted, passphrase: 'password' };
405  const signature = sign.sign(signOptions, 'hex');
406
407  const verify = crypto.createVerify('SHA1');
408  verify.update(input);
409
410  assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true);
411}
412