diff --git a/.eslintrc b/.eslintrc index a755cdb..ff010df 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,3 +1,26 @@ { - "extends": ["standard"] + "root": true, + + "extends": "@ljharb", + + "rules": { + "func-style": "off", + }, + + "globals": { + "Uint8Array": false, + "Uint16Array": false, + }, + + "overrides": [ + { + "files": [ + "./index.js", + "./test/index.js", + ], + "rules": { + "no-underscore-dangle": "warn", + }, + }, + ], } diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..fbfd907 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: [ljharb] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: npm/cipher-base +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/workflows/node-aught.yml b/.github/workflows/node-aught.yml new file mode 100644 index 0000000..1af040e --- /dev/null +++ b/.github/workflows/node-aught.yml @@ -0,0 +1,11 @@ +name: 'Tests: node.js < 10' + +on: [pull_request, push] + +jobs: + tests: + uses: ljharb/actions/.github/workflows/node.yml@main + with: + range: '0.10 - 9' + type: minors + command: npm run tests-only diff --git a/.github/workflows/node-pretest.yml b/.github/workflows/node-pretest.yml new file mode 100644 index 0000000..765edf7 --- /dev/null +++ b/.github/workflows/node-pretest.yml @@ -0,0 +1,7 @@ +name: 'Tests: pretest/posttest' + +on: [pull_request, push] + +jobs: + tests: + uses: ljharb/actions/.github/workflows/pretest.yml@main diff --git a/.github/workflows/node-tens.yml b/.github/workflows/node-tens.yml new file mode 100644 index 0000000..1f55be0 --- /dev/null +++ b/.github/workflows/node-tens.yml @@ -0,0 +1,11 @@ +name: 'Tests: node.js >= 10' + +on: [pull_request, push] + +jobs: + tests: + uses: ljharb/actions/.github/workflows/node.yml@main + with: + range: '>= 10' + type: minors + command: npm run tests-only diff --git a/.github/workflows/node-twenties.yml b/.github/workflows/node-twenties.yml new file mode 100644 index 0000000..209e157 --- /dev/null +++ b/.github/workflows/node-twenties.yml @@ -0,0 +1,14 @@ +name: 'Tests: node.js >= 20' + +on: [pull_request, push] + +permissions: + contents: read + +jobs: + tests: + uses: ljharb/actions/.github/workflows/node.yml@main + with: + range: '>= 20' + type: minors + command: npm run tests-only diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml new file mode 100644 index 0000000..8b2a811 --- /dev/null +++ b/.github/workflows/rebase.yml @@ -0,0 +1,22 @@ +name: Automatic Rebase + +on: [pull_request_target] + +permissions: + contents: read + +jobs: + _: + permissions: + contents: write # for ljharb/rebase to push code to rebase + pull-requests: read # for ljharb/rebase to get info about PR + + name: "Automatic Rebase" + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: ljharb/rebase@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/require-allow-edits.yml b/.github/workflows/require-allow-edits.yml new file mode 100644 index 0000000..7b842f8 --- /dev/null +++ b/.github/workflows/require-allow-edits.yml @@ -0,0 +1,12 @@ +name: Require “Allow Edits” + +on: [pull_request_target] + +jobs: + _: + name: "Require “Allow Edits”" + + runs-on: ubuntu-latest + + steps: + - uses: ljharb/require-allow-edits@main diff --git a/.gitignore b/.gitignore index 3c3629e..34aaa91 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,8 @@ node_modules + +# Only apps should have lockfiles +npm-shrinkwrap.json +package-lock.json +yarn.lock + +.npmignore diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..eacea13 --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +package-lock=false +allow-same-version=true +message=v%s diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index eb83acd..0000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: node_js -node_js: - - "0.11" - - "0.10" - - "0.12" - - "iojs" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a99ec0d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,66 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [v1.0.6](https://github.com/browserify/cipher-base/compare/v1.0.5...v1.0.6) - 2024-11-26 + +### Commits + +- [Fix] io.js 3.0 - Node.js 5.3 typed array support [`b7ddd2a`](https://github.com/browserify/cipher-base/commit/b7ddd2ac24e65cc47befc1e0eb5026422f8ab037) + +## [v1.0.5](https://github.com/browserify/cipher-base/compare/v1.0.4...v1.0.5) - 2024-11-17 + +### Commits + +- [Tests] standard -> eslint, make test dir, etc [`ae02fd6`](https://github.com/browserify/cipher-base/commit/ae02fd6624c41ac4ac18077be797111d1955bc76) +- [Tests] migrate from travis to GHA [`66387d7`](https://github.com/browserify/cipher-base/commit/66387d71461287ad9067bb1bcbfdc47403a33ee7) +- [meta] fix package.json indentation [`5c02918`](https://github.com/browserify/cipher-base/commit/5c02918ac58c875ed36913c2dc3e1043f4d1c99c) +- [Fix] return valid values on multi-byte-wide TypedArray input [`8fd1364`](https://github.com/browserify/cipher-base/commit/8fd136432ca298a664f5637629cf2b42a6c7f294) +- [meta] add `auto-changelog` [`88dc806`](https://github.com/browserify/cipher-base/commit/88dc806806d3dc41444dbf639c87c00f82c949b3) +- [meta] add `npmignore` and `safe-publish-latest` [`7a137d7`](https://github.com/browserify/cipher-base/commit/7a137d749ce7ea7ea56b9c096844b1b8ab723f61) +- Only apps should have lockfiles [`42528f2`](https://github.com/browserify/cipher-base/commit/42528f291db16bf2e7d5f831ebe2ad87fd0b1f42) +- [Deps] update `inherits`, `safe-buffer` [`0e7a2d9`](https://github.com/browserify/cipher-base/commit/0e7a2d9a33a391e82fa9cf512d6e25cc91ab8613) +- [meta] add missing `engines.node` [`f2dc13e`](https://github.com/browserify/cipher-base/commit/f2dc13e47bbcf3c873db9a9e0f83e5f29d0783fe) + +## [v1.0.4](https://github.com/browserify/cipher-base/compare/v1.0.3...v1.0.4) - 2017-07-06 + +### Merged + +- Safe Buffer & License [`#8`](https://github.com/browserify/cipher-base/pull/8) +- fix break in node 8 [`#9`](https://github.com/browserify/cipher-base/pull/9) + +### Commits + +- use safe-buffer [`193bbeb`](https://github.com/browserify/cipher-base/commit/193bbeb0d60ff206b072d4e18b32fcf150eb0fad) +- add MIT LICENSE [`a9fb6e3`](https://github.com/browserify/cipher-base/commit/a9fb6e316c25b25d2bd04fa5b6eb748318bdcb04) +- index: add missing Buffer.alloc [`2397a9e`](https://github.com/browserify/cipher-base/commit/2397a9e0db33b2f3d6abbd89f01db1c066804a91) +- rename README to README, add .gitignore [`9345e1d`](https://github.com/browserify/cipher-base/commit/9345e1d21fd10555bc4f39ccae5512ca0202c5f6) + +## [v1.0.3](https://github.com/browserify/cipher-base/compare/v1.0.2...v1.0.3) - 2016-09-13 + +### Merged + +- closure-compiler fix - final is a reserved word [`#4`](https://github.com/browserify/cipher-base/pull/4) + +## [v1.0.2](https://github.com/browserify/cipher-base/compare/v1.0.1...v1.0.2) - 2015-10-23 + +### Merged + +- fix up typo in README [`#1`](https://github.com/browserify/cipher-base/pull/1) + +### Commits + +- Update api-methods. [`c4a7156`](https://github.com/browserify/cipher-base/commit/c4a7156c7beedb524ce5005374d2fd1327fa3ca4) +- travis [`a5873f7`](https://github.com/browserify/cipher-base/commit/a5873f7dbfe542d7ec28bcb3e306096d1d2b951c) +- Add api-methods [`208b338`](https://github.com/browserify/cipher-base/commit/208b33841d3e8a6f5322e54bdf442e1ee0d77590) +- Update error messages [`dc19a24`](https://github.com/browserify/cipher-base/commit/dc19a24119786518c1c835d948e36ddae3bf3adb) + +## v1.0.1 - 2015-09-26 + +### Commits + +- first [`8fbd9e7`](https://github.com/browserify/cipher-base/commit/8fbd9e7654d384b578e63d12e0311ae16dc07741) +- fix error in 0.10 [`61491fe`](https://github.com/browserify/cipher-base/commit/61491fe31fcc9bee0901f93b26cee6eaaca07575) diff --git a/index.js b/index.js index 6728005..804c108 100644 --- a/index.js +++ b/index.js @@ -1,99 +1,167 @@ -var Buffer = require('safe-buffer').Buffer -var Transform = require('stream').Transform -var StringDecoder = require('string_decoder').StringDecoder -var inherits = require('inherits') - -function CipherBase (hashMode) { - Transform.call(this) - this.hashMode = typeof hashMode === 'string' - if (this.hashMode) { - this[hashMode] = this._finalOrDigest - } else { - this.final = this._finalOrDigest - } - if (this._final) { - this.__final = this._final - this._final = null - } - this._decoder = null - this._encoding = null +'use strict'; + +var Buffer = require('safe-buffer').Buffer; +var Transform = require('stream').Transform; +var StringDecoder = require('string_decoder').StringDecoder; +var inherits = require('inherits'); + +function CipherBase(hashMode) { + Transform.call(this); + this.hashMode = typeof hashMode === 'string'; + if (this.hashMode) { + this[hashMode] = this._finalOrDigest; + } else { + this['final'] = this._finalOrDigest; + } + if (this._final) { + this.__final = this._final; + this._final = null; + } + this._decoder = null; + this._encoding = null; } -inherits(CipherBase, Transform) +inherits(CipherBase, Transform); -CipherBase.prototype.update = function (data, inputEnc, outputEnc) { - if (typeof data === 'string') { - data = Buffer.from(data, inputEnc) - } +var useUint8Array = typeof Uint8Array !== 'undefined'; +var useArrayBuffer = typeof ArrayBuffer !== 'undefined' + && typeof Uint8Array !== 'undefined' + && ArrayBuffer.isView + && (Buffer.prototype instanceof Uint8Array || Buffer.TYPED_ARRAY_SUPPORT); + +function toBuffer(data, encoding) { + /* + * No need to do anything for exact instance + * This is only valid when safe-buffer.Buffer === buffer.Buffer, i.e. when Buffer.from/Buffer.alloc existed + */ + if (data instanceof Buffer) { + return data; + } + + // Convert strings to Buffer + if (typeof data === 'string') { + return Buffer.from(data, encoding); + } - var outData = this._update(data) - if (this.hashMode) return this + /* + * Wrap any TypedArray instances and DataViews + * Makes sense only on engines with full TypedArray support -- let Buffer detect that + */ + if (useArrayBuffer && ArrayBuffer.isView(data)) { + // Bug in Node.js <6.3.1, which treats this as out-of-bounds + if (data.byteLength === 0) { + return Buffer.alloc(0); + } - if (outputEnc) { - outData = this._toString(outData, outputEnc) - } + var res = Buffer.from(data.buffer, data.byteOffset, data.byteLength); + /* + * Recheck result size, as offset/length doesn't work on Node.js <5.10 + * We just go to Uint8Array case if this fails + */ + if (res.byteLength === data.byteLength) { + return res; + } + } - return outData + /* + * Uint8Array in engines where Buffer.from might not work with ArrayBuffer, just copy over + * Doesn't make sense with other TypedArray instances + */ + if (useUint8Array && data instanceof Uint8Array) { + return Buffer.from(data); + } + + /* + * Old Buffer polyfill on an engine that doesn't have TypedArray support + * Also, this is from a different Buffer polyfill implementation then we have, as instanceof check failed + * Convert to our current Buffer implementation + */ + if ( + Buffer.isBuffer(data) + && data.constructor + && typeof data.constructor.isBuffer === 'function' + && data.constructor.isBuffer(data) + ) { + return Buffer.from(data); + } + + throw new TypeError('The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView.'); } -CipherBase.prototype.setAutoPadding = function () {} +CipherBase.prototype.update = function (data, inputEnc, outputEnc) { + var bufferData = toBuffer(data, inputEnc); // asserts correct input type + var outData = this._update(bufferData); + if (this.hashMode) { + return this; + } + + if (outputEnc) { + outData = this._toString(outData, outputEnc); + } + + return outData; +}; + +CipherBase.prototype.setAutoPadding = function () {}; CipherBase.prototype.getAuthTag = function () { - throw new Error('trying to get auth tag in unsupported state') -} + throw new Error('trying to get auth tag in unsupported state'); +}; CipherBase.prototype.setAuthTag = function () { - throw new Error('trying to set auth tag in unsupported state') -} + throw new Error('trying to set auth tag in unsupported state'); +}; CipherBase.prototype.setAAD = function () { - throw new Error('trying to set aad in unsupported state') -} + throw new Error('trying to set aad in unsupported state'); +}; CipherBase.prototype._transform = function (data, _, next) { - var err - try { - if (this.hashMode) { - this._update(data) - } else { - this.push(this._update(data)) - } - } catch (e) { - err = e - } finally { - next(err) - } -} + var err; + try { + if (this.hashMode) { + this._update(data); + } else { + this.push(this._update(data)); + } + } catch (e) { + err = e; + } finally { + next(err); + } +}; CipherBase.prototype._flush = function (done) { - var err - try { - this.push(this.__final()) - } catch (e) { - err = e - } - - done(err) -} + var err; + try { + this.push(this.__final()); + } catch (e) { + err = e; + } + + done(err); +}; CipherBase.prototype._finalOrDigest = function (outputEnc) { - var outData = this.__final() || Buffer.alloc(0) - if (outputEnc) { - outData = this._toString(outData, outputEnc, true) - } - return outData -} + var outData = this.__final() || Buffer.alloc(0); + if (outputEnc) { + outData = this._toString(outData, outputEnc, true); + } + return outData; +}; CipherBase.prototype._toString = function (value, enc, fin) { - if (!this._decoder) { - this._decoder = new StringDecoder(enc) - this._encoding = enc - } + if (!this._decoder) { + this._decoder = new StringDecoder(enc); + this._encoding = enc; + } - if (this._encoding !== enc) throw new Error('can\'t switch encodings') + if (this._encoding !== enc) { + throw new Error('can’t switch encodings'); + } - var out = this._decoder.write(value) - if (fin) { - out += this._decoder.end() - } + var out = this._decoder.write(value); + if (fin) { + out += this._decoder.end(); + } - return out -} + return out; +}; -module.exports = CipherBase +module.exports = CipherBase; diff --git a/package.json b/package.json index f372599..daef6d8 100644 --- a/package.json +++ b/package.json @@ -1,32 +1,62 @@ { - "name": "cipher-base", - "version": "1.0.4", - "description": "abstract base class for crypto-streams", - "main": "index.js", - "scripts": { - "test": "node test.js | tspec" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/crypto-browserify/cipher-base.git" - }, - "keywords": [ - "cipher", - "stream" - ], - "author": "Calvin Metcalf ", - "license": "MIT", - "bugs": { - "url": "https://github.com/crypto-browserify/cipher-base/issues" - }, - "homepage": "https://github.com/crypto-browserify/cipher-base#readme", - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "devDependencies": { - "standard": "^10.0.2", - "tap-spec": "^4.1.0", - "tape": "^4.2.0" - } + "name": "cipher-base", + "version": "1.0.6", + "description": "abstract base class for crypto-streams", + "main": "index.js", + "scripts": { + "prepack": "npmignore --auto --commentLines=autogenerated", + "prepublishOnly": "safe-publish-latest", + "prepublish": "not-in-publish || npm run prepublishOnly", + "lint": "eslint --ext=js,.mjs .", + "pretest": "npm run lint", + "test": "npm run tests-only", + "tests-only": "tape 'test/**/*.js'", + "posttest": "npx npm@'>=10.2' audit --production", + "version": "auto-changelog && git add CHANGELOG.md", + "postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\"" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/crypto-browserify/cipher-base.git" + }, + "keywords": [ + "cipher", + "stream" + ], + "author": "Calvin Metcalf ", + "license": "MIT", + "bugs": { + "url": "https://github.com/crypto-browserify/cipher-base/issues" + }, + "homepage": "https://github.com/crypto-browserify/cipher-base#readme", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "devDependencies": { + "@ljharb/eslint-config": "^21.1.1", + "auto-changelog": "^2.5.0", + "encoding": "^0.1.13", + "eslint": "=8.8.0", + "in-publish": "^2.0.1", + "npmignore": "^0.3.1", + "safe-publish-latest": "^2.0.0", + "tape": "^5.9.0" + }, + "publishConfig": { + "ignore": [ + ".github/workflows" + ] + }, + "auto-changelog": { + "output": "CHANGELOG.md", + "template": "keepachangelog", + "unreleased": false, + "commitLimit": false, + "backfillLimit": false, + "hideCredit": true + }, + "engines": { + "node": ">= 0.10" + } } diff --git a/test.js b/test.js deleted file mode 100644 index 29d3492..0000000 --- a/test.js +++ /dev/null @@ -1,111 +0,0 @@ -var Buffer = require('safe-buffer').Buffer -var CipherBase = require('./') - -var test = require('tape') -var inherits = require('inherits') - -test('basic version', function (t) { - function Cipher () { - CipherBase.call(this) - } - inherits(Cipher, CipherBase) - Cipher.prototype._update = function (input) { - t.ok(Buffer.isBuffer(input)) - return input - } - Cipher.prototype._final = function () { - // noop - } - var cipher = new Cipher() - var utf8 = 'abc123abcd' - var update = cipher.update(utf8, 'utf8', 'base64') + cipher.final('base64') - var string = (Buffer.from(update, 'base64')).toString() - t.equals(utf8, string) - t.end() -}) -test('hash mode', function (t) { - function Cipher () { - CipherBase.call(this, 'finalName') - this._cache = [] - } - inherits(Cipher, CipherBase) - Cipher.prototype._update = function (input) { - t.ok(Buffer.isBuffer(input)) - this._cache.push(input) - } - Cipher.prototype._final = function () { - return Buffer.concat(this._cache) - } - var cipher = new Cipher() - var utf8 = 'abc123abcd' - var update = cipher.update(utf8, 'utf8').finalName('base64') - var string = (Buffer.from(update, 'base64')).toString() - - t.equals(utf8, string) - t.end() -}) -test('hash mode as stream', function (t) { - function Cipher () { - CipherBase.call(this, 'finalName') - this._cache = [] - } - inherits(Cipher, CipherBase) - Cipher.prototype._update = function (input) { - t.ok(Buffer.isBuffer(input)) - this._cache.push(input) - } - Cipher.prototype._final = function () { - return Buffer.concat(this._cache) - } - var cipher = new Cipher() - cipher.on('error', function (e) { - t.notOk(e) - }) - var utf8 = 'abc123abcd' - cipher.end(utf8, 'utf8') - var update = cipher.read().toString('base64') - var string = (Buffer.from(update, 'base64')).toString() - - t.equals(utf8, string) - t.end() -}) - -test('encodings', function (t) { - inherits(Cipher, CipherBase) - function Cipher () { - CipherBase.call(this) - } - Cipher.prototype._update = function (input) { - return input - } - Cipher.prototype._final = function () { - // noop - } - t.test('mix and match encoding', function (t) { - t.plan(2) - - var cipher = new Cipher() - cipher.update('foo', 'utf8', 'utf8') - t.throws(function () { - cipher.update('foo', 'utf8', 'base64') - }) - cipher = new Cipher() - cipher.update('foo', 'utf8', 'base64') - t.doesNotThrow(function () { - cipher.update('foo', 'utf8') - cipher.final('base64') - }) - }) - t.test('handle long uft8 plaintexts', function (t) { - t.plan(1) - var txt = 'ふっかつ あきる すぶり はやい つける まゆげ たんさん みんぞく ねほりはほり せまい たいまつばな ひはん' - - var cipher = new Cipher() - var decipher = new Cipher() - var enc = decipher.update(cipher.update(txt, 'utf8', 'base64'), 'base64', 'utf8') - enc += decipher.update(cipher.final('base64'), 'base64', 'utf8') - enc += decipher.final('utf8') - - t.equals(txt, enc) - }) -}) diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..8a3ceeb --- /dev/null +++ b/test/index.js @@ -0,0 +1,221 @@ +'use strict'; + +var Buffer = require('safe-buffer').Buffer; +var CipherBase = require('../'); + +var test = require('tape'); +var inherits = require('inherits'); + +test('basic version', function (t) { + function Cipher() { + CipherBase.call(this); + } + + inherits(Cipher, CipherBase); + + Cipher.prototype._update = function (input) { + t.ok(Buffer.isBuffer(input)); + return input; + }; + + Cipher.prototype._final = function () { + // noop + }; + + var cipher = new Cipher(); + var utf8 = 'abc123abcd'; + var update = cipher.update(utf8, 'utf8', 'base64') + cipher['final']('base64'); + var string = Buffer.from(update, 'base64').toString(); + + t.equals(utf8, string); + + t.end(); +}); + +test('hash mode', function (t) { + function Cipher() { + CipherBase.call(this, 'finalName'); + this._cache = []; + } + inherits(Cipher, CipherBase); + Cipher.prototype._update = function (input) { + t.ok(Buffer.isBuffer(input)); + this._cache.push(input); + }; + Cipher.prototype._final = function () { + return Buffer.concat(this._cache); + }; + var cipher = new Cipher(); + var utf8 = 'abc123abcd'; + var update = cipher.update(utf8, 'utf8').finalName('base64'); + var string = Buffer.from(update, 'base64').toString(); + + t.equals(utf8, string); + + t.end(); +}); + +test('hash mode as stream', function (t) { + function Cipher() { + CipherBase.call(this, 'finalName'); + this._cache = []; + } + inherits(Cipher, CipherBase); + Cipher.prototype._update = function (input) { + t.ok(Buffer.isBuffer(input)); + this._cache.push(input); + }; + Cipher.prototype._final = function () { + return Buffer.concat(this._cache); + }; + var cipher = new Cipher(); + cipher.on('error', function (e) { + t.notOk(e); + }); + var utf8 = 'abc123abcd'; + cipher.end(utf8, 'utf8'); + var update = cipher.read().toString('base64'); + var string = Buffer.from(update, 'base64').toString(); + + t.equals(utf8, string); + + t.end(); +}); + +test('encodings', function (t) { + function Cipher() { + CipherBase.call(this); + } + inherits(Cipher, CipherBase); + + Cipher.prototype._update = function (input) { + return input; + }; + + Cipher.prototype._final = function () { + // noop + }; + + t.test('mix and match encoding', function (st) { + st.plan(2); + + var cipher = new Cipher(); + cipher.update('foo', 'utf8', 'utf8'); + + st['throws'](function () { + cipher.update('foo', 'utf8', 'base64'); + }); + + cipher = new Cipher(); + cipher.update('foo', 'utf8', 'base64'); + + st.doesNotThrow(function () { + cipher.update('foo', 'utf8'); + cipher['final']('base64'); + }); + }); + + t.test('handle long uft8 plaintexts', function (st) { + st.plan(1); + var txt = 'ふっかつ あきる すぶり はやい つける まゆげ たんさん みんぞく ねほりはほり せまい たいまつばな ひはん'; + + var cipher = new Cipher(); + var decipher = new Cipher(); + var enc = decipher.update(cipher.update(txt, 'utf8', 'base64'), 'base64', 'utf8'); + enc += decipher.update(cipher['final']('base64'), 'base64', 'utf8'); + enc += decipher['final']('utf8'); + + st.equals(txt, enc); + }); +}); + +test('handle SafeBuffer instances', function (t) { + function Cipher() { + CipherBase.call(this, 'finalName'); + this._cache = []; + } + inherits(Cipher, CipherBase); + Cipher.prototype._update = function (input) { + t.ok(Buffer.isBuffer(input)); + this._cache.push(input); + }; + Cipher.prototype._final = function () { + return Buffer.concat(this._cache); + }; + + var cipher = new Cipher(); + var final = cipher.update(Buffer.from('a0c1', 'hex')).finalName('hex'); + t.equals(final, 'a0c1'); + + t.end(); +}); + +test('handle Uint8Array view', function (t) { + function Cipher() { + CipherBase.call(this, 'finalName'); + this._cache = []; + } + inherits(Cipher, CipherBase); + Cipher.prototype._update = function (input) { + t.ok(Buffer.isBuffer(input)); + this._cache.push(input); + }; + Cipher.prototype._final = function () { + return Buffer.concat(this._cache); + }; + + var buf = new Uint8Array([0, 1, 2, 3, 4, 5]); + var uarr = new Uint8Array(buf.buffer, 2, 3); + + var cipher = new Cipher(); + var final = cipher.update(uarr).finalName('hex'); + t.equals(final, '020304'); + + t.end(); +}); + +test('handle empty Uint8Array instances', function (t) { + function Cipher() { + CipherBase.call(this, 'finalName'); + this._cache = []; + } + inherits(Cipher, CipherBase); + Cipher.prototype._update = function (input) { + t.ok(Buffer.isBuffer(input)); + this._cache.push(input); + }; + Cipher.prototype._final = function () { + return Buffer.concat(this._cache); + }; + + var cipher = new Cipher(); + var final = cipher.update(new Uint8Array(0)).finalName('hex'); + t.equals(final, ''); + + t.end(); +}); + +test('handle UInt16Array', function (t) { + function Cipher() { + CipherBase.call(this, 'finalName'); + this._cache = []; + } + inherits(Cipher, CipherBase); + Cipher.prototype._update = function (input) { + t.ok(Buffer.isBuffer(input)); + this._cache.push(input); + }; + Cipher.prototype._final = function () { + return Buffer.concat(this._cache); + }; + + if (ArrayBuffer.isView && (Buffer.prototype instanceof Uint8Array || Buffer.TYPED_ARRAY_SUPPORT)) { + var cipher = new Cipher(); + var final = cipher.update(new Uint16Array([1234, 512])).finalName('hex'); + t.equals(final, 'd2040002'); + } else { + t.skip('ArrayBuffer.isView and/or TypedArray not fully supported'); + } + + t.end(); +});