Skip to content

Commit 1149af6

Browse files
panvaruyadorno
authored andcommittedJan 21, 2021
crypto: add keyObject.asymmetricKeyDetails for asymmetric keys
This API exposes key details. It is conceptually different from the previously discussed keyObject.fields property since it does not give access to information that could compromise the security of the key, and the obtained information cannot be used to uniquely identify a key. The intended purpose is to determine "security properties" of keys, e.g. to generate a new key pair with the same parameters, or to decide whether a key is secure enough. closes #30045 PR-URL: #36188 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent 82eccdd commit 1149af6

File tree

5 files changed

+139
-4
lines changed

5 files changed

+139
-4
lines changed
 

‎doc/api/crypto.md

+19
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,25 @@ passing keys as strings or `Buffer`s due to improved security features.
12841284
The receiver obtains a cloned `KeyObject`, and the `KeyObject` does not need to
12851285
be listed in the `transferList` argument.
12861286

1287+
### `keyObject.asymmetricKeyDetails`
1288+
<!-- YAML
1289+
added: REPLACEME
1290+
-->
1291+
1292+
* {Object}
1293+
* `modulusLength`: {number} Key size in bits (RSA, DSA).
1294+
* `publicExponent`: {bigint} Public exponent (RSA).
1295+
* `divisorLength`: {number} Size of `q` in bits (DSA).
1296+
* `namedCurve`: {string} Name of the curve (EC).
1297+
1298+
This property exists only on asymmetric keys. Depending on the type of the key,
1299+
this object contains information about the key. None of the information obtained
1300+
through this property can be used to uniquely identify a key or to compromise
1301+
the security of the key.
1302+
1303+
RSA-PSS parameters, DH, or any future key type details might be exposed via this
1304+
API using additional attributes.
1305+
12871306
### `keyObject.asymmetricKeyType`
12881307
<!-- YAML
12891308
added: v11.6.0

‎lib/internal/crypto/keys.js

+29
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const {
55
ObjectDefineProperty,
66
ObjectSetPrototypeOf,
77
Symbol,
8+
Uint8Array,
89
} = primordials;
910

1011
const {
@@ -36,6 +37,7 @@ const {
3637
kHandle,
3738
kKeyObject,
3839
getArrayBufferOrView,
40+
bigIntArrayToUnsignedBigInt,
3941
} = require('internal/crypto/util');
4042

4143
const {
@@ -128,12 +130,39 @@ const [
128130
}
129131

130132
const kAsymmetricKeyType = Symbol('kAsymmetricKeyType');
133+
const kAsymmetricKeyDetails = Symbol('kAsymmetricKeyDetails');
134+
135+
function normalizeKeyDetails(details = {}) {
136+
if (details.publicExponent !== undefined) {
137+
return {
138+
...details,
139+
publicExponent:
140+
bigIntArrayToUnsignedBigInt(new Uint8Array(details.publicExponent))
141+
};
142+
}
143+
return details;
144+
}
131145

132146
class AsymmetricKeyObject extends KeyObject {
133147
get asymmetricKeyType() {
134148
return this[kAsymmetricKeyType] ||
135149
(this[kAsymmetricKeyType] = this[kHandle].getAsymmetricKeyType());
136150
}
151+
152+
get asymmetricKeyDetails() {
153+
switch (this.asymmetricKeyType) {
154+
case 'rsa':
155+
case 'rsa-pss':
156+
case 'dsa':
157+
case 'ec':
158+
return this[kAsymmetricKeyDetails] ||
159+
(this[kAsymmetricKeyDetails] = normalizeKeyDetails(
160+
this[kHandle].keyDetail({})
161+
));
162+
default:
163+
return {};
164+
}
165+
}
137166
}
138167

139168
class PublicKeyObject extends AsymmetricKeyObject {

‎lib/internal/crypto/util.js

+13
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const {
44
ArrayPrototypeIncludes,
55
ArrayPrototypePush,
6+
BigInt,
67
FunctionPrototypeBind,
78
Number,
89
Promise,
@@ -308,6 +309,17 @@ function bigIntArrayToUnsignedInt(input) {
308309
return result;
309310
}
310311

312+
function bigIntArrayToUnsignedBigInt(input) {
313+
let result = 0n;
314+
315+
for (let n = 0; n < input.length; ++n) {
316+
const n_reversed = input.length - n - 1;
317+
result |= BigInt(input[n]) << 8n * BigInt(n_reversed);
318+
}
319+
320+
return result;
321+
}
322+
311323
function getStringOption(options, key) {
312324
let value;
313325
if (options && (value = options[key]) != null)
@@ -413,6 +425,7 @@ module.exports = {
413425
jobPromise,
414426
lazyRequire,
415427
validateMaxBufferLength,
428+
bigIntArrayToUnsignedBigInt,
416429
bigIntArrayToUnsignedInt,
417430
getStringOption,
418431
getUsagesUnion,

‎test/parallel/test-crypto-key-objects.js

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
7070
assert.strictEqual(key.type, 'secret');
7171
assert.strictEqual(key.symmetricKeySize, 32);
7272
assert.strictEqual(key.asymmetricKeyType, undefined);
73+
assert.strictEqual(key.asymmetricKeyDetails, undefined);
7374

7475
const exportedKey = key.export();
7576
assert(keybuf.equals(exportedKey));

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

+77-4
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,31 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
114114
testSignVerify(publicKey, privateKey);
115115
}
116116

117+
{
118+
// Test sync key generation with key objects with a non-standard
119+
// publicExpononent
120+
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
121+
publicExponent: 3,
122+
modulusLength: 512
123+
});
124+
125+
assert.strictEqual(typeof publicKey, 'object');
126+
assert.strictEqual(publicKey.type, 'public');
127+
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa');
128+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
129+
modulusLength: 512,
130+
publicExponent: 3n
131+
});
132+
133+
assert.strictEqual(typeof privateKey, 'object');
134+
assert.strictEqual(privateKey.type, 'private');
135+
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa');
136+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
137+
modulusLength: 512,
138+
publicExponent: 3n
139+
});
140+
}
141+
117142
{
118143
// Test sync key generation with key objects.
119144
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
@@ -123,10 +148,18 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
123148
assert.strictEqual(typeof publicKey, 'object');
124149
assert.strictEqual(publicKey.type, 'public');
125150
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa');
151+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
152+
modulusLength: 512,
153+
publicExponent: 65537n
154+
});
126155

