Skip to content

Commit

Permalink
crypto: introduce crypto/promises
Browse files Browse the repository at this point in the history
Refs: #36181
  • Loading branch information
panva committed Feb 4, 2021
1 parent 03380bc commit 87f39d5
Show file tree
Hide file tree
Showing 9 changed files with 934 additions and 2 deletions.
128 changes: 126 additions & 2 deletions doc/api/crypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -2551,8 +2551,8 @@ added:
* Returns: {Buffer}

Computes the Diffie-Hellman secret based on a `privateKey` and a `publicKey`.
Both keys must have the same `asymmetricKeyType`, which must be one of `'dh'`
(for Diffie-Hellman), `'ec'` (for ECDH), `'x448'`, or `'x25519'` (for ECDH-ES).
Both keys must have the same `asymmetricKeyType`, which must be one of `'dh'`,
`'ec'`, `'x448'`, or `'x25519'`.

### `crypto.generateKey(type, options, callback)`
<!-- YAML
Expand Down Expand Up @@ -3895,6 +3895,130 @@ Type: {Crypto} An implementation of the Web Crypto API standard.

See the [Web Crypto API documentation][] for details.

## `crypto` Promises API
<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental
The `crypto.promises` API provides an alternative set of asynchronous crypto
methods that return `Promise` objects and execute operations in libuv's
threadpool.
The API is accessible via `require('crypto').promises` or `require('crypto/promises')`.

### `cryptoPromises.diffieHellman(options)`
<!-- YAML
added: REPLACEME
-->

* `options`: {Object}
* `privateKey`: {KeyObject|CryptoKey}
* `publicKey`: {KeyObject|CryptoKey}
* Returns: {Promise} containing {Buffer}

Computes the Diffie-Hellman secret based on a `privateKey` and a `publicKey`.
Both keys must have the same `asymmetricKeyType`, which must be one of `'dh'`,
`'ec'`, `'x448'`, or `'x25519'`.

### `cryptoPromises.digest(algorithm, data[, options])`
<!-- YAML
added: REPLACEME
-->

* `algorithm` {string}
* `data` {ArrayBuffer|TypedArray|DataView|Buffer}
* `options` {Object}
* `outputLength` {number} Used to specify the desired output length in bytes
for XOF hash functions such as `'shake256'`.
* Returns: {Promise} containing {Buffer}

Calculates the digest for the `data` using the given `algorithm`.

The `algorithm` is dependent on the available algorithms supported by the
version of OpenSSL on the platform. Examples are `'sha256'`, `'sha512'`, etc.
On recent releases of OpenSSL, `openssl list -digest-algorithms`
(`openssl list-message-digest-algorithms` for older versions of OpenSSL) will
display the available digest algorithms.

### `cryptoPromises.hmac(algorithm, data, key)`
<!-- YAML
added: REPLACEME
-->

* `algorithm` {string}
* `data` {ArrayBuffer|TypedArray|DataView|Buffer}
* `key` {KeyObject|CryptoKey}
* Returns: {Promise} containing {Buffer}

Calculates the HMAC digest for the `data` using the given `algorithm`.

The `algorithm` is dependent on the available algorithms supported by the
version of OpenSSL on the platform. Examples are `'sha256'`, `'sha512'`, etc.
On recent releases of OpenSSL, `openssl list -digest-algorithms`
(`openssl list-message-digest-algorithms` for older versions of OpenSSL) will
display the available digest algorithms.

### `cryptoPromises.sign(algorithm, data, key[, options])`
<!-- YAML
added: REPLACEME
-->

