From 80509570c29652cbbfa3cedf3b42594ad839e81f Mon Sep 17 00:00:00 2001 From: himself65 Date: Fri, 9 Jul 2021 12:07:32 +0800 Subject: [PATCH] crypto: fix `generateKeyPair` with encoding 'jwk' Fixes: https://github.com/nodejs/node/issues/39205 --- lib/internal/crypto/keygen.js | 4 +++ lib/internal/crypto/keys.js | 4 +++ src/crypto/crypto_keys.cc | 45 +++++++++++++++++------------ src/crypto/crypto_keys.h | 3 +- test/parallel/test-crypto-keygen.js | 19 ++++++++++++ 5 files changed, 56 insertions(+), 19 deletions(-) diff --git a/lib/internal/crypto/keygen.js b/lib/internal/crypto/keygen.js index 06490e24a9c24f..16d9e9d3e81663 100644 --- a/lib/internal/crypto/keygen.js +++ b/lib/internal/crypto/keygen.js @@ -31,6 +31,7 @@ const { SecretKeyObject, parsePublicKeyEncoding, parsePrivateKeyEncoding, + isJwk } = require('internal/crypto/keys'); const { @@ -62,6 +63,9 @@ const { isArrayBufferView } = require('internal/util/types'); function wrapKey(key, ctor) { if (typeof key === 'string' || isArrayBufferView(key)) return key; + else if (isJwk(key)) { + return key; + } return new ctor(key); } diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index c24b2d14eb5001..c0336927bdd99a 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -17,6 +17,7 @@ const { kKeyTypePrivate, kKeyFormatPEM, kKeyFormatDER, + kKeyFormatJWK, kKeyEncodingPKCS1, kKeyEncodingPKCS8, kKeyEncodingSPKI, @@ -265,6 +266,8 @@ function parseKeyFormat(formatStr, defaultFormat, optionName) { return kKeyFormatPEM; else if (formatStr === 'der') return kKeyFormatDER; + else if (formatStr === 'jwk') + return kKeyFormatJWK; throw new ERR_INVALID_ARG_VALUE(optionName, formatStr); } @@ -766,4 +769,5 @@ module.exports = { PrivateKeyObject, isKeyObject, isCryptoKey, + isJwk, }; diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc index 21cc988cee6a18..e048a82e451613 100644 --- a/src/crypto/crypto_keys.cc +++ b/src/crypto/crypto_keys.cc @@ -605,6 +605,21 @@ static inline Maybe Tristate(bool b) { return b ? Just(true) : Nothing(); } +Maybe ExportJWKInner(Environment* env, + std::shared_ptr key, + Local result) { + switch (key->GetKeyType()) { + case kKeyTypeSecret: + return ExportJWKSecretKey(env, key, result.As()); + case kKeyTypePublic: + // Fall through + case kKeyTypePrivate: + return ExportJWKAsymmetricKey(env, key, result.As()); + default: + UNREACHABLE(); + } +} + Maybe ManagedEVPPKey::ToEncodedPublicKey( Environment* env, ManagedEVPPKey key, @@ -617,6 +632,11 @@ Maybe ManagedEVPPKey::ToEncodedPublicKey( std::shared_ptr data = KeyObjectData::CreateAsymmetric(kKeyTypePublic, std::move(key)); return Tristate(KeyObjectHandle::Create(env, data).ToLocal(out)); + } else if (config.format_ == kKeyFormatJWK) { + std::shared_ptr data = + KeyObjectData::CreateAsymmetric(kKeyTypePublic, std::move(key)); + *out = Object::New(env->isolate()); + return ExportJWKInner(env, data, *out); } return Tristate(WritePublicKey(env, key.get(), config).ToLocal(out)); @@ -632,6 +652,11 @@ Maybe ManagedEVPPKey::ToEncodedPrivateKey( std::shared_ptr data = KeyObjectData::CreateAsymmetric(kKeyTypePrivate, std::move(key)); return Tristate(KeyObjectHandle::Create(env, data).ToLocal(out)); + } else if (config.format_ == kKeyFormatJWK) { + std::shared_ptr data = + KeyObjectData::CreateAsymmetric(kKeyTypePrivate, std::move(key)); + *out = Object::New(env->isolate()); + return ExportJWKInner(env, data, *out); } return Tristate(WritePrivateKey(env, key.get(), config).ToLocal(out)); @@ -1211,24 +1236,7 @@ void KeyObjectHandle::ExportJWK( CHECK(args[0]->IsObject()); - switch (key->Data()->GetKeyType()) { - case kKeyTypeSecret: - if (ExportJWKSecretKey(env, key->Data(), args[0].As()) - .IsNothing()) { - return; - } - break; - case kKeyTypePublic: - // Fall through - case kKeyTypePrivate: - if (ExportJWKAsymmetricKey(env, key->Data(), args[0].As()) - .IsNothing()) { - return; - } - break; - default: - UNREACHABLE(); - } + ExportJWKInner(env, key->Data(), args[0]); args.GetReturnValue().Set(args[0]); } @@ -1380,6 +1388,7 @@ void Initialize(Environment* env, Local target) { NODE_DEFINE_CONSTANT(target, kKeyEncodingSEC1); NODE_DEFINE_CONSTANT(target, kKeyFormatDER); NODE_DEFINE_CONSTANT(target, kKeyFormatPEM); + NODE_DEFINE_CONSTANT(target, kKeyFormatJWK); NODE_DEFINE_CONSTANT(target, kKeyTypeSecret); NODE_DEFINE_CONSTANT(target, kKeyTypePublic); NODE_DEFINE_CONSTANT(target, kKeyTypePrivate); diff --git a/src/crypto/crypto_keys.h b/src/crypto/crypto_keys.h index 3662b3a3b8688b..df3ab8ab181437 100644 --- a/src/crypto/crypto_keys.h +++ b/src/crypto/crypto_keys.h @@ -31,7 +31,8 @@ enum PKEncodingType { enum PKFormatType { kKeyFormatDER, - kKeyFormatPEM + kKeyFormatPEM, + kKeyFormatJWK }; enum KeyType { diff --git a/test/parallel/test-crypto-keygen.js b/test/parallel/test-crypto-keygen.js index 4612fc4a1ac40a..52611c04cf0281 100644 --- a/test/parallel/test-crypto-keygen.js +++ b/test/parallel/test-crypto-keygen.js @@ -603,6 +603,25 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); passphrase: 'top secret' }); })); + + // Test async elliptic curve key generation with 'jwk' encoding + generateKeyPair('ec', { + namedCurve: "P-384", + publicKeyEncoding: { + type: 'spki', + format: 'jwk' + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'jwk' + } + }, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(typeof publicKey, 'object') + assert.strictEqual(typeof privateKey, 'object') + assert.strictEqual(publicKey.x, privateKey.x) + assert.strictEqual(publicKey.y, privateKey.y) + assert.strictEqual(publicKey.kty, privateKey.kty) + })); } // Test invalid parameter encoding.