127156
assert.strictEqual(typeof privateKey, 'object');
128157
assert.strictEqual(privateKey.type, 'private');
129158
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa');
159+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
160+
modulusLength: 512,
161+
publicExponent: 65537n
162+
});
130163
}
131164

132165
{
@@ -268,9 +301,17 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
268301
}, common.mustSucceed((publicKey, privateKey) => {
269302
assert.strictEqual(publicKey.type, 'public');
270303
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss');
304+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
305+
modulusLength: 512,
306+
publicExponent: 65537n
307+
});
271308

272309
assert.strictEqual(privateKey.type, 'private');
273310
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss');
311+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
312+
modulusLength: 512,
313+
publicExponent: 65537n
314+
});
274315

275316
// Unlike RSA, RSA-PSS does not allow encryption.
276317
assert.throws(() => {
@@ -342,6 +383,28 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
342383
}));
343384
}
344385

386+
{
387+
// Test async DSA key object generation.
388+
generateKeyPair('dsa', {
389+
modulusLength: 512,
390+
divisorLength: 256
391+
}, common.mustSucceed((publicKey, privateKey) => {
392+
assert.strictEqual(publicKey.type, 'public');
393+
assert.strictEqual(publicKey.asymmetricKeyType, 'dsa');
394+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
395+
modulusLength: 512,
396+
divisorLength: 256
397+
});
398+
399+
assert.strictEqual(privateKey.type, 'private');
400+
assert.strictEqual(privateKey.asymmetricKeyType, 'dsa');
401+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
402+
modulusLength: 512,
403+
divisorLength: 256
404+
});
405+
}));
406+
}
407+
345408
{
346409
// Test async elliptic curve key generation, e.g. for ECDSA, with a SEC1
347410
// private key.
@@ -925,16 +988,24 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
925988
// It should recognize both NIST and standard curve names.
926989
generateKeyPair('ec', {
927990
namedCurve: 'P-256',
928-
publicKeyEncoding: { type: 'spki', format: 'pem' },
929-
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
930991
}, common.mustSucceed((publicKey, privateKey) => {
992+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
993+
namedCurve: 'prime256v1'
994+
});
995+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
996+
namedCurve: 'prime256v1'
997+
});
931998
}));
932999

9331000
generateKeyPair('ec', {
9341001
namedCurve: 'secp256k1',
935-
publicKeyEncoding: { type: 'spki', format: 'pem' },
936-
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
9371002
}, common.mustSucceed((publicKey, privateKey) => {
1003+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
1004+
namedCurve: 'secp256k1'
1005+
});
1006+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
1007+
namedCurve: 'secp256k1'
1008+
});
9381009
}));
9391010
}
9401011

@@ -945,9 +1016,11 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
9451016
generateKeyPair(keyType, common.mustSucceed((publicKey, privateKey) => {
9461017
assert.strictEqual(publicKey.type, 'public');
9471018
assert.strictEqual(publicKey.asymmetricKeyType, keyType);
1019+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {});
9481020

9491021
assert.strictEqual(privateKey.type, 'private');
9501022
assert.strictEqual(privateKey.asymmetricKeyType, keyType);
1023+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {});
9511024
}));
9521025
});
9531026
}

0 commit comments

Comments
 (0)
Please sign in to comment.