From a8f460f12d81f63d95b3f1bc12a89e36cae2b271 Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Tue, 4 Apr 2017 15:59:30 -0700 Subject: [PATCH] crypto: support all ArrayBufferView types PR-URL: https://github.com/nodejs/node/pull/12223 Reviewed-By: Matteo Collina Reviewed-By: James M Snell Reviewed-By: Anna Henningsen --- doc/api/crypto.md | 127 +++++++++++++++++--------------- lib/crypto.js | 5 +- test/parallel/test-crypto-dh.js | 26 ++----- 3 files changed, 76 insertions(+), 82 deletions(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index fcbdcb6bfcb621..3ce81075dad96d 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -62,7 +62,7 @@ const cert2 = crypto.Certificate(); -- `spkac` {string | Buffer | Uint8Array} +- `spkac` {string | Buffer | TypedArray | DataView} - Returns {Buffer} The challenge component of the `spkac` data structure, which includes a public key and a challenge. @@ -78,7 +78,7 @@ console.log(challenge.toString('utf8')); -- `spkac` {string | Buffer | Uint8Array} +- `spkac` {string | Buffer | TypedArray | DataView} - Returns {Buffer} The public key component of the `spkac` data structure, which includes a public key and a challenge. @@ -94,7 +94,7 @@ console.log(publicKey); -- `spkac` {Buffer | Uint8Array} +- `spkac` {Buffer | TypedArray | DataView} - Returns {boolean} `true` if the given `spkac` data structure is valid, `false` otherwise. @@ -234,15 +234,16 @@ changes: pr-url: https://github.com/nodejs/node/pull/5522 description: The default `input_encoding` changed from `binary` to `utf8`. --> -- `data` {string | Buffer | Uint8Array} +- `data` {string | Buffer | TypedArray | DataView} - `input_encoding` {string} - `output_encoding` {string} Updates the cipher with `data`. If the `input_encoding` argument is given, its value must be one of `'utf8'`, `'ascii'`, or `'latin1'` and the `data` argument is a string using the specified encoding. If the `input_encoding` -argument is not given, `data` must be a [`Buffer`][] or `Uint8Array`. -If `data` is a [`Buffer`][] or `Uint8Array`, then `input_encoding` is ignored. +argument is not given, `data` must be a [`Buffer`][], `TypedArray`, or +`DataView`. If `data` is a [`Buffer`][], `TypedArray`, or `DataView`, then +`input_encoding` is ignored. The `output_encoding` specifies the output format of the enciphered data, and can be `'latin1'`, `'base64'` or `'hex'`. If the `output_encoding` @@ -340,7 +341,7 @@ changes: pr-url: https://github.com/nodejs/node/pull/9398 description: This method now returns a reference to `decipher`. --> -- `buffer` {Buffer | Uint8Array} +- `buffer` {Buffer | TypedArray | DataView} - Returns the {Cipher} for method chaining. When using an authenticated encryption mode (only `GCM` is currently @@ -357,7 +358,7 @@ changes: pr-url: https://github.com/nodejs/node/pull/9398 description: This method now returns a reference to `decipher`. --> -- `buffer` {Buffer | Uint8Array} +- `buffer` {Buffer | TypedArray | DataView} - Returns the {Cipher} for method chaining. When using an authenticated encryption mode (only `GCM` is currently @@ -394,7 +395,7 @@ changes: pr-url: https://github.com/nodejs/node/pull/5522 description: The default `input_encoding` changed from `binary` to `utf8`. --> -- `data` {string | Buffer | Uint8Array} +- `data` {string | Buffer | TypedArray | DataView} - `input_encoding` {string} - `output_encoding` {string} @@ -448,7 +449,7 @@ assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex')); -- `other_public_key` {string | Buffer | Uint8Array} +- `other_public_key` {string | Buffer | TypedArray | DataView} - `input_encoding` {string} - `output_encoding` {string} @@ -457,7 +458,8 @@ party's public key and returns the computed shared secret. The supplied key is interpreted using the specified `input_encoding`, and secret is encoded using specified `output_encoding`. Encodings can be `'latin1'`, `'hex'`, or `'base64'`. If the `input_encoding` is not -provided, `other_public_key` is expected to be a [`Buffer`][] or `Uint8Array`. +provided, `other_public_key` is expected to be a [`Buffer`][], +`TypedArray`, or `DataView`. If `output_encoding` is given a string is returned; otherwise, a [`Buffer`][] is returned. @@ -518,25 +520,25 @@ string is returned; otherwise a [`Buffer`][] is returned. -- `private_key` {string | Buffer | Uint8Array} +- `private_key` {string | Buffer | TypedArray | DataView} - `encoding` {string} Sets the Diffie-Hellman private key. If the `encoding` argument is provided and is either `'latin1'`, `'hex'`, or `'base64'`, `private_key` is expected to be a string. If no `encoding` is provided, `private_key` is expected -to be a [`Buffer`][] or `Uint8Array`. +to be a [`Buffer`][], `TypedArray`, or `DataView`. ### diffieHellman.setPublicKey(public_key[, encoding]) -- `public_key` {string | Buffer | Uint8Array} +- `public_key` {string | Buffer | TypedArray | DataView} - `encoding` {string} Sets the Diffie-Hellman public key. If the `encoding` argument is provided and is either `'latin1'`, `'hex'` or `'base64'`, `public_key` is expected to be a string. If no `encoding` is provided, `public_key` is expected -to be a [`Buffer`][] or `Uint8Array`. +to be a [`Buffer`][], `TypedArray`, or `DataView`. ### diffieHellman.verifyError -- `other_public_key` {string | Buffer | Uint8Array} +- `other_public_key` {string | Buffer | TypedArray | DataView} - `input_encoding` {string} - `output_encoding` {string} @@ -602,7 +604,8 @@ party's public key and returns the computed shared secret. The supplied key is interpreted using specified `input_encoding`, and the returned secret is encoded using the specified `output_encoding`. Encodings can be `'latin1'`, `'hex'`, or `'base64'`. If the `input_encoding` is not -provided, `other_public_key` is expected to be a [`Buffer`][] or `Uint8Array`. +provided, `other_public_key` is expected to be a [`Buffer`][], `TypedArray`, or +`DataView`. If `output_encoding` is given a string will be returned; otherwise a [`Buffer`][] is returned. @@ -658,13 +661,14 @@ returned. -- `private_key` {string | Buffer | Uint8Array} +- `private_key` {string | Buffer | TypedArray | DataView} - `encoding` {string} Sets the EC Diffie-Hellman private key. The `encoding` can be `'latin1'`, `'hex'` or `'base64'`. If `encoding` is provided, `private_key` is expected -to be a string; otherwise `private_key` is expected to be a [`Buffer`][] -or `Uint8Array`. +to be a string; otherwise `private_key` is expected to be a [`Buffer`][], +`TypedArray`, or `DataView`. + If `private_key` is not valid for the curve specified when the `ECDH` object was created, an error is thrown. Upon setting the private key, the associated public point (key) is also generated and set in the ECDH object. @@ -677,12 +681,12 @@ deprecated: v5.2.0 > Stability: 0 - Deprecated -- `public_key` {string | Buffer | Uint8Array} +- `public_key` {string | Buffer | TypedArray | DataView} - `encoding` {string} Sets the EC Diffie-Hellman public key. Key encoding can be `'latin1'`, `'hex'` or `'base64'`. If `encoding` is provided `public_key` is expected to -be a string; otherwise a [`Buffer`][] or `Uint8Array` is expected. +be a string; otherwise a [`Buffer`][], `TypedArray`, or `DataView` is expected. Note that there is not normally a reason to call this method because `ECDH` only requires a private key and the other party's public key to compute the @@ -795,14 +799,14 @@ changes: pr-url: https://github.com/nodejs/node/pull/5522 description: The default `input_encoding` changed from `binary` to `utf8`. --> -- `data` {string | Buffer | Uint8Array} +- `data` {string | Buffer | TypedArray | DataView} - `input_encoding` {string} Updates the hash content with the given `data`, the encoding of which is given in `input_encoding` and can be `'utf8'`, `'ascii'` or `'latin1'`. If `encoding` is not provided, and the `data` is a string, an -encoding of `'utf8'` is enforced. If `data` is a [`Buffer`][] or `Uint8Array` -then `input_encoding` is ignored. +encoding of `'utf8'` is enforced. If `data` is a [`Buffer`][], `TypedArray`, or +`DataView`, then `input_encoding` is ignored. This can be called many times with new data as it is streamed. @@ -884,14 +888,14 @@ changes: pr-url: https://github.com/nodejs/node/pull/5522 description: The default `input_encoding` changed from `binary` to `utf8`. --> -- `data` {string | Buffer | Uint8Array} +- `data` {string | Buffer | TypedArray | DataView} - `input_encoding` {string} Updates the `Hmac` content with the given `data`, the encoding of which is given in `input_encoding` and can be `'utf8'`, `'ascii'` or `'latin1'`. If `encoding` is not provided, and the `data` is a string, an -encoding of `'utf8'` is enforced. If `data` is a [`Buffer`][] or `Uint8Array` -then `input_encoding` is ignored. +encoding of `'utf8'` is enforced. If `data` is a [`Buffer`][], `TypedArray`, or +`DataView`, then `input_encoding` is ignored. This can be called many times with new data as it is streamed. @@ -1010,14 +1014,14 @@ changes: pr-url: https://github.com/nodejs/node/pull/5522 description: The default `input_encoding` changed from `binary` to `utf8`. --> -- `data` {string | Buffer | Uint8Array} +- `data` {string | Buffer | TypedArray | DataView} - `input_encoding` {string} Updates the `Sign` content with the given `data`, the encoding of which is given in `input_encoding` and can be `'utf8'`, `'ascii'` or `'latin1'`. If `encoding` is not provided, and the `data` is a string, an -encoding of `'utf8'` is enforced. If `data` is a [`Buffer`][] or `Uint8Array` -then `input_encoding` is ignored. +encoding of `'utf8'` is enforced. If `data` is a [`Buffer`][], `TypedArray`, or +`DataView`, then `input_encoding` is ignored. This can be called many times with new data as it is streamed. @@ -1074,14 +1078,14 @@ changes: pr-url: https://github.com/nodejs/node/pull/5522 description: The default `input_encoding` changed from `binary` to `utf8`. --> -- `data` {string | Buffer | Uint8Array} +- `data` {string | Buffer | TypedArray | DataView} - `input_encoding` {string} Updates the `Verify` content with the given `data`, the encoding of which is given in `input_encoding` and can be `'utf8'`, `'ascii'` or `'latin1'`. If `encoding` is not provided, and the `data` is a string, an -encoding of `'utf8'` is enforced. If `data` is a [`Buffer`][] or `Uint8Array` -then `input_encoding` is ignored. +encoding of `'utf8'` is enforced. If `data` is a [`Buffer`][], `TypedArray`, or +`DataView`, then `input_encoding` is ignored. This can be called many times with new data as it is streamed. @@ -1094,7 +1098,7 @@ changes: description: Support for RSASSA-PSS and additional options was added. --> - `object` {string | Object} -- `signature` {string | Buffer | Uint8Array} +- `signature` {string | Buffer | TypedArray | DataView} - `signature_format` {string} Verifies the provided data using the given `object` and `signature`. @@ -1118,8 +1122,8 @@ or an object with one or more of the following properties: The `signature` argument is the previously calculated signature for the data, in the `signature_format` which can be `'latin1'`, `'hex'` or `'base64'`. If a `signature_format` is specified, the `signature` is expected to be a -string; otherwise `signature` is expected to be a [`Buffer`][] or -`Uint8Array`. +string; otherwise `signature` is expected to be a [`Buffer`][], +`TypedArray`, or `DataView`. Returns `true` or `false` depending on the validity of the signature for the data and public key. @@ -1167,7 +1171,7 @@ currently in use. Setting to true requires a FIPS build of Node.js. added: v0.1.94 --> - `algorithm` {string} -- `password` {string | Buffer | Uint8Array} +- `password` {string | Buffer | TypedArray | DataView} Creates and returns a `Cipher` object that uses the given `algorithm` and `password`. @@ -1177,8 +1181,8 @@ recent OpenSSL releases, `openssl list-cipher-algorithms` will display the available cipher algorithms. The `password` is used to derive the cipher key and initialization vector (IV). -The value must be either a `'latin1'` encoded string, a [`Buffer`][] or a -`Uint8Array`. +The value must be either a `'latin1'` encoded string, a [`Buffer`][], a +`TypedArray`, or a `DataView`. The implementation of `crypto.createCipher()` derives keys using the OpenSSL function [`EVP_BytesToKey`][] with the digest algorithm set to MD5, one @@ -1194,8 +1198,8 @@ to create the `Cipher` object. ### crypto.createCipheriv(algorithm, key, iv) - `algorithm` {string} -- `key` {string | Buffer | Uint8Array} -- `iv` {string | Buffer | Uint8Array} +- `key` {string | Buffer | TypedArray | DataView} +- `iv` {string | Buffer | TypedArray | DataView} Creates and returns a `Cipher` object, with the given `algorithm`, `key` and initialization vector (`iv`). @@ -1206,7 +1210,7 @@ available cipher algorithms. The `key` is the raw key used by the `algorithm` and `iv` is an [initialization vector][]. Both arguments must be `'utf8'` encoded strings, -[Buffers][`Buffer`] or `Uint8Array`s. +[Buffers][`Buffer`], `TypedArray`, or `DataView`s. ### crypto.createCredentials(details) - `algorithm` {string} -- `password` {string | Buffer | Uint8Array} +- `password` {string | Buffer | TypedArray | DataView} Creates and returns a `Decipher` object that uses the given `algorithm` and `password` (key). @@ -1253,8 +1257,8 @@ to create the `Decipher` object. added: v0.1.94 --> - `algorithm` {string} -- `key` {string | Buffer | Uint8Array} -- `iv` {string | Buffer | Uint8Array} +- `key` {string | Buffer | TypedArray | DataView} +- `iv` {string | Buffer | TypedArray | DataView} Creates and returns a `Decipher` object that uses the given `algorithm`, `key` and initialization vector (`iv`). @@ -1271,6 +1275,9 @@ The `key` is the raw key used by the `algorithm` and `iv` is an -- `prime` {string | Buffer | Uint8Array} +- `prime` {string | Buffer | TypedArray | DataView} - `prime_encoding` {string} -- `generator` {number | string | Buffer | Uint8Array} Defaults to `2`. +- `generator` {number | string | Buffer | TypedArray | DataView} Defaults to `2`. - `generator_encoding` {string} Creates a `DiffieHellman` key exchange object using the supplied `prime` and an @@ -1294,17 +1301,17 @@ The `prime_encoding` and `generator_encoding` arguments can be `'latin1'`, `'hex'`, or `'base64'`. If `prime_encoding` is specified, `prime` is expected to be a string; otherwise -a [`Buffer`][] or `Uint8Array` is expected. +a [`Buffer`][], `TypedArray`, or `DataView` is expected. If `generator_encoding` is specified, `generator` is expected to be a string; -otherwise either a number or [`Buffer`][] or `Uint8Array` is expected. +otherwise a number, [`Buffer`][], `TypedArray`, or `DataView` is expected. ### crypto.createDiffieHellman(prime_length[, generator]) - `prime_length` {number} -- `generator` {number | string | Buffer | Uint8Array} Defaults to `2`. +- `generator` {number | string | Buffer | TypedArray | DataView} Defaults to `2`. Creates a `DiffieHellman` key exchange object and generates a prime of `prime_length` bits using an optional specific numeric `generator`. @@ -1361,7 +1368,7 @@ input.on('readable', () => { added: v0.1.94 --> - `algorithm` {string} -- `key` {string | Buffer | Uint8Array} +- `key` {string | Buffer | TypedArray | DataView} Creates and returns an `Hmac` object that uses the given `algorithm` and `key`. @@ -1600,7 +1607,7 @@ added: v0.11.14 - `padding` {crypto.constants} An optional padding value defined in `crypto.constants`, which may be: `crypto.constants.RSA_NO_PADDING`, `RSA_PKCS1_PADDING`, or `crypto.constants.RSA_PKCS1_OAEP_PADDING`. -- `buffer` {Buffer | Uint8Array} +- `buffer` {Buffer | TypedArray | DataView} Decrypts `buffer` with `private_key`. @@ -1617,7 +1624,7 @@ added: v1.1.0 - `padding` {crypto.constants} An optional padding value defined in `crypto.constants`, which may be: `crypto.constants.RSA_NO_PADDING` or `RSA_PKCS1_PADDING`. -- `buffer` {Buffer | Uint8Array} +- `buffer` {Buffer | TypedArray | DataView} Encrypts `buffer` with `private_key`. @@ -1634,7 +1641,7 @@ added: v1.1.0 - `padding` {crypto.constants} An optional padding value defined in `crypto.constants`, which may be: `crypto.constants.RSA_NO_PADDING`, `RSA_PKCS1_PADDING`, or `crypto.constants.RSA_PKCS1_OAEP_PADDING`. -- `buffer` {Buffer | Uint8Array} +- `buffer` {Buffer | TypedArray | DataView} Decrypts `buffer` with `public_key`. @@ -1654,7 +1661,7 @@ added: v0.11.14 - `padding` {crypto.constants} An optional padding value defined in `crypto.constants`, which may be: `crypto.constants.RSA_NO_PADDING`, `RSA_PKCS1_PADDING`, or `crypto.constants.RSA_PKCS1_OAEP_PADDING`. -- `buffer` {Buffer | Uint8Array} +- `buffer` {Buffer | TypedArray | DataView} Encrypts `buffer` with `public_key`. @@ -1739,16 +1746,16 @@ is a bit field taking one of or a mix of the following flags (defined in -- `a` {Buffer | Uint8Array} -- `b` {Buffer | Uint8Array} +- `a` {Buffer | TypedArray | DataView} +- `b` {Buffer | TypedArray | DataView} Returns true if `a` is equal to `b`, without leaking timing information that would allow an attacker to guess one of the values. This is suitable for comparing HMAC digests or secret values like authentication cookies or [capability urls](https://www.w3.org/TR/capability-urls/). -`a` and `b` must both be `Buffer`s or `Uint8Array`s, and they must have the -same length. +`a` and `b` must both be `Buffer`s, `TypedArray`s, or `DataView`s, and they +must have the same length. **Note**: Use of `crypto.timingSafeEqual` does not guarantee that the *surrounding* code is timing-safe. Care should be taken to ensure that the diff --git a/lib/crypto.js b/lib/crypto.js index 0785ab617ea376..22da71f03e9f96 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -42,7 +42,6 @@ const timingSafeEqual = binding.timingSafeEqual; const Buffer = require('buffer').Buffer; const stream = require('stream'); const util = require('util'); -const { isUint8Array } = process.binding('util'); const LazyTransform = require('internal/streams/lazy_transform'); const DH_GENERATOR = 2; @@ -415,9 +414,9 @@ function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) { if (typeof sizeOrKey !== 'number' && typeof sizeOrKey !== 'string' && - !isUint8Array(sizeOrKey)) { + !ArrayBuffer.isView(sizeOrKey)) { throw new TypeError('First argument should be number, string, ' + - 'Uint8Array or Buffer'); + 'Buffer, TypedArray, or DataView'); } if (keyEncoding) { diff --git a/test/parallel/test-crypto-dh.js b/test/parallel/test-crypto-dh.js index 1c1ad0741deb8f..6ff22c83a5e58d 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -23,8 +23,9 @@ assert.strictEqual(secret2.toString('base64'), secret1); assert.strictEqual(dh1.verifyError, 0); assert.strictEqual(dh2.verifyError, 0); -const argumentsError = - /^TypeError: First argument should be number, string, Uint8Array or Buffer$/; +const argumentsError = new RegExp('^TypeError: First argument should be ' + + 'number, string, Buffer, TypedArray, or ' + + 'DataView$'); assert.throws(() => { crypto.createDiffieHellman([0x1, 0x2]); @@ -126,23 +127,10 @@ const modp2buf = Buffer.from([ assert.strictEqual(exmodp2.verifyError, DH_NOT_SUITABLE_GENERATOR); } -{ - // Ensure specific generator (string with encoding) works as expected. - const exmodp2 = crypto.createDiffieHellman(modp2buf, '02', 'hex'); - exmodp2.generateKeys(); - const modp2Secret = modp2.computeSecret(exmodp2.getPublicKey()) - .toString('hex'); - const exmodp2Secret = exmodp2.computeSecret(modp2.getPublicKey()) - .toString('hex'); - assert.strictEqual(modp2Secret, exmodp2Secret); - assert.strictEqual(exmodp2.verifyError, DH_NOT_SUITABLE_GENERATOR); -} - -{ - // Ensure specific generator (string with encoding) works as expected, - // with a Uint8Array as the first argument to createDiffieHellman(). - const exmodp2 = crypto.createDiffieHellman(new Uint8Array(modp2buf), - '02', 'hex'); +for (const buf of [modp2buf, ...common.getArrayBufferViews(modp2buf)]) { + // Ensure specific generator (string with encoding) works as expected with + // any ArrayBufferViews as the first argument to createDiffieHellman(). + const exmodp2 = crypto.createDiffieHellman(buf, '02', 'hex'); exmodp2.generateKeys(); const modp2Secret = modp2.computeSecret(exmodp2.getPublicKey()) .toString('hex');