diff --git a/doc/api/crypto.md b/doc/api/crypto.md index e8583803e768b1..777bae667e9c80 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -2258,6 +2258,10 @@ be listed in the `transferList` argument. -* `privateKey` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey} +* `privateKey` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject} * `dsaEncoding` {string} * `padding` {integer} * `saltLength` {integer} @@ -2754,6 +2761,9 @@ This can be called many times with new data as it is streamed. -* `key` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey} +* `key` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject} * `dsaEncoding` {string} * `padding` {integer} * `saltLength` {integer} @@ -3536,6 +3546,9 @@ operations. The specific constants currently defined are described in * `algorithm` {string} -* `key` {string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey} +* `key` {string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject} * `iv` {string|ArrayBuffer|Buffer|TypedArray|DataView|null} * `options` {Object} [`stream.transform` options][] * Returns: {Cipheriv} @@ -3613,6 +3626,9 @@ given IV will be. * `algorithm` {string} -* `key` {string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey} +* `key` {string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject} * `iv` {string|ArrayBuffer|Buffer|TypedArray|DataView|null} * `options` {Object} [`stream.transform` options][] * Returns: {Decipheriv} @@ -3839,6 +3855,9 @@ input.on('readable', () => { * `algorithm` {string} -* `key` {string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey} +* `key` {string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject} * `options` {Object} [`stream.transform` options][] * `encoding` {string} The string encoding to use when `key` is a string. * Returns: {Hmac} @@ -3932,6 +3951,9 @@ input.on('readable', () => { -* `privateKey` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey} +* `privateKey` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject} * `oaepHash` {string} The hash function to use for OAEP padding and MGF1. **Default:** `'sha1'` * `oaepLabel` {string|ArrayBuffer|Buffer|TypedArray|DataView} The label to @@ -5280,6 +5308,9 @@ attempting to use `RSA_PKCS1_PADDING` will fail. -* `privateKey` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey} - * `key` {string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey} +* `privateKey` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject} + * `key` {string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject} A PEM encoded private key. * `passphrase` {string|ArrayBuffer|Buffer|TypedArray|DataView} An optional passphrase for the private key. @@ -5321,6 +5352,9 @@ object, the `padding` property can be passed. Otherwise, this function uses -* `key` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey} +* `key` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject} * `passphrase` {string|ArrayBuffer|Buffer|TypedArray|DataView} An optional passphrase for the private key. * `padding` {crypto.constants} An optional padding value defined in @@ -5363,6 +5397,9 @@ be passed instead of a public key. -* `key` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey} - * `key` {string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey} - A PEM encoded public or private key, {KeyObject}, or {CryptoKey}. +* `key` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject} + * `key` {string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject} + A PEM encoded public or private key, or {KeyObject}. * `oaepHash` {string} The hash function to use for OAEP padding and MGF1. **Default:** `'sha1'` * `oaepLabel` {string|ArrayBuffer|Buffer|TypedArray|DataView} The label to @@ -6104,6 +6141,9 @@ Throws an error if FIPS mode is not available. -Type: Runtime +Type: End-of-Life -Passing a [`CryptoKey`][] to `node:crypto` functions is deprecated and -will throw an error in a future version. This includes -[`crypto.createPublicKey()`][], [`crypto.createPrivateKey()`][], -[`crypto.sign()`][], [`crypto.verify()`][], -[`crypto.publicEncrypt()`][], [`crypto.publicDecrypt()`][], -[`crypto.privateEncrypt()`][], [`crypto.privateDecrypt()`][], -[`Sign.prototype.sign()`][], [`Verify.prototype.verify()`][], -[`crypto.createHmac()`][], [`crypto.createCipheriv()`][], -[`crypto.createDecipheriv()`][], [`crypto.encapsulate()`][], and -[`crypto.decapsulate()`][]. +Passing a [`CryptoKey`][] to `node:crypto` functions is no longer supported. ### DEP0204: `KeyObject.from()` with non-extractable `CryptoKey` -Type: Runtime +Type: End-of-Life Passing a non-extractable [`CryptoKey`][] to [`KeyObject.from()`][] is -deprecated and will throw an error in a future version. +no longer supported. ### DEP0205: `module.register()` @@ -4600,9 +4597,7 @@ will throw an error in a future version. [`ReadStream.open()`]: fs.md#class-fsreadstream [`Server.getConnections()`]: net.md#servergetconnectionscallback [`Server.listen({fd: })`]: net.md#serverlistenhandle-backlog-callback -[`Sign.prototype.sign()`]: crypto.md#signsignprivatekey-outputencoding [`String.prototype.toWellFormed`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toWellFormed -[`Verify.prototype.verify()`]: crypto.md#verifyverifyobject-signature-signatureencoding [`WriteStream.open()`]: fs.md#class-fswritestream [`assert`]: assert.md [`asyncResource.runInAsyncScope()`]: async_context.md#asyncresourceruninasyncscopefn-thisarg-args @@ -4620,21 +4615,11 @@ will throw an error in a future version. [`crypto.createDecipheriv()`]: crypto.md#cryptocreatedecipherivalgorithm-key-iv-options [`crypto.createHash()`]: crypto.md#cryptocreatehashalgorithm-options [`crypto.createHmac()`]: crypto.md#cryptocreatehmacalgorithm-key-options -[`crypto.createPrivateKey()`]: crypto.md#cryptocreateprivatekeykey -[`crypto.createPublicKey()`]: crypto.md#cryptocreatepublickeykey -[`crypto.decapsulate()`]: crypto.md#cryptodecapsulatekey-ciphertext-callback -[`crypto.encapsulate()`]: crypto.md#cryptoencapsulatekey-callback [`crypto.fips`]: crypto.md#cryptofips [`crypto.pbkdf2()`]: crypto.md#cryptopbkdf2password-salt-iterations-keylen-digest-callback -[`crypto.privateDecrypt()`]: crypto.md#cryptoprivatedecryptprivatekey-buffer -[`crypto.privateEncrypt()`]: crypto.md#cryptoprivateencryptprivatekey-buffer -[`crypto.publicDecrypt()`]: crypto.md#cryptopublicdecryptkey-buffer -[`crypto.publicEncrypt()`]: crypto.md#cryptopublicencryptkey-buffer [`crypto.randomBytes()`]: crypto.md#cryptorandombytessize-callback [`crypto.scrypt()`]: crypto.md#cryptoscryptpassword-salt-keylen-options-callback [`crypto.setEngine()`]: crypto.md#cryptosetengineengine-flags -[`crypto.sign()`]: crypto.md#cryptosignalgorithm-data-key-callback -[`crypto.verify()`]: crypto.md#cryptoverifyalgorithm-data-key-signature-callback [`decipher.final()`]: crypto.md#decipherfinaloutputencoding [`decipher.setAuthTag()`]: crypto.md#deciphersetauthtagbuffer-encoding [`dirent.parentPath`]: fs.md#direntparentpath diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index 2722ecc0520e2c..685618724b4f82 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -74,7 +74,6 @@ const { const { customInspectSymbol: kInspect, - getDeprecationWarningEmitter, kEnumerableProperty, kEmptyObject, lazyDOMException, @@ -82,19 +81,6 @@ const { const { inspect } = require('internal/util/inspect'); -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) => !getCryptoKeyExtractable(key), -); - // Key input contexts. const kConsumePublic = 0; const kConsumePrivate = 1; @@ -163,7 +149,12 @@ const { static from(key) { if (!isCryptoKey(key)) throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key); - maybeEmitDEP0204(key); + if (!getCryptoKeyExtractable(key)) { + throw new ERR_INVALID_ARG_VALUE( + 'key', + key, + 'must be an extractable CryptoKey'); + } const handle = getCryptoKeyHandle(key); switch (getCryptoKeyType(key)) { /* eslint-disable no-use-before-define */ @@ -635,7 +626,6 @@ function getKeyTypes(allowKeyObject, bufferOnly = false) { 'DataView', 'string', // Only if bufferOnly == false 'KeyObject', // Only if allowKeyObject == true && bufferOnly == false - 'CryptoKey', // Only if allowKeyObject == true && bufferOnly == false ]; if (bufferOnly) { return ArrayPrototypeSlice(types, 0, 4); @@ -653,11 +643,6 @@ function prepareAsymmetricKey(key, ctx, name = 'key') { validateAsymmetricKeyType(type, ctx, key); return { data: getKeyObjectHandle(key) }; } - if (isCryptoKey(key)) { - emitDEP0203(); - validateAsymmetricKeyType(getCryptoKeyType(key), ctx, key); - return { data: getCryptoKeyHandle(key) }; - } if (isStringOrBuffer(key)) { // Expect PEM by default, mostly for backward compatibility. return { format: kKeyFormatPEM, data: getArrayBufferOrView(key, name) }; @@ -672,11 +657,6 @@ function prepareAsymmetricKey(key, ctx, name = 'key') { validateAsymmetricKeyType(type, ctx, data); return { data: getKeyObjectHandle(data) }; } - if (isCryptoKey(data)) { - emitDEP0203(); - validateAsymmetricKeyType(getCryptoKeyType(data), ctx, data); - return { data: getCryptoKeyHandle(data) }; - } if (format === 'jwk') { validateObject(data, `${name}.key`); return { data, format: kKeyFormatJWK }; @@ -743,13 +723,6 @@ function prepareSecretKey(key, encoding, bufferOnly = false) { throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(type, 'secret'); return getKeyObjectHandle(key); } - if (isCryptoKey(key)) { - emitDEP0203(); - const type = getCryptoKeyType(key); - if (type !== 'secret') - throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(type, 'secret'); - return getCryptoKeyHandle(key); - } } if (typeof key !== 'string' && !isArrayBufferView(key) && diff --git a/test/parallel/test-crypto-dep0203.js b/test/parallel/test-crypto-dep0203.js index a973301bcde1af..16a4ec122d7a7b 100644 --- a/test/parallel/test-crypto-dep0203.js +++ b/test/parallel/test-crypto-dep0203.js @@ -4,20 +4,52 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); +const assert = require('assert'); 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( + const secretKey = await globalThis.crypto.subtle.generateKey( { name: 'AES-CBC', length: 128 }, true, ['encrypt'], ); - crypto.createCipheriv('aes-128-cbc', key, Buffer.alloc(16)); + assert.throws(() => { + crypto.createCipheriv('aes-128-cbc', secretKey, Buffer.alloc(16)); + }, { + code: 'ERR_INVALID_ARG_TYPE', + }); + + const { publicKey } = await globalThis.crypto.subtle.generateKey( + { name: 'ECDSA', namedCurve: 'P-256' }, + true, + ['sign', 'verify'], + ); + + assert.throws(() => { + crypto.createPublicKey(publicKey); + }, { + code: 'ERR_INVALID_ARG_TYPE', + }); + + assert.throws(() => { + crypto.publicEncrypt({ key: publicKey }, Buffer.alloc(0)); + }, { + code: 'ERR_INVALID_ARG_TYPE', + }); + + const ecdh = await globalThis.crypto.subtle.generateKey( + { name: 'ECDH', namedCurve: 'P-256' }, + true, + ['deriveBits'], + ); + + assert.throws(() => { + crypto.diffieHellman({ + privateKey: ecdh.privateKey, + publicKey: ecdh.publicKey, + }); + }, { + code: 'ERR_INVALID_ARG_TYPE', + }); })().then(common.mustCall()); diff --git a/test/parallel/test-crypto-dep0204.js b/test/parallel/test-crypto-dep0204.js index 29cd4d821fab94..fcde954536b7a6 100644 --- a/test/parallel/test-crypto-dep0204.js +++ b/test/parallel/test-crypto-dep0204.js @@ -4,14 +4,9 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); +const assert = require('assert'); 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 }, @@ -19,5 +14,42 @@ common.expectWarning({ ['encrypt'], ); - KeyObject.from(key); + assert.throws(() => { + KeyObject.from(key); + }, { + code: 'ERR_INVALID_ARG_VALUE', + message: /must be an extractable CryptoKey/, + }); + + let proto = Object.getPrototypeOf(key); + while (proto && !Object.getOwnPropertyDescriptor(proto, 'extractable')) { + proto = Object.getPrototypeOf(proto); + } + assert.ok(proto, 'could not find CryptoKey.prototype'); + const descriptor = Object.getOwnPropertyDescriptor(proto, 'extractable'); + Object.defineProperty(proto, 'extractable', { + __proto__: null, + configurable: true, + get() { return true; }, + }); + + try { + assert.strictEqual(key.extractable, true); + assert.throws(() => { + KeyObject.from(key); + }, { + code: 'ERR_INVALID_ARG_VALUE', + message: /must be an extractable CryptoKey/, + }); + } finally { + Object.defineProperty(proto, 'extractable', descriptor); + } + + const extractableKey = await globalThis.crypto.subtle.generateKey( + { name: 'AES-CBC', length: 128 }, + true, + ['encrypt'], + ); + + assert.strictEqual(KeyObject.from(extractableKey).type, 'secret'); })().then(common.mustCall()); diff --git a/test/parallel/test-crypto-key-objects-messageport.js b/test/parallel/test-crypto-key-objects-messageport.js index d23fde0d00d79b..f0555b45f1ffa0 100644 --- a/test/parallel/test-crypto-key-objects-messageport.js +++ b/test/parallel/test-crypto-key-objects-messageport.js @@ -48,7 +48,7 @@ process.env.HAS_STARTED_WORKER = 1; modulusLength: 1024 }); const cryptoKey = await subtle.generateKey( - { name: 'AES-CBC', length: 128 }, false, ['encrypt']); + { name: 'AES-CBC', length: 128 }, true, ['encrypt']); // Get immutable representations of all keys. const keys = [secretKey, publicKey, privateKey, cryptoKey] diff --git a/test/parallel/test-crypto-key-objects-to-crypto-key.js b/test/parallel/test-crypto-key-objects-to-crypto-key.js index 54449329cb551a..f9ea91eb0bf7a7 100644 --- a/test/parallel/test-crypto-key-objects-to-crypto-key.js +++ b/test/parallel/test-crypto-key-objects-to-crypto-key.js @@ -20,7 +20,14 @@ function assertCryptoKey(cryptoKey, keyObject, algorithm, extractable, usages) { assert.strictEqual(cryptoKey.algorithm.name, algorithm); assert.strictEqual(cryptoKey.extractable, extractable); assert.deepStrictEqual(cryptoKey.usages, usages); - assert.strictEqual(keyObject.equals(KeyObject.from(cryptoKey)), true); + if (extractable) { + assert.strictEqual(keyObject.equals(KeyObject.from(cryptoKey)), true); + } else { + assert.throws(() => KeyObject.from(cryptoKey), { + code: 'ERR_INVALID_ARG_VALUE', + message: /must be an extractable CryptoKey/, + }); + } } { diff --git a/test/parallel/test-webcrypto-cryptokey-hidden-slots.js b/test/parallel/test-webcrypto-cryptokey-hidden-slots.js index 792a1a59c4c5eb..7728ba02665117 100644 --- a/test/parallel/test-webcrypto-cryptokey-hidden-slots.js +++ b/test/parallel/test-webcrypto-cryptokey-hidden-slots.js @@ -21,32 +21,17 @@ if (!common.hasCrypto) const assert = require('node:assert'); const { - createHmac, KeyObject, - sign: cryptoSign, - verify: cryptoVerify, } = require('node:crypto'); const { inspect } = require('node:util'); const { subtle } = globalThis.crypto; -common.expectWarning({ - DeprecationWarning: { - DEP0203: 'Passing a CryptoKey to node:crypto functions is deprecated.', - }, -}); - (async () => { const key = await subtle.generateKey( { name: 'HMAC', hash: 'SHA-256' }, true, ['sign', 'verify'], ); - const { publicKey: ecPublicKey, privateKey: ecPrivateKey } = - await subtle.generateKey( - { name: 'ECDSA', namedCurve: 'P-256' }, - false, - ['sign', 'verify'], - ); const { publicKey: rsaPublicKey } = await subtle.generateKey( { name: 'RSA-PSS', @@ -182,24 +167,11 @@ common.expectWarning({ assert.strictEqual(jwk.ext, true); assert.deepStrictEqual(jwk.key_ops.sort(), ['sign', 'verify']); - // 4) The Node.js crypto bridge must also read the real native - // slots directly, both for KeyObject.from() and for deprecated - // direct CryptoKey consumption. + // 4) KeyObject.from() must also read the real native slots directly. const keyObject = KeyObject.from(key); assert.strictEqual(keyObject.type, 'secret'); assert.deepStrictEqual(keyObject.export(), Buffer.from(jwk.k, 'base64url')); - const payload = Buffer.from('payload'); - const digest = createHmac('sha256', key).update(payload).digest('hex'); - const expectedDigest = - createHmac('sha256', keyObject).update(payload).digest('hex'); - assert.strictEqual(digest, expectedDigest); - - const signature = cryptoSign('sha256', payload, ecPrivateKey); - assert.strictEqual( - cryptoVerify('sha256', payload, ecPublicKey, signature), - true); - // 5) Importing back from the exported JWK must yield an equivalent // key, i.e. the real algorithm and usages round-trip. const reimported = await subtle.importKey('jwk', jwk, diff --git a/test/parallel/test-webcrypto-derivekey.js b/test/parallel/test-webcrypto-derivekey.js index e04a7eab1bd8ef..bfc24763b34c7f 100644 --- a/test/parallel/test-webcrypto-derivekey.js +++ b/test/parallel/test-webcrypto-derivekey.js @@ -1,3 +1,4 @@ +// Flags: --expose-internals 'use strict'; const common = require('../common'); @@ -9,7 +10,7 @@ const { hasOpenSSL } = require('../common/crypto'); const assert = require('assert'); const { subtle } = globalThis.crypto; -const { KeyObject } = require('crypto'); +const { getCryptoKeyHandle } = require('internal/crypto/keys'); // This is only a partial test. The WebCrypto Web Platform Tests // will provide much greater coverage. @@ -211,8 +212,8 @@ const { KeyObject } = require('crypto'); assert.strictEqual(derived.algorithm.length, expected); } else { // KDFs cannot be exportable and do not indicate their length - const secretKey = KeyObject.from(derived); - assert.strictEqual(secretKey.symmetricKeySize, expected / 8); + assert.strictEqual(getCryptoKeyHandle(derived).getSymmetricKeySize(), + expected / 8); } } } diff --git a/test/parallel/test-webcrypto-encap-decap-ml-kem.js b/test/parallel/test-webcrypto-encap-decap-ml-kem.js index 450ba2cefb0a4f..84c527b74df886 100644 --- a/test/parallel/test-webcrypto-encap-decap-ml-kem.js +++ b/test/parallel/test-webcrypto-encap-decap-ml-kem.js @@ -1,3 +1,4 @@ +// Flags: --expose-internals 'use strict'; const common = require('../common'); @@ -12,11 +13,15 @@ if (!hasOpenSSL(3, 5)) const assert = require('assert'); const crypto = require('crypto'); -const { KeyObject } = crypto; +const { getCryptoKeyHandle } = require('internal/crypto/keys'); const { subtle } = globalThis.crypto; const vectors = require('../fixtures/crypto/ml-kem')(); +function getCryptoKeyData(key) { + return getCryptoKeyHandle(key).export(); +} + async function testEncapsulateKey({ name, publicKeyPem, privateKeyPem, results }) { const [ publicKey, @@ -151,9 +156,9 @@ async function testDecapsulateKey({ name, publicKeyPem, privateKeyPem, results } assert.strictEqual(decapsulatedKey.extractable, false); assert.deepStrictEqual(decapsulatedKey.usages, ['deriveBits']); - // Verify the keys are the same by using KeyObject.from() and comparing - const originalKeyData = KeyObject.from(encapsulated.sharedKey).export(); - const decapsulatedKeyData = KeyObject.from(decapsulatedKey).export(); + // Verify the non-extractable keys are the same. + const originalKeyData = getCryptoKeyData(encapsulated.sharedKey); + const decapsulatedKeyData = getCryptoKeyData(decapsulatedKey); assert(originalKeyData.equals(decapsulatedKeyData)); // Test with test vector ciphertext and expected shared key @@ -166,7 +171,7 @@ async function testDecapsulateKey({ name, publicKeyPem, privateKeyPem, results } ['deriveBits'] ); - const vectorKeyData = KeyObject.from(vectorDecapsulatedKey).export(); + const vectorKeyData = getCryptoKeyData(vectorDecapsulatedKey); assert(vectorKeyData.equals(results.sharedKey)); // Test failure when using wrong key type