From a8abe9c6a543e3c9162e12cbcdf1212d2ffd88b0 Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Mon, 3 Apr 2017 17:35:17 -0700 Subject: [PATCH 1/8] src: allow ArrayBufferView as instance of Buffer --- src/node_buffer.cc | 23 +++++++++++---------- src/node_util.cc | 1 + src/util.h | 4 ++-- test/parallel/test-buffer-write-noassert.js | 2 +- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/node_buffer.cc b/src/node_buffer.cc index d8879094fa9547..50760df8a3887e 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -70,6 +70,7 @@ namespace Buffer { using v8::ArrayBuffer; using v8::ArrayBufferCreationMode; +using v8::ArrayBufferView; using v8::Context; using v8::EscapableHandleScope; using v8::FunctionCallbackInfo; @@ -195,41 +196,41 @@ inline MUST_USE_RESULT bool ParseArrayIndex(Local arg, // Buffer methods bool HasInstance(Local val) { - return val->IsUint8Array(); + return val->IsArrayBufferView(); } bool HasInstance(Local obj) { - return obj->IsUint8Array(); + return obj->IsArrayBufferView(); } char* Data(Local val) { - CHECK(val->IsUint8Array()); - Local ui = val.As(); + CHECK(val->IsArrayBufferView()); + Local ui = val.As(); ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents(); return static_cast(ab_c.Data()) + ui->ByteOffset(); } char* Data(Local obj) { - CHECK(obj->IsUint8Array()); - Local ui = obj.As(); + CHECK(obj->IsArrayBufferView()); + Local ui = obj.As(); ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents(); return static_cast(ab_c.Data()) + ui->ByteOffset(); } size_t Length(Local val) { - CHECK(val->IsUint8Array()); - Local ui = val.As(); + CHECK(val->IsArrayBufferView()); + Local ui = val.As(); return ui->ByteLength(); } size_t Length(Local obj) { - CHECK(obj->IsUint8Array()); - Local ui = obj.As(); + CHECK(obj->IsArrayBufferView()); + Local ui = obj.As(); return ui->ByteLength(); } @@ -800,7 +801,7 @@ void WriteFloatGeneric(const FunctionCallbackInfo& args) { THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); } - Local ts_obj = args[0].As(); + Local ts_obj = args[0].As(); ArrayBuffer::Contents ts_obj_c = ts_obj->Buffer()->GetContents(); const size_t ts_obj_offset = ts_obj->ByteOffset(); const size_t ts_obj_length = ts_obj->ByteLength(); diff --git a/src/node_util.cc b/src/node_util.cc index 813995de796e44..ee50b7f584c81e 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -20,6 +20,7 @@ using v8::Value; #define VALUE_METHOD_MAP(V) \ V(isArrayBuffer, IsArrayBuffer) \ + V(isArrayBufferView, IsArrayBufferView) \ V(isDataView, IsDataView) \ V(isDate, IsDate) \ V(isExternal, IsExternal) \ diff --git a/src/util.h b/src/util.h index f43ccef8b8404c..c924209a3c8c45 100644 --- a/src/util.h +++ b/src/util.h @@ -439,8 +439,8 @@ class BufferValue : public MaybeStackBuffer { } while (0) #define SPREAD_BUFFER_ARG(val, name) \ - CHECK((val)->IsUint8Array()); \ - v8::Local name = (val).As(); \ + CHECK((val)->IsArrayBufferView()); \ + v8::Local name = (val).As(); \ v8::ArrayBuffer::Contents name##_c = name->Buffer()->GetContents(); \ const size_t name##_offset = name->ByteOffset(); \ const size_t name##_length = name->ByteLength(); \ diff --git a/test/parallel/test-buffer-write-noassert.js b/test/parallel/test-buffer-write-noassert.js index 10e9fd8b766837..9a5e4e6671fad9 100644 --- a/test/parallel/test-buffer-write-noassert.js +++ b/test/parallel/test-buffer-write-noassert.js @@ -18,7 +18,7 @@ function write(funx, args, result, res) { if (!/Int/.test(funx)) { assert.throws( - () => Buffer.alloc(9)[funx].apply(new Uint32Array(1), args), + () => Buffer.alloc(9)[funx].apply(new Map(), args), /^TypeError: argument should be a Buffer$/ ); } From dae94ffb7187b3c307f3bcc9b7c4410181492d13 Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Mon, 3 Apr 2017 18:29:41 -0700 Subject: [PATCH 2/8] test: add common.getArrayBufferViews(buf) --- test/README.md | 6 ++++++ test/common.js | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/test/README.md b/test/README.md index 65c9797f1c7e2a..0f959f0bf75028 100644 --- a/test/README.md +++ b/test/README.md @@ -216,6 +216,12 @@ The expected error should be [subclassed by the `internal/errors` module](https: Tests whether `name` and `expected` are part of a raised warning. +## getArrayBufferViews(buf) +* `buf` [<Buffer>](https://nodejs.org/api/buffer.html#buffer_class_buffer) +* return [<ArrayBufferView[]>](https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView) + +Returns an instance of all possible `ArrayBufferView`s of the provided Buffer. + ### hasCrypto * return [<Boolean>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type) diff --git a/test/common.js b/test/common.js index e77ee100cbe6cb..6ffc5b04f8b157 100644 --- a/test/common.js +++ b/test/common.js @@ -654,3 +654,29 @@ exports.skipIfInspectorDisabled = function skipIfInspectorDisabled() { process.exit(0); } }; + +const arrayBufferViews = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + DataView +]; + +exports.getArrayBufferViews = function getArrayBufferViews(buf) { + const { buffer, byteOffset, byteLength } = buf; + + const out = []; + for (const type of arrayBufferViews) { + const { BYTES_PER_ELEMENT = 1 } = type; + if (Number.isInteger(byteLength % BYTES_PER_ELEMENT)) { + out.push(new type(buffer, byteOffset, byteLength / BYTES_PER_ELEMENT)); + } + } + return out; +}; From c58b94543a4462f576c740aa40e84ca08779ab09 Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Tue, 4 Apr 2017 15:59:30 -0700 Subject: [PATCH 3/8] crypto: support all ArrayBufferView types --- doc/api/crypto.md | 127 +++++++++++++++++--------------- lib/crypto.js | 6 +- test/parallel/test-crypto-dh.js | 25 ++----- 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 3e7ed5e9c86960..141f1e335c96ce 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -42,7 +42,7 @@ const timingSafeEqual = binding.timingSafeEqual; const Buffer = require('buffer').Buffer; const stream = require('stream'); const util = require('util'); -const { isUint8Array } = process.binding('util'); +const { isArrayBufferView } = process.binding('util'); const LazyTransform = require('internal/streams/lazy_transform'); const DH_GENERATOR = 2; @@ -414,9 +414,9 @@ function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) { if (typeof sizeOrKey !== 'number' && typeof sizeOrKey !== 'string' && - !isUint8Array(sizeOrKey)) { + !isArrayBufferView(sizeOrKey)) { throw new TypeError('First argument should be number, string, ' + - 'Uint8Array or Buffer'); + 'ArrayBufferView or Buffer'); } if (keyEncoding) { diff --git a/test/parallel/test-crypto-dh.js b/test/parallel/test-crypto-dh.js index 3e664ce236ed5b..a4361579a7333c 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -23,8 +23,8 @@ 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, ArrayBufferView or Buffer$'); assert.throws(() => { crypto.createDiffieHellman([0x1, 0x2]); @@ -126,23 +126,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'); From 07a399be636fff1e1fd10453fcc13725566c0840 Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Mon, 3 Apr 2017 18:15:13 -0700 Subject: [PATCH 4/8] zlib: support all ArrayBufferView types --- doc/api/zlib.md | 69 +++++++++++++++---- lib/zlib.js | 28 ++++---- .../parallel/test-zlib-convenience-methods.js | 26 ++++--- .../test-zlib-deflate-constructors.js | 3 +- test/parallel/test-zlib-dictionary.js | 3 +- .../test-zlib-not-string-or-buffer.js | 4 +- 6 files changed, 90 insertions(+), 43 deletions(-) diff --git a/doc/api/zlib.md b/doc/api/zlib.md index 928070d5778ca7..a91456bb0b835e 100644 --- a/doc/api/zlib.md +++ b/doc/api/zlib.md @@ -300,7 +300,7 @@ ignored by the decompression classes. * `level` {integer} (compression only) * `memLevel` {integer} (compression only) * `strategy` {integer} (compression only) -* `dictionary` {Buffer|Uint8Array} (deflate/inflate only, empty dictionary by +* `dictionary` {Buffer|TypedArray|DataView} (deflate/inflate only, empty dictionary by default) See the description of `deflateInit2` and `inflateInit2` at @@ -477,9 +477,9 @@ Returns a new [Unzip][] object with an [options][]. -All of these take a [Buffer][], [Uint8Array][], or string as the first -argument, an optional second argument to supply options to the `zlib` classes -and will call the supplied callback with `callback(error, result)`. +All of these take a [`Buffer`][], [`TypedArray`][], [`DataView`][], or string as +the first argument, an optional second argument to supply options to the `zlib` +classes and will call the supplied callback with `callback(error, result)`. Every method has a `*Sync` counterpart, which accept the same arguments, but without a callback. @@ -488,6 +488,9 @@ without a callback. -- `buffer` {Buffer|Uint8Array|string} +- `buffer` {Buffer|TypedArray|DataView|string} Compress a chunk of data with [Deflate][]. @@ -509,6 +515,9 @@ Compress a chunk of data with [Deflate][]. -- `buffer` {Buffer|Uint8Array|string} +- `buffer` {Buffer|TypedArray|DataView|string} Compress a chunk of data with [DeflateRaw][]. @@ -530,6 +542,9 @@ Compress a chunk of data with [DeflateRaw][]. -- `buffer` {Buffer|Uint8Array|string} +- `buffer` {Buffer|TypedArray|DataView|string} Decompress a chunk of data with [Gunzip][]. @@ -551,6 +569,9 @@ Decompress a chunk of data with [Gunzip][]. -- `buffer` {Buffer|Uint8Array|string} +- `buffer` {Buffer|TypedArray|DataView|string} Compress a chunk of data with [Gzip][]. @@ -572,6 +596,9 @@ Compress a chunk of data with [Gzip][]. -- `buffer` {Buffer|Uint8Array|string} +- `buffer` {Buffer|TypedArray|DataView|string} Decompress a chunk of data with [Inflate][]. @@ -593,6 +623,9 @@ Decompress a chunk of data with [Inflate][]. -- `buffer` {Buffer|Uint8Array|string} +- `buffer` {Buffer|TypedArray|DataView|string} Decompress a chunk of data with [InflateRaw][]. @@ -614,6 +650,9 @@ Decompress a chunk of data with [InflateRaw][]. -- `buffer` {Buffer|Uint8Array|string} +- `buffer` {Buffer|TypedArray|DataView|string} Decompress a chunk of data with [Unzip][]. @@ -644,5 +686,6 @@ Decompress a chunk of data with [Unzip][]. [InflateRaw]: #zlib_class_zlib_inflateraw [Unzip]: #zlib_class_zlib_unzip [`.flush()`]: #zlib_zlib_flush_kind_callback -[Buffer]: buffer.html -[Uint8Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array +[`Buffer`]: buffer.html#buffer_class_buffer +[`DataView`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView +[`TypedArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray diff --git a/lib/zlib.js b/lib/zlib.js index 07040c3ebc581e..05c4fe8104e310 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -24,7 +24,7 @@ const Buffer = require('buffer').Buffer; const internalUtil = require('internal/util'); const Transform = require('_stream_transform'); -const { isUint8Array } = process.binding('util'); +const { isArrayBufferView } = process.binding('util'); const binding = process.binding('zlib'); const assert = require('assert').ok; const kMaxLength = require('buffer').kMaxLength; @@ -79,9 +79,9 @@ function isInvalidStrategy(strategy) { } function zlibBuffer(engine, buffer, callback) { - // Streams do not support non-Buffer Uint8Arrays yet. Convert it to a + // Streams do not support non-Buffer ArrayBufferViews yet. Convert it to a // Buffer without copying. - if (isUint8Array(buffer) && + if (isArrayBufferView(buffer) && Object.getPrototypeOf(buffer) !== Buffer.prototype) { buffer = Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength); } @@ -99,7 +99,7 @@ function zlibBuffer(engine, buffer, callback) { var chunk; while (null !== (chunk = engine.read())) { buffers.push(chunk); - nread += chunk.length; + nread += chunk.byteLength; } engine.once('readable', flow); } @@ -129,9 +129,9 @@ function zlibBuffer(engine, buffer, callback) { function zlibBufferSync(engine, buffer) { if (typeof buffer === 'string') buffer = Buffer.from(buffer); - else if (!isUint8Array(buffer)) - throw new TypeError('"buffer" argument must be a string, Buffer, or ' + - 'Uint8Array'); + else if (!isArrayBufferView(buffer)) + throw new TypeError('"buffer" argument must be a string, Buffer, ' + + 'TypedArray, or DataView'); var flushFlag = engine._finishFlushFlag; @@ -214,9 +214,9 @@ class Zlib extends Transform { throw new TypeError('Invalid strategy: ' + opts.strategy); if (opts.dictionary) { - if (!isUint8Array(opts.dictionary)) { + if (!isArrayBufferView(opts.dictionary)) { throw new TypeError( - 'Invalid dictionary: it should be a Buffer or an Uint8Array'); + 'Invalid dictionary: it should be a Buffer, TypedArray, or DataView'); } } @@ -309,9 +309,9 @@ class Zlib extends Transform { var flushFlag; var ws = this._writableState; var ending = ws.ending || ws.ended; - var last = ending && (!chunk || ws.length === chunk.length); + var last = ending && (!chunk || ws.length === chunk.byteLength); - if (chunk !== null && !isUint8Array(chunk)) + if (chunk !== null && !isArrayBufferView(chunk)) return cb(new TypeError('invalid input')); if (!this._handle) @@ -328,7 +328,7 @@ class Zlib extends Transform { flushFlag = this._flushFlag; // once we've flushed the last of the queue, stop flushing and // go back to the normal behavior. - if (chunk.length >= ws.length) { + if (chunk.byteLength >= ws.length) { this._flushFlag = this._opts.flush || constants.Z_NO_FLUSH; } } @@ -337,7 +337,7 @@ class Zlib extends Transform { } _processChunk(chunk, flushFlag, cb) { - var availInBefore = chunk && chunk.length; + var availInBefore = chunk && chunk.byteLength; var availOutBefore = this._chunkSize - this._offset; var inOff = 0; @@ -417,7 +417,7 @@ class Zlib extends Transform { self.push(out); } else { buffers.push(out); - nread += out.length; + nread += out.byteLength; } } diff --git a/test/parallel/test-zlib-convenience-methods.js b/test/parallel/test-zlib-convenience-methods.js index df56f21ff49529..78bb105906fb00 100644 --- a/test/parallel/test-zlib-convenience-methods.js +++ b/test/parallel/test-zlib-convenience-methods.js @@ -26,24 +26,28 @@ const common = require('../common'); const assert = require('assert'); const zlib = require('zlib'); -const expectStr = 'blahblahblahblahblahblah'; +// Must be a multiple of 4 characters in total to test all ArrayBufferView +// types. +const expectStr = 'blah'.repeat(8); const expectBuf = Buffer.from(expectStr); -const expectUint8Array = new Uint8Array(expectBuf); + const opts = { level: 9, chunkSize: 1024, }; -for (const method of [ - ['gzip', 'gunzip'], - ['gzip', 'unzip'], - ['deflate', 'inflate'], - ['deflateRaw', 'inflateRaw'], +for (const [type, expect] of [ + ['string', expectStr], + ['Buffer', expectBuf], + ...common.getArrayBufferViews(expectBuf).map((obj) => + [obj[Symbol.toStringTag], obj] + ) ]) { - for (const [type, expect] of [ - ['string', expectStr], - ['Buffer', expectBuf], - ['Uint8Array', expectUint8Array] + for (const method of [ + ['gzip', 'gunzip'], + ['gzip', 'unzip'], + ['deflate', 'inflate'], + ['deflateRaw', 'inflateRaw'], ]) { zlib[method[0]](expect, opts, common.mustCall((err, result) => { zlib[method[1]](result, opts, common.mustCall((err, result) => { diff --git a/test/parallel/test-zlib-deflate-constructors.js b/test/parallel/test-zlib-deflate-constructors.js index c495d2d11d5acb..49695fbaa55585 100644 --- a/test/parallel/test-zlib-deflate-constructors.js +++ b/test/parallel/test-zlib-deflate-constructors.js @@ -107,5 +107,6 @@ assert.throws( // Throws if opts.dictionary is not a Buffer assert.throws( () => { new zlib.Deflate({dictionary: 'not a buffer'}); }, - /^TypeError: Invalid dictionary: it should be a Buffer or an Uint8Array$/ + new RegExp('^TypeError: Invalid dictionary: it should be a Buffer, ' + + 'TypedArray, or DataView$') ); diff --git a/test/parallel/test-zlib-dictionary.js b/test/parallel/test-zlib-dictionary.js index a3b55bc72d4fc6..1662e63bca135c 100644 --- a/test/parallel/test-zlib-dictionary.js +++ b/test/parallel/test-zlib-dictionary.js @@ -41,7 +41,6 @@ const spdyDict = Buffer.from([ 'ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1', '.1statusversionurl\0' ].join('')); -const spdyDictUint8Array = new Uint8Array(spdyDict); const input = [ 'HTTP/1.1 200 Ok', @@ -168,7 +167,7 @@ function deflateRawResetDictionaryTest(spdyDict) { }); } -for (const dict of [spdyDict, spdyDictUint8Array]) { +for (const dict of [spdyDict, ...common.getArrayBufferViews(spdyDict)]) { basicDictionaryTest(dict); deflateResetDictionaryTest(dict); rawDictionaryTest(dict); diff --git a/test/parallel/test-zlib-not-string-or-buffer.js b/test/parallel/test-zlib-not-string-or-buffer.js index 510e111f709699..43488f00754342 100644 --- a/test/parallel/test-zlib-not-string-or-buffer.js +++ b/test/parallel/test-zlib-not-string-or-buffer.js @@ -7,8 +7,8 @@ require('../common'); const assert = require('assert'); const zlib = require('zlib'); -const expected = - /^TypeError: "buffer" argument must be a string, Buffer, or Uint8Array$/; +const expected = new RegExp('^TypeError: "buffer" argument must be a string, ' + + 'Buffer, TypedArray, or DataView$'); assert.throws(() => { zlib.deflateSync(undefined); }, expected); assert.throws(() => { zlib.deflateSync(null); }, expected); From 1459f278e42483e1730b804a5c00235cdb8cc48a Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Wed, 5 Apr 2017 08:30:17 -0700 Subject: [PATCH 5/8] fixup crypto --- lib/crypto.js | 5 ++--- test/parallel/test-crypto-dh.js | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/crypto.js b/lib/crypto.js index 141f1e335c96ce..f93809baddc346 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 { isArrayBufferView } = process.binding('util'); const LazyTransform = require('internal/streams/lazy_transform'); const DH_GENERATOR = 2; @@ -414,9 +413,9 @@ function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) { if (typeof sizeOrKey !== 'number' && typeof sizeOrKey !== 'string' && - !isArrayBufferView(sizeOrKey)) { + !ArrayBuffer.isView(sizeOrKey)) { throw new TypeError('First argument should be number, string, ' + - 'ArrayBufferView 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 a4361579a7333c..11fdd4c1cf5dfc 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -24,7 +24,8 @@ assert.strictEqual(dh1.verifyError, 0); assert.strictEqual(dh2.verifyError, 0); const argumentsError = new RegExp('^TypeError: First argument should be ' + - 'number, string, ArrayBufferView or Buffer$'); + 'number, string, Buffer, TypedArray, or ' + + 'DataView$'); assert.throws(() => { crypto.createDiffieHellman([0x1, 0x2]); From 039208e7f44a1544d802fd16865fc2c5afb9a6fb Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Wed, 5 Apr 2017 08:33:08 -0700 Subject: [PATCH 6/8] fixup zlib --- lib/zlib.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/zlib.js b/lib/zlib.js index 05c4fe8104e310..00be56dffc44fb 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -24,7 +24,6 @@ const Buffer = require('buffer').Buffer; const internalUtil = require('internal/util'); const Transform = require('_stream_transform'); -const { isArrayBufferView } = process.binding('util'); const binding = process.binding('zlib'); const assert = require('assert').ok; const kMaxLength = require('buffer').kMaxLength; @@ -81,7 +80,7 @@ function isInvalidStrategy(strategy) { function zlibBuffer(engine, buffer, callback) { // Streams do not support non-Buffer ArrayBufferViews yet. Convert it to a // Buffer without copying. - if (isArrayBufferView(buffer) && + if (ArrayBuffer.isView(buffer) && Object.getPrototypeOf(buffer) !== Buffer.prototype) { buffer = Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength); } @@ -129,7 +128,7 @@ function zlibBuffer(engine, buffer, callback) { function zlibBufferSync(engine, buffer) { if (typeof buffer === 'string') buffer = Buffer.from(buffer); - else if (!isArrayBufferView(buffer)) + else if (!ArrayBuffer.isView(buffer)) throw new TypeError('"buffer" argument must be a string, Buffer, ' + 'TypedArray, or DataView'); @@ -214,7 +213,7 @@ class Zlib extends Transform { throw new TypeError('Invalid strategy: ' + opts.strategy); if (opts.dictionary) { - if (!isArrayBufferView(opts.dictionary)) { + if (!ArrayBuffer.isView(opts.dictionary)) { throw new TypeError( 'Invalid dictionary: it should be a Buffer, TypedArray, or DataView'); } @@ -311,7 +310,7 @@ class Zlib extends Transform { var ending = ws.ending || ws.ended; var last = ending && (!chunk || ws.length === chunk.byteLength); - if (chunk !== null && !isArrayBufferView(chunk)) + if (chunk !== null && !ArrayBuffer.isView(chunk)) return cb(new TypeError('invalid input')); if (!this._handle) From 072e898694bafb184251f6881a6ab7315ccbabfa Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Wed, 5 Apr 2017 08:33:29 -0700 Subject: [PATCH 7/8] fixup test --- test/common.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/common.js b/test/common.js index 6ffc5b04f8b157..ba377bebd1366a 100644 --- a/test/common.js +++ b/test/common.js @@ -674,7 +674,7 @@ exports.getArrayBufferViews = function getArrayBufferViews(buf) { const out = []; for (const type of arrayBufferViews) { const { BYTES_PER_ELEMENT = 1 } = type; - if (Number.isInteger(byteLength % BYTES_PER_ELEMENT)) { + if (byteLength % BYTES_PER_ELEMENT === 0) { out.push(new type(buffer, byteOffset, byteLength / BYTES_PER_ELEMENT)); } } From 13152405dd8f248192fea558a9bc206201f884f1 Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Wed, 5 Apr 2017 08:33:58 -0700 Subject: [PATCH 8/8] fixup util --- src/node_util.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/node_util.cc b/src/node_util.cc index ee50b7f584c81e..813995de796e44 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -20,7 +20,6 @@ using v8::Value; #define VALUE_METHOD_MAP(V) \ V(isArrayBuffer, IsArrayBuffer) \ - V(isArrayBufferView, IsArrayBufferView) \ V(isDataView, IsDataView) \ V(isDate, IsDate) \ V(isExternal, IsExternal) \