diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 8f3f1fac1fae0b..245b29720c3b3f 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -2255,12 +2255,20 @@ be listed in the `transferList` argument. * `key` {CryptoKey} * Returns: {KeyObject} -Example: Converting a `CryptoKey` instance to a `KeyObject`: +Returns the underlying {KeyObject} of a {CryptoKey}. The returned {KeyObject} +does not retain any of the restrictions imposed by the Web Crypto API on the +original {CryptoKey}, such as the allowed key usages, the algorithm or hash +algorithm bindings, and the extractability flag. In particular, the underlying +key material of the returned {KeyObject} can always be exported. ```mjs const { KeyObject } = await import('node:crypto'); @@ -3522,6 +3530,9 @@ operations. The specific constants currently defined are described in -Type: Documentation-only +Type: Runtime Passing a [`CryptoKey`][] to `node:crypto` functions is deprecated and will throw an error in a future version. This includes @@ -4507,12 +4510,15 @@ will throw an error in a future version. This includes -Type: Documentation-only +Type: Runtime Passing a non-extractable [`CryptoKey`][] to [`KeyObject.from()`][] is deprecated and will throw an error in a future version. diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index 80c073a1dbfac1..ad532ed6885910 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -74,6 +74,7 @@ const { const { customInspectSymbol: kInspect, + getDeprecationWarningEmitter, kEnumerableProperty, lazyDOMException, } = require('internal/util'); @@ -89,6 +90,18 @@ const kKeyUsages = Symbol('kKeyUsages'); const kCachedAlgorithm = Symbol('kCachedAlgorithm'); const kCachedKeyUsages = Symbol('kCachedKeyUsages'); +const emitDEP0203 = getDeprecationWarningEmitter( + 'DEP0203', + 'Passing a CryptoKey to node:crypto functions is deprecated.', +); + +const maybeEmitDEP0204 = getDeprecationWarningEmitter( + 'DEP0204', + 'Passing a non-extractable CryptoKey to KeyObject.from() is deprecated.', + undefined, + false, + (key) => !key[kExtractable], +); // Key input contexts. const kConsumePublic = 0; @@ -140,6 +153,7 @@ const { static from(key) { if (!isCryptoKey(key)) throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key); + maybeEmitDEP0204(key); return key[kKeyObject]; } @@ -796,21 +810,28 @@ function prepareAsymmetricKey(key, ctx) { if (isKeyObject(key)) { // Best case: A key object, as simple as that. return { data: getKeyObjectHandle(key, ctx) }; - } else if (isCryptoKey(key)) { + } + if (isCryptoKey(key)) { + emitDEP0203(); return { data: getKeyObjectHandle(key[kKeyObject], ctx) }; - } else if (isStringOrBuffer(key)) { + } + if (isStringOrBuffer(key)) { // Expect PEM by default, mostly for backward compatibility. return { format: kKeyFormatPEM, data: getArrayBufferOrView(key, 'key') }; - } else if (typeof key === 'object') { + } + if (typeof key === 'object') { const { key: data, encoding, format } = 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)) + } + if (isCryptoKey(data)) { + emitDEP0203(); return { data: getKeyObjectHandle(data[kKeyObject], ctx) }; - else if (format === 'jwk') { + } + if (format === 'jwk') { validateObject(data, 'key.key'); return { data: getKeyObjectHandleFromJwk(data, ctx), format: 'jwk' }; } else if (format === 'raw-public' || format === 'raw-private' || @@ -836,6 +857,7 @@ function prepareAsymmetricKey(key, ctx) { ...parseKeyEncoding(key, undefined, isPublic), }; } + throw new ERR_INVALID_ARG_TYPE( 'key', getKeyTypes(ctx !== kCreatePrivate), @@ -856,7 +878,9 @@ function prepareSecretKey(key, encoding, bufferOnly = false) { if (key.type !== 'secret') throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, 'secret'); return key[kHandle]; - } else if (isCryptoKey(key)) { + } + if (isCryptoKey(key)) { + emitDEP0203(); if (key[kKeyType] !== 'secret') throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key[kKeyType], 'secret'); return key[kKeyObject][kHandle]; diff --git a/test/parallel/test-crypto-dep0203.js b/test/parallel/test-crypto-dep0203.js new file mode 100644 index 00000000000000..a973301bcde1af --- /dev/null +++ b/test/parallel/test-crypto-dep0203.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const crypto = require('crypto'); + +common.expectWarning({ + DeprecationWarning: { + DEP0203: 'Passing a CryptoKey to node:crypto functions is deprecated.', + }, +}); + +(async () => { + const key = await globalThis.crypto.subtle.generateKey( + { name: 'AES-CBC', length: 128 }, + true, + ['encrypt'], + ); + + crypto.createCipheriv('aes-128-cbc', key, Buffer.alloc(16)); +})().then(common.mustCall()); diff --git a/test/parallel/test-crypto-dep0204.js b/test/parallel/test-crypto-dep0204.js new file mode 100644 index 00000000000000..29cd4d821fab94 --- /dev/null +++ b/test/parallel/test-crypto-dep0204.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const { KeyObject } = require('crypto'); + +common.expectWarning({ + DeprecationWarning: { + DEP0204: 'Passing a non-extractable CryptoKey to KeyObject.from() is deprecated.', + }, +}); + +(async () => { + const key = await globalThis.crypto.subtle.generateKey( + { name: 'AES-CBC', length: 128 }, + false, // non-extractable + ['encrypt'], + ); + + KeyObject.from(key); +})().then(common.mustCall());