From b7c31b34dac5f64ab2af66bb8ffaea4f14452566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Fri, 5 Feb 2021 18:41:51 +0100 Subject: [PATCH 1/6] crypto: rename KeyObject.from to fromCryptoKey --- doc/api/deprecations.md | 12 ++++++++++++ lib/internal/crypto/keys.js | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index b337441c3a8c11..86b32b399833a1 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..4094737e5c130f 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -116,6 +116,10 @@ const [ } static from(key) { + return KeyObject.fromCryptoKey(key); + } + + static fromCryptoKey(key) { if (!isCryptoKey(key)) throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key); return key[kKeyObject]; From 1d12a20e89af82bb59d9ca2833a2ce99bfd5ab9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Fri, 5 Feb 2021 22:12:59 +0100 Subject: [PATCH 2/6] Update doc/api/deprecations.md Co-authored-by: Filip Skokan --- doc/api/deprecations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index 86b32b399833a1..3d310ad521212b 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -2747,7 +2747,7 @@ an explicit [`"exports"` or `"main"` entry][] with the exact file extension. From d632cd6289d363e5b48040cf59c388144faae972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Sat, 6 Feb 2021 18:49:14 +0100 Subject: [PATCH 3/6] crypto: allow CryptoKeys in create*Key --- lib/internal/crypto/keys.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index 4094737e5c130f..fd2f21c494c382 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -408,7 +408,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 === kCreatePublic) ? kConsumePublic : ctx; + 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') }; @@ -416,10 +417,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 === kCreatePublic) ? kConsumePublic : ctx; + return { data: getKeyObjectHandle(data[kKeyObject], actualCtx) }; + } // Either PEM or DER using PKCS#1 or SPKI. if (!isStringOrBuffer(data)) { throw new ERR_INVALID_ARG_TYPE( From 6a7a614696e1eaf0a36be6777d49d186a7fffa14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Sat, 6 Feb 2021 18:52:15 +0100 Subject: [PATCH 4/6] fixup! crypto: rename KeyObject.from to fromCryptoKey --- lib/internal/crypto/keys.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index fd2f21c494c382..84aeb04b248680 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -116,10 +116,6 @@ const [ } static from(key) { - return KeyObject.fromCryptoKey(key); - } - - static fromCryptoKey(key) { if (!isCryptoKey(key)) throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key); return key[kKeyObject]; From 01555406d61ebec838aa446eac1345c5da934c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Sat, 6 Feb 2021 20:23:21 +0100 Subject: [PATCH 5/6] fixup! crypto: allow CryptoKeys in create*Key --- lib/internal/crypto/keys.js | 11 +++-- src/crypto/crypto_keys.cc | 2 +- test/parallel/test-webcrypto-to-keyobject.js | 48 ++++++++++++++++++++ 3 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 test/parallel/test-webcrypto-to-keyobject.js diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index 84aeb04b248680..c25379bc0b417f 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,7 @@ function prepareAsymmetricKey(key, ctx) { // Best case: A key object, as simple as that. return { data: getKeyObjectHandle(key, ctx) }; } else if (isCryptoKey(key)) { - const actualCtx = (ctx === kCreatePublic) ? kConsumePublic : ctx; + const actualCtx = ctx & ~kCreateContextFlag; return { data: getKeyObjectHandle(key[kKeyObject], actualCtx) }; } else if (isStringOrBuffer(key)) { // Expect PEM by default, mostly for backward compatibility. @@ -416,7 +417,7 @@ function prepareAsymmetricKey(key, ctx) { if (isKeyObject(data)) { return { data: getKeyObjectHandle(data, ctx) }; } else if (isCryptoKey(data)) { - const actualCtx = (ctx === kCreatePublic) ? kConsumePublic : ctx; + const actualCtx = ctx & ~kCreateContextFlag; return { data: getKeyObjectHandle(data[kKeyObject], actualCtx) }; } // Either PEM or DER using PKCS#1 or SPKI. @@ -472,7 +473,7 @@ function prepareSecretKey(key, encoding, bufferOnly = false) { } function createSecretKey(key, encoding) { - key = prepareSecretKey(key, encoding, true); + key = prepareSecretKey(key, encoding, false); if (key.byteLength === 0) throw new ERR_OUT_OF_RANGE('key.byteLength', '> 0', key.byteLength); const handle = new KeyObjectHandle(); 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()); From 27a268f660bbdf002f8144a46bb4c0562910c426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Sat, 6 Feb 2021 20:38:02 +0100 Subject: [PATCH 6/6] fixup! crypto: allow CryptoKeys in create*Key --- lib/internal/crypto/keys.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index c25379bc0b417f..ac455dc5f45d13 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -473,7 +473,11 @@ function prepareSecretKey(key, encoding, bufferOnly = false) { } function createSecretKey(key, encoding) { - key = prepareSecretKey(key, encoding, false); + // 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); const handle = new KeyObjectHandle();