Skip to content

Commit cbf6a01

Browse files
himself65targos
authored andcommitted
crypto: fix generateKeyPair with encoding 'jwk'
Fixes: #39205 PR-URL: #39319 Reviewed-By: Filip Skokan <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 7a731ef commit cbf6a01

File tree

8 files changed

+195
-58
lines changed

8 files changed

+195
-58
lines changed

Diff for: lib/internal/crypto/keygen.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const {
3131
SecretKeyObject,
3232
parsePublicKeyEncoding,
3333
parsePrivateKeyEncoding,
34+
isJwk
3435
} = require('internal/crypto/keys');
3536

3637
const {
@@ -60,7 +61,9 @@ const {
6061
const { isArrayBufferView } = require('internal/util/types');
6162

6263
function wrapKey(key, ctor) {
63-
if (typeof key === 'string' || isArrayBufferView(key))
64+
if (typeof key === 'string' ||
65+
isArrayBufferView(key) ||
66+
isJwk(key))
6467
return key;
6568
return new ctor(key);
6669
}

Diff for: lib/internal/crypto/keys.js

+10-31
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const {
1717
kKeyTypePrivate,
1818
kKeyFormatPEM,
1919
kKeyFormatDER,
20+
kKeyFormatJWK,
2021
kKeyEncodingPKCS1,
2122
kKeyEncodingPKCS8,
2223
kKeyEncodingSPKI,
@@ -37,8 +38,6 @@ const {
3738
ERR_INVALID_ARG_VALUE,
3839
ERR_OUT_OF_RANGE,
3940
ERR_OPERATION_FAILED,
40-
ERR_CRYPTO_JWK_UNSUPPORTED_CURVE,
41-
ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE,
4241
ERR_CRYPTO_INVALID_JWK,
4342
}
4443
} = require('internal/errors');
@@ -151,7 +150,6 @@ const {
151150

152151
const kAsymmetricKeyType = Symbol('kAsymmetricKeyType');
153152
const kAsymmetricKeyDetails = Symbol('kAsymmetricKeyDetails');
154-
const kAsymmetricKeyJWKProperties = Symbol('kAsymmetricKeyJWKProperties');
155153

156154
function normalizeKeyDetails(details = {}) {
157155
if (details.publicExponent !== undefined) {
@@ -189,28 +187,6 @@ const {
189187
return {};
190188
}
191189
}
192-
193-
[kAsymmetricKeyJWKProperties]() {
194-
switch (this.asymmetricKeyType) {
195-
case 'rsa': return {};
196-
case 'ec':
197-
switch (this.asymmetricKeyDetails.namedCurve) {
198-
case 'prime256v1': return { crv: 'P-256' };
199-
case 'secp256k1': return { crv: 'secp256k1' };
200-
case 'secp384r1': return { crv: 'P-384' };
201-
case 'secp521r1': return { crv: 'P-521' };
202-
default:
203-
throw new ERR_CRYPTO_JWK_UNSUPPORTED_CURVE(
204-
this.asymmetricKeyDetails.namedCurve);
205-
}
206-
case 'ed25519': return { crv: 'Ed25519' };
207-
case 'ed448': return { crv: 'Ed448' };
208-
case 'x25519': return { crv: 'X25519' };
209-
case 'x448': return { crv: 'X448' };
210-
default:
211-
throw new ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE();
212-
}
213-
}
214190
}
215191

216192
class PublicKeyObject extends AsymmetricKeyObject {
@@ -220,8 +196,7 @@ const {
220196

221197
export(options) {
222198
if (options && options.format === 'jwk') {
223-
const properties = this[kAsymmetricKeyJWKProperties]();
224-
return this[kHandle].exportJwk(properties);
199+
return this[kHandle].exportJwk({});
225200
}
226201
const {
227202
format,
@@ -242,8 +217,7 @@ const {
242217
throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
243218
'jwk', 'does not support encryption');
244219
}
245-
const properties = this[kAsymmetricKeyJWKProperties]();
246-
return this[kHandle].exportJwk(properties);
220+
return this[kHandle].exportJwk({});
247221
}
248222
const {
249223
format,
@@ -265,6 +239,8 @@ function parseKeyFormat(formatStr, defaultFormat, optionName) {
265239
return kKeyFormatPEM;
266240
else if (formatStr === 'der')
267241
return kKeyFormatDER;
242+
else if (formatStr === 'jwk')
243+
return kKeyFormatJWK;
268244
throw new ERR_INVALID_ARG_VALUE(optionName, formatStr);
269245
}
270246

@@ -305,12 +281,14 @@ function parseKeyFormatAndType(enc, keyType, isPublic, objName) {
305281
isInput ? kKeyFormatPEM : undefined,
306282
option('format', objName));
307283

284+
const isRequired = (!isInput ||
285+
format === kKeyFormatDER) &&
286+
format !== kKeyFormatJWK;
308287
const type = parseKeyType(typeStr,
309-
!isInput || format === kKeyFormatDER,
288+
isRequired,
310289
keyType,
311290
isPublic,
312291
option('type', objName));
313-
314292
return { format, type };
315293
}
316294

@@ -766,4 +744,5 @@ module.exports = {
766744
PrivateKeyObject,
767745
isKeyObject,
768746
isCryptoKey,
747+
isJwk,
769748
};

Diff for: lib/internal/errors.js

-2
Original file line numberDiff line numberDiff line change
@@ -923,8 +923,6 @@ E('ERR_CRYPTO_INVALID_JWK', 'Invalid JWK data', TypeError);
923923
E('ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE',
924924
'Invalid key object type %s, expected %s.', TypeError);
925925
E('ERR_CRYPTO_INVALID_STATE', 'Invalid state for operation %s', Error);
926-
E('ERR_CRYPTO_JWK_UNSUPPORTED_CURVE', 'Unsupported JWK EC curve: %s.', Error);
927-
E('ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE', 'Unsupported JWK Key Type.', Error);
928926
E('ERR_CRYPTO_PBKDF2_ERROR', 'PBKDF2 error', Error);
929927
E('ERR_CRYPTO_SCRYPT_INVALID_PARAMETER', 'Invalid scrypt parameter', Error);
930928
E('ERR_CRYPTO_SCRYPT_NOT_SUPPORTED', 'Scrypt algorithm not supported', Error);

Diff for: src/crypto/crypto_ec.cc

+28
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,34 @@ Maybe<bool> ExportJWKEcKey(
740740
return Nothing<bool>();
741741
}
742742

743+
Local<String> crv_name;
744+
const int nid = EC_GROUP_get_curve_name(group);
745+
switch (nid) {
746+
case NID_X9_62_prime256v1:
747+
crv_name = OneByteString(env->isolate(), "P-256");
748+
break;
749+
case NID_secp256k1:
750+
crv_name = OneByteString(env->isolate(), "secp256k1");
751+
break;
752+
case NID_secp384r1:
753+
crv_name = OneByteString(env->isolate(), "P-384");
754+
break;
755+
case NID_secp521r1:
756+
crv_name = OneByteString(env->isolate(), "P-521");
757+
break;
758+
default: {
759+
THROW_ERR_CRYPTO_JWK_UNSUPPORTED_CURVE(
760+
env, "Unsupported JWK EC curve: %s.", OBJ_nid2sn(nid));
761+
return Nothing<bool>();
762+
}
763+
}
764+
if (target->Set(
765+
env->context(),
766+
env->jwk_crv_string(),
767+
crv_name).IsNothing()) {
768+
return Nothing<bool>();
769+
}
770+
743771
if (key->GetKeyType() == kKeyTypePrivate) {
744772
const BIGNUM* pvt = EC_KEY_get0_private_key(ec);
745773
return SetEncodedValue(

Diff for: src/crypto/crypto_keys.cc

+34-23
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@ void GetKeyFormatAndTypeFromJs(
6161
config->type_ = Just<PKEncodingType>(static_cast<PKEncodingType>(
6262
args[*offset + 1].As<Int32>()->Value()));
6363
} else {
64-
CHECK(context == kKeyContextInput && config->format_ == kKeyFormatPEM);
64+
CHECK(
65+
(context == kKeyContextInput &&
66+
config->format_ == kKeyFormatPEM) ||
67+
(context == kKeyContextGenerate &&
68+
config->format_ == kKeyFormatJWK));
6569
CHECK(args[*offset + 1]->IsNullOrUndefined());
6670
config->type_ = Nothing<PKEncodingType>();
6771
}
@@ -487,9 +491,7 @@ Maybe<bool> ExportJWKAsymmetricKey(
487491
std::shared_ptr<KeyObjectData> key,
488492
Local<Object> target) {
489493
switch (EVP_PKEY_id(key->GetAsymmetricKey().get())) {
490-
case EVP_PKEY_RSA:
491-
// Fall through
492-
case EVP_PKEY_RSA_PSS: return ExportJWKRsaKey(env, key, target);
494+
case EVP_PKEY_RSA: return ExportJWKRsaKey(env, key, target);
493495
case EVP_PKEY_EC: return ExportJWKEcKey(env, key, target);
494496
case EVP_PKEY_ED25519:
495497
// Fall through
@@ -499,7 +501,7 @@ Maybe<bool> ExportJWKAsymmetricKey(
499501
// Fall through
500502
case EVP_PKEY_X448: return ExportJWKEdKey(env, key, target);
501503
}
502-
THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
504+
THROW_ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE(env);
503505
return Just(false);
504506
}
505507

@@ -605,6 +607,21 @@ static inline Maybe<bool> Tristate(bool b) {
605607
return b ? Just(true) : Nothing<bool>();
606608
}
607609

610+
Maybe<bool> ExportJWKInner(Environment* env,
611+
std::shared_ptr<KeyObjectData> key,
612+
Local<Value> result) {
613+
switch (key->GetKeyType()) {
614+
case kKeyTypeSecret:
615+
return ExportJWKSecretKey(env, key, result.As<Object>());
616+
case kKeyTypePublic:
617+
// Fall through
618+
case kKeyTypePrivate:
619+
return ExportJWKAsymmetricKey(env, key, result.As<Object>());
620+
default:
621+
UNREACHABLE();
622+
}
623+
}
624+
608625
Maybe<bool> ManagedEVPPKey::ToEncodedPublicKey(
609626
Environment* env,
610627
ManagedEVPPKey key,
@@ -617,6 +634,11 @@ Maybe<bool> ManagedEVPPKey::ToEncodedPublicKey(
617634
std::shared_ptr<KeyObjectData> data =
618635
KeyObjectData::CreateAsymmetric(kKeyTypePublic, std::move(key));
619636
return Tristate(KeyObjectHandle::Create(env, data).ToLocal(out));
637+
} else if (config.format_ == kKeyFormatJWK) {
638+
std::shared_ptr<KeyObjectData> data =
639+
KeyObjectData::CreateAsymmetric(kKeyTypePublic, std::move(key));
640+
*out = Object::New(env->isolate());
641+
return ExportJWKInner(env, data, *out);
620642
}
621643

622644
return Tristate(WritePublicKey(env, key.get(), config).ToLocal(out));
@@ -632,6 +654,11 @@ Maybe<bool> ManagedEVPPKey::ToEncodedPrivateKey(
632654
std::shared_ptr<KeyObjectData> data =
633655
KeyObjectData::CreateAsymmetric(kKeyTypePrivate, std::move(key));
634656
return Tristate(KeyObjectHandle::Create(env, data).ToLocal(out));
657+
} else if (config.format_ == kKeyFormatJWK) {
658+
std::shared_ptr<KeyObjectData> data =
659+
KeyObjectData::CreateAsymmetric(kKeyTypePrivate, std::move(key));
660+
*out = Object::New(env->isolate());
661+
return ExportJWKInner(env, data, *out);
635662
}
636663

637664
return Tristate(WritePrivateKey(env, key.get(), config).ToLocal(out));
@@ -1211,24 +1238,7 @@ void KeyObjectHandle::ExportJWK(
12111238

12121239
CHECK(args[0]->IsObject());
12131240

1214-
switch (key->Data()->GetKeyType()) {
1215-
case kKeyTypeSecret:
1216-
if (ExportJWKSecretKey(env, key->Data(), args[0].As<Object>())
1217-
.IsNothing()) {
1218-
return;
1219-
}
1220-
break;
1221-
case kKeyTypePublic:
1222-
// Fall through
1223-
case kKeyTypePrivate:
1224-
if (ExportJWKAsymmetricKey(env, key->Data(), args[0].As<Object>())
1225-
.IsNothing()) {
1226-
return;
1227-
}
1228-
break;
1229-
default:
1230-
UNREACHABLE();
1231-
}
1241+
ExportJWKInner(env, key->Data(), args[0]);
12321242

12331243
args.GetReturnValue().Set(args[0]);
12341244
}
@@ -1380,6 +1390,7 @@ void Initialize(Environment* env, Local<Object> target) {
13801390
NODE_DEFINE_CONSTANT(target, kKeyEncodingSEC1);
13811391
NODE_DEFINE_CONSTANT(target, kKeyFormatDER);
13821392
NODE_DEFINE_CONSTANT(target, kKeyFormatPEM);
1393+
NODE_DEFINE_CONSTANT(target, kKeyFormatJWK);
13831394
NODE_DEFINE_CONSTANT(target, kKeyTypeSecret);
13841395
NODE_DEFINE_CONSTANT(target, kKeyTypePublic);
13851396
NODE_DEFINE_CONSTANT(target, kKeyTypePrivate);

Diff for: src/crypto/crypto_keys.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ enum PKEncodingType {
3131

3232
enum PKFormatType {
3333
kKeyFormatDER,
34-
kKeyFormatPEM
34+
kKeyFormatPEM,
35+
kKeyFormatJWK
3536
};
3637

3738
enum KeyType {

Diff for: src/node_errors.h

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ void OnFatalError(const char* location, const char* message);
4949
V(ERR_CRYPTO_INVALID_SCRYPT_PARAMS, RangeError) \
5050
V(ERR_CRYPTO_INVALID_STATE, Error) \
5151
V(ERR_CRYPTO_INVALID_TAG_LENGTH, RangeError) \
52+
V(ERR_CRYPTO_JWK_UNSUPPORTED_CURVE, Error) \
53+
V(ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE, Error) \
5254
V(ERR_CRYPTO_OPERATION_FAILED, Error) \
5355
V(ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH, RangeError) \
5456
V(ERR_CRYPTO_UNKNOWN_CIPHER, Error) \
@@ -136,6 +138,7 @@ ERRORS_WITH_CODE(V)
136138
V(ERR_CRYPTO_INVALID_SCRYPT_PARAMS, "Invalid scrypt params") \
137139
V(ERR_CRYPTO_INVALID_STATE, "Invalid state") \
138140
V(ERR_CRYPTO_INVALID_TAG_LENGTH, "Invalid taglength") \
141+
V(ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE, "Unsupported JWK Key Type.") \
139142
V(ERR_CRYPTO_OPERATION_FAILED, "Operation failed") \
140143
V(ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH, \
141144
"Input buffers must have the same byte length") \

0 commit comments

Comments
 (0)