crypto: add support for EdDSA key pair generation
PR-URL: https://github.com/nodejs/node/pull/26554 Refs: https://github.com/nodejs/node/pull/26319 Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
This commit is contained in:
parent
1a6fb71f71
commit
3a9592496c
@ -1903,12 +1903,15 @@ algorithm names.
|
||||
<!-- YAML
|
||||
added: v10.12.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/26554
|
||||
description: Add ability to generate Ed25519 and Ed448 key pairs.
|
||||
- version: v11.6.0
|
||||
pr-url: https://github.com/nodejs/node/pull/24234
|
||||
description: The `generateKeyPair` and `generateKeyPairSync` functions now
|
||||
produce key objects if no encoding was specified.
|
||||
-->
|
||||
* `type`: {string} Must be `'rsa'`, `'dsa'` or `'ec'`.
|
||||
* `type`: {string} Must be `'rsa'`, `'dsa'`, `'ec'`, `'ed25519'`, or `'ed448'`.
|
||||
* `options`: {Object}
|
||||
- `modulusLength`: {number} Key size in bits (RSA, DSA).
|
||||
- `publicExponent`: {number} Public exponent (RSA). **Default:** `0x10001`.
|
||||
@ -1921,8 +1924,8 @@ changes:
|
||||
- `publicKey`: {string | Buffer | KeyObject}
|
||||
- `privateKey`: {string | Buffer | KeyObject}
|
||||
|
||||
Generates a new asymmetric key pair of the given `type`. Only RSA, DSA and EC
|
||||
are currently supported.
|
||||
Generates a new asymmetric key pair of the given `type`. RSA, DSA, EC, Ed25519
|
||||
and Ed448 are currently supported.
|
||||
|
||||
If a `publicKeyEncoding` or `privateKeyEncoding` was specified, this function
|
||||
behaves as if [`keyObject.export()`][] had been called on its result. Otherwise,
|
||||
@ -1960,12 +1963,15 @@ a `Promise` for an `Object` with `publicKey` and `privateKey` properties.
|
||||
<!-- YAML
|
||||
added: v10.12.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/26554
|
||||
description: Add ability to generate Ed25519 and Ed448 key pairs.
|
||||
- version: v11.6.0
|
||||
pr-url: https://github.com/nodejs/node/pull/24234
|
||||
description: The `generateKeyPair` and `generateKeyPairSync` functions now
|
||||
produce key objects if no encoding was specified.
|
||||
-->
|
||||
* `type`: {string} Must be `'rsa'`, `'dsa'` or `'ec'`.
|
||||
* `type`: {string} Must be `'rsa'`, `'dsa'`, `'ec'`, `'ed25519'`, or `'ed448'`.
|
||||
* `options`: {Object}
|
||||
- `modulusLength`: {number} Key size in bits (RSA, DSA).
|
||||
- `publicExponent`: {number} Public exponent (RSA). **Default:** `0x10001`.
|
||||
@ -1977,8 +1983,8 @@ changes:
|
||||
- `publicKey`: {string | Buffer | KeyObject}
|
||||
- `privateKey`: {string | Buffer | KeyObject}
|
||||
|
||||
Generates a new asymmetric key pair of the given `type`. Only RSA, DSA and EC
|
||||
are currently supported.
|
||||
Generates a new asymmetric key pair of the given `type`. RSA, DSA, EC, Ed25519
|
||||
and Ed448 are currently supported.
|
||||
|
||||
If a `publicKeyEncoding` or `privateKeyEncoding` was specified, this function
|
||||
behaves as if [`keyObject.export()`][] had been called on its result. Otherwise,
|
||||
|
@ -5,6 +5,9 @@ const {
|
||||
generateKeyPairRSA,
|
||||
generateKeyPairDSA,
|
||||
generateKeyPairEC,
|
||||
generateKeyPairEdDSA,
|
||||
EVP_PKEY_ED25519,
|
||||
EVP_PKEY_ED448,
|
||||
OPENSSL_EC_NAMED_CURVE,
|
||||
OPENSSL_EC_EXPLICIT_CURVE
|
||||
} = internalBinding('crypto');
|
||||
@ -119,18 +122,25 @@ function parseKeyEncoding(keyType, options) {
|
||||
|
||||
function check(type, options, callback) {
|
||||
validateString(type, 'type');
|
||||
if (options == null || typeof options !== 'object')
|
||||
throw new ERR_INVALID_ARG_TYPE('options', 'object', options);
|
||||
|
||||
// These will be set after parsing the type and type-specific options to make
|
||||
// the order a bit more intuitive.
|
||||
let cipher, passphrase, publicType, publicFormat, privateType, privateFormat;
|
||||
|
||||
if (options !== undefined && typeof options !== 'object')
|
||||
throw new ERR_INVALID_ARG_TYPE('options', 'object', options);
|
||||
|
||||
function needOptions() {
|
||||
if (options == null)
|
||||
throw new ERR_INVALID_ARG_TYPE('options', 'object', options);
|
||||
return options;
|
||||
}
|
||||
|
||||
let impl;
|
||||
switch (type) {
|
||||
case 'rsa':
|
||||
{
|
||||
const { modulusLength } = options;
|
||||
const { modulusLength } = needOptions();
|
||||
if (!isUint32(modulusLength))
|
||||
throw new ERR_INVALID_OPT_VALUE('modulusLength', modulusLength);
|
||||
|
||||
@ -149,7 +159,7 @@ function check(type, options, callback) {
|
||||
break;
|
||||
case 'dsa':
|
||||
{
|
||||
const { modulusLength } = options;
|
||||
const { modulusLength } = needOptions();
|
||||
if (!isUint32(modulusLength))
|
||||
throw new ERR_INVALID_OPT_VALUE('modulusLength', modulusLength);
|
||||
|
||||
@ -168,7 +178,7 @@ function check(type, options, callback) {
|
||||
break;
|
||||
case 'ec':
|
||||
{
|
||||
const { namedCurve } = options;
|
||||
const { namedCurve } = needOptions();
|
||||
if (typeof namedCurve !== 'string')
|
||||
throw new ERR_INVALID_OPT_VALUE('namedCurve', namedCurve);
|
||||
let { paramEncoding } = options;
|
||||
@ -185,19 +195,32 @@ function check(type, options, callback) {
|
||||
cipher, passphrase, wrap);
|
||||
}
|
||||
break;
|
||||
case 'ed25519':
|
||||
case 'ed448':
|
||||
{
|
||||
const id = type === 'ed25519' ? EVP_PKEY_ED25519 : EVP_PKEY_ED448;
|
||||
impl = (wrap) => generateKeyPairEdDSA(id,
|
||||
publicFormat, publicType,
|
||||
privateFormat, privateType,
|
||||
cipher, passphrase, wrap);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ERR_INVALID_ARG_VALUE('type', type,
|
||||
"must be one of 'rsa', 'dsa', 'ec'");
|
||||
"must be one of 'rsa', 'dsa', 'ec', " +
|
||||
"'ed25519', 'ed448'");
|
||||
}
|
||||
|
||||
({
|
||||
cipher,
|
||||
passphrase,
|
||||
publicType,
|
||||
publicFormat,
|
||||
privateType,
|
||||
privateFormat
|
||||
} = parseKeyEncoding(type, options));
|
||||
if (options) {
|
||||
({
|
||||
cipher,
|
||||
passphrase,
|
||||
publicType,
|
||||
publicFormat,
|
||||
privateType,
|
||||
privateFormat
|
||||
} = parseKeyEncoding(type, options));
|
||||
}
|
||||
|
||||
return impl;
|
||||
}
|
||||
|
@ -5739,6 +5739,18 @@ class ECKeyPairGenerationConfig : public KeyPairGenerationConfig {
|
||||
const int param_encoding_;
|
||||
};
|
||||
|
||||
class EdDSAKeyPairGenerationConfig : public KeyPairGenerationConfig {
|
||||
public:
|
||||
explicit EdDSAKeyPairGenerationConfig(int id) : id_(id) {}
|
||||
|
||||
EVPKeyCtxPointer Setup() override {
|
||||
return EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(id_, nullptr));
|
||||
}
|
||||
|
||||
private:
|
||||
const int id_;
|
||||
};
|
||||
|
||||
class GenerateKeyPairJob : public CryptoJob {
|
||||
public:
|
||||
GenerateKeyPairJob(Environment* env,
|
||||
@ -5912,6 +5924,14 @@ void GenerateKeyPairEC(const FunctionCallbackInfo<Value>& args) {
|
||||
GenerateKeyPair(args, 2, std::move(config));
|
||||
}
|
||||
|
||||
void GenerateKeyPairEdDSA(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK(args[0]->IsInt32());
|
||||
const int id = args[0].As<Int32>()->Value();
|
||||
std::unique_ptr<KeyPairGenerationConfig> config(
|
||||
new EdDSAKeyPairGenerationConfig(id));
|
||||
GenerateKeyPair(args, 1, std::move(config));
|
||||
}
|
||||
|
||||
|
||||
void GetSSLCiphers(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
@ -6313,6 +6333,9 @@ void Initialize(Local<Object> target,
|
||||
env->SetMethod(target, "generateKeyPairRSA", GenerateKeyPairRSA);
|
||||
env->SetMethod(target, "generateKeyPairDSA", GenerateKeyPairDSA);
|
||||
env->SetMethod(target, "generateKeyPairEC", GenerateKeyPairEC);
|
||||
env->SetMethod(target, "generateKeyPairEdDSA", GenerateKeyPairEdDSA);
|
||||
NODE_DEFINE_CONSTANT(target, EVP_PKEY_ED25519);
|
||||
NODE_DEFINE_CONSTANT(target, EVP_PKEY_ED448);
|
||||
NODE_DEFINE_CONSTANT(target, OPENSSL_EC_NAMED_CURVE);
|
||||
NODE_DEFINE_CONSTANT(target, OPENSSL_EC_EXPLICIT_CURVE);
|
||||
NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS1);
|
||||
|
@ -425,7 +425,7 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
|
||||
type: TypeError,
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
message: "The argument 'type' must be one of " +
|
||||
"'rsa', 'dsa', 'ec'. Received 'rsa2'"
|
||||
"'rsa', 'dsa', 'ec', 'ed25519', 'ed448'. Received 'rsa2'"
|
||||
});
|
||||
}
|
||||
|
||||
@ -437,6 +437,15 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
|
||||
message: 'The "options" argument must be of ' +
|
||||
'type object. Received type undefined'
|
||||
});
|
||||
|
||||
// Even if no options are required, it should be impossible to pass anything
|
||||
// but an object (or undefined).
|
||||
common.expectsError(() => generateKeyPair('ed448', 0, common.mustNotCall()), {
|
||||
type: TypeError,
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: 'The "options" argument must be of ' +
|
||||
'type object. Received type number'
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
@ -774,6 +783,23 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
|
||||
}));
|
||||
}
|
||||
|
||||
// Test EdDSA key generation.
|
||||
{
|
||||
if (!/^1\.1\.0/.test(process.versions.openssl)) {
|
||||
['ed25519', 'ed448'].forEach((keyType) => {
|
||||
generateKeyPair(keyType, common.mustCall((err, publicKey, privateKey) => {
|
||||
assert.ifError(err);
|
||||
|
||||
assert.strictEqual(publicKey.type, 'public');
|
||||
assert.strictEqual(publicKey.asymmetricKeyType, keyType);
|
||||
|
||||
assert.strictEqual(privateKey.type, 'private');
|
||||
assert.strictEqual(privateKey.asymmetricKeyType, keyType);
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Test invalid key encoding types.
|
||||
{
|
||||
// Invalid public key type.
|
||||
|
Loading…
x
Reference in New Issue
Block a user