Skip to content

Commit e04a260

Browse files
joyeecheungUlisesGascon
authored andcommittedSep 10, 2023
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 <luigipinca@gmail.com>

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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
cipher: 'aes-256-cbc',
30+
passphrase: 'secret'
31+
}
32+
}, common.mustSucceed((publicKeyDER, privateKeyDER) => {
33+
assert(Buffer.isBuffer(publicKeyDER));
34+
assertApproximateSize(publicKeyDER, 74);
35+
36+
assert(Buffer.isBuffer(privateKeyDER));
37+
38+
// Since the private key is encrypted, signing shouldn't work anymore.
39+
const publicKey = {
40+
key: publicKeyDER,
41+
type: 'pkcs1',
42+
format: 'der',
43+
};
44+
assert.throws(() => {
45+
testSignVerify(publicKey, {
46+
key: privateKeyDER,
47+
format: 'der',
48+
type: 'pkcs8'
49+
});
50+
}, {
51+
name: 'TypeError',
52+
code: 'ERR_MISSING_PASSPHRASE',
53+
message: 'Passphrase required for encrypted key'
54+
});
55+
56+
// Signing should work with the correct password.
57+
58+
const privateKey = {
59+
key: privateKeyDER,
60+
format: 'der',
61+
type: 'pkcs8',
62+
passphrase: 'secret'
63+
};
64+
testEncryptDecrypt(publicKey, privateKey);
65+
testSignVerify(publicKey, privateKey);
66+
}));
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
testSignVerify,
13+
spkiExp,
14+
pkcs8EncExp,
15+
} = require('../common/crypto');
16+
17+
// Test async elliptic curve key generation, e.g. for ECDSA, with an encrypted
18+
// private key with paramEncoding explicit.
19+
{
20+
generateKeyPair('ec', {
21+
namedCurve: 'P-256',
22+
paramEncoding: 'explicit',
23+
publicKeyEncoding: {
24+
type: 'spki',
25+
format: 'pem'
26+
},
27+
privateKeyEncoding: {
28+
type: 'pkcs8',
29+
format: 'pem',
30+
cipher: 'aes-128-cbc',
31+
passphrase: 'top secret'
32+
}
33+
}, common.mustSucceed((publicKey, privateKey) => {
34+
assert.strictEqual(typeof publicKey, 'string');
35+
assert.match(publicKey, spkiExp);
36+
assert.strictEqual(typeof privateKey, 'string');
37+
assert.match(privateKey, pkcs8EncExp);
38+
39+
// Since the private key is encrypted, signing shouldn't work anymore.
40+
assert.throws(() => testSignVerify(publicKey, privateKey),
41+
common.hasOpenSSL3 ? {
42+
message: 'error:07880109:common libcrypto ' +
43+
'routines::interrupted or cancelled'
44+
} : {
45+
name: 'TypeError',
46+
code: 'ERR_MISSING_PASSPHRASE',
47+
message: 'Passphrase required for encrypted key'
48+
});
49+
50+
testSignVerify(publicKey, {
51+
key: privateKey,
52+
passphrase: 'top secret'
53+
});
54+
}));
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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+
testSignVerify,
13+
spkiExp,
14+
sec1EncExp,
15+
} = require('../common/crypto');
16+
17+
{
18+
// Test async explicit elliptic curve key generation with an encrypted
19+
// private key.
20+
generateKeyPair('ec', {
21+
namedCurve: 'prime256v1',
22+
paramEncoding: 'explicit',
23+
publicKeyEncoding: {
24+
type: 'spki',
25+
format: 'pem'
26+
},
27+
privateKeyEncoding: {
28+
type: 'sec1',
29+
format: 'pem',
30+
cipher: 'aes-128-cbc',
31+
passphrase: 'secret'
32+
}
33+
}, common.mustSucceed((publicKey, privateKey) => {
34+
assert.strictEqual(typeof publicKey, 'string');
35+
assert.match(publicKey, spkiExp);
36+
assert.strictEqual(typeof privateKey, 'string');
37+
assert.match(privateKey, sec1EncExp('AES-128-CBC'));
38+
39+
// Since the private key is encrypted, signing shouldn't work anymore.
40+
assert.throws(() => testSignVerify(publicKey, privateKey),
41+
common.hasOpenSSL3 ? {
42+
message: 'error:07880109:common libcrypto ' +
43+
'routines::interrupted or cancelled'
44+
} : {
45+
name: 'TypeError',
46+
code: 'ERR_MISSING_PASSPHRASE',
47+
message: 'Passphrase required for encrypted key'
48+
});
49+
50+
testSignVerify(publicKey, { key: privateKey, passphrase: 'secret' });
51+
}));
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
testSignVerify,
13+
spkiExp,
14+
sec1Exp,
15+
} = require('../common/crypto');
16+
17+
// Test async explicit elliptic curve key generation, e.g. for ECDSA,
18+
// with a SEC1 private key with paramEncoding explicit.
19+
{
20+
generateKeyPair('ec', {
21+
namedCurve: 'prime256v1',
22+
paramEncoding: 'explicit',
23+
publicKeyEncoding: {
24+
type: 'spki',
25+
format: 'pem'
26+
},
27+
privateKeyEncoding: {
28+
type: 'sec1',
29+
format: 'pem'
30+
}
31+
}, common.mustSucceed((publicKey, privateKey) => {
32+
assert.strictEqual(typeof publicKey, 'string');
33+
assert.match(publicKey, spkiExp);
34+
assert.strictEqual(typeof privateKey, 'string');
35+
assert.match(privateKey, sec1Exp);
36+
37+
testSignVerify(publicKey, privateKey);
38+
}));
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
testSignVerify,
13+
spkiExp,
14+
pkcs8EncExp,
15+
} = require('../common/crypto');
16+
17+
// Test async elliptic curve key generation, e.g. for ECDSA, with an encrypted
18+
// private key.
19+
{
20+
generateKeyPair('ec', {
21+
namedCurve: 'P-256',
22+
paramEncoding: 'named',
23+
publicKeyEncoding: {
24+
type: 'spki',
25+
format: 'pem'
26+
},
27+
privateKeyEncoding: {
28+
type: 'pkcs8',
29+
format: 'pem',
30+
cipher: 'aes-128-cbc',
31+
passphrase: 'top secret'
32+
}
33+
}, common.mustSucceed((publicKey, privateKey) => {
34+
assert.strictEqual(typeof publicKey, 'string');
35+
assert.match(publicKey, spkiExp);
36+
assert.strictEqual(typeof privateKey, 'string');
37+
assert.match(privateKey, pkcs8EncExp);
38+
39+
// Since the private key is encrypted, signing shouldn't work anymore.
40+
assert.throws(() => testSignVerify(publicKey, privateKey),
41+
common.hasOpenSSL3 ? {
42+
message: 'error:07880109:common libcrypto ' +
43+
'routines::interrupted or cancelled'
44+
} : {
45+
name: 'TypeError',
46+
code: 'ERR_MISSING_PASSPHRASE',
47+
message: 'Passphrase required for encrypted key'
48+
});
49+
50+
testSignVerify(publicKey, {
51+
key: privateKey,
52+
passphrase: 'top secret'
53+
});
54+
}));
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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+
testSignVerify,
13+
spkiExp,
14+
sec1EncExp,
15+
} = require('../common/crypto');
16+
17+
{
18+
// Test async named elliptic curve key generation with an encrypted
19+
// private key.
20+
generateKeyPair('ec', {
21+
namedCurve: 'prime256v1',
22+
paramEncoding: 'named',
23+
publicKeyEncoding: {
24+
type: 'spki',
25+
format: 'pem'
26+
},
27+
privateKeyEncoding: {
28+
type: 'sec1',
29+
format: 'pem',
30+
cipher: 'aes-128-cbc',
31+
passphrase: 'secret'
32+
}
33+
}, common.mustSucceed((publicKey, privateKey) => {
34+
assert.strictEqual(typeof publicKey, 'string');
35+
assert.match(publicKey, spkiExp);
36+
assert.strictEqual(typeof privateKey, 'string');
37+
assert.match(privateKey, sec1EncExp('AES-128-CBC'));
38+
39+
// Since the private key is encrypted, signing shouldn't work anymore.
40+
assert.throws(() => testSignVerify(publicKey, privateKey),
41+
common.hasOpenSSL3 ? {
42+
message: 'error:07880109:common libcrypto ' +
43+
'routines::interrupted or cancelled'
44+
} : {
45+
name: 'TypeError',
46+
code: 'ERR_MISSING_PASSPHRASE',
47+
message: 'Passphrase required for encrypted key'
48+
});
49+
50+
testSignVerify(publicKey, { key: privateKey, passphrase: 'secret' });
51+
}));
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
testSignVerify,
13+
spkiExp,
14+
sec1Exp,
15+
} = require('../common/crypto');
16+
17+
// Test async named elliptic curve key generation, e.g. for ECDSA,
18+
// with a SEC1 private key.
19+
{
20+
generateKeyPair('ec', {
21+
namedCurve: 'prime256v1',
22+
paramEncoding: 'named',
23+
publicKeyEncoding: {
24+
type: 'spki',
25+
format: 'pem'
26+
},
27+
privateKeyEncoding: {
28+
type: 'sec1',
29+
format: 'pem'
30+
}
31+
}, common.mustSucceed((publicKey, privateKey) => {
32+
assert.strictEqual(typeof publicKey, 'string');
33+
assert.match(publicKey, spkiExp);
34+
assert.strictEqual(typeof privateKey, 'string');
35+
assert.match(privateKey, sec1Exp);
36+
37+
testSignVerify(publicKey, privateKey);
38+
}));
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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+
pkcs1EncExp,
16+
} = require('../common/crypto');
17+
18+
// Test async RSA key generation with an encrypted private key.
19+
{
20+
generateKeyPair('rsa', {
21+
publicExponent: 0x10001,
22+
modulusLength: 512,
23+
publicKeyEncoding: {
24+
type: 'pkcs1',
25+
format: 'der'
26+
},
27+
privateKeyEncoding: {
28+
type: 'pkcs1',
29+
format: 'pem',
30+
cipher: 'aes-256-cbc',
31+
passphrase: 'secret'
32+
}
33+
}, common.mustSucceed((publicKeyDER, privateKey) => {
34+
assert(Buffer.isBuffer(publicKeyDER));
35+
assertApproximateSize(publicKeyDER, 74);
36+
37+
assert.strictEqual(typeof privateKey, 'string');
38+
assert.match(privateKey, pkcs1EncExp('AES-256-CBC'));
39+
40+
// Since the private key is encrypted, signing shouldn't work anymore.
41+
const publicKey = {
42+
key: publicKeyDER,
43+
type: 'pkcs1',
44+
format: 'der',
45+
};
46+
const expectedError = common.hasOpenSSL3 ? {
47+
name: 'Error',
48+
message: 'error:07880109:common libcrypto routines::interrupted or ' +
49+
'cancelled'
50+
} : {
51+
name: 'TypeError',
52+
code: 'ERR_MISSING_PASSPHRASE',
53+
message: 'Passphrase required for encrypted key'
54+
};
55+
assert.throws(() => testSignVerify(publicKey, privateKey), expectedError);
56+
57+
const key = { key: privateKey, passphrase: 'secret' };
58+
testEncryptDecrypt(publicKey, key);
59+
testSignVerify(publicKey, key);
60+
}));
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
// This tests check that generateKeyPair returns correct bit length in
13+
// KeyObject's asymmetricKeyDetails.
14+
// https://github.com/nodejs/node/issues/46102#issuecomment-1372153541
15+
{
16+
generateKeyPair('rsa', {
17+
modulusLength: 513,
18+
}, common.mustSucceed((publicKey, privateKey) => {
19+
assert.strictEqual(privateKey.asymmetricKeyDetails.modulusLength, 513);
20+
assert.strictEqual(publicKey.asymmetricKeyDetails.modulusLength, 513);
21+
}));
22+
23+
generateKeyPair('rsa-pss', {
24+
modulusLength: 513,
25+
}, common.mustSucceed((publicKey, privateKey) => {
26+
assert.strictEqual(privateKey.asymmetricKeyDetails.modulusLength, 513);
27+
assert.strictEqual(publicKey.asymmetricKeyDetails.modulusLength, 513);
28+
}));
29+
30+
if (common.hasOpenSSL3) {
31+
generateKeyPair('dsa', {
32+
modulusLength: 2049,
33+
divisorLength: 256,
34+
}, common.mustSucceed((publicKey, privateKey) => {
35+
assert.strictEqual(privateKey.asymmetricKeyDetails.modulusLength, 2049);
36+
assert.strictEqual(publicKey.asymmetricKeyDetails.modulusLength, 2049);
37+
}));
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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 classic Diffie-Hellman key generation.
13+
{
14+
generateKeyPair('dh', {
15+
primeLength: 1024
16+
}, common.mustSucceed((publicKey, privateKey) => {
17+
assert.strictEqual(publicKey.type, 'public');
18+
assert.strictEqual(publicKey.asymmetricKeyType, 'dh');
19+
20+
assert.strictEqual(privateKey.type, 'private');
21+
assert.strictEqual(privateKey.asymmetricKeyType, 'dh');
22+
}));
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
// This test makes sure deprecated and new options may be used
13+
// simultaneously so long as they're identical values.
14+
{
15+
generateKeyPair('rsa-pss', {
16+
modulusLength: 512,
17+
saltLength: 16,
18+
hash: 'sha256',
19+
hashAlgorithm: 'sha256',
20+
mgf1Hash: 'sha256',
21+
mgf1HashAlgorithm: 'sha256'
22+
}, common.mustSucceed((publicKey, privateKey) => {
23+
assert.strictEqual(publicKey.type, 'public');
24+
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss');
25+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
26+
modulusLength: 512,
27+
publicExponent: 65537n,
28+
hashAlgorithm: 'sha256',
29+
mgf1HashAlgorithm: 'sha256',
30+
saltLength: 16
31+
});
32+
33+
assert.strictEqual(privateKey.type, 'private');
34+
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss');
35+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
36+
modulusLength: 512,
37+
publicExponent: 65537n,
38+
hashAlgorithm: 'sha256',
39+
mgf1HashAlgorithm: 'sha256',
40+
saltLength: 16
41+
});
42+
}));
43+
}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 EdDSA key generation.
13+
{
14+
if (!/^1\.1\.0/.test(process.versions.openssl)) {
15+
['ed25519', 'ed448', 'x25519', 'x448'].forEach((keyType) => {
16+
generateKeyPair(keyType, common.mustSucceed((publicKey, privateKey) => {
17+
assert.strictEqual(publicKey.type, 'public');
18+
assert.strictEqual(publicKey.asymmetricKeyType, keyType);
19+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {});
20+
21+
assert.strictEqual(privateKey.type, 'private');
22+
assert.strictEqual(privateKey.asymmetricKeyType, keyType);
23+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {});
24+
}));
25+
});
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
// Passing an empty passphrase string should not throw ERR_OSSL_CRYPTO_MALLOC_FAILURE even on OpenSSL 3.
13+
// Regression test for https://github.com/nodejs/node/issues/41428.
14+
generateKeyPair('rsa', {
15+
modulusLength: 4096,
16+
publicKeyEncoding: {
17+
type: 'spki',
18+
format: 'pem'
19+
},
20+
privateKeyEncoding: {
21+
type: 'pkcs8',
22+
format: 'pem',
23+
cipher: 'aes-256-cbc',
24+
passphrase: ''
25+
}
26+
}, common.mustSucceed((publicKey, privateKey) => {
27+
assert.strictEqual(typeof publicKey, 'string');
28+
assert.strictEqual(typeof privateKey, 'string');
29+
}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
createPrivateKey,
10+
generateKeyPair,
11+
} = require('crypto');
12+
const {
13+
testSignVerify,
14+
} = require('../common/crypto');
15+
16+
// Passing an empty passphrase string should not cause OpenSSL's default
17+
// passphrase prompt in the terminal.
18+
// See https://github.com/nodejs/node/issues/35898.
19+
for (const type of ['pkcs1', 'pkcs8']) {
20+
generateKeyPair('rsa', {
21+
modulusLength: 1024,
22+
privateKeyEncoding: {
23+
type,
24+
format: 'pem',
25+
cipher: 'aes-256-cbc',
26+
passphrase: ''
27+
}
28+
}, common.mustSucceed((publicKey, privateKey) => {
29+
assert.strictEqual(publicKey.type, 'public');
30+
31+
for (const passphrase of ['', Buffer.alloc(0)]) {
32+
const privateKeyObject = createPrivateKey({
33+
passphrase,
34+
key: privateKey
35+
});
36+
assert.strictEqual(privateKeyObject.asymmetricKeyType, 'rsa');
37+
}
38+
39+
// Encrypting with an empty passphrase is not the same as not encrypting
40+
// the key, and not specifying a passphrase should fail when decoding it.
41+
assert.throws(() => {
42+
return testSignVerify(publicKey, privateKey);
43+
}, common.hasOpenSSL3 ? {
44+
name: 'Error',
45+
code: 'ERR_OSSL_CRYPTO_INTERRUPTED_OR_CANCELLED',
46+
message: 'error:07880109:common libcrypto routines::interrupted or cancelled'
47+
} : {
48+
name: 'TypeError',
49+
code: 'ERR_MISSING_PASSPHRASE',
50+
message: 'Passphrase required for encrypted key'
51+
});
52+
}));
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
9+
const {
10+
generateKeyPairSync,
11+
} = require('crypto');
12+
13+
// Test invalid parameter encoding.
14+
{
15+
assert.throws(() => generateKeyPairSync('dsa', {
16+
modulusLength: 4096,
17+
publicKeyEncoding: {
18+
format: 'jwk'
19+
},
20+
privateKeyEncoding: {
21+
format: 'jwk'
22+
}
23+
}), {
24+
name: 'Error',
25+
code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE',
26+
message: 'Unsupported JWK Key Type.'
27+
});
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
9+
const {
10+
generateKeyPairSync,
11+
} = require('crypto');
12+
13+
{
14+
assert.throws(() => generateKeyPairSync('ec', {
15+
namedCurve: 'secp224r1',
16+
publicKeyEncoding: {
17+
format: 'jwk'
18+
},
19+
privateKeyEncoding: {
20+
format: 'jwk'
21+
}
22+
}), {
23+
name: 'Error',
24+
code: 'ERR_CRYPTO_JWK_UNSUPPORTED_CURVE',
25+
message: 'Unsupported JWK EC curve: secp224r1.'
26+
});
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
testEncryptDecrypt,
13+
testSignVerify,
14+
} = require('../common/crypto');
15+
16+
// Tests key objects are returned when key encodings are not specified.
17+
{
18+
// If no publicKeyEncoding is specified, a key object should be returned.
19+
generateKeyPair('rsa', {
20+
modulusLength: 1024,
21+
privateKeyEncoding: {
22+
type: 'pkcs1',
23+
format: 'pem'
24+
}
25+
}, common.mustSucceed((publicKey, privateKey) => {
26+
assert.strictEqual(typeof publicKey, 'object');
27+
assert.strictEqual(publicKey.type, 'public');
28+
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa');
29+
30+
// The private key should still be a string.
31+
assert.strictEqual(typeof privateKey, 'string');
32+
33+
testEncryptDecrypt(publicKey, privateKey);
34+
testSignVerify(publicKey, privateKey);
35+
}));
36+
37+
// If no privateKeyEncoding is specified, a key object should be returned.
38+
generateKeyPair('rsa', {
39+
modulusLength: 1024,
40+
publicKeyEncoding: {
41+
type: 'pkcs1',
42+
format: 'pem'
43+
}
44+
}, common.mustSucceed((publicKey, privateKey) => {
45+
// The public key should still be a string.
46+
assert.strictEqual(typeof publicKey, 'string');
47+
48+
assert.strictEqual(typeof privateKey, 'object');
49+
assert.strictEqual(privateKey.type, 'private');
50+
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa');
51+
52+
testEncryptDecrypt(publicKey, privateKey);
53+
testSignVerify(publicKey, privateKey);
54+
}));
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
generateKeyPairSync,
10+
} = require('crypto');
11+
12+
// Test sync key generation with key objects.
13+
{
14+
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
15+
modulusLength: 512
16+
});
17+
18+
assert.strictEqual(typeof publicKey, 'object');
19+
assert.strictEqual(publicKey.type, 'public');
20+
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa');
21+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
22+
modulusLength: 512,
23+
publicExponent: 65537n
24+
});
25+
26+
assert.strictEqual(typeof privateKey, 'object');
27+
assert.strictEqual(privateKey.type, 'private');
28+
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa');
29+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
30+
modulusLength: 512,
31+
publicExponent: 65537n
32+
});
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
generateKeyPairSync,
11+
getCurves,
12+
} = require('crypto');
13+
14+
// This test creates EC key pairs on curves without associated OIDs.
15+
// Specifying a key encoding should not crash.
16+
{
17+
if (process.versions.openssl >= '1.1.1i') {
18+
for (const namedCurve of ['Oakley-EC2N-3', 'Oakley-EC2N-4']) {
19+
if (!getCurves().includes(namedCurve))
20+
continue;
21+
22+
const expectedErrorCode =
23+
common.hasOpenSSL3 ? 'ERR_OSSL_MISSING_OID' : 'ERR_OSSL_EC_MISSING_OID';
24+
const params = {
25+
namedCurve,
26+
publicKeyEncoding: {
27+
format: 'der',
28+
type: 'spki'
29+
}
30+
};
31+
32+
assert.throws(() => {
33+
generateKeyPairSync('ec', params);
34+
}, {
35+
code: expectedErrorCode
36+
});
37+
38+
generateKeyPair('ec', params, common.mustCall((err) => {
39+
assert.strictEqual(err.code, expectedErrorCode);
40+
}));
41+
}
42+
}
43+
}
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+
// 'rsa-pss' should not add a RSASSA-PSS-params sequence by default.
13+
// Regression test for: https://github.com/nodejs/node/issues/39936
14+
{
15+
generateKeyPair('rsa-pss', {
16+
modulusLength: 512
17+
}, common.mustSucceed((publicKey, privateKey) => {
18+
const expectedKeyDetails = {
19+
modulusLength: 512,
20+
publicExponent: 65537n
21+
};
22+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, expectedKeyDetails);
23+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, expectedKeyDetails);
24+
25+
// To allow backporting the fix to versions that do not support
26+
// asymmetricKeyDetails for RSA-PSS params, also verify that the exported
27+
// AlgorithmIdentifier member of the SubjectPublicKeyInfo has the expected
28+
// length of 11 bytes (as opposed to > 11 bytes if node added params).
29+
const spki = publicKey.export({ format: 'der', type: 'spki' });
30+
assert.strictEqual(spki[3], 11, spki.toString('hex'));
31+
}));
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
generateKeyPairSync,
10+
} = require('crypto');
11+
12+
// Test sync key generation with key objects with a non-standard
13+
// publicExponent
14+
{
15+
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
16+
publicExponent: 3,
17+
modulusLength: 512
18+
});
19+
20+
assert.strictEqual(typeof publicKey, 'object');
21+
assert.strictEqual(publicKey.type, 'public');
22+
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa');
23+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
24+
modulusLength: 512,
25+
publicExponent: 3n
26+
});
27+
28+
assert.strictEqual(typeof privateKey, 'object');
29+
assert.strictEqual(privateKey.type, 'private');
30+
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa');
31+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
32+
modulusLength: 512,
33+
publicExponent: 3n
34+
});
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
pkcs1PubExp,
16+
pkcs1PrivExp,
17+
} = require('../common/crypto');
18+
const { promisify } = require('util');
19+
20+
// Test the util.promisified API with async RSA key generation.
21+
{
22+
promisify(generateKeyPair)('rsa', {
23+
publicExponent: 0x10001,
24+
modulusLength: 512,
25+
publicKeyEncoding: {
26+
type: 'pkcs1',
27+
format: 'pem'
28+
},
29+
privateKeyEncoding: {
30+
type: 'pkcs1',
31+
format: 'pem'
32+
}
33+
}).then(common.mustCall((keys) => {
34+
const { publicKey, privateKey } = keys;
35+
assert.strictEqual(typeof publicKey, 'string');
36+
assert.match(publicKey, pkcs1PubExp);
37+
assertApproximateSize(publicKey, 180);
38+
39+
assert.strictEqual(typeof privateKey, 'string');
40+
assert.match(privateKey, pkcs1PrivExp);
41+
assertApproximateSize(privateKey, 512);
42+
43+
testEncryptDecrypt(publicKey, privateKey);
44+
testSignVerify(publicKey, privateKey);
45+
}));
46+
}
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+
// RFC 8017, 9.1.: "Assuming that the mask generation function is based on a
13+
// hash function, it is RECOMMENDED that the hash function be the same as the
14+
// one that is applied to the message."
15+
{
16+
17+
generateKeyPair('rsa-pss', {
18+
modulusLength: 512,
19+
hashAlgorithm: 'sha256',
20+
saltLength: 16
21+
}, common.mustSucceed((publicKey, privateKey) => {
22+
const expectedKeyDetails = {
23+
modulusLength: 512,
24+
publicExponent: 65537n,
25+
hashAlgorithm: 'sha256',
26+
mgf1HashAlgorithm: 'sha256',
27+
saltLength: 16
28+
};
29+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, expectedKeyDetails);
30+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, expectedKeyDetails);
31+
}));
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
// RFC 8017, A.2.3.: "For a given hashAlgorithm, the default value of
13+
// saltLength is the octet length of the hash value."
14+
{
15+
generateKeyPair('rsa-pss', {
16+
modulusLength: 512,
17+
hashAlgorithm: 'sha512'
18+
}, common.mustSucceed((publicKey, privateKey) => {
19+
const expectedKeyDetails = {
20+
modulusLength: 512,
21+
publicExponent: 65537n,
22+
hashAlgorithm: 'sha512',
23+
mgf1HashAlgorithm: 'sha512',
24+
saltLength: 64
25+
};
26+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, expectedKeyDetails);
27+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, expectedKeyDetails);
28+
}));
29+
30+
// It is still possible to explicitly set saltLength to 0.
31+
generateKeyPair('rsa-pss', {
32+
modulusLength: 512,
33+
hashAlgorithm: 'sha512',
34+
saltLength: 0
35+
}, common.mustSucceed((publicKey, privateKey) => {
36+
const expectedKeyDetails = {
37+
modulusLength: 512,
38+
publicExponent: 65537n,
39+
hashAlgorithm: 'sha512',
40+
mgf1HashAlgorithm: 'sha512',
41+
saltLength: 0
42+
};
43+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, expectedKeyDetails);
44+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, expectedKeyDetails);
45+
}));
46+
}
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+
constants,
10+
generateKeyPair,
11+
} = require('crypto');
12+
const {
13+
testEncryptDecrypt,
14+
testSignVerify,
15+
} = require('../common/crypto');
16+
17+
// Test RSA-PSS.
18+
{
19+
generateKeyPair('rsa-pss', {
20+
modulusLength: 512,
21+
saltLength: 16,
22+
hashAlgorithm: 'sha256',
23+
mgf1HashAlgorithm: 'sha256'
24+
}, common.mustSucceed((publicKey, privateKey) => {
25+
assert.strictEqual(publicKey.type, 'public');
26+
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss');
27+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
28+
modulusLength: 512,
29+
publicExponent: 65537n,
30+
hashAlgorithm: 'sha256',
31+
mgf1HashAlgorithm: 'sha256',
32+
saltLength: 16
33+
});
34+
35+
assert.strictEqual(privateKey.type, 'private');
36+
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss');
37+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
38+
modulusLength: 512,
39+
publicExponent: 65537n,
40+
hashAlgorithm: 'sha256',
41+
mgf1HashAlgorithm: 'sha256',
42+
saltLength: 16
43+
});
44+
45+
// Unlike RSA, RSA-PSS does not allow encryption.
46+
assert.throws(() => {
47+
testEncryptDecrypt(publicKey, privateKey);
48+
}, /operation not supported for this keytype/);
49+
50+
// RSA-PSS also does not permit signing with PKCS1 padding.
51+
assert.throws(() => {
52+
testSignVerify({
53+
key: publicKey,
54+
padding: constants.RSA_PKCS1_PADDING
55+
}, {
56+
key: privateKey,
57+
padding: constants.RSA_PKCS1_PADDING
58+
});
59+
}, /illegal or unsupported padding mode/);
60+
61+
// The padding should correctly default to RSA_PKCS1_PSS_PADDING now.
62+
testSignVerify(publicKey, privateKey);
63+
}));
64+
}
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
generateKeyPairSync,
10+
} = require('crypto');
11+
const {
12+
assertApproximateSize,
13+
testEncryptDecrypt,
14+
testSignVerify,
15+
pkcs1PubExp,
16+
pkcs8Exp,
17+
} = require('../common/crypto');
18+
19+
// To make the test faster, we will only test sync key generation once and
20+
// with a relatively small key.
21+
{
22+
const ret = generateKeyPairSync('rsa', {
23+
publicExponent: 3,
24+
modulusLength: 512,
25+
publicKeyEncoding: {
26+
type: 'pkcs1',
27+
format: 'pem'
28+
},
29+
privateKeyEncoding: {
30+
type: 'pkcs8',
31+
format: 'pem'
32+
}
33+
});
34+
35+
assert.strictEqual(Object.keys(ret).length, 2);
36+
const { publicKey, privateKey } = ret;
37+
38+
assert.strictEqual(typeof publicKey, 'string');
39+
assert.match(publicKey, pkcs1PubExp);
40+
assertApproximateSize(publicKey, 162);
41+
assert.strictEqual(typeof privateKey, 'string');
42+
assert.match(privateKey, pkcs8Exp);
43+
assertApproximateSize(privateKey, 512);
44+
45+
testEncryptDecrypt(publicKey, privateKey);
46+
testSignVerify(publicKey, privateKey);
47+
}

‎test/parallel/test-crypto-keygen.js

+4-1,042
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.