Skip to content

Commit 6730f43

Browse files
committed
crypto: add support for EdDSA key pair generation
Refs: #26319
1 parent f2064df commit 6730f43

File tree

3 files changed

+87
-13
lines changed

3 files changed

+87
-13
lines changed

lib/internal/crypto/keygen.js

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const {
55
generateKeyPairRSA,
66
generateKeyPairDSA,
77
generateKeyPairEC,
8+
generateKeyPairEdDSA,
89
OPENSSL_EC_NAMED_CURVE,
910
OPENSSL_EC_EXPLICIT_CURVE
1011
} = internalBinding('crypto');
@@ -119,18 +120,25 @@ function parseKeyEncoding(keyType, options) {
119120

120121
function check(type, options, callback) {
121122
validateString(type, 'type');
122-
if (options == null || typeof options !== 'object')
123-
throw new ERR_INVALID_ARG_TYPE('options', 'object', options);
124123

125124
// These will be set after parsing the type and type-specific options to make
126125
// the order a bit more intuitive.
127126
let cipher, passphrase, publicType, publicFormat, privateType, privateFormat;
128127

128+
if (options !== undefined && typeof options !== 'object')
129+
throw new ERR_INVALID_ARG_TYPE('options', 'object', options);
130+
131+
function needOptions() {
132+
if (options == null)
133+
throw new ERR_INVALID_ARG_TYPE('options', 'object', options);
134+
return options;
135+
}
136+
129137
let impl;
130138
switch (type) {
131139
case 'rsa':
132140
{
133-
const { modulusLength } = options;
141+
const { modulusLength } = needOptions();
134142
if (!isUint32(modulusLength))
135143
throw new ERR_INVALID_OPT_VALUE('modulusLength', modulusLength);
136144

@@ -149,7 +157,7 @@ function check(type, options, callback) {
149157
break;
150158
case 'dsa':
151159
{
152-
const { modulusLength } = options;
160+
const { modulusLength } = needOptions();
153161
if (!isUint32(modulusLength))
154162
throw new ERR_INVALID_OPT_VALUE('modulusLength', modulusLength);
155163

@@ -168,7 +176,7 @@ function check(type, options, callback) {
168176
break;
169177
case 'ec':
170178
{
171-
const { namedCurve } = options;
179+
const { namedCurve } = needOptions();
172180
if (typeof namedCurve !== 'string')
173181
throw new ERR_INVALID_OPT_VALUE('namedCurve', namedCurve);
174182
let { paramEncoding } = options;
@@ -185,19 +193,30 @@ function check(type, options, callback) {
185193
cipher, passphrase, wrap);
186194
}
187195
break;
196+
case 'ed25519':
197+
case 'ed448':
198+
{
199+
impl = (wrap) => generateKeyPairEdDSA(type,
200+
publicFormat, publicType,
201+
privateFormat, privateType,
202+
cipher, passphrase, wrap);
203+
}
204+
break;
188205
default:
189206
throw new ERR_INVALID_ARG_VALUE('type', type,
190207
"must be one of 'rsa', 'dsa', 'ec'");
191208
}
192209

193-
({
194-
cipher,
195-
passphrase,
196-
publicType,
197-
publicFormat,
198-
privateType,
199-
privateFormat
200-
} = parseKeyEncoding(type, options));
210+
if (options) {
211+
({
212+
cipher,
213+
passphrase,
214+
publicType,
215+
publicFormat,
216+
privateType,
217+
privateFormat
218+
} = parseKeyEncoding(type, options));
219+
}
201220

202221
return impl;
203222
}

src/node_crypto.cc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5766,6 +5766,18 @@ class ECKeyPairGenerationConfig : public KeyPairGenerationConfig {
57665766
const int param_encoding_;
57675767
};
57685768

5769+
class EdDSAKeyPairGenerationConfig : public KeyPairGenerationConfig {
5770+
public:
5771+
explicit EdDSAKeyPairGenerationConfig(int id) : id_(id) {}
5772+
5773+
EVPKeyCtxPointer Setup() override {
5774+
return EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(id_, nullptr));
5775+
}
5776+
5777+
private:
5778+
const int id_;
5779+
};
5780+
57695781
class GenerateKeyPairJob : public CryptoJob {
57705782
public:
57715783
GenerateKeyPairJob(Environment* env,
@@ -5939,6 +5951,22 @@ void GenerateKeyPairEC(const FunctionCallbackInfo<Value>& args) {
59395951
GenerateKeyPair(args, 2, std::move(config));
59405952
}
59415953

5954+
void GenerateKeyPairEdDSA(const FunctionCallbackInfo<Value>& args) {
5955+
CHECK(args[0]->IsString());
5956+
String::Utf8Value curve_name(args.GetIsolate(), args[0].As<String>());
5957+
int id;
5958+
if (strcmp(*curve_name, "ed25519") == 0) {
5959+
id = EVP_PKEY_ED25519;
5960+
} else {
5961+
CHECK_EQ(strcmp(*curve_name, "ed448"), 0);
5962+
id = EVP_PKEY_ED448;
5963+
}
5964+
5965+
std::unique_ptr<KeyPairGenerationConfig> config(
5966+
new EdDSAKeyPairGenerationConfig(id));
5967+
GenerateKeyPair(args, 1, std::move(config));
5968+
}
5969+
59425970

59435971
void GetSSLCiphers(const FunctionCallbackInfo<Value>& args) {
59445972
Environment* env = Environment::GetCurrent(args);
@@ -6340,6 +6368,7 @@ void Initialize(Local<Object> target,
63406368
env->SetMethod(target, "generateKeyPairRSA", GenerateKeyPairRSA);
63416369
env->SetMethod(target, "generateKeyPairDSA", GenerateKeyPairDSA);
63426370
env->SetMethod(target, "generateKeyPairEC", GenerateKeyPairEC);
6371+
env->SetMethod(target, "generateKeyPairEdDSA", GenerateKeyPairEdDSA);
63436372
NODE_DEFINE_CONSTANT(target, OPENSSL_EC_NAMED_CURVE);
63446373
NODE_DEFINE_CONSTANT(target, OPENSSL_EC_EXPLICIT_CURVE);
63456374
NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS1);

test/parallel/test-crypto-keygen.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,15 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
439439
message: 'The "options" argument must be of ' +
440440
'type object. Received type undefined'
441441
});
442+
443+
// Even if no options are required, it should be impossible to pass anything
444+
// but an object (or undefined).
445+
common.expectsError(() => generateKeyPair('ed448', 0, common.mustNotCall()), {
446+
type: TypeError,
447+
code: 'ERR_INVALID_ARG_TYPE',
448+
message: 'The "options" argument must be of ' +
449+
'type object. Received type number'
450+
});
442451
}
443452

444453
{
@@ -778,6 +787,23 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
778787
}));
779788
}
780789

790+
// Test EdDSA key generation.
791+
{
792+
if (!/^1\.1\.0/.test(process.versions.openssl)) {
793+
['ed25519', 'ed448'].forEach((keyType) => {
794+
generateKeyPair(keyType, common.mustCall((err, publicKey, privateKey) => {
795+
assert.ifError(err);
796+
797+
assert.strictEqual(publicKey.type, 'public');
798+
assert.strictEqual(publicKey.asymmetricKeyType, keyType);
799+
800+
assert.strictEqual(privateKey.type, 'private');
801+
assert.strictEqual(privateKey.asymmetricKeyType, keyType);
802+
}));
803+
});
804+
}
805+
}
806+
781807
// Test invalid key encoding types.
782808
{
783809
// Invalid public key type.

0 commit comments

Comments
 (0)