diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index b337441c3a8c11..3d310ad521212b 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -2743,6 +2743,18 @@ Previously, `index.js` and extension searching lookups would apply to With this deprecation, all ES module main entry point resolutions require an explicit [`"exports"` or `"main"` entry][] with the exact file extension. +### DEP0XXX: `KeyObject.from` + + +Type: Documentation-only. + +Use `KeyObject.fromCryptoKey` instead. + [Legacy URL API]: url.md#url_legacy_url_api [NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf [RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3 diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index c4c60f3d226de7..ac455dc5f45d13 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -72,8 +72,9 @@ const kKeyUsages = Symbol('kKeyUsages'); // Key input contexts. const kConsumePublic = 0; const kConsumePrivate = 1; -const kCreatePublic = 2; -const kCreatePrivate = 3; +const kCreateContextFlag = 2; +const kCreatePublic = kConsumePublic | kCreateContextFlag; +const kCreatePrivate = kConsumePrivate | kCreateContextFlag; const encodingNames = []; for (const m of [[kKeyEncodingPKCS1, 'pkcs1'], [kKeyEncodingPKCS8, 'pkcs8'], @@ -404,7 +405,8 @@ function prepareAsymmetricKey(key, ctx) { // Best case: A key object, as simple as that. return { data: getKeyObjectHandle(key, ctx) }; } else if (isCryptoKey(key)) { - return { data: getKeyObjectHandle(key[kKeyObject], ctx) }; + const actualCtx = ctx & ~kCreateContextFlag; + return { data: getKeyObjectHandle(key[kKeyObject], actualCtx) }; } else if (isStringOrBuffer(key)) { // Expect PEM by default, mostly for backward compatibility. return { format: kKeyFormatPEM, data: getArrayBufferOrView(key, 'key') }; @@ -412,10 +414,12 @@ function prepareAsymmetricKey(key, ctx) { const { key: data, encoding } = key; // The 'key' property can be a KeyObject as well to allow specifying // additional options such as padding along with the key. - if (isKeyObject(data)) + if (isKeyObject(data)) { return { data: getKeyObjectHandle(data, ctx) }; - else if (isCryptoKey(data)) - return { data: getKeyObjectHandle(data[kKeyObject], ctx) }; + } else if (isCryptoKey(data)) { + const actualCtx = ctx & ~kCreateContextFlag; + return { data: getKeyObjectHandle(data[kKeyObject], actualCtx) }; + } // Either PEM or DER using PKCS#1 or SPKI. if (!isStringOrBuffer(data)) { throw new ERR_INVALID_ARG_TYPE( @@ -469,6 +473,10 @@ function prepareSecretKey(key, encoding, bufferOnly = false) { } function createSecretKey(key, encoding) { + // TODO(tniessen): This doesn't really work. + if (isCryptoKey(key)) + return key[kKeyObject]; + key = prepareSecretKey(key, encoding, true); if (key.byteLength === 0) throw new ERR_OUT_OF_RANGE('key.byteLength', '> 0', key.byteLength); diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc index 9c9fc4a9fcaf59..4e76259664fdd1 100644 --- a/src/crypto/crypto_keys.cc +++ b/src/crypto/crypto_keys.cc @@ -952,7 +952,7 @@ void KeyObjectHandle::Init(const FunctionCallbackInfo& args) { CHECK_EQ(args.Length(), 5); offset = 1; - pkey = ManagedEVPPKey::GetPrivateKeyFromJs(args, &offset, false); + pkey = ManagedEVPPKey::GetPrivateKeyFromJs(args, &offset, true); if (!pkey) return; key->data_ = KeyObjectData::CreateAsymmetric(type, pkey); diff --git a/test/parallel/test-webcrypto-to-keyobject.js b/test/parallel/test-webcrypto-to-keyobject.js new file mode 100644 index 00000000000000..af99e5de2bda64 --- /dev/null +++ b/test/parallel/test-webcrypto-to-keyobject.js @@ -0,0 +1,48 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { Buffer } = require('buffer'); +const { + createSecretKey, + createPublicKey, + createPrivateKey, + webcrypto: { subtle } +} = require('crypto'); + +(async function() { + const { publicKey, privateKey } = await subtle.generateKey({ + name: 'RSA-OAEP', + modulusLength: 1024, + publicExponent: Buffer.from([1, 0, 1]), + hash: 'SHA-256' + }, false, ['encrypt', 'decrypt']); + + const nodePublicKey = createPublicKey(publicKey); + assert.strictEqual(nodePublicKey.type, 'public'); + assert.strictEqual(nodePublicKey.asymmetricKeyType, 'rsa'); + assert.strictEqual(nodePublicKey.asymmetricKeyDetails.modulusLength, 1024); + + const nodePublicKeyFromPrivate = createPublicKey(privateKey); + assert.strictEqual(nodePublicKeyFromPrivate.type, 'public'); + assert.strictEqual(nodePublicKeyFromPrivate.asymmetricKeyType, 'rsa'); + assert.strictEqual( + nodePublicKeyFromPrivate.asymmetricKeyDetails.modulusLength, 1024); + + const nodePrivateKey = createPrivateKey(privateKey); + assert.strictEqual(nodePrivateKey.type, 'private'); + assert.strictEqual(nodePrivateKey.asymmetricKeyType, 'rsa'); + assert.strictEqual(nodePrivateKey.asymmetricKeyDetails.modulusLength, 1024); + + const aesKey = await subtle.generateKey({ + name: 'AES-GCM', + length: 256 + }, false, ['encrypt', 'decrypt']); + + const nodeSecretKey = createSecretKey(aesKey); + assert.strictEqual(nodeSecretKey.type, 'secret'); + assert.strictEqual(nodeSecretKey.symmetricKeySize, 32); +})().then(common.mustCall());