Skip to content

Commit 2efd015

Browse files
committed
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
1 parent 6a7eb32 commit 2efd015

File tree

5 files changed

+98
-6
lines changed

5 files changed

+98
-6
lines changed

doc/api/crypto.md

+21
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,27 @@ 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+
1294+
This property exists only on asymmetric keys. Depending on the type of the key,
1295+
this object contains information about the key. None of the information obtained
1296+
through this property can be used to uniquely identify a key or to compromise
1297+
the security of the key.
1298+
1299+
For `'rsa'` and `'rsa-pss'` keys, this object has the properties `modulusLength`
1300+
and `publicExponent`.
1301+
1302+
For `'dsa'` keys, this object has the properties `modulusLength` and
1303+
`divisorLength`.
1304+
1305+
For `'ec'` keys with a known curve, this object has the string property
1306+
`namedCurve`.
1307+
12871308
### `keyObject.asymmetricKeyType`
12881309
<!-- YAML
12891310
added: v11.6.0

lib/internal/crypto/keys.js

+23
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+
bigIntArrayToUnsignedInt,
3941
} = require('internal/crypto/util');
4042

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

130132
const kAsymmetricKeyType = Symbol('kAsymmetricKeyType');
133+
const kAsymmetricKeyDetails = Symbol('kAsymmetricKeyDetails');
134+
135+
// TODO: should the returne details object be frozen or otherwise protected
136+
// from manipulation?
137+
function normalizeKeyDetails(details = {}) {
138+
if ('publicExponent' in details) {
139+
return {
140+
...details,
141+
publicExponent:
142+
bigIntArrayToUnsignedInt(new Uint8Array(details.publicExponent))
143+
};
144+
}
145+
return details;
146+
}
131147

132148
class AsymmetricKeyObject extends KeyObject {
133149
get asymmetricKeyType() {
134150
return this[kAsymmetricKeyType] ||
135151
(this[kAsymmetricKeyType] = this[kHandle].getAsymmetricKeyType());
136152
}
153+
154+
get asymmetricKeyDetails() {
155+
return this[kAsymmetricKeyDetails] ||
156+
(this[kAsymmetricKeyDetails] = normalizeKeyDetails(
157+
this[kHandle].keyDetail({})
158+
));
159+
}
137160
}
138161

139162
class PublicKeyObject extends AsymmetricKeyObject {

src/crypto/crypto_keys.cc

+1-2
Original file line numberDiff line numberDiff line change
@@ -543,8 +543,7 @@ Maybe<bool> GetAsymmetricKeyDetail(
543543
case EVP_PKEY_EC: return GetEcKeyDetail(env, key, target);
544544
case EVP_PKEY_DH: return GetDhKeyDetail(env, key, target);
545545
}
546-
THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
547-
return Nothing<bool>();
546+
return Just(false);
548547
}
549548
} // namespace
550549

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

+52-4
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,18 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
123123
assert.strictEqual(typeof publicKey, 'object');
124124
assert.strictEqual(publicKey.type, 'public');
125125
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa');
126+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
127+
modulusLength: 512,
128+
publicExponent: 65537
129+
});
126130

127131
assert.strictEqual(typeof privateKey, 'object');
128132
assert.strictEqual(privateKey.type, 'private');
129133
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa');
134+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
135+
modulusLength: 512,
136+
publicExponent: 65537
137+
});
130138
}
131139

132140
{
@@ -268,9 +276,17 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
268276
}, common.mustSucceed((publicKey, privateKey) => {
269277
assert.strictEqual(publicKey.type, 'public');
270278
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss');
279+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
280+
modulusLength: 512,
281+
publicExponent: 65537
282+
});
271283

272284
assert.strictEqual(privateKey.type, 'private');
273285
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss');
286+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
287+
modulusLength: 512,
288+
publicExponent: 65537
289+
});
274290

275291
// Unlike RSA, RSA-PSS does not allow encryption.
276292
assert.throws(() => {
@@ -342,6 +358,28 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
342358
}));
343359
}
344360

361+
{
362+
// Test async DSA key object generation.
363+
generateKeyPair('dsa', {
364+
modulusLength: 512,
365+
divisorLength: 256
366+
}, common.mustSucceed((publicKey, privateKey) => {
367+
assert.strictEqual(publicKey.type, 'public');
368+
assert.strictEqual(publicKey.asymmetricKeyType, 'dsa');
369+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
370+
modulusLength: 512,
371+
divisorLength: 256
372+
});
373+
374+
assert.strictEqual(privateKey.type, 'private');
375+
assert.strictEqual(privateKey.asymmetricKeyType, 'dsa');
376+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
377+
modulusLength: 512,
378+
divisorLength: 256
379+
});
380+
}));
381+
}
382+
345383
{
346384
// Test async elliptic curve key generation, e.g. for ECDSA, with a SEC1
347385
// private key.
@@ -925,16 +963,24 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
925963
// It should recognize both NIST and standard curve names.
926964
generateKeyPair('ec', {
927965
namedCurve: 'P-256',
928-
publicKeyEncoding: { type: 'spki', format: 'pem' },
929-
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
930966
}, common.mustSucceed((publicKey, privateKey) => {
967+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
968+
namedCurve: 'prime256v1'
969+
});
970+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
971+
namedCurve: 'prime256v1'
972+
});
931973
}));
932974

933975
generateKeyPair('ec', {
934976
namedCurve: 'secp256k1',
935-
publicKeyEncoding: { type: 'spki', format: 'pem' },
936-
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
937977
}, common.mustSucceed((publicKey, privateKey) => {
978+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {
979+
namedCurve: 'secp256k1'
980+
});
981+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {
982+
namedCurve: 'secp256k1'
983+
});
938984
}));
939985
}
940986

@@ -945,9 +991,11 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
945991
generateKeyPair(keyType, common.mustSucceed((publicKey, privateKey) => {
946992
assert.strictEqual(publicKey.type, 'public');
947993
assert.strictEqual(publicKey.asymmetricKeyType, keyType);
994+
assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {});
948995

949996
assert.strictEqual(privateKey.type, 'private');
950997
assert.strictEqual(privateKey.asymmetricKeyType, keyType);
998+
assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {});
951999
}));
9521000
});
9531001
}

0 commit comments

Comments
 (0)