Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion doc/api/crypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -2255,12 +2255,20 @@

<!-- YAML
added: v15.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/62453

Check warning on line 2260 in doc/api/crypto.md

View workflow job for this annotation

GitHub Actions / lint-pr-url

pr-url doesn't match the URL of the current PR.
description: Passing a non-extractable CryptoKey as `key` is deprecated.
-->

* `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');
Expand Down Expand Up @@ -3522,6 +3530,9 @@
<!-- YAML
added: v0.1.94
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/62453

Check warning on line 3534 in doc/api/crypto.md

View workflow job for this annotation

GitHub Actions / lint-pr-url

pr-url doesn't match the URL of the current PR.
description: Passing a CryptoKey as `key` is deprecated.
- version:
- v17.9.0
- v16.17.0
Expand Down Expand Up @@ -3596,6 +3607,9 @@
<!-- YAML
added: v0.1.94
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/62453

Check warning on line 3611 in doc/api/crypto.md

View workflow job for this annotation

GitHub Actions / lint-pr-url

pr-url doesn't match the URL of the current PR.
description: Passing a CryptoKey as `key` is deprecated.
- version:
- v17.9.0
- v16.17.0
Expand Down Expand Up @@ -3819,6 +3833,9 @@
<!-- YAML
added: v0.1.94
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/62453

Check warning on line 3837 in doc/api/crypto.md

View workflow job for this annotation

GitHub Actions / lint-pr-url

pr-url doesn't match the URL of the current PR.
description: Passing a CryptoKey as `key` is deprecated.
- version: v15.0.0
pr-url: https://github.com/nodejs/node/pull/35093
description: The key can also be an ArrayBuffer or CryptoKey. The
Expand Down Expand Up @@ -3909,6 +3926,9 @@
<!-- YAML
added: v11.6.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/62453

Check warning on line 3930 in doc/api/crypto.md

View workflow job for this annotation

GitHub Actions / lint-pr-url

pr-url doesn't match the URL of the current PR.
description: Passing a CryptoKey as `key` is deprecated.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/62240
description: Added support for `'raw-private'` and `'raw-seed'`
Expand Down Expand Up @@ -3957,6 +3977,9 @@
<!-- YAML
added: v11.6.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/62453

Check warning on line 3981 in doc/api/crypto.md

View workflow job for this annotation

GitHub Actions / lint-pr-url

pr-url doesn't match the URL of the current PR.
description: Passing a CryptoKey as `key` is deprecated.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/62240
description: Added support for `'raw-public'` format.
Expand Down
10 changes: 8 additions & 2 deletions doc/api/deprecations.md
Original file line number Diff line number Diff line change
Expand Up @@ -4485,12 +4485,15 @@

<!-- YAML
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/62453

Check warning on line 4489 in doc/api/deprecations.md

View workflow job for this annotation

GitHub Actions / lint-pr-url

pr-url doesn't match the URL of the current PR.
description: Runtime deprecation.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/62321
description: Documentation-only deprecation.
-->

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
Expand All @@ -4507,12 +4510,15 @@

<!-- YAML
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/62453

Check warning on line 4514 in doc/api/deprecations.md

View workflow job for this annotation

GitHub Actions / lint-pr-url

pr-url doesn't match the URL of the current PR.
description: Runtime deprecation.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/62321
description: Documentation-only deprecation.
-->

Type: Documentation-only
Type: Runtime

Passing a non-extractable [`CryptoKey`][] to [`KeyObject.from()`][] is
deprecated and will throw an error in a future version.
Expand Down
63 changes: 45 additions & 18 deletions lib/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ const { addAbortListener } = require('internal/events/abort_listener');
const kCapture = Symbol('kCapture');
const kErrorMonitor = Symbol('events.errorMonitor');
const kShapeMode = Symbol('shapeMode');
const kEmitting = Symbol('events.emitting');
const kMaxEventTargetListeners = Symbol('events.maxEventTargetListeners');
const kMaxEventTargetListenersWarned =
Symbol('events.maxEventTargetListenersWarned');
Expand Down Expand Up @@ -514,19 +515,22 @@ EventEmitter.prototype.emit = function emit(type, ...args) {
addCatch(this, result, type, args);
}
} else {
const len = handler.length;
const listeners = arrayClone(handler);
for (let i = 0; i < len; ++i) {
const result = ReflectApply(listeners[i], this, args);

// We check if result is undefined first because that
// is the most common case so we do not pay any perf
// penalty.
// This code is duplicated because extracting it away
// would make it non-inlineable.
if (result !== undefined && result !== null) {
addCatch(this, result, type, args);
handler[kEmitting]++;
try {
for (let i = 0; i < handler.length; ++i) {
const result = ReflectApply(handler[i], this, args);

// We check if result is undefined first because that
// is the most common case so we do not pay any perf
// penalty.
// This code is duplicated because extracting it away
// would make it non-inlineable.
if (result !== undefined && result !== null) {
addCatch(this, result, type, args);
}
}
} finally {
handler[kEmitting]--;
}
}

Expand Down Expand Up @@ -565,13 +569,17 @@ function _addListener(target, type, listener, prepend) {
} else {
if (typeof existing === 'function') {
// Adding the second element, need to change to array.
existing = events[type] =
prepend ? [listener, existing] : [existing, listener];
existing = prepend ? [listener, existing] : [existing, listener];
existing[kEmitting] = 0;
events[type] = existing;
// If we've already got an array, just append.
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
existing = ensureMutableListenerArray(events, type, existing);
if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
}

// Check for listener leak
Expand Down Expand Up @@ -674,7 +682,7 @@ EventEmitter.prototype.removeListener =
if (events === undefined)
return this;

const list = events[type];
let list = events[type];
if (list === undefined)
return this;

Expand All @@ -692,6 +700,7 @@ EventEmitter.prototype.removeListener =
if (events.removeListener !== undefined)
this.emit('removeListener', type, list.listener || listener);
} else if (typeof list !== 'function') {
list = ensureMutableListenerArray(events, type, list);
let position = -1;

for (let i = list.length - 1; i >= 0; i--) {
Expand Down Expand Up @@ -875,6 +884,24 @@ function arrayClone(arr) {
return ArrayPrototypeSlice(arr);
}

function cloneEventListenerArray(arr) {
const copy = arrayClone(arr);
copy[kEmitting] = 0;
if (arr.warned) {
copy.warned = true;
}
return copy;
}

function ensureMutableListenerArray(events, type, handler) {
if (handler[kEmitting] > 0) {
const copy = cloneEventListenerArray(handler);
events[type] = copy;
return copy;
}
return handler;
}

function unwrapListeners(arr) {
const ret = arrayClone(arr);
for (let i = 0; i < ret.length; ++i) {
Expand Down
38 changes: 31 additions & 7 deletions lib/internal/crypto/keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ const {

const {
customInspectSymbol: kInspect,
getDeprecationWarningEmitter,
kEnumerableProperty,
lazyDOMException,
} = require('internal/util');
Expand All @@ -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;
Expand Down Expand Up @@ -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];
}

Expand Down Expand Up @@ -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' ||
Expand All @@ -836,6 +857,7 @@ function prepareAsymmetricKey(key, ctx) {
...parseKeyEncoding(key, undefined, isPublic),
};
}

throw new ERR_INVALID_ARG_TYPE(
'key',
getKeyTypes(ctx !== kCreatePrivate),
Expand All @@ -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];
Expand Down
4 changes: 3 additions & 1 deletion src/util-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,9 @@ void ArrayBufferViewContents<T, S>::Read(v8::Local<v8::ArrayBufferView> abv) {
static_assert(sizeof(T) == 1, "Only supports one-byte data at the moment");
length_ = abv->ByteLength();
if (length_ > sizeof(stack_storage_) || abv->HasBuffer()) {
data_ = static_cast<T*>(abv->Buffer()->Data()) + abv->ByteOffset();
auto buf_data = abv->Buffer()->Data();
data_ = buf_data != nullptr ? static_cast<T*>(buf_data) + abv->ByteOffset()
: stack_storage_;
} else {
abv->CopyContents(stack_storage_, sizeof(stack_storage_));
data_ = stack_storage_;
Expand Down
23 changes: 23 additions & 0 deletions test/parallel/test-crypto-authenticated.js
Original file line number Diff line number Diff line change
Expand Up @@ -772,3 +772,26 @@ for (const test of TEST_CASES) {
decipher.final();
}, /Unsupported state or unable to authenticate data/);
}

// Refs: https://github.com/nodejs/node/issues/62342
{
const key = crypto.randomBytes(16);
const nonce = crypto.randomBytes(13);

const cipher = crypto.createCipheriv('aes-128-ccm', key, nonce, {
authTagLength: 16,
});
cipher.setAAD(Buffer.alloc(0), { plaintextLength: 0 });
cipher.update(new DataView(new ArrayBuffer(0)));
cipher.final();
const tag = cipher.getAuthTag();
assert.strictEqual(tag.length, 16);

const decipher = crypto.createDecipheriv('aes-128-ccm', key, nonce, {
authTagLength: 16,
});
decipher.setAuthTag(tag);
decipher.setAAD(Buffer.alloc(0), { plaintextLength: 0 });
decipher.update(new DataView(new ArrayBuffer(0)));
decipher.final();
}
23 changes: 23 additions & 0 deletions test/parallel/test-crypto-dep0203.js
Original file line number Diff line number Diff line change
@@ -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());
23 changes: 23 additions & 0 deletions test/parallel/test-crypto-dep0204.js
Original file line number Diff line number Diff line change
@@ -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());
Loading
Loading