• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const common = require('../common');
4if (!common.hasCrypto)
5  common.skip('missing crypto');
6
7const assert = require('assert');
8const crypto = require('crypto');
9const {
10  createSign,
11  createVerify,
12  publicEncrypt,
13  privateDecrypt,
14  sign,
15  verify,
16} = crypto;
17
18// The values below (modp2/modp2buf) are for a 1024 bits long prime from
19// RFC 2412 E.2, see https://tools.ietf.org/html/rfc2412. */
20const modp2buf = Buffer.from([
21  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f,
22  0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x62, 0x8b,
23  0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67,
24  0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22,
25  0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, 0xef, 0x95,
26  0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d,
27  0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51,
28  0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6,
29  0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff,
30  0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, 0xee, 0x38, 0x6b, 0xfb,
31  0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b,
32  0x1f, 0xe6, 0x49, 0x28, 0x66, 0x51, 0xec, 0xe6, 0x53, 0x81,
33  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
34]);
35
36function testDH({ publicKey: alicePublicKey, privateKey: alicePrivateKey },
37                { publicKey: bobPublicKey, privateKey: bobPrivateKey },
38                expectedValue) {
39  const buf1 = crypto.diffieHellman({
40    privateKey: alicePrivateKey,
41    publicKey: bobPublicKey,
42  });
43  const buf2 = crypto.diffieHellman({
44    privateKey: bobPrivateKey,
45    publicKey: alicePublicKey,
46  });
47  assert.deepStrictEqual(buf1, buf2);
48
49  if (expectedValue !== undefined)
50    assert.deepStrictEqual(buf1, expectedValue);
51}
52
53// Asserts that the size of the given key (in chars or bytes) is within 10% of
54// the expected size.
55function assertApproximateSize(key, expectedSize) {
56  const u = typeof key === 'string' ? 'chars' : 'bytes';
57  const min = Math.floor(0.9 * expectedSize);
58  const max = Math.ceil(1.1 * expectedSize);
59  assert(key.length >= min,
60         `Key (${key.length} ${u}) is shorter than expected (${min} ${u})`);
61  assert(key.length <= max,
62         `Key (${key.length} ${u}) is longer than expected (${max} ${u})`);
63}
64
65// Tests that a key pair can be used for encryption / decryption.
66function testEncryptDecrypt(publicKey, privateKey) {
67  const message = 'Hello Node.js world!';
68  const plaintext = Buffer.from(message, 'utf8');
69  for (const key of [publicKey, privateKey]) {
70    const ciphertext = publicEncrypt(key, plaintext);
71    const received = privateDecrypt(privateKey, ciphertext);
72    assert.strictEqual(received.toString('utf8'), message);
73  }
74}
75
76// Tests that a key pair can be used for signing / verification.
77function testSignVerify(publicKey, privateKey) {
78  const message = Buffer.from('Hello Node.js world!');
79
80  function oldSign(algo, data, key) {
81    return createSign(algo).update(data).sign(key);
82  }
83
84  function oldVerify(algo, data, key, signature) {
85    return createVerify(algo).update(data).verify(key, signature);
86  }
87
88  for (const signFn of [sign, oldSign]) {
89    const signature = signFn('SHA256', message, privateKey);
90    for (const verifyFn of [verify, oldVerify]) {
91      for (const key of [publicKey, privateKey]) {
92        const okay = verifyFn('SHA256', message, key, signature);
93        assert(okay);
94      }
95    }
96  }
97}
98
99// Constructs a regular expression for a PEM-encoded key with the given label.
100function getRegExpForPEM(label, cipher) {
101  const head = `\\-\\-\\-\\-\\-BEGIN ${label}\\-\\-\\-\\-\\-`;
102  const rfc1421Header = cipher == null ? '' :
103    `\nProc-Type: 4,ENCRYPTED\nDEK-Info: ${cipher},[^\n]+\n`;
104  const body = '([a-zA-Z0-9\\+/=]{64}\n)*[a-zA-Z0-9\\+/=]{1,64}';
105  const end = `\\-\\-\\-\\-\\-END ${label}\\-\\-\\-\\-\\-`;
106  return new RegExp(`^${head}${rfc1421Header}\n${body}\n${end}\n$`);
107}
108
109const pkcs1PubExp = getRegExpForPEM('RSA PUBLIC KEY');
110const pkcs1PrivExp = getRegExpForPEM('RSA PRIVATE KEY');
111const pkcs1EncExp = (cipher) => getRegExpForPEM('RSA PRIVATE KEY', cipher);
112const spkiExp = getRegExpForPEM('PUBLIC KEY');
113const pkcs8Exp = getRegExpForPEM('PRIVATE KEY');
114const pkcs8EncExp = getRegExpForPEM('ENCRYPTED PRIVATE KEY');
115const sec1Exp = getRegExpForPEM('EC PRIVATE KEY');
116const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
117
118module.exports = {
119  modp2buf,
120  testDH,
121  assertApproximateSize,
122  testEncryptDecrypt,
123  testSignVerify,
124  pkcs1PubExp,
125  pkcs1PrivExp,
126  pkcs1EncExp,  // used once
127  spkiExp,
128  pkcs8Exp, // used once
129  pkcs8EncExp,  // used once
130  sec1Exp,
131  sec1EncExp,
132};
133