Skip to content

Commit 7224940

Browse files
joyeecheungnodejs-github-bot
authored andcommitted
test: split test-crypto-keygen.js
To avoid timing out on ARM machines in the CI. PR-URL: #49221 Refs: #49202 Refs: #41206 Reviewed-By: Luigi Pinca <[email protected]>
1 parent a81d5e1 commit 7224940

32 files changed

+1456
-1042
lines changed

test/common/crypto.js

+84
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ if (!common.hasCrypto)
66

77
const assert = require('assert');
88
const crypto = require('crypto');
9+
const {
10+
createSign,
11+
createVerify,
12+
publicEncrypt,
13+
privateDecrypt,
14+
sign,
15+
verify,
16+
} = crypto;
917

1018
// The values below (modp2/modp2buf) are for a 1024 bits long prime from
1119
// RFC 2412 E.2, see https://tools.ietf.org/html/rfc2412. */
@@ -42,7 +50,83 @@ function testDH({ publicKey: alicePublicKey, privateKey: alicePrivateKey },
4250
assert.deepStrictEqual(buf1, expectedValue);
4351
}
4452

53+
// Asserts that the size of the given key (in chars or bytes) is within 10% of
54+
// the expected size.
55+
function 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.
66+
function 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.
77+
function 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.
100+
function 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+
109+
const pkcs1PubExp = getRegExpForPEM('RSA PUBLIC KEY');
110+
const pkcs1PrivExp = getRegExpForPEM('RSA PRIVATE KEY');
111+
const pkcs1EncExp = (cipher) => getRegExpForPEM('RSA PRIVATE KEY', cipher);
112+
const spkiExp = getRegExpForPEM('PUBLIC KEY');
113+
const pkcs8Exp = getRegExpForPEM('PRIVATE KEY');
114+
const pkcs8EncExp = getRegExpForPEM('ENCRYPTED PRIVATE KEY');
115+
const sec1Exp = getRegExpForPEM('EC PRIVATE KEY');
116+
const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
117+
45118
module.exports = {
46119
modp2buf,
47120
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,
48132
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const assert = require('assert');
8+
const {
9+
generateKeyPair,
10+
} = require('crypto');
11+
12+
// Test async DSA key object generation.
13+
{
14+
generateKeyPair('dsa', {
15+
modulusLength: common.hasOpenSSL3 ? 2048 : 512,
16+
divisorLength: 256
17+
}, common.mustSucceed((publicKey, privateKey) => {
18+
assert.strictEqual(publicKey.type, 'public');
19+
assert.strictEqual(publicKey.asymmetricKeyType, 'dsa');
20+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
21+
modulusLength: common.hasOpenSSL3 ? 2048 : 512,
22+
divisorLength: 256
23+
});
24+
25+
assert.strictEqual(privateKey.type, 'private');
26+
assert.strictEqual(privateKey.asymmetricKeyType, 'dsa');
27+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
28+
modulusLength: common.hasOpenSSL3 ? 2048 : 512,
29+
divisorLength: 256
30+
});
31+
}));
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const assert = require('assert');
8+
const {
9+
generateKeyPair,
10+
} = require('crypto');
11+
const {
12+
assertApproximateSize,
13+
testSignVerify,
14+
spkiExp,
15+
} = require('../common/crypto');
16+
17+
// Test async DSA key generation.
18+
{
19+
const privateKeyEncoding = {
20+
type: 'pkcs8',
21+
format: 'der'
22+
};
23+
24+
generateKeyPair('dsa', {
25+
modulusLength: common.hasOpenSSL3 ? 2048 : 512,
26+
divisorLength: 256,
27+
publicKeyEncoding: {
28+
type: 'spki',
29+
format: 'pem'
30+
},
31+
privateKeyEncoding: {
32+
cipher: 'aes-128-cbc',
33+
passphrase: 'secret',
34+
...privateKeyEncoding
35+
}
36+
}, common.mustSucceed((publicKey, privateKeyDER) => {
37+
assert.strictEqual(typeof publicKey, 'string');
38+
assert.match(publicKey, spkiExp);
39+
// The private key is DER-encoded.
40+
assert(Buffer.isBuffer(privateKeyDER));
41+
42+
assertApproximateSize(publicKey, common.hasOpenSSL3 ? 1194 : 440);
43+
assertApproximateSize(privateKeyDER, common.hasOpenSSL3 ? 721 : 336);
44+
45+
// Since the private key is encrypted, signing shouldn't work anymore.
46+
assert.throws(() => {
47+
return testSignVerify(publicKey, {
48+
key: privateKeyDER,
49+
...privateKeyEncoding
50+
});
51+
}, {
52+
name: 'TypeError',
53+
code: 'ERR_MISSING_PASSPHRASE',
54+
message: 'Passphrase required for encrypted key'
55+
});
56+
57+
// Signing should work with the correct password.
58+
testSignVerify(publicKey, {
59+
key: privateKeyDER,
60+
...privateKeyEncoding,
61+
passphrase: 'secret'
62+
});
63+
}));
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const assert = require('assert');
8+
const {
9+
generateKeyPair,
10+
} = require('crypto');
11+
12+
// Test async elliptic curve key generation with 'jwk' encoding
13+
{
14+
[
15+
['ec', ['P-384', 'P-256', 'P-521', 'secp256k1']],
16+
['rsa'],
17+
['ed25519'],
18+
['ed448'],
19+
['x25519'],
20+
['x448'],
21+
].forEach((types) => {
22+
const [type, options] = types;
23+
switch (type) {
24+
case 'ec': {
25+
return options.forEach((curve) => {
26+
generateKeyPair(type, {
27+
namedCurve: curve,
28+
publicKeyEncoding: {
29+
format: 'jwk'
30+
},
31+
privateKeyEncoding: {
32+
format: 'jwk'
33+
}
34+
}, common.mustSucceed((publicKey, privateKey) => {
35+
assert.strictEqual(typeof publicKey, 'object');
36+
assert.strictEqual(typeof privateKey, 'object');
37+
assert.strictEqual(publicKey.x, privateKey.x);
38+
assert.strictEqual(publicKey.y, privateKey.y);
39+
assert(!publicKey.d);
40+
assert(privateKey.d);
41+
assert.strictEqual(publicKey.kty, 'EC');
42+
assert.strictEqual(publicKey.kty, privateKey.kty);
43+
assert.strictEqual(publicKey.crv, curve);
44+
assert.strictEqual(publicKey.crv, privateKey.crv);
45+
}));
46+
});
47+
}
48+
case 'rsa': {
49+
return generateKeyPair(type, {
50+
modulusLength: 4096,
51+
publicKeyEncoding: {
52+
format: 'jwk'
53+
},
54+
privateKeyEncoding: {
55+
format: 'jwk'
56+
}
57+
}, common.mustSucceed((publicKey, privateKey) => {
58+
assert.strictEqual(typeof publicKey, 'object');
59+
assert.strictEqual(typeof privateKey, 'object');
60+
assert.strictEqual(publicKey.kty, 'RSA');
61+
assert.strictEqual(publicKey.kty, privateKey.kty);
62+
assert.strictEqual(typeof publicKey.n, 'string');
63+
assert.strictEqual(publicKey.n, privateKey.n);
64+
assert.strictEqual(typeof publicKey.e, 'string');
65+
assert.strictEqual(publicKey.e, privateKey.e);
66+
assert.strictEqual(typeof privateKey.d, 'string');
67+
assert.strictEqual(typeof privateKey.p, 'string');
68+
assert.strictEqual(typeof privateKey.q, 'string');
69+
assert.strictEqual(typeof privateKey.dp, 'string');
70+
assert.strictEqual(typeof privateKey.dq, 'string');
71+
assert.strictEqual(typeof privateKey.qi, 'string');
72+
}));
73+
}
74+
case 'ed25519':
75+
case 'ed448':
76+
case 'x25519':
77+
case 'x448': {
78+
generateKeyPair(type, {
79+
publicKeyEncoding: {
80+
format: 'jwk'
81+
},
82+
privateKeyEncoding: {
83+
format: 'jwk'
84+
}
85+
}, common.mustSucceed((publicKey, privateKey) => {
86+
assert.strictEqual(typeof publicKey, 'object');
87+
assert.strictEqual(typeof privateKey, 'object');
88+
assert.strictEqual(publicKey.x, privateKey.x);
89+
assert(!publicKey.d);
90+
assert(privateKey.d);
91+
assert.strictEqual(publicKey.kty, 'OKP');
92+
assert.strictEqual(publicKey.kty, privateKey.kty);
93+
const expectedCrv = `${type.charAt(0).toUpperCase()}${type.slice(1)}`;
94+
assert.strictEqual(publicKey.crv, expectedCrv);
95+
assert.strictEqual(publicKey.crv, privateKey.crv);
96+
}));
97+
}
98+
}
99+
});
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const assert = require('assert');
8+
const {
9+
generateKeyPair,
10+
} = require('crypto');
11+
const {
12+
assertApproximateSize,
13+
testEncryptDecrypt,
14+
testSignVerify,
15+
} = require('../common/crypto');
16+
17+
// Test async RSA key generation with an encrypted private key, but encoded as DER.
18+
{
19+
generateKeyPair('rsa', {
20+
publicExponent: 0x10001,
21+
modulusLength: 512,
22+
publicKeyEncoding: {
23+
type: 'pkcs1',
24+
format: 'der'
25+
},
26+
privateKeyEncoding: {
27+
type: 'pkcs8',
28+
format: 'der'
29+
}
30+
}, common.mustSucceed((publicKeyDER, privateKeyDER) => {
31+
assert(Buffer.isBuffer(publicKeyDER));
32+
assertApproximateSize(publicKeyDER, 74);
33+
34+
assert(Buffer.isBuffer(privateKeyDER));
35+
36+
const publicKey = {
37+
key: publicKeyDER,
38+
type: 'pkcs1',
39+
format: 'der',
40+
};
41+
const privateKey = {
42+
key: privateKeyDER,
43+
format: 'der',
44+
type: 'pkcs8',
45+
passphrase: 'secret'
46+
};
47+
testEncryptDecrypt(publicKey, privateKey);
48+
testSignVerify(publicKey, privateKey);
49+
}));
50+
}

0 commit comments

Comments
 (0)