Skip to content
Open
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
29 changes: 19 additions & 10 deletions doc/api/crypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -2743,14 +2743,14 @@ encoding of `'utf8'` is enforced. If `data` is a [`Buffer`][], `TypedArray`, or

This can be called many times with new data as it is streamed.

### `verify.verify(object, signature[, signatureEncoding])`
### `verify.verify(key, signature[, signatureEncoding])`

<!-- YAML
added: v0.1.92
changes:
- version: v15.0.0
pr-url: https://github.com/nodejs/node/pull/35093
description: The object can also be an ArrayBuffer and CryptoKey.
description: The key can also be an ArrayBuffer and CryptoKey.
- version:
- v13.2.0
- v12.16.0
Expand All @@ -2769,7 +2769,7 @@ changes:

<!--lint disable maximum-line-length remark-lint-->

* `object` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey}
* `key` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey}
* `dsaEncoding` {string}
* `padding` {integer}
* `saltLength` {integer}
Expand All @@ -2780,10 +2780,10 @@ changes:

<!--lint enable maximum-line-length remark-lint-->

Verifies the provided data using the given `object` and `signature`.
Verifies the provided data using the given `key` and `signature`.

If `object` is not a [`KeyObject`][], this function behaves as if
`object` had been passed to [`crypto.createPublicKey()`][]. If it is an
If `key` is not a [`KeyObject`][], this function behaves as if
`key` had been passed to [`crypto.createPublicKey()`][]. If it is an
object, the following additional properties can be passed:

* `dsaEncoding` {string} For DSA and ECDSA, this option specifies the
Expand Down Expand Up @@ -4142,23 +4142,32 @@ added:
- v13.9.0
- v12.17.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/62527
description: Accept key data in addition to KeyObject instances.
- version: v23.11.0
pr-url: https://github.com/nodejs/node/pull/57274
description: Optional callback argument added.
-->

* `options` {Object}
* `privateKey` {KeyObject}
* `publicKey` {KeyObject}
* `privateKey` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject}
* `publicKey` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject}
* `callback` {Function}
* `err` {Error}
* `secret` {Buffer}
* Returns: {Buffer} if the `callback` function is not provided.

Computes the Diffie-Hellman shared secret based on a `privateKey` and a `publicKey`.
Both keys must have the same `asymmetricKeyType` and must support either the DH or
Both keys must represent the same asymmetric key type and must support either the DH or
ECDH operation.

If `options.privateKey` is not a [`KeyObject`][], this function behaves as if
`options.privateKey` had been passed to [`crypto.createPrivateKey()`][].

If `options.publicKey` is not a [`KeyObject`][], this function behaves as if
`options.publicKey` had been passed to [`crypto.createPublicKey()`][].

If the `callback` function is provided this function uses libuv's threadpool.

### `crypto.encapsulate(key[, callback])`
Expand Down Expand Up @@ -6941,7 +6950,7 @@ See the [list of SSL OP Flags][] for details.
[`stream.transform` options]: stream.md#new-streamtransformoptions
[`util.promisify()`]: util.md#utilpromisifyoriginal
[`verify.update()`]: #verifyupdatedata-inputencoding
[`verify.verify()`]: #verifyverifyobject-signature-signatureencoding
[`verify.verify()`]: #verifyverifykey-signature-signatureencoding
[`x509.fingerprint256`]: #x509fingerprint256
[`x509.verify(publicKey)`]: #x509verifypublickey
[argon2]: https://www.rfc-editor.org/rfc/rfc9106.html
Expand Down
13 changes: 7 additions & 6 deletions lib/internal/crypto/cipher.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,15 @@ const { normalizeEncoding } = require('internal/util');
const { StringDecoder } = require('string_decoder');