* `algorithm` {string|null|undefined}
* `data` {ArrayBuffer|TypedArray|DataView|Buffer}
* `key` {KeyObject|CryptoKey}
* `options` {Object}
* `dsaEncoding` {string} For DSA and ECDSA, this option specifies the
format of the generated signature. It can be one of the following:
* `'der'` (default): DER-encoded ASN.1 signature structure encoding `(r, s)`.
* `'ieee-p1363'`: Signature format `r || s` as proposed in IEEE-P1363.
* `padding` {integer} Optional padding value for RSA, one of the following:
* `crypto.constants.RSA_PKCS1_PADDING` (default)
* `crypto.constants.RSA_PKCS1_PSS_PADDING`
* `saltLength` {integer} Salt length for when padding is
`RSA_PKCS1_PSS_PADDING`. The special value
`crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest
size, `crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN` (default) sets it to the
maximum permissible value.
* Returns: {Promise} containing {Buffer}

Calculates the signature for `data` using the given private key and
algorithm. If `algorithm` is `null` or `undefined`, then the algorithm is
dependent upon the key type (especially Ed25519 and Ed448).

### `cryptoPromises.verify(algorithm, data, key, signature[, options])`
<!-- YAML
added: REPLACEME
-->

* `algorithm` {string|null|undefined}
* `data` {ArrayBuffer|TypedArray|DataView|Buffer}
* `key` {KeyObject|CryptoKey}
* `signature` {ArrayBuffer|TypedArray|DataView|Buffer}
* `options` {Object}
* `dsaEncoding` {string} For DSA and ECDSA, this option specifies the
format of the generated signature. It can be one of the following:
* `'der'` (default): DER-encoded ASN.1 signature structure encoding `(r, s)`.
* `'ieee-p1363'`: Signature format `r || s` as proposed in IEEE-P1363.
* `padding` {integer} Optional padding value for RSA, one of the following:
* `crypto.constants.RSA_PKCS1_PADDING` (default)
* `crypto.constants.RSA_PKCS1_PSS_PADDING`
* `saltLength` {integer} Salt length for when padding is
`RSA_PKCS1_PSS_PADDING`. The special value
`crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest
size, `crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN` (default) sets it to the
maximum permissible value.
* Returns: {Promise} containing {boolean}

Verifies the given signature for `data` using the given key and algorithm. If
`algorithm` is `null` or `undefined`, then the algorithm is dependent upon the
key type (especially Ed25519 and Ed448).

The `signature` argument is the previously calculated signature for the `data`.

Because public keys can be derived from private keys, a private key or a public
key may be passed for `key`.

## Notes

### Legacy streams API (prior to Node.js 0.10)
Expand Down
12 changes: 12 additions & 0 deletions lib/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ function createVerify(algorithm, options) {
return new Verify(algorithm, options);
}

// Lazy loaded
let promises = null;

module.exports = {
// Methods
checkPrime,
Expand Down Expand Up @@ -327,5 +330,14 @@ ObjectDefineProperties(module.exports, {
value: pendingDeprecation ?
deprecate(randomBytes, 'crypto.rng is deprecated.', 'DEP0115') :
randomBytes
},
promises: {
configurable: true,
enumerable: true,
get() {
if (promises === null)
promises = require('internal/crypto/promises').exports;
return promises;
}
}
});
3 changes: 3 additions & 0 deletions lib/crypto/promises.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use strict';

module.exports = require('internal/crypto/promises').exports;
42 changes: 42 additions & 0 deletions lib/internal/crypto/diffiehellman.js
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,46 @@ function deriveBitsDH(publicKey, privateKey, callback) {
job.run();
}

async function asyncDiffieHellman(publicKey, privateKey) {
const { asymmetricKeyType } = privateKey;

if (asymmetricKeyType === 'dh') {
return new Promise((resolve, reject) => {
deriveBitsDH(
publicKey[kHandle],
privateKey[kHandle],
(err, bits) => {
if (err) reject(err);
else resolve(bits);
});
});
}

if (asymmetricKeyType === 'x25519' || asymmetricKeyType === 'x448') {
return new Promise((resolve, reject) => {
deriveBitsECDH(
`NODE-${asymmetricKeyType.toUpperCase()}`,
publicKey[kHandle],
privateKey[kHandle],
(err, bits) => {
if (err) reject(err);
else resolve(bits);
});
});
}

return new Promise((resolve, reject) => {
deriveBitsECDH(
privateKey.asymmetricKeyDetails.namedCurve,
publicKey[kHandle],
privateKey[kHandle],
(err, bits) => {
if (err) reject(err);
else resolve(bits);
});
});
}

function verifyAcceptableDhKeyUse(name, type, usages) {
let checkSet;
switch (type) {
Expand Down Expand Up @@ -601,9 +641,11 @@ module.exports = {
DiffieHellman,
DiffieHellmanGroup,
ECDH,
asyncDiffieHellman,
diffieHellman,
deriveBitsECDH,
deriveBitsDH,
dhEnabledKeyTypes,
dhGenerateKey,
asyncDeriveBitsECDH,
asyncDeriveBitsDH,
Expand Down
Loading

0 comments on commit 87f39d5

Please sign in to comment.