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 <panva.ip@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 7a731ef commit cbf6a01

8 files changed

Lines changed: 195 additions & 58 deletions

File tree

‎lib/internal/crypto/keygen.js‎

Lines changed: 4 additions & 1 deletion
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
functionwrapKey(key,ctor){
63-
if(typeofkey==='string'||isArrayBufferView(key))
64+
if(typeofkey==='string'||
65+
isArrayBufferView(key)||
66+
isJwk(key))
6467
returnkey;
6568
returnnewctor(key);
6669
}

‎lib/internal/crypto/keys.js‎

Lines changed: 10 additions & 31 deletions
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
constkAsymmetricKeyType=Symbol('kAsymmetricKeyType');
153152
constkAsymmetricKeyDetails=Symbol('kAsymmetricKeyDetails');
154-
constkAsymmetricKeyJWKProperties=Symbol('kAsymmetricKeyJWKProperties');
155153

156154
functionnormalizeKeyDetails(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-
thrownewERR_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-
thrownewERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE();
212-
}
213-
}
214190
}
215191

216192
classPublicKeyObjectextendsAsymmetricKeyObject{
@@ -220,8 +196,7 @@ const {
220196

221197
export(options){
222198
if(options&&options.format==='jwk'){
223-
constproperties=this[kAsymmetricKeyJWKProperties]();
224-
returnthis[kHandle].exportJwk(properties);
199+
returnthis[kHandle].exportJwk({});
225200
}
226201
const{
227202
format,
@@ -242,8 +217,7 @@ const {
242217
thrownewERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
243218
'jwk','does not support encryption');
244219
}
245-
constproperties=this[kAsymmetricKeyJWKProperties]();
246-
returnthis[kHandle].exportJwk(properties);
220+
returnthis[kHandle].exportJwk({});
247221
}
248222
const{
249223
format,
@@ -265,6 +239,8 @@ function parseKeyFormat(formatStr, defaultFormat, optionName) {
265239
returnkKeyFormatPEM;
266240
elseif(formatStr==='der')
267241
returnkKeyFormatDER;
242+
elseif(formatStr==='jwk')
243+
returnkKeyFormatJWK;
268244
thrownewERR_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+
constisRequired=(!isInput||
285+
format===kKeyFormatDER)&&
286+
format!==kKeyFormatJWK;
308287
consttype=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
};

‎lib/internal/errors.js‎

Lines changed: 0 additions & 2 deletions
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);

‎src/crypto/crypto_ec.cc‎

Lines changed: 28 additions & 0 deletions
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+
constint 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
constBIGNUM* pvt = EC_KEY_get0_private_key(ec);
745773
returnSetEncodedValue(

‎src/crypto/crypto_keys.cc‎

Lines changed: 34 additions & 23 deletions
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-
caseEVP_PKEY_RSA:
491-
// Fall through
492-
caseEVP_PKEY_RSA_PSS: returnExportJWKRsaKey(env, key, target);
494+
caseEVP_PKEY_RSA: returnExportJWKRsaKey(env, key, target);
493495
caseEVP_PKEY_EC: returnExportJWKEcKey(env, key, target);
494496
caseEVP_PKEY_ED25519:
495497
// Fall through
@@ -499,7 +501,7 @@ Maybe<bool> ExportJWKAsymmetricKey(
499501
// Fall through
500502
caseEVP_PKEY_X448: returnExportJWKEdKey(env, key, target);
501503
}
502-
THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
504+
THROW_ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE(env);
503505
returnJust(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+
casekKeyTypeSecret:
615+
returnExportJWKSecretKey(env, key, result.As<Object>());
616+
casekKeyTypePublic:
617+
// Fall through
618+
casekKeyTypePrivate:
619+
returnExportJWKAsymmetricKey(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
returnTristate(KeyObjectHandle::Create(env, data).ToLocal(out));
637+
} elseif (config.format_ == kKeyFormatJWK) {
638+
std::shared_ptr<KeyObjectData> data =
639+
KeyObjectData::CreateAsymmetric(kKeyTypePublic, std::move(key));
640+
*out = Object::New(env->isolate());
641+
returnExportJWKInner(env, data, *out);
620642
}
621643

622644
returnTristate(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
returnTristate(KeyObjectHandle::Create(env, data).ToLocal(out));
657+
} elseif (config.format_ == kKeyFormatJWK) {
658+
std::shared_ptr<KeyObjectData> data =
659+
KeyObjectData::CreateAsymmetric(kKeyTypePrivate, std::move(key));
660+
*out = Object::New(env->isolate());
661+
returnExportJWKInner(env, data, *out);
635662
}
636663

637664
returnTristate(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-
casekKeyTypeSecret:
1216-
if (ExportJWKSecretKey(env, key->Data(), args[0].As<Object>())
1217-
.IsNothing()) {
1218-
return;
1219-
}
1220-
break;
1221-
casekKeyTypePublic:
1222-
// Fall through
1223-
casekKeyTypePrivate:
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);

‎src/crypto/crypto_keys.h‎

Lines changed: 2 additions & 1 deletion
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 {

‎src/node_errors.h‎

Lines changed: 3 additions & 0 deletions
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)