function rsaFunctionFor(method, defaultPadding, keyType) {
return (options, buffer) => {
const keyName = keyType === 'private' ? 'privateKey' : undefined;
return (key, buffer) => {
const { format, type, data, passphrase, namedCurve } =
keyType === 'private' ?
preparePrivateKey(options) :
preparePublicOrPrivateKey(options);
const padding = options.padding || defaultPadding;
const { oaepHash, encoding } = options;
let { oaepLabel } = options;
preparePrivateKey(key, keyName) :
preparePublicOrPrivateKey(key, keyName);
const padding = key.padding || defaultPadding;
const { oaepHash, encoding } = key;
let { oaepLabel } = key;
if (oaepHash !== undefined)
validateString(oaepHash, 'key.oaepHash');
if (oaepLabel !== undefined)
Expand Down
79 changes: 61 additions & 18 deletions lib/internal/crypto/diffiehellman.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ const {
DiffieHellman: _DiffieHellman,
DiffieHellmanGroup: _DiffieHellmanGroup,
ECDH: _ECDH,
ECDHBitsJob,
ECDHConvertKey: _ECDHConvertKey,
kCryptoJobAsync,
kCryptoJobSync,
Expand Down Expand Up @@ -52,9 +51,11 @@ const {
} = require('internal/util');

const {
KeyObject,
isKeyObject,
kAlgorithm,
kKeyType,
preparePrivateKey,
preparePublicOrPrivateKey,
} = require('internal/crypto/keys');

const {
Expand Down Expand Up @@ -284,31 +285,65 @@ function diffieHellman(options, callback) {
validateFunction(callback, 'callback');

const { privateKey, publicKey } = options;
if (!(privateKey instanceof KeyObject))

// TODO(@panva): remove these non-semver-major error code preserving measures
// in a semver-major followup, the final state is just preparePublicOrPrivateKey
// and preparePrivateKey
if (privateKey == null)
throw new ERR_INVALID_ARG_VALUE('options.privateKey', privateKey);

if (!(publicKey instanceof KeyObject))
if (publicKey == null)
throw new ERR_INVALID_ARG_VALUE('options.publicKey', publicKey);

if (privateKey.type !== 'private')
throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(privateKey.type, 'private');
if (isKeyObject(privateKey)) {
if (privateKey.type !== 'private')
throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(privateKey.type, 'private');
}

if (publicKey.type !== 'public' && publicKey.type !== 'private') {
throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(publicKey.type,
'private or public');
if (isKeyObject(publicKey)) {
if (publicKey.type !== 'public' && publicKey.type !== 'private') {
throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(publicKey.type,
'private or public');
}
}

const privateType = privateKey.asymmetricKeyType;
const publicType = publicKey.asymmetricKeyType;
if (privateType !== publicType || !dhEnabledKeyTypes.has(privateType)) {
throw new ERR_CRYPTO_INCOMPATIBLE_KEY('key types for Diffie-Hellman',
`${privateType} and ${publicType}`);
if (isKeyObject(privateKey) && isKeyObject(publicKey)) {
const privateType = privateKey.asymmetricKeyType;
const publicType = publicKey.asymmetricKeyType;
if (privateType !== publicType || !dhEnabledKeyTypes.has(privateType)) {
throw new ERR_CRYPTO_INCOMPATIBLE_KEY('key types for Diffie-Hellman',
`${privateType} and ${publicType}`);
}
}

const {
data: pubData,
format: pubFormat,
type: pubType,
passphrase: pubPassphrase,
namedCurve: pubNamedCurve,
} = preparePublicOrPrivateKey(publicKey, 'options.publicKey');

const {
data: privData,
format: privFormat,
type: privType,
passphrase: privPassphrase,
namedCurve: privNamedCurve,
} = preparePrivateKey(privateKey, 'options.privateKey');

const job = new DHBitsJob(
callback ? kCryptoJobAsync : kCryptoJobSync,
publicKey[kHandle],
privateKey[kHandle]);
pubData,
pubFormat,
pubType,
pubPassphrase,
pubNamedCurve,
privData,
privFormat,
privType,
privPassphrase,
privNamedCurve);

if (!callback) {
const { 0: err, 1: secret } = job.run();
Expand Down Expand Up @@ -349,10 +384,18 @@ async function ecdhDeriveBits(algorithm, baseKey, length) {
throw lazyDOMException('Named curve mismatch', 'InvalidAccessError');
}

const bits = await jobPromise(() => new ECDHBitsJob(
const bits = await jobPromise(() => new DHBitsJob(
kCryptoJobAsync,
key[kKeyObject][kHandle],
baseKey[kKeyObject][kHandle]));
undefined,
undefined,
undefined,
undefined,
baseKey[kKeyObject][kHandle],
undefined,
undefined,
undefined,
undefined));

// If a length is not specified, return the full derived secret
if (length === null)
Expand Down
4 changes: 2 additions & 2 deletions lib/internal/crypto/keygen.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ function parseKeyEncoding(keyType, options = kEmptyObject) {
format: publicFormat,
type: publicType,
} = parsePublicKeyEncoding(publicKeyEncoding, keyType,
'publicKeyEncoding'));
'options.publicKeyEncoding'));
} else {
throw new ERR_INVALID_ARG_VALUE('options.publicKeyEncoding',
publicKeyEncoding);
Expand All @@ -164,7 +164,7 @@ function parseKeyEncoding(keyType, options = kEmptyObject) {
cipher,
passphrase,
} = parsePrivateKeyEncoding(privateKeyEncoding, keyType,
'privateKeyEncoding'));
'options.privateKeyEncoding'));
} else {
throw new ERR_INVALID_ARG_VALUE('options.privateKeyEncoding',
privateKeyEncoding);
Expand Down
38 changes: 19 additions & 19 deletions lib/internal/crypto/keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -466,9 +466,9 @@ function parseKeyType(typeStr, required, keyType, isPublic, optionName) {
throw new ERR_INVALID_ARG_VALUE(optionName, typeStr);
}

function option(name, objName) {
return objName === undefined ?
`options.${name}` : `options.${objName}.${name}`;
function option(name, prefix) {
return prefix === undefined ?
`options.${name}` : `${prefix}.${name}`;
}

function parseKeyFormatAndType(enc, keyType, isPublic, objName) {
Expand Down Expand Up @@ -628,7 +628,7 @@ function getKeyTypes(allowKeyObject, bufferOnly = false) {
}


function prepareAsymmetricKey(key, ctx) {
function prepareAsymmetricKey(key, ctx, name = 'key') {
if (isKeyObject(key)) {
// Best case: A key object, as simple as that.
return { data: getKeyObjectHandle(key, ctx) };
Expand All @@ -639,7 +639,7 @@ function prepareAsymmetricKey(key, ctx) {
}
if (isStringOrBuffer(key)) {
// Expect PEM by default, mostly for backward compatibility.
return { format: kKeyFormatPEM, data: getArrayBufferOrView(key, 'key') };
return { format: kKeyFormatPEM, data: getArrayBufferOrView(key, name) };
}
if (typeof key === 'object') {
const { key: data, encoding, format } = key;
Expand All @@ -654,23 +654,23 @@ function prepareAsymmetricKey(key, ctx) {
return { data: getKeyObjectHandle(data[kKeyObject], ctx) };
}
if (format === 'jwk') {
validateObject(data, 'key.key');
validateObject(data, `${name}.key`);
return { data, format: kKeyFormatJWK };
} else if (format === 'raw-public' || format === 'raw-private' ||
format === 'raw-seed') {
if (!isStringOrBuffer(data)) {
throw new ERR_INVALID_ARG_TYPE(
'key.key',
`${name}.key`,
['ArrayBuffer', 'Buffer', 'TypedArray', 'DataView'],
data);
}
validateString(key.asymmetricKeyType, 'key.asymmetricKeyType');
validateString(key.asymmetricKeyType, `${name}.asymmetricKeyType`);
if (key.asymmetricKeyType === 'ec') {
validateString(key.namedCurve, 'key.namedCurve');
validateString(key.namedCurve, `${name}.namedCurve`);
}
const rawFormat = parseKeyFormat(format, undefined, 'options.format');
const rawFormat = parseKeyFormat(format, undefined, `${name}.format`);
return {
data: getArrayBufferOrView(data, 'key.key'),
data: getArrayBufferOrView(data, `${name}.key`),
format: rawFormat,
type: key.asymmetricKeyType,
namedCurve: key.namedCurve ?? null,
Expand All @@ -680,31 +680,31 @@ function prepareAsymmetricKey(key, ctx) {
// Either PEM or DER using PKCS#1 or SPKI.
if (!isStringOrBuffer(data)) {
throw new ERR_INVALID_ARG_TYPE(
'key.key',
`${name}.key`,
getKeyTypes(ctx !== kCreatePrivate),
data);
}

const isPublic =
(ctx === kConsumePrivate || ctx === kCreatePrivate) ? false : undefined;
return {
data: getArrayBufferOrView(data, 'key', encoding),
...parseKeyEncoding(key, undefined, isPublic),
data: getArrayBufferOrView(data, `${name}.key`, encoding),
...parseKeyEncoding(key, undefined, isPublic, name),
};
}

throw new ERR_INVALID_ARG_TYPE(
'key',
name,
getKeyTypes(ctx !== kCreatePrivate),
key);
}

function preparePrivateKey(key) {
return prepareAsymmetricKey(key, kConsumePrivate);
function preparePrivateKey(key, name) {
return prepareAsymmetricKey(key, kConsumePrivate, name);
}

function preparePublicOrPrivateKey(key) {
return prepareAsymmetricKey(key, kConsumePublic);
function preparePublicOrPrivateKey(key, name) {
return prepareAsymmetricKey(key, kConsumePublic, name);
}

function prepareSecretKey(key, encoding, bufferOnly = false) {
Expand Down
Loading
Loading