Skip to content

Commit fb226ff

Browse files
panvarichardlau
authored andcommittedSep 10, 2021
crypto: add rsa-pss keygen parameters
PR-URL: #39927 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 22a78a7 commit fb226ff

File tree

5 files changed

+184
-22
lines changed

5 files changed

+184
-22
lines changed
 

‎doc/api/crypto.md

+16
Original file line numberDiff line numberDiff line change
@@ -3375,6 +3375,10 @@ generateKey('hmac', { length: 64 }, (err, key) => {
33753375
<!-- YAML
33763376
added: v10.12.0
33773377
changes:
3378+
- version: REPLACEME
3379+
pr-url: https://github.com/nodejs/node/pull/39927
3380+
description: Add ability to define `RSASSA-PSS-params` sequence parameters
3381+
for RSA-PSS keys pairs.
33783382
- version:
33793383
- v13.9.0
33803384
- v12.17.0
@@ -3400,6 +3404,10 @@ changes:
34003404
* `options`: {Object}
34013405
* `modulusLength`: {number} Key size in bits (RSA, DSA).
34023406
* `publicExponent`: {number} Public exponent (RSA). **Default:** `0x10001`.
3407+
* `hashAlgorithm`: {string} Name of the message digest (RSA-PSS).
3408+
* `mgf1HashAlgorithm`: {string} Name of the message digest used by
3409+
MGF1 (RSA-PSS).
3410+
* `saltLength`: {number} Minimal salt length in bytes (RSA-PSS).
34033411
* `divisorLength`: {number} Size of `q` in bits (DSA).
34043412
* `namedCurve`: {string} Name of the curve to use (EC).
34053413
* `prime`: {Buffer} The prime parameter (DH).
@@ -3478,6 +3486,10 @@ a `Promise` for an `Object` with `publicKey` and `privateKey` properties.
34783486
<!-- YAML
34793487
added: v10.12.0
34803488
changes:
3489+
- version: REPLACEME
3490+
pr-url: https://github.com/nodejs/node/pull/39927
3491+
description: Add ability to define `RSASSA-PSS-params` sequence parameters
3492+
for RSA-PSS keys pairs.
34813493
- version:
34823494
- v13.9.0
34833495
- v12.17.0
@@ -3503,6 +3515,10 @@ changes:
35033515
* `options`: {Object}
35043516
* `modulusLength`: {number} Key size in bits (RSA, DSA).
35053517
* `publicExponent`: {number} Public exponent (RSA). **Default:** `0x10001`.
3518+
* `hashAlgorithm`: {string} Name of the message digest (RSA-PSS).
3519+
* `mgf1HashAlgorithm`: {string} Name of the message digest used by
3520+
MGF1 (RSA-PSS).
3521+
* `saltLength`: {number} Minimal salt length in bytes (RSA-PSS).
35063522
* `divisorLength`: {number} Size of `q` in bits (DSA).
35073523
* `namedCurve`: {string} Name of the curve to use (EC).
35083524
* `prime`: {Buffer} The prime parameter (DH).

‎doc/api/deprecations.md

+13
Original file line numberDiff line numberDiff line change
@@ -2799,6 +2799,19 @@ non-number value for `hints` option, a non-nullish non-boolean value for `all`
27992799
option, or a non-nullish non-boolean value for `verbatim` option in
28002800
[`dns.lookup()`][] and [`dnsPromises.lookup()`][] is deprecated.
28012801

2802+
### DEP0154: RSA-PSS generate key pair options
2803+
<!-- YAML
2804+
changes:
2805+
- version: REPLACEME
2806+
pr-url: https://github.com/nodejs/node/pull/39927
2807+
description: Documentation-only deprecation.
2808+
-->
2809+
2810+
Type: Documentation-only (supports [`--pending-deprecation`][])
2811+
2812+
The `'hash'` and `'mgf1Hash'` options are replaced with `'hashAlgorithm'`
2813+
and `'mgf1HashAlgorithm'`.
2814+
28022815
[Legacy URL API]: url.md#url_legacy_url_api
28032816
[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
28042817
[RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3

‎lib/internal/crypto/keygen.js

+36-7
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ const {
6060

6161
const { isArrayBufferView } = require('internal/util/types');
6262

63+
const { getOptionValue } = require('internal/options');
64+
const pendingDeprecation = getOptionValue('--pending-deprecation');
65+
6366
function wrapKey(key, ctor) {
6467
if (typeof key === 'string' ||
6568
isArrayBufferView(key) ||
@@ -193,21 +196,47 @@ function createJob(mode, type, options) {
193196
...encoding);
194197
}
195198

196-
const { hash, mgf1Hash, saltLength } = options;
197-
if (hash !== undefined && typeof hash !== 'string')
198-
throw new ERR_INVALID_ARG_VALUE('options.hash', hash);
199-
if (mgf1Hash !== undefined && typeof mgf1Hash !== 'string')
200-
throw new ERR_INVALID_ARG_VALUE('options.mgf1Hash', mgf1Hash);
199+
const {
200+
hash, mgf1Hash, hashAlgorithm, mgf1HashAlgorithm, saltLength
201+
} = options;
201202
if (saltLength !== undefined && (!isInt32(saltLength) || saltLength < 0))
202203
throw new ERR_INVALID_ARG_VALUE('options.saltLength', saltLength);
204+
if (hashAlgorithm !== undefined && typeof hashAlgorithm !== 'string')
205+
throw new ERR_INVALID_ARG_VALUE('options.hashAlgorithm', hashAlgorithm);
206+
if (mgf1HashAlgorithm !== undefined &&
207+
typeof mgf1HashAlgorithm !== 'string')
208+
throw new ERR_INVALID_ARG_VALUE('options.mgf1HashAlgorithm',
209+
mgf1HashAlgorithm);
210+
if (hash !== undefined) {
211+
pendingDeprecation && process.emitWarning(
212+
'"options.hash" is deprecated, ' +
213+
'use "options.hashAlgorithm" instead.',
214+
'DeprecationWarning',
215+
'DEP0154');
216+
if (typeof hash !== 'string' ||
217+
(hashAlgorithm && hash !== hashAlgorithm)) {
218+
throw new ERR_INVALID_ARG_VALUE('options.hash', hash);
219+
}
220+
}
221+
if (mgf1Hash !== undefined) {
222+
pendingDeprecation && process.emitWarning(
223+
'"options.mgf1Hash" is deprecated, ' +
224+
'use "options.mgf1HashAlgorithm" instead.',
225+
'DeprecationWarning',
226+
'DEP0154');
227+
if (typeof mgf1Hash !== 'string' ||
228+
(mgf1HashAlgorithm && mgf1Hash !== mgf1HashAlgorithm)) {
229+
throw new ERR_INVALID_ARG_VALUE('options.mgf1Hash', mgf1Hash);
230+
}
231+
}
203232

204233
return new RsaKeyPairGenJob(
205234
mode,
206235
kKeyVariantRSA_PSS,
207236
modulusLength,
208237
publicExponent,
209-
hash,
210-
mgf1Hash,
238+
hashAlgorithm || hash,
239+
mgf1HashAlgorithm || mgf1Hash,
211240
saltLength,
212241
...encoding);
213242
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Flags: --pending-deprecation
2+
3+
'use strict';
4+
5+
const common = require('../common');
6+
if (!common.hasCrypto)
7+
common.skip('missing crypto');
8+
9+
const DeprecationWarning = [];
10+
DeprecationWarning.push([
11+
'"options.hash" is deprecated, use "options.hashAlgorithm" instead.',
12+
'DEP0154']);
13+
DeprecationWarning.push([
14+
'"options.mgf1Hash" is deprecated, use "options.mgf1HashAlgorithm" instead.',
15+
'DEP0154']);
16+
17+
common.expectWarning({ DeprecationWarning });
18+
19+
const assert = require('assert');
20+
const { generateKeyPair } = require('crypto');
21+
22+
{
23+
// This test makes sure deprecated options still work as intended
24+
25+
generateKeyPair('rsa-pss', {
26+
modulusLength: 512,
27+
saltLength: 16,
28+
hash: 'sha256',
29+
mgf1Hash: 'sha256'
30+
}, common.mustSucceed((publicKey, privateKey) => {
31+
assert.strictEqual(publicKey.type, 'public');
32+
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss');
33+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
34+
modulusLength: 512,
35+
publicExponent: 65537n,
36+
hashAlgorithm: 'sha256',
37+
mgf1HashAlgorithm: 'sha256',
38+
saltLength: 16
39+
});
40+
41+
assert.strictEqual(privateKey.type, 'private');
42+
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss');
43+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
44+
modulusLength: 512,
45+
publicExponent: 65537n,
46+
hashAlgorithm: 'sha256',
47+
mgf1HashAlgorithm: 'sha256',
48+
saltLength: 16
49+
});
50+
}));
51+
}

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

+68-15
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,8 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
302302
generateKeyPair('rsa-pss', {
303303
modulusLength: 512,
304304
saltLength: 16,
305-
hash: 'sha256',
306-
mgf1Hash: 'sha256'
305+
hashAlgorithm: 'sha256',
306+
mgf1HashAlgorithm: 'sha256'
307307
}, common.mustSucceed((publicKey, privateKey) => {
308308
assert.strictEqual(publicKey.type, 'public');
309309
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss');
@@ -1324,12 +1324,12 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
13241324
assert.throws(() => {
13251325
generateKeyPairSync('rsa-pss', {
13261326
modulusLength: 4096,
1327-
hash: hashValue
1327+
hashAlgorithm: hashValue
13281328
});
13291329
}, {
13301330
name: 'TypeError',
13311331
code: 'ERR_INVALID_ARG_VALUE',
1332-
message: "The property 'options.hash' is invalid. " +
1332+
message: "The property 'options.hashAlgorithm' is invalid. " +
13331333
`Received ${inspect(hashValue)}`
13341334
});
13351335
}
@@ -1339,8 +1339,8 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
13391339
generateKeyPair('rsa-pss', {
13401340
modulusLength: 512,
13411341
saltLength: 2147483648,
1342-
hash: 'sha256',
1343-
mgf1Hash: 'sha256'
1342+
hashAlgorithm: 'sha256',
1343+
mgf1HashAlgorithm: 'sha256'
13441344
}, common.mustNotCall());
13451345
}, {
13461346
name: 'TypeError',
@@ -1353,8 +1353,8 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
13531353
generateKeyPair('rsa-pss', {
13541354
modulusLength: 512,
13551355
saltLength: -1,
1356-
hash: 'sha256',
1357-
mgf1Hash: 'sha256'
1356+
hashAlgorithm: 'sha256',
1357+
mgf1HashAlgorithm: 'sha256'
13581358
}, common.mustNotCall());
13591359
}, {
13601360
name: 'TypeError',
@@ -1451,8 +1451,8 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
14511451
generateKeyPair('rsa-pss', {
14521452
modulusLength: 512,
14531453
saltLength: 16,
1454-
hash: 'sha256',
1455-
mgf1Hash: undefined
1454+
hashAlgorithm: 'sha256',
1455+
mgf1HashAlgorithm: undefined
14561456
});
14571457
},
14581458
{
@@ -1462,21 +1462,21 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
14621462
}
14631463
);
14641464

1465-
for (const mgf1Hash of [null, 0, false, {}, []]) {
1465+
for (const mgf1HashAlgorithm of [null, 0, false, {}, []]) {
14661466
assert.throws(
14671467
() => {
14681468
generateKeyPair('rsa-pss', {
14691469
modulusLength: 512,
14701470
saltLength: 16,
1471-
hash: 'sha256',
1472-
mgf1Hash
1471+
hashAlgorithm: 'sha256',
1472+
mgf1HashAlgorithm
14731473
}, common.mustNotCall());
14741474
},
14751475
{
14761476
name: 'TypeError',
14771477
code: 'ERR_INVALID_ARG_VALUE',
1478-
message: "The property 'options.mgf1Hash' is invalid. " +
1479-
`Received ${inspect(mgf1Hash)}`
1478+
message: "The property 'options.mgf1HashAlgorithm' is invalid. " +
1479+
`Received ${inspect(mgf1HashAlgorithm)}`
14801480

14811481
}
14821482
);
@@ -1568,3 +1568,56 @@ if (!common.hasOpenSSL3) {
15681568
}
15691569
}
15701570
}
1571+
1572+
{
1573+
// This test makes sure deprecated and new options may be used
1574+
// simultaneously so long as they're identical values.
1575+
1576+
generateKeyPair('rsa-pss', {
1577+
modulusLength: 512,
1578+
saltLength: 16,
1579+
hash: 'sha256',
1580+
hashAlgorithm: 'sha256',
1581+
mgf1Hash: 'sha256',
1582+
mgf1HashAlgorithm: 'sha256'
1583+
}, common.mustSucceed((publicKey, privateKey) => {
1584+
assert.strictEqual(publicKey.type, 'public');
1585+
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss');
1586+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
1587+
modulusLength: 512,
1588+
publicExponent: 65537n,
1589+
hashAlgorithm: 'sha256',
1590+
mgf1HashAlgorithm: 'sha256',
1591+
saltLength: 16
1592+
});
1593+
1594+
assert.strictEqual(privateKey.type, 'private');
1595+
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss');
1596+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
1597+
modulusLength: 512,
1598+
publicExponent: 65537n,
1599+
hashAlgorithm: 'sha256',
1600+
mgf1HashAlgorithm: 'sha256',
1601+
saltLength: 16
1602+
});
1603+
}));
1604+
}
1605+
1606+
{
1607+
// This test makes sure deprecated and new options must
1608+
// be the same value.
1609+
1610+
assert.throws(() => generateKeyPair('rsa-pss', {
1611+
modulusLength: 512,
1612+
saltLength: 16,
1613+
mgf1Hash: 'sha256',
1614+
mgf1HashAlgorithm: 'sha1'
1615+
}, common.mustNotCall()), { code: 'ERR_INVALID_ARG_VALUE' });
1616+
1617+
assert.throws(() => generateKeyPair('rsa-pss', {
1618+
modulusLength: 512,
1619+
saltLength: 16,
1620+
hash: 'sha256',
1621+
hashAlgorithm: 'sha1'
1622+
}, common.mustNotCall()), { code: 'ERR_INVALID_ARG_VALUE' });
1623+
}

0 commit comments

Comments
 (0)
Please sign in to comment.