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
|
<!-- YAML
|
||||||
added: v10.12.0
|
added: v10.12.0
|
||||||
changes:
|
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
|
- version: v11.6.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/24234
|
pr-url: https://github.com/nodejs/node/pull/24234
|
||||||
description: The `generateKeyPair` and `generateKeyPairSync` functions now
|
description: The `generateKeyPair` and `generateKeyPairSync` functions now
|
||||||
produce key objects if no encoding was specified.
|
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}
|
* `options`: {Object}
|
||||||
- `modulusLength`: {number} Key size in bits (RSA, DSA).
|
- `modulusLength`: {number} Key size in bits (RSA, DSA).
|
||||||
- `publicExponent`: {number} Public exponent (RSA). **Default:** `0x10001`.
|
- `publicExponent`: {number} Public exponent (RSA). **Default:** `0x10001`.
|
||||||
@ -1921,8 +1924,8 @@ changes:
|
|||||||
- `publicKey`: {string | Buffer | KeyObject}
|
- `publicKey`: {string | Buffer | KeyObject}
|
||||||
- `privateKey`: {string | Buffer | KeyObject}
|
- `privateKey`: {string | Buffer | KeyObject}
|
||||||
|
|
||||||
Generates a new asymmetric key pair of the given `type`. Only RSA, DSA and EC
|
Generates a new asymmetric key pair of the given `type`. RSA, DSA, EC, Ed25519
|
||||||
are currently supported.
|
and Ed448 are currently supported.
|
||||||
|
|
||||||
If a `publicKeyEncoding` or `privateKeyEncoding` was specified, this function
|
If a `publicKeyEncoding` or `privateKeyEncoding` was specified, this function
|
||||||
behaves as if [`keyObject.export()`][] had been called on its result. Otherwise,
|
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
|
<!-- YAML
|
||||||
added: v10.12.0
|
added: v10.12.0
|
||||||
changes:
|
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
|
- version: v11.6.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/24234
|
pr-url: https://github.com/nodejs/node/pull/24234
|
||||||
description: The `generateKeyPair` and `generateKeyPairSync` functions now
|
description: The `generateKeyPair` and `generateKeyPairSync` functions now
|
||||||
produce key objects if no encoding was specified.
|
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}
|
* `options`: {Object}
|
||||||
- `modulusLength`: {number} Key size in bits (RSA, DSA).
|
- `modulusLength`: {number} Key size in bits (RSA, DSA).
|
||||||
- `publicExponent`: {number} Public exponent (RSA). **Default:** `0x10001`.
|
- `publicExponent`: {number} Public exponent (RSA). **Default:** `0x10001`.
|
||||||
@ -1977,8 +1983,8 @@ changes:
|
|||||||
- `publicKey`: {string | Buffer | KeyObject}
|
- `publicKey`: {string | Buffer | KeyObject}
|
||||||
- `privateKey`: {string | Buffer | KeyObject}
|
- `privateKey`: {string | Buffer | KeyObject}
|
||||||
|
|
||||||
Generates a new asymmetric key pair of the given `type`. Only RSA, DSA and EC
|
Generates a new asymmetric key pair of the given `type`. RSA, DSA, EC, Ed25519
|
||||||
are currently supported.
|
and Ed448 are currently supported.
|
||||||
|
|
||||||
If a `publicKeyEncoding` or `privateKeyEncoding` was specified, this function
|
If a `publicKeyEncoding` or `privateKeyEncoding` was specified, this function
|
||||||
behaves as if [`keyObject.export()`][] had been called on its result. Otherwise,
|
behaves as if [`keyObject.export()`][] had been called on its result. Otherwise,
|
||||||
|
@ -5,6 +5,9 @@ const {
|
|||||||
generateKeyPairRSA,
|
generateKeyPairRSA,
|
||||||
generateKeyPairDSA,
|
generateKeyPairDSA,
|
||||||
generateKeyPairEC,
|
generateKeyPairEC,
|
||||||
|
generateKeyPairEdDSA,
|
||||||
|
EVP_PKEY_ED25519,
|
||||||
|
EVP_PKEY_ED448,
|
||||||
OPENSSL_EC_NAMED_CURVE,
|
OPENSSL_EC_NAMED_CURVE,
|
||||||
OPENSSL_EC_EXPLICIT_CURVE
|
OPENSSL_EC_EXPLICIT_CURVE
|
||||||
} = internalBinding('crypto');
|
} = internalBinding('crypto');
|
||||||
@ -119,18 +122,25 @@ function parseKeyEncoding(keyType, options) {
|
|||||||
|
|
||||||
function check(type, options, callback) {
|
function check(type, options, callback) {
|
||||||
validateString(type, 'type');
|
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
|
// These will be set after parsing the type and type-specific options to make
|
||||||
// the order a bit more intuitive.
|
// the order a bit more intuitive.
|
||||||
let cipher, passphrase, publicType, publicFormat, privateType, privateFormat;
|
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;
|
let impl;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'rsa':
|
case 'rsa':
|
||||||
{
|
{
|
||||||
const { modulusLength } = options;
|
const { modulusLength } = needOptions();
|
||||||
if (!isUint32(modulusLength))
|
if (!isUint32(modulusLength))
|
||||||
throw new ERR_INVALID_OPT_VALUE('modulusLength', modulusLength);
|
throw new ERR_INVALID_OPT_VALUE('modulusLength', modulusLength);
|
||||||
|
|
||||||
@ -149,7 +159,7 @@ function check(type, options, callback) {
|
|||||||
break;
|
break;
|
||||||
case 'dsa':
|
case 'dsa':
|
||||||
{
|
{
|
||||||
const { modulusLength } = options;
|
const { modulusLength } = needOptions();
|
||||||
if (!isUint32(modulusLength))
|
if (!isUint32(modulusLength))
|
||||||
throw new ERR_INVALID_OPT_VALUE('modulusLength', modulusLength);
|
throw new ERR_INVALID_OPT_VALUE('modulusLength', modulusLength);
|
||||||
|
|
||||||
@ -168,7 +178,7 @@ function check(type, options, callback) {
|
|||||||
break;
|
break;
|
||||||
case 'ec':
|
case 'ec':
|
||||||
{
|
{
|
||||||
const { namedCurve } = options;
|
const { namedCurve } = needOptions();
|
||||||
if (typeof namedCurve !== 'string')
|
if (typeof namedCurve !== 'string')
|
||||||
throw new ERR_INVALID_OPT_VALUE('namedCurve', namedCurve);
|
throw new ERR_INVALID_OPT_VALUE('namedCurve', namedCurve);
|
||||||
let { paramEncoding } = options;
|
let { paramEncoding } = options;
|
||||||
@ -185,19 +195,32 @@ function check(type, options, callback) {
|
|||||||
cipher, passphrase, wrap);
|
cipher, passphrase, wrap);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
throw new ERR_INVALID_ARG_VALUE('type', type,
|
throw new ERR_INVALID_ARG_VALUE('type', type,
|
||||||
"must be one of 'rsa', 'dsa', 'ec'");
|
"must be one of 'rsa', 'dsa', 'ec', " +
|
||||||
|
"'ed25519', 'ed448'");
|
||||||
}
|
}
|
||||||
|
|
||||||
({
|
if (options) {
|
||||||
cipher,
|
({
|
||||||
passphrase,
|
cipher,
|
||||||
publicType,
|
passphrase,
|
||||||
publicFormat,
|
publicType,
|
||||||
privateType,
|
publicFormat,
|
||||||
privateFormat
|
privateType,
|
||||||
} = parseKeyEncoding(type, options));
|
privateFormat
|
||||||
|
} = parseKeyEncoding(type, options));
|
||||||
|
}
|
||||||
|
|
||||||
return impl;
|
return impl;
|
||||||
}
|
}
|
||||||
|
@ -5739,6 +5739,18 @@ class ECKeyPairGenerationConfig : public KeyPairGenerationConfig {
|
|||||||
const int param_encoding_;
|
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 {
|
class GenerateKeyPairJob : public CryptoJob {
|
||||||
public:
|
public:
|
||||||
GenerateKeyPairJob(Environment* env,
|
GenerateKeyPairJob(Environment* env,
|
||||||
@ -5912,6 +5924,14 @@ void GenerateKeyPairEC(const FunctionCallbackInfo<Value>& args) {
|
|||||||
GenerateKeyPair(args, 2, std::move(config));
|
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) {
|
void GetSSLCiphers(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
@ -6313,6 +6333,9 @@ void Initialize(Local<Object> target,
|
|||||||
env->SetMethod(target, "generateKeyPairRSA", GenerateKeyPairRSA);
|
env->SetMethod(target, "generateKeyPairRSA", GenerateKeyPairRSA);
|
||||||
env->SetMethod(target, "generateKeyPairDSA", GenerateKeyPairDSA);
|
env->SetMethod(target, "generateKeyPairDSA", GenerateKeyPairDSA);
|
||||||
env->SetMethod(target, "generateKeyPairEC", GenerateKeyPairEC);
|
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_NAMED_CURVE);
|
||||||
NODE_DEFINE_CONSTANT(target, OPENSSL_EC_EXPLICIT_CURVE);
|
NODE_DEFINE_CONSTANT(target, OPENSSL_EC_EXPLICIT_CURVE);
|
||||||
NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS1);
|
NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS1);
|
||||||
|
@ -425,7 +425,7 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
|
|||||||
type: TypeError,
|
type: TypeError,
|
||||||
code: 'ERR_INVALID_ARG_VALUE',
|
code: 'ERR_INVALID_ARG_VALUE',
|
||||||
message: "The argument 'type' must be one of " +
|
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 ' +
|
message: 'The "options" argument must be of ' +
|
||||||
'type object. Received type undefined'
|
'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.
|
// Test invalid key encoding types.
|
||||||
{
|
{
|
||||||
// Invalid public key type.
|
// Invalid public key type.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user