├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── aes.js ├── authCipher.js ├── bench ├── index.js └── package.json ├── browser.js ├── decrypter.js ├── encrypter.js ├── ghash.js ├── incr32.js ├── index.js ├── modes ├── cbc.js ├── cfb.js ├── cfb1.js ├── cfb8.js ├── ctr.js ├── ecb.js ├── index.js ├── list.json └── ofb.js ├── package.json ├── scripts └── populateFixtures.js ├── streamCipher.js └── test ├── extra.json ├── fixtures.json └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bench/ 2 | scripts/ 3 | test/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "4" 5 | - "5" 6 | - "6" 7 | - "7" 8 | - "8" 9 | matrix: 10 | include: 11 | - node_js: "7" 12 | env: TEST_SUITE=standard 13 | env: 14 | - TEST_SUITE=unit 15 | script: npm run-script $TEST_SUITE 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Crypto-js derived code is Copyright (c) 2009-2013, Jeff Mott 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | 13 | --- AND 14 | 15 | The MIT License (MIT) 16 | 17 | Triplesec derived code is Copyright (c) 2013, Maxwell Krohn 18 | browserify-aes contributions are Copyright (c) 2014-2017 browserify-aes contributors 19 | 20 | Permission is hereby granted, free of charge, to any person obtaining a copy 21 | of this software and associated documentation files (the "Software"), to deal 22 | in the Software without restriction, including without limitation the rights 23 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 | copies of the Software, and to permit persons to whom the Software is 25 | furnished to do so, subject to the following conditions: 26 | 27 | The above copyright notice and this permission notice shall be included in all 28 | copies or substantial portions of the Software. 29 | 30 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 36 | SOFTWARE. 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # browserify-aes 2 | [![Build Status](https://travis-ci.org/crypto-browserify/browserify-aes.svg)](https://travis-ci.org/crypto-browserify/browserify-aes) 3 | 4 | Node style aes for use in the browser. 5 | Implements: 6 | 7 | - createCipher 8 | - createCipheriv 9 | - createDecipher 10 | - createDecipheriv 11 | - getCiphers 12 | 13 | In node.js, the `crypto` implementation is used, in browsers it falls back to a pure JavaScript implementation. 14 | 15 | `EVP_BytesToKey` is a straight up port of the same function from OpenSSL as there is literally no documenation on it beyond it using 'undocumented extensions' for longer keys. 16 | 17 | ## LICENSES 18 | Much of this library has been taken from the AES implementation in [triplesec](https://github.com/keybase/triplesec) (apparently licensed MIT), which is a partial derivation of [crypto-js](https://code.google.com/p/crypto-js/), which is licensed BSD-3 clause, the relevant LICENSE text for both MIT and BSD-3 can be found in [LICENSE](LICENSE). 19 | 20 | Parts of `ghash.js` are a partial derivation of work by Juho Vähä-Herttua in [SJCL](https://github.com/bitwiseshiftleft/sjcl), which is LICENSED `BSD-2 || GPL-2`, with the LICENSE text included inline in `ghash.js`. 21 | -------------------------------------------------------------------------------- /aes.js: -------------------------------------------------------------------------------- 1 | // based on the aes implimentation in triple sec 2 | // https://github.com/keybase/triplesec 3 | // which is in turn based on the one from crypto-js 4 | // https://code.google.com/p/crypto-js/ 5 | 6 | var Buffer = require('safe-buffer').Buffer 7 | 8 | function asUInt32Array (buf) { 9 | if (!Buffer.isBuffer(buf)) buf = Buffer.from(buf) 10 | 11 | var len = (buf.length / 4) | 0 12 | var out = new Array(len) 13 | 14 | for (var i = 0; i < len; i++) { 15 | out[i] = buf.readUInt32BE(i * 4) 16 | } 17 | 18 | return out 19 | } 20 | 21 | function scrubVec (v) { 22 | for (var i = 0; i < v.length; v++) { 23 | v[i] = 0 24 | } 25 | } 26 | 27 | function cryptBlock (M, keySchedule, SUB_MIX, SBOX, nRounds) { 28 | var SUB_MIX0 = SUB_MIX[0] 29 | var SUB_MIX1 = SUB_MIX[1] 30 | var SUB_MIX2 = SUB_MIX[2] 31 | var SUB_MIX3 = SUB_MIX[3] 32 | 33 | var s0 = M[0] ^ keySchedule[0] 34 | var s1 = M[1] ^ keySchedule[1] 35 | var s2 = M[2] ^ keySchedule[2] 36 | var s3 = M[3] ^ keySchedule[3] 37 | var t0, t1, t2, t3 38 | var ksRow = 4 39 | 40 | for (var round = 1; round < nRounds; round++) { 41 | t0 = SUB_MIX0[s0 >>> 24] ^ SUB_MIX1[(s1 >>> 16) & 0xff] ^ SUB_MIX2[(s2 >>> 8) & 0xff] ^ SUB_MIX3[s3 & 0xff] ^ keySchedule[ksRow++] 42 | t1 = SUB_MIX0[s1 >>> 24] ^ SUB_MIX1[(s2 >>> 16) & 0xff] ^ SUB_MIX2[(s3 >>> 8) & 0xff] ^ SUB_MIX3[s0 & 0xff] ^ keySchedule[ksRow++] 43 | t2 = SUB_MIX0[s2 >>> 24] ^ SUB_MIX1[(s3 >>> 16) & 0xff] ^ SUB_MIX2[(s0 >>> 8) & 0xff] ^ SUB_MIX3[s1 & 0xff] ^ keySchedule[ksRow++] 44 | t3 = SUB_MIX0[s3 >>> 24] ^ SUB_MIX1[(s0 >>> 16) & 0xff] ^ SUB_MIX2[(s1 >>> 8) & 0xff] ^ SUB_MIX3[s2 & 0xff] ^ keySchedule[ksRow++] 45 | s0 = t0 46 | s1 = t1 47 | s2 = t2 48 | s3 = t3 49 | } 50 | 51 | t0 = ((SBOX[s0 >>> 24] << 24) | (SBOX[(s1 >>> 16) & 0xff] << 16) | (SBOX[(s2 >>> 8) & 0xff] << 8) | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++] 52 | t1 = ((SBOX[s1 >>> 24] << 24) | (SBOX[(s2 >>> 16) & 0xff] << 16) | (SBOX[(s3 >>> 8) & 0xff] << 8) | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++] 53 | t2 = ((SBOX[s2 >>> 24] << 24) | (SBOX[(s3 >>> 16) & 0xff] << 16) | (SBOX[(s0 >>> 8) & 0xff] << 8) | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++] 54 | t3 = ((SBOX[s3 >>> 24] << 24) | (SBOX[(s0 >>> 16) & 0xff] << 16) | (SBOX[(s1 >>> 8) & 0xff] << 8) | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++] 55 | t0 = t0 >>> 0 56 | t1 = t1 >>> 0 57 | t2 = t2 >>> 0 58 | t3 = t3 >>> 0 59 | 60 | return [t0, t1, t2, t3] 61 | } 62 | 63 | // AES constants 64 | var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36] 65 | var G = (function () { 66 | // Compute double table 67 | var d = new Array(256) 68 | for (var j = 0; j < 256; j++) { 69 | if (j < 128) { 70 | d[j] = j << 1 71 | } else { 72 | d[j] = (j << 1) ^ 0x11b 73 | } 74 | } 75 | 76 | var SBOX = [] 77 | var INV_SBOX = [] 78 | var SUB_MIX = [[], [], [], []] 79 | var INV_SUB_MIX = [[], [], [], []] 80 | 81 | // Walk GF(2^8) 82 | var x = 0 83 | var xi = 0 84 | for (var i = 0; i < 256; ++i) { 85 | // Compute sbox 86 | var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4) 87 | sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63 88 | SBOX[x] = sx 89 | INV_SBOX[sx] = x 90 | 91 | // Compute multiplication 92 | var x2 = d[x] 93 | var x4 = d[x2] 94 | var x8 = d[x4] 95 | 96 | // Compute sub bytes, mix columns tables 97 | var t = (d[sx] * 0x101) ^ (sx * 0x1010100) 98 | SUB_MIX[0][x] = (t << 24) | (t >>> 8) 99 | SUB_MIX[1][x] = (t << 16) | (t >>> 16) 100 | SUB_MIX[2][x] = (t << 8) | (t >>> 24) 101 | SUB_MIX[3][x] = t 102 | 103 | // Compute inv sub bytes, inv mix columns tables 104 | t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100) 105 | INV_SUB_MIX[0][sx] = (t << 24) | (t >>> 8) 106 | INV_SUB_MIX[1][sx] = (t << 16) | (t >>> 16) 107 | INV_SUB_MIX[2][sx] = (t << 8) | (t >>> 24) 108 | INV_SUB_MIX[3][sx] = t 109 | 110 | if (x === 0) { 111 | x = xi = 1 112 | } else { 113 | x = x2 ^ d[d[d[x8 ^ x2]]] 114 | xi ^= d[d[xi]] 115 | } 116 | } 117 | 118 | return { 119 | SBOX: SBOX, 120 | INV_SBOX: INV_SBOX, 121 | SUB_MIX: SUB_MIX, 122 | INV_SUB_MIX: INV_SUB_MIX 123 | } 124 | })() 125 | 126 | function AES (key) { 127 | this._key = asUInt32Array(key) 128 | this._reset() 129 | } 130 | 131 | AES.blockSize = 4 * 4 132 | AES.keySize = 256 / 8 133 | AES.prototype.blockSize = AES.blockSize 134 | AES.prototype.keySize = AES.keySize 135 | AES.prototype._reset = function () { 136 | var keyWords = this._key 137 | var keySize = keyWords.length 138 | var nRounds = keySize + 6 139 | var ksRows = (nRounds + 1) * 4 140 | 141 | var keySchedule = [] 142 | for (var k = 0; k < keySize; k++) { 143 | keySchedule[k] = keyWords[k] 144 | } 145 | 146 | for (k = keySize; k < ksRows; k++) { 147 | var t = keySchedule[k - 1] 148 | 149 | if (k % keySize === 0) { 150 | t = (t << 8) | (t >>> 24) 151 | t = 152 | (G.SBOX[t >>> 24] << 24) | 153 | (G.SBOX[(t >>> 16) & 0xff] << 16) | 154 | (G.SBOX[(t >>> 8) & 0xff] << 8) | 155 | (G.SBOX[t & 0xff]) 156 | 157 | t ^= RCON[(k / keySize) | 0] << 24 158 | } else if (keySize > 6 && k % keySize === 4) { 159 | t = 160 | (G.SBOX[t >>> 24] << 24) | 161 | (G.SBOX[(t >>> 16) & 0xff] << 16) | 162 | (G.SBOX[(t >>> 8) & 0xff] << 8) | 163 | (G.SBOX[t & 0xff]) 164 | } 165 | 166 | keySchedule[k] = keySchedule[k - keySize] ^ t 167 | } 168 | 169 | var invKeySchedule = [] 170 | for (var ik = 0; ik < ksRows; ik++) { 171 | var ksR = ksRows - ik 172 | var tt = keySchedule[ksR - (ik % 4 ? 0 : 4)] 173 | 174 | if (ik < 4 || ksR <= 4) { 175 | invKeySchedule[ik] = tt 176 | } else { 177 | invKeySchedule[ik] = 178 | G.INV_SUB_MIX[0][G.SBOX[tt >>> 24]] ^ 179 | G.INV_SUB_MIX[1][G.SBOX[(tt >>> 16) & 0xff]] ^ 180 | G.INV_SUB_MIX[2][G.SBOX[(tt >>> 8) & 0xff]] ^ 181 | G.INV_SUB_MIX[3][G.SBOX[tt & 0xff]] 182 | } 183 | } 184 | 185 | this._nRounds = nRounds 186 | this._keySchedule = keySchedule 187 | this._invKeySchedule = invKeySchedule 188 | } 189 | 190 | AES.prototype.encryptBlockRaw = function (M) { 191 | M = asUInt32Array(M) 192 | return cryptBlock(M, this._keySchedule, G.SUB_MIX, G.SBOX, this._nRounds) 193 | } 194 | 195 | AES.prototype.encryptBlock = function (M) { 196 | var out = this.encryptBlockRaw(M) 197 | var buf = Buffer.allocUnsafe(16) 198 | buf.writeUInt32BE(out[0], 0) 199 | buf.writeUInt32BE(out[1], 4) 200 | buf.writeUInt32BE(out[2], 8) 201 | buf.writeUInt32BE(out[3], 12) 202 | return buf 203 | } 204 | 205 | AES.prototype.decryptBlock = function (M) { 206 | M = asUInt32Array(M) 207 | 208 | // swap 209 | var m1 = M[1] 210 | M[1] = M[3] 211 | M[3] = m1 212 | 213 | var out = cryptBlock(M, this._invKeySchedule, G.INV_SUB_MIX, G.INV_SBOX, this._nRounds) 214 | var buf = Buffer.allocUnsafe(16) 215 | buf.writeUInt32BE(out[0], 0) 216 | buf.writeUInt32BE(out[3], 4) 217 | buf.writeUInt32BE(out[2], 8) 218 | buf.writeUInt32BE(out[1], 12) 219 | return buf 220 | } 221 | 222 | AES.prototype.scrub = function () { 223 | scrubVec(this._keySchedule) 224 | scrubVec(this._invKeySchedule) 225 | scrubVec(this._key) 226 | } 227 | 228 | module.exports.AES = AES 229 | -------------------------------------------------------------------------------- /authCipher.js: -------------------------------------------------------------------------------- 1 | var aes = require('./aes') 2 | var Buffer = require('safe-buffer').Buffer 3 | var Transform = require('cipher-base') 4 | var inherits = require('inherits') 5 | var GHASH = require('./ghash') 6 | var xor = require('buffer-xor') 7 | var incr32 = require('./incr32') 8 | 9 | function xorTest (a, b) { 10 | var out = 0 11 | if (a.length !== b.length) out++ 12 | 13 | var len = Math.min(a.length, b.length) 14 | for (var i = 0; i < len; ++i) { 15 | out += (a[i] ^ b[i]) 16 | } 17 | 18 | return out 19 | } 20 | 21 | function calcIv (self, iv, ck) { 22 | if (iv.length === 12) { 23 | self._finID = Buffer.concat([iv, Buffer.from([0, 0, 0, 1])]) 24 | return Buffer.concat([iv, Buffer.from([0, 0, 0, 2])]) 25 | } 26 | var ghash = new GHASH(ck) 27 | var len = iv.length 28 | var toPad = len % 16 29 | ghash.update(iv) 30 | if (toPad) { 31 | toPad = 16 - toPad 32 | ghash.update(Buffer.alloc(toPad, 0)) 33 | } 34 | ghash.update(Buffer.alloc(8, 0)) 35 | var ivBits = len * 8 36 | var tail = Buffer.alloc(8) 37 | tail.writeUIntBE(ivBits, 0, 8) 38 | ghash.update(tail) 39 | self._finID = ghash.state 40 | var out = Buffer.from(self._finID) 41 | incr32(out) 42 | return out 43 | } 44 | function StreamCipher (mode, key, iv, decrypt) { 45 | Transform.call(this) 46 | 47 | var h = Buffer.alloc(4, 0) 48 | 49 | this._cipher = new aes.AES(key) 50 | var ck = this._cipher.encryptBlock(h) 51 | this._ghash = new GHASH(ck) 52 | iv = calcIv(this, iv, ck) 53 | 54 | this._prev = Buffer.from(iv) 55 | this._cache = Buffer.allocUnsafe(0) 56 | this._secCache = Buffer.allocUnsafe(0) 57 | this._decrypt = decrypt 58 | this._alen = 0 59 | this._len = 0 60 | this._mode = mode 61 | 62 | this._authTag = null 63 | this._called = false 64 | } 65 | 66 | inherits(StreamCipher, Transform) 67 | 68 | StreamCipher.prototype._update = function (chunk) { 69 | if (!this._called && this._alen) { 70 | var rump = 16 - (this._alen % 16) 71 | if (rump < 16) { 72 | rump = Buffer.alloc(rump, 0) 73 | this._ghash.update(rump) 74 | } 75 | } 76 | 77 | this._called = true 78 | var out = this._mode.encrypt(this, chunk) 79 | if (this._decrypt) { 80 | this._ghash.update(chunk) 81 | } else { 82 | this._ghash.update(out) 83 | } 84 | this._len += chunk.length 85 | return out 86 | } 87 | 88 | StreamCipher.prototype._final = function () { 89 | if (this._decrypt && !this._authTag) throw new Error('Unsupported state or unable to authenticate data') 90 | 91 | var tag = xor(this._ghash.final(this._alen * 8, this._len * 8), this._cipher.encryptBlock(this._finID)) 92 | if (this._decrypt && xorTest(tag, this._authTag)) throw new Error('Unsupported state or unable to authenticate data') 93 | 94 | this._authTag = tag 95 | this._cipher.scrub() 96 | } 97 | 98 | StreamCipher.prototype.getAuthTag = function getAuthTag () { 99 | if (this._decrypt || !Buffer.isBuffer(this._authTag)) throw new Error('Attempting to get auth tag in unsupported state') 100 | 101 | return this._authTag 102 | } 103 | 104 | StreamCipher.prototype.setAuthTag = function setAuthTag (tag) { 105 | if (!this._decrypt) throw new Error('Attempting to set auth tag in unsupported state') 106 | 107 | this._authTag = tag 108 | } 109 | 110 | StreamCipher.prototype.setAAD = function setAAD (buf) { 111 | if (this._called) throw new Error('Attempting to set AAD in unsupported state') 112 | 113 | this._ghash.update(buf) 114 | this._alen += buf.length 115 | } 116 | 117 | module.exports = StreamCipher 118 | -------------------------------------------------------------------------------- /bench/index.js: -------------------------------------------------------------------------------- 1 | let Benchmark = require('benchmark') 2 | let _local = require('../browser') 3 | let _npm = require('browserify-aes/browser') 4 | let key = Buffer.alloc(16, 0xff) 5 | let iv = Buffer.alloc(16, 0x01) 6 | 7 | function test (mod, message) { 8 | let cipher = mod.createCipheriv('aes-128-ctr', key, iv) 9 | let b = cipher.update(message) 10 | return Buffer.concat([b, cipher.final()]) 11 | } 12 | 13 | let local = (m) => test(_local, m) 14 | let npm = (m) => test(_npm, m) 15 | 16 | function run (message) { 17 | if ( 18 | local(message).toString('hex') !== 19 | npm(message).toString('hex') 20 | ) throw new Error('not equal') 21 | 22 | new Benchmark.Suite() 23 | .add('local', () => local(message)) 24 | .add('npm', () => npm(message)) 25 | .on('cycle', (e) => console.log(String(e.target))) 26 | .run() 27 | } 28 | 29 | let lorem = Buffer.allocUnsafe(800) 30 | run(lorem.slice(0, 20), key) 31 | run(lorem.slice(0, 80), key) 32 | run(lorem, key) 33 | -------------------------------------------------------------------------------- /bench/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bench", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "benchmark": "^2.1.4", 13 | "browserify-aes": "^1.0.6" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /browser.js: -------------------------------------------------------------------------------- 1 | var ciphers = require('./encrypter') 2 | var deciphers = require('./decrypter') 3 | var modes = require('./modes/list.json') 4 | 5 | function getCiphers () { 6 | return Object.keys(modes) 7 | } 8 | 9 | exports.createCipher = exports.Cipher = ciphers.createCipher 10 | exports.createCipheriv = exports.Cipheriv = ciphers.createCipheriv 11 | exports.createDecipher = exports.Decipher = deciphers.createDecipher 12 | exports.createDecipheriv = exports.Decipheriv = deciphers.createDecipheriv 13 | exports.listCiphers = exports.getCiphers = getCiphers 14 | -------------------------------------------------------------------------------- /decrypter.js: -------------------------------------------------------------------------------- 1 | var AuthCipher = require('./authCipher') 2 | var Buffer = require('safe-buffer').Buffer 3 | var MODES = require('./modes') 4 | var StreamCipher = require('./streamCipher') 5 | var Transform = require('cipher-base') 6 | var aes = require('./aes') 7 | var ebtk = require('evp_bytestokey') 8 | var inherits = require('inherits') 9 | 10 | function Decipher (mode, key, iv) { 11 | Transform.call(this) 12 | 13 | this._cache = new Splitter() 14 | this._last = void 0 15 | this._cipher = new aes.AES(key) 16 | this._prev = Buffer.from(iv) 17 | this._mode = mode 18 | this._autopadding = true 19 | } 20 | 21 | inherits(Decipher, Transform) 22 | 23 | Decipher.prototype._update = function (data) { 24 | this._cache.add(data) 25 | var chunk 26 | var thing 27 | var out = [] 28 | while ((chunk = this._cache.get(this._autopadding))) { 29 | thing = this._mode.decrypt(this, chunk) 30 | out.push(thing) 31 | } 32 | return Buffer.concat(out) 33 | } 34 | 35 | Decipher.prototype._final = function () { 36 | var chunk = this._cache.flush() 37 | if (this._autopadding) { 38 | return unpad(this._mode.decrypt(this, chunk)) 39 | } else if (chunk) { 40 | throw new Error('data not multiple of block length') 41 | } 42 | } 43 | 44 | Decipher.prototype.setAutoPadding = function (setTo) { 45 | this._autopadding = !!setTo 46 | return this 47 | } 48 | 49 | function Splitter () { 50 | this.cache = Buffer.allocUnsafe(0) 51 | } 52 | 53 | Splitter.prototype.add = function (data) { 54 | this.cache = Buffer.concat([this.cache, data]) 55 | } 56 | 57 | Splitter.prototype.get = function (autoPadding) { 58 | var out 59 | if (autoPadding) { 60 | if (this.cache.length > 16) { 61 | out = this.cache.slice(0, 16) 62 | this.cache = this.cache.slice(16) 63 | return out 64 | } 65 | } else { 66 | if (this.cache.length >= 16) { 67 | out = this.cache.slice(0, 16) 68 | this.cache = this.cache.slice(16) 69 | return out 70 | } 71 | } 72 | 73 | return null 74 | } 75 | 76 | Splitter.prototype.flush = function () { 77 | if (this.cache.length) return this.cache 78 | } 79 | 80 | function unpad (last) { 81 | var padded = last[15] 82 | if (padded < 1 || padded > 16) { 83 | throw new Error('unable to decrypt data') 84 | } 85 | var i = -1 86 | while (++i < padded) { 87 | if (last[(i + (16 - padded))] !== padded) { 88 | throw new Error('unable to decrypt data') 89 | } 90 | } 91 | if (padded === 16) return 92 | 93 | return last.slice(0, 16 - padded) 94 | } 95 | 96 | function createDecipheriv (suite, password, iv) { 97 | var config = MODES[suite.toLowerCase()] 98 | if (!config) throw new TypeError('invalid suite type') 99 | 100 | if (typeof iv === 'string') iv = Buffer.from(iv) 101 | if (config.mode !== 'GCM' && iv.length !== config.iv) throw new TypeError('invalid iv length ' + iv.length) 102 | 103 | if (typeof password === 'string') password = Buffer.from(password) 104 | if (password.length !== config.key / 8) throw new TypeError('invalid key length ' + password.length) 105 | 106 | if (config.type === 'stream') { 107 | return new StreamCipher(config.module, password, iv, true) 108 | } else if (config.type === 'auth') { 109 | return new AuthCipher(config.module, password, iv, true) 110 | } 111 | 112 | return new Decipher(config.module, password, iv) 113 | } 114 | 115 | function createDecipher (suite, password) { 116 | var config = MODES[suite.toLowerCase()] 117 | if (!config) throw new TypeError('invalid suite type') 118 | 119 | var keys = ebtk(password, false, config.key, config.iv) 120 | return createDecipheriv(suite, keys.key, keys.iv) 121 | } 122 | 123 | exports.createDecipher = createDecipher 124 | exports.createDecipheriv = createDecipheriv 125 | -------------------------------------------------------------------------------- /encrypter.js: -------------------------------------------------------------------------------- 1 | var MODES = require('./modes') 2 | var AuthCipher = require('./authCipher') 3 | var Buffer = require('safe-buffer').Buffer 4 | var StreamCipher = require('./streamCipher') 5 | var Transform = require('cipher-base') 6 | var aes = require('./aes') 7 | var ebtk = require('evp_bytestokey') 8 | var inherits = require('inherits') 9 | 10 | function Cipher (mode, key, iv) { 11 | Transform.call(this) 12 | 13 | this._cache = new Splitter() 14 | this._cipher = new aes.AES(key) 15 | this._prev = Buffer.from(iv) 16 | this._mode = mode 17 | this._autopadding = true 18 | } 19 | 20 | inherits(Cipher, Transform) 21 | 22 | Cipher.prototype._update = function (data) { 23 | this._cache.add(data) 24 | var chunk 25 | var thing 26 | var out = [] 27 | 28 | while ((chunk = this._cache.get())) { 29 | thing = this._mode.encrypt(this, chunk) 30 | out.push(thing) 31 | } 32 | 33 | return Buffer.concat(out) 34 | } 35 | 36 | var PADDING = Buffer.alloc(16, 0x10) 37 | 38 | Cipher.prototype._final = function () { 39 | var chunk = this._cache.flush() 40 | if (this._autopadding) { 41 | chunk = this._mode.encrypt(this, chunk) 42 | this._cipher.scrub() 43 | return chunk 44 | } 45 | 46 | if (!chunk.equals(PADDING)) { 47 | this._cipher.scrub() 48 | throw new Error('data not multiple of block length') 49 | } 50 | } 51 | 52 | Cipher.prototype.setAutoPadding = function (setTo) { 53 | this._autopadding = !!setTo 54 | return this 55 | } 56 | 57 | function Splitter () { 58 | this.cache = Buffer.allocUnsafe(0) 59 | } 60 | 61 | Splitter.prototype.add = function (data) { 62 | this.cache = Buffer.concat([this.cache, data]) 63 | } 64 | 65 | Splitter.prototype.get = function () { 66 | if (this.cache.length > 15) { 67 | var out = this.cache.slice(0, 16) 68 | this.cache = this.cache.slice(16) 69 | return out 70 | } 71 | return null 72 | } 73 | 74 | Splitter.prototype.flush = function () { 75 | var len = 16 - this.cache.length 76 | var padBuff = Buffer.allocUnsafe(len) 77 | 78 | var i = -1 79 | while (++i < len) { 80 | padBuff.writeUInt8(len, i) 81 | } 82 | 83 | return Buffer.concat([this.cache, padBuff]) 84 | } 85 | 86 | function createCipheriv (suite, password, iv) { 87 | var config = MODES[suite.toLowerCase()] 88 | if (!config) throw new TypeError('invalid suite type') 89 | 90 | if (typeof password === 'string') password = Buffer.from(password) 91 | if (password.length !== config.key / 8) throw new TypeError('invalid key length ' + password.length) 92 | 93 | if (typeof iv === 'string') iv = Buffer.from(iv) 94 | if (config.mode !== 'GCM' && iv.length !== config.iv) throw new TypeError('invalid iv length ' + iv.length) 95 | 96 | if (config.type === 'stream') { 97 | return new StreamCipher(config.module, password, iv) 98 | } else if (config.type === 'auth') { 99 | return new AuthCipher(config.module, password, iv) 100 | } 101 | 102 | return new Cipher(config.module, password, iv) 103 | } 104 | 105 | function createCipher (suite, password) { 106 | var config = MODES[suite.toLowerCase()] 107 | if (!config) throw new TypeError('invalid suite type') 108 | 109 | var keys = ebtk(password, false, config.key, config.iv) 110 | return createCipheriv(suite, keys.key, keys.iv) 111 | } 112 | 113 | exports.createCipheriv = createCipheriv 114 | exports.createCipher = createCipher 115 | -------------------------------------------------------------------------------- /ghash.js: -------------------------------------------------------------------------------- 1 | // Parts of this file are derived from SJCL, which is LICENSED (BSD-2 or GPL-2), see https://github.com/bitwiseshiftleft/sjcl/blob/master/LICENSE.txt 2 | // 3 | // SJCL is open. You can use, modify and redistribute it under a BSD 4 | // license or under the GNU GPL, version 2.0. 5 | // 6 | //--------------------------------------------------------------------- 7 | // 8 | // http://opensource.org/licenses/BSD-2-Clause 9 | // 10 | // Copyright (c) 2009-2015, Emily Stark, Mike Hamburg and Dan Boneh at Stanford University. All rights reserved. 11 | // 12 | // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 13 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 14 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | // 17 | // --------------------------------------------------------------------- 18 | // 19 | // http://opensource.org/licenses/GPL-2.0 20 | // 21 | // The Stanford Javascript Crypto Library (hosted here on GitHub) is a 22 | // project by the Stanford Computer Security Lab to build a secure, 23 | // powerful, fast, small, easy-to-use, cross-browser library for 24 | // cryptography in Javascript. 25 | // 26 | // Copyright (c) 2009-2015, Emily Stark, Mike Hamburg and Dan Boneh at 27 | // Stanford University. 28 | // 29 | // This program is free software; you can redistribute it and/or modify it 30 | // under the terms of the GNU General Public License as published by the 31 | // Free Software Foundation; either version 2 of the License, or (at your 32 | // option) any later version. 33 | // 34 | // This program is distributed in the hope that it will be useful, but 35 | // WITHOUT ANY WARRANTY; without even the implied warranty of 36 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 37 | // Public License for more details. 38 | // 39 | // You should have received a copy of the GNU General Public License along 40 | // with this program; if not, write to the Free Software Foundation, Inc., 41 | // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 42 | 43 | var Buffer = require('safe-buffer').Buffer 44 | var ZEROES = Buffer.alloc(16, 0) 45 | 46 | function toArray (buf) { 47 | return [ 48 | buf.readUInt32BE(0), 49 | buf.readUInt32BE(4), 50 | buf.readUInt32BE(8), 51 | buf.readUInt32BE(12) 52 | ] 53 | } 54 | 55 | function fromArray (out) { 56 | var buf = Buffer.allocUnsafe(16) 57 | buf.writeUInt32BE(out[0] >>> 0, 0) 58 | buf.writeUInt32BE(out[1] >>> 0, 4) 59 | buf.writeUInt32BE(out[2] >>> 0, 8) 60 | buf.writeUInt32BE(out[3] >>> 0, 12) 61 | return buf 62 | } 63 | 64 | function GHASH (key) { 65 | this.h = key 66 | this.state = Buffer.alloc(16, 0) 67 | this.cache = Buffer.allocUnsafe(0) 68 | } 69 | 70 | // from http://bitwiseshiftleft.github.io/sjcl/doc/symbols/src/core_gcm.js.html 71 | // by Juho Vähä-Herttua 72 | GHASH.prototype.ghash = function (block) { 73 | var i = -1 74 | while (++i < block.length) { 75 | this.state[i] ^= block[i] 76 | } 77 | this._multiply() 78 | } 79 | 80 | GHASH.prototype._multiply = function () { 81 | var Vi = toArray(this.h) 82 | var Zi = [0, 0, 0, 0] 83 | var j, xi, lsbVi 84 | var i = -1 85 | while (++i < 128) { 86 | xi = (this.state[~~(i / 8)] & (1 << (7 - (i % 8)))) !== 0 87 | if (xi) { 88 | // Z_i+1 = Z_i ^ V_i 89 | Zi[0] ^= Vi[0] 90 | Zi[1] ^= Vi[1] 91 | Zi[2] ^= Vi[2] 92 | Zi[3] ^= Vi[3] 93 | } 94 | 95 | // Store the value of LSB(V_i) 96 | lsbVi = (Vi[3] & 1) !== 0 97 | 98 | // V_i+1 = V_i >> 1 99 | for (j = 3; j > 0; j--) { 100 | Vi[j] = (Vi[j] >>> 1) | ((Vi[j - 1] & 1) << 31) 101 | } 102 | Vi[0] = Vi[0] >>> 1 103 | 104 | // If LSB(V_i) is 1, V_i+1 = (V_i >> 1) ^ R 105 | if (lsbVi) { 106 | Vi[0] = Vi[0] ^ (0xe1 << 24) 107 | } 108 | } 109 | this.state = fromArray(Zi) 110 | } 111 | 112 | GHASH.prototype.update = function (buf) { 113 | this.cache = Buffer.concat([this.cache, buf]) 114 | var chunk 115 | while (this.cache.length >= 16) { 116 | chunk = this.cache.slice(0, 16) 117 | this.cache = this.cache.slice(16) 118 | this.ghash(chunk) 119 | } 120 | } 121 | 122 | GHASH.prototype.final = function (abl, bl) { 123 | if (this.cache.length) { 124 | this.ghash(Buffer.concat([this.cache, ZEROES], 16)) 125 | } 126 | 127 | this.ghash(fromArray([0, abl, 0, bl])) 128 | return this.state 129 | } 130 | 131 | module.exports = GHASH 132 | -------------------------------------------------------------------------------- /incr32.js: -------------------------------------------------------------------------------- 1 | function incr32 (iv) { 2 | var len = iv.length 3 | var item 4 | while (len--) { 5 | item = iv.readUInt8(len) 6 | if (item === 255) { 7 | iv.writeUInt8(0, len) 8 | } else { 9 | item++ 10 | iv.writeUInt8(item, len) 11 | break 12 | } 13 | } 14 | } 15 | module.exports = incr32 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var crypto = require('crypto') 2 | 3 | exports.createCipher = exports.Cipher = crypto.createCipher 4 | exports.createCipheriv = exports.Cipheriv = crypto.createCipheriv 5 | exports.createDecipher = exports.Decipher = crypto.createDecipher 6 | exports.createDecipheriv = exports.Decipheriv = crypto.createDecipheriv 7 | exports.listCiphers = exports.getCiphers = crypto.getCiphers 8 | -------------------------------------------------------------------------------- /modes/cbc.js: -------------------------------------------------------------------------------- 1 | var xor = require('buffer-xor') 2 | 3 | exports.encrypt = function (self, block) { 4 | var data = xor(block, self._prev) 5 | 6 | self._prev = self._cipher.encryptBlock(data) 7 | return self._prev 8 | } 9 | 10 | exports.decrypt = function (self, block) { 11 | var pad = self._prev 12 | 13 | self._prev = block 14 | var out = self._cipher.decryptBlock(block) 15 | 16 | return xor(out, pad) 17 | } 18 | -------------------------------------------------------------------------------- /modes/cfb.js: -------------------------------------------------------------------------------- 1 | var Buffer = require('safe-buffer').Buffer 2 | var xor = require('buffer-xor') 3 | 4 | function encryptStart (self, data, decrypt) { 5 | var len = data.length 6 | var out = xor(data, self._cache) 7 | self._cache = self._cache.slice(len) 8 | self._prev = Buffer.concat([self._prev, decrypt ? data : out]) 9 | return out 10 | } 11 | 12 | exports.encrypt = function (self, data, decrypt) { 13 | var out = Buffer.allocUnsafe(0) 14 | var len 15 | 16 | while (data.length) { 17 | if (self._cache.length === 0) { 18 | self._cache = self._cipher.encryptBlock(self._prev) 19 | self._prev = Buffer.allocUnsafe(0) 20 | } 21 | 22 | if (self._cache.length <= data.length) { 23 | len = self._cache.length 24 | out = Buffer.concat([out, encryptStart(self, data.slice(0, len), decrypt)]) 25 | data = data.slice(len) 26 | } else { 27 | out = Buffer.concat([out, encryptStart(self, data, decrypt)]) 28 | break 29 | } 30 | } 31 | 32 | return out 33 | } 34 | -------------------------------------------------------------------------------- /modes/cfb1.js: -------------------------------------------------------------------------------- 1 | var Buffer = require('safe-buffer').Buffer 2 | 3 | function encryptByte (self, byteParam, decrypt) { 4 | var pad 5 | var i = -1 6 | var len = 8 7 | var out = 0 8 | var bit, value 9 | while (++i < len) { 10 | pad = self._cipher.encryptBlock(self._prev) 11 | bit = (byteParam & (1 << (7 - i))) ? 0x80 : 0 12 | value = pad[0] ^ bit 13 | out += ((value & 0x80) >> (i % 8)) 14 | self._prev = shiftIn(self._prev, decrypt ? bit : value) 15 | } 16 | return out 17 | } 18 | 19 | function shiftIn (buffer, value) { 20 | var len = buffer.length 21 | var i = -1 22 | var out = Buffer.allocUnsafe(buffer.length) 23 | buffer = Buffer.concat([buffer, Buffer.from([value])]) 24 | 25 | while (++i < len) { 26 | out[i] = buffer[i] << 1 | buffer[i + 1] >> (7) 27 | } 28 | 29 | return out 30 | } 31 | 32 | exports.encrypt = function (self, chunk, decrypt) { 33 | var len = chunk.length 34 | var out = Buffer.allocUnsafe(len) 35 | var i = -1 36 | 37 | while (++i < len) { 38 | out[i] = encryptByte(self, chunk[i], decrypt) 39 | } 40 | 41 | return out 42 | } 43 | -------------------------------------------------------------------------------- /modes/cfb8.js: -------------------------------------------------------------------------------- 1 | var Buffer = require('safe-buffer').Buffer 2 | 3 | function encryptByte (self, byteParam, decrypt) { 4 | var pad = self._cipher.encryptBlock(self._prev) 5 | var out = pad[0] ^ byteParam 6 | 7 | self._prev = Buffer.concat([ 8 | self._prev.slice(1), 9 | Buffer.from([decrypt ? byteParam : out]) 10 | ]) 11 | 12 | return out 13 | } 14 | 15 | exports.encrypt = function (self, chunk, decrypt) { 16 | var len = chunk.length 17 | var out = Buffer.allocUnsafe(len) 18 | var i = -1 19 | 20 | while (++i < len) { 21 | out[i] = encryptByte(self, chunk[i], decrypt) 22 | } 23 | 24 | return out 25 | } 26 | -------------------------------------------------------------------------------- /modes/ctr.js: -------------------------------------------------------------------------------- 1 | var xor = require('buffer-xor') 2 | var Buffer = require('safe-buffer').Buffer 3 | var incr32 = require('../incr32') 4 | 5 | function getBlock (self) { 6 | var out = self._cipher.encryptBlockRaw(self._prev) 7 | incr32(self._prev) 8 | return out 9 | } 10 | 11 | var blockSize = 16 12 | exports.encrypt = function (self, chunk) { 13 | var chunkNum = Math.ceil(chunk.length / blockSize) 14 | var start = self._cache.length 15 | self._cache = Buffer.concat([ 16 | self._cache, 17 | Buffer.allocUnsafe(chunkNum * blockSize) 18 | ]) 19 | for (var i = 0; i < chunkNum; i++) { 20 | var out = getBlock(self) 21 | var offset = start + i * blockSize 22 | self._cache.writeUInt32BE(out[0], offset + 0) 23 | self._cache.writeUInt32BE(out[1], offset + 4) 24 | self._cache.writeUInt32BE(out[2], offset + 8) 25 | self._cache.writeUInt32BE(out[3], offset + 12) 26 | } 27 | var pad = self._cache.slice(0, chunk.length) 28 | self._cache = self._cache.slice(chunk.length) 29 | return xor(chunk, pad) 30 | } 31 | -------------------------------------------------------------------------------- /modes/ecb.js: -------------------------------------------------------------------------------- 1 | exports.encrypt = function (self, block) { 2 | return self._cipher.encryptBlock(block) 3 | } 4 | 5 | exports.decrypt = function (self, block) { 6 | return self._cipher.decryptBlock(block) 7 | } 8 | -------------------------------------------------------------------------------- /modes/index.js: -------------------------------------------------------------------------------- 1 | var modeModules = { 2 | ECB: require('./ecb'), 3 | CBC: require('./cbc'), 4 | CFB: require('./cfb'), 5 | CFB8: require('./cfb8'), 6 | CFB1: require('./cfb1'), 7 | OFB: require('./ofb'), 8 | CTR: require('./ctr'), 9 | GCM: require('./ctr') 10 | } 11 | 12 | var modes = require('./list.json') 13 | 14 | for (var key in modes) { 15 | modes[key].module = modeModules[modes[key].mode] 16 | } 17 | 18 | module.exports = modes 19 | -------------------------------------------------------------------------------- /modes/list.json: -------------------------------------------------------------------------------- 1 | { 2 | "aes-128-ecb": { 3 | "cipher": "AES", 4 | "key": 128, 5 | "iv": 0, 6 | "mode": "ECB", 7 | "type": "block" 8 | }, 9 | "aes-192-ecb": { 10 | "cipher": "AES", 11 | "key": 192, 12 | "iv": 0, 13 | "mode": "ECB", 14 | "type": "block" 15 | }, 16 | "aes-256-ecb": { 17 | "cipher": "AES", 18 | "key": 256, 19 | "iv": 0, 20 | "mode": "ECB", 21 | "type": "block" 22 | }, 23 | "aes-128-cbc": { 24 | "cipher": "AES", 25 | "key": 128, 26 | "iv": 16, 27 | "mode": "CBC", 28 | "type": "block" 29 | }, 30 | "aes-192-cbc": { 31 | "cipher": "AES", 32 | "key": 192, 33 | "iv": 16, 34 | "mode": "CBC", 35 | "type": "block" 36 | }, 37 | "aes-256-cbc": { 38 | "cipher": "AES", 39 | "key": 256, 40 | "iv": 16, 41 | "mode": "CBC", 42 | "type": "block" 43 | }, 44 | "aes128": { 45 | "cipher": "AES", 46 | "key": 128, 47 | "iv": 16, 48 | "mode": "CBC", 49 | "type": "block" 50 | }, 51 | "aes192": { 52 | "cipher": "AES", 53 | "key": 192, 54 | "iv": 16, 55 | "mode": "CBC", 56 | "type": "block" 57 | }, 58 | "aes256": { 59 | "cipher": "AES", 60 | "key": 256, 61 | "iv": 16, 62 | "mode": "CBC", 63 | "type": "block" 64 | }, 65 | "aes-128-cfb": { 66 | "cipher": "AES", 67 | "key": 128, 68 | "iv": 16, 69 | "mode": "CFB", 70 | "type": "stream" 71 | }, 72 | "aes-192-cfb": { 73 | "cipher": "AES", 74 | "key": 192, 75 | "iv": 16, 76 | "mode": "CFB", 77 | "type": "stream" 78 | }, 79 | "aes-256-cfb": { 80 | "cipher": "AES", 81 | "key": 256, 82 | "iv": 16, 83 | "mode": "CFB", 84 | "type": "stream" 85 | }, 86 | "aes-128-cfb8": { 87 | "cipher": "AES", 88 | "key": 128, 89 | "iv": 16, 90 | "mode": "CFB8", 91 | "type": "stream" 92 | }, 93 | "aes-192-cfb8": { 94 | "cipher": "AES", 95 | "key": 192, 96 | "iv": 16, 97 | "mode": "CFB8", 98 | "type": "stream" 99 | }, 100 | "aes-256-cfb8": { 101 | "cipher": "AES", 102 | "key": 256, 103 | "iv": 16, 104 | "mode": "CFB8", 105 | "type": "stream" 106 | }, 107 | "aes-128-cfb1": { 108 | "cipher": "AES", 109 | "key": 128, 110 | "iv": 16, 111 | "mode": "CFB1", 112 | "type": "stream" 113 | }, 114 | "aes-192-cfb1": { 115 | "cipher": "AES", 116 | "key": 192, 117 | "iv": 16, 118 | "mode": "CFB1", 119 | "type": "stream" 120 | }, 121 | "aes-256-cfb1": { 122 | "cipher": "AES", 123 | "key": 256, 124 | "iv": 16, 125 | "mode": "CFB1", 126 | "type": "stream" 127 | }, 128 | "aes-128-ofb": { 129 | "cipher": "AES", 130 | "key": 128, 131 | "iv": 16, 132 | "mode": "OFB", 133 | "type": "stream" 134 | }, 135 | "aes-192-ofb": { 136 | "cipher": "AES", 137 | "key": 192, 138 | "iv": 16, 139 | "mode": "OFB", 140 | "type": "stream" 141 | }, 142 | "aes-256-ofb": { 143 | "cipher": "AES", 144 | "key": 256, 145 | "iv": 16, 146 | "mode": "OFB", 147 | "type": "stream" 148 | }, 149 | "aes-128-ctr": { 150 | "cipher": "AES", 151 | "key": 128, 152 | "iv": 16, 153 | "mode": "CTR", 154 | "type": "stream" 155 | }, 156 | "aes-192-ctr": { 157 | "cipher": "AES", 158 | "key": 192, 159 | "iv": 16, 160 | "mode": "CTR", 161 | "type": "stream" 162 | }, 163 | "aes-256-ctr": { 164 | "cipher": "AES", 165 | "key": 256, 166 | "iv": 16, 167 | "mode": "CTR", 168 | "type": "stream" 169 | }, 170 | "aes-128-gcm": { 171 | "cipher": "AES", 172 | "key": 128, 173 | "iv": 12, 174 | "mode": "GCM", 175 | "type": "auth" 176 | }, 177 | "aes-192-gcm": { 178 | "cipher": "AES", 179 | "key": 192, 180 | "iv": 12, 181 | "mode": "GCM", 182 | "type": "auth" 183 | }, 184 | "aes-256-gcm": { 185 | "cipher": "AES", 186 | "key": 256, 187 | "iv": 12, 188 | "mode": "GCM", 189 | "type": "auth" 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /modes/ofb.js: -------------------------------------------------------------------------------- 1 | var xor = require('buffer-xor') 2 | 3 | function getBlock (self) { 4 | self._prev = self._cipher.encryptBlock(self._prev) 5 | return self._prev 6 | } 7 | 8 | exports.encrypt = function (self, chunk) { 9 | while (self._cache.length < chunk.length) { 10 | self._cache = Buffer.concat([self._cache, getBlock(self)]) 11 | } 12 | 13 | var pad = self._cache.slice(0, chunk.length) 14 | self._cache = self._cache.slice(chunk.length) 15 | return xor(chunk, pad) 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "browserify-aes", 3 | "version": "1.2.0", 4 | "description": "aes, for browserify", 5 | "browser": "browser.js", 6 | "main": "index.js", 7 | "directories": { 8 | "test": "test" 9 | }, 10 | "scripts": { 11 | "standard": "standard", 12 | "unit": "node test/index.js | tspec", 13 | "test": "npm run standard && npm run unit" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git://github.com/crypto-browserify/browserify-aes.git" 18 | }, 19 | "keywords": [ 20 | "aes", 21 | "crypto", 22 | "browserify" 23 | ], 24 | "author": "", 25 | "license": "MIT AND BSD-3-Clause AND (BSD-2-Clause OR GPL-2.0)", 26 | "bugs": { 27 | "url": "https://github.com/crypto-browserify/browserify-aes/issues" 28 | }, 29 | "homepage": "https://github.com/crypto-browserify/browserify-aes", 30 | "dependencies": { 31 | "buffer-xor": "^1.0.3", 32 | "cipher-base": "^1.0.0", 33 | "create-hash": "^1.1.0", 34 | "evp_bytestokey": "^1.0.3", 35 | "inherits": "^2.0.1", 36 | "safe-buffer": "^5.0.1" 37 | }, 38 | "devDependencies": { 39 | "standard": "^9.0.0", 40 | "tap-spec": "^4.1.1", 41 | "tape": "^4.6.3" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /scripts/populateFixtures.js: -------------------------------------------------------------------------------- 1 | var modes = require('./modes/list.json') 2 | var fixtures = require('./test/fixtures.json') 3 | var crypto = require('crypto') 4 | var types = ['aes-128-cfb1', 'aes-192-cfb1', 'aes-256-cfb1'] 5 | var ebtk = require('./EVP_BytesToKey') 6 | var fs = require('fs') 7 | 8 | fixtures.forEach(function (fixture) { 9 | types.forEach(function (cipher) { 10 | var suite = crypto.createCipher(cipher, new Buffer(fixture.password)) 11 | var buf = new Buffer('') 12 | buf = Buffer.concat([buf, suite.update(new Buffer(fixture.text))]) 13 | buf = Buffer.concat([buf, suite.final()]) 14 | fixture.results.ciphers[cipher] = buf.toString('hex') 15 | if (modes[cipher].mode === 'ECB') { 16 | return 17 | } 18 | var suite2 = crypto.createCipheriv(cipher, ebtk(crypto, fixture.password, modes[cipher].key).key, new Buffer(fixture.iv, 'hex')) 19 | var buf2 = new Buffer('') 20 | buf2 = Buffer.concat([buf2, suite2.update(new Buffer(fixture.text))]) 21 | buf2 = Buffer.concat([buf2, suite2.final()]) 22 | fixture.results.cipherivs[cipher] = buf2.toString('hex') 23 | }) 24 | }) 25 | fs.writeFileSync('./test/fixturesNew.json', JSON.stringify(fixtures, false, 4)) 26 | -------------------------------------------------------------------------------- /streamCipher.js: -------------------------------------------------------------------------------- 1 | var aes = require('./aes') 2 | var Buffer = require('safe-buffer').Buffer 3 | var Transform = require('cipher-base') 4 | var inherits = require('inherits') 5 | 6 | function StreamCipher (mode, key, iv, decrypt) { 7 | Transform.call(this) 8 | 9 | this._cipher = new aes.AES(key) 10 | this._prev = Buffer.from(iv) 11 | this._cache = Buffer.allocUnsafe(0) 12 | this._secCache = Buffer.allocUnsafe(0) 13 | this._decrypt = decrypt 14 | this._mode = mode 15 | } 16 | 17 | inherits(StreamCipher, Transform) 18 | 19 | StreamCipher.prototype._update = function (chunk) { 20 | return this._mode.encrypt(this, chunk, this._decrypt) 21 | } 22 | 23 | StreamCipher.prototype._final = function () { 24 | this._cipher.scrub() 25 | } 26 | 27 | module.exports = StreamCipher 28 | -------------------------------------------------------------------------------- /test/extra.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "algo": "aes-128-gcm", 4 | "key": "6970787039613669314d623455536234", 5 | "iv": "583673497131313748307652", 6 | "plain": "Hello World!", 7 | "ct": "4BE13896F64DFA2C2D0F2C76", 8 | "tag": "272B422F62EB545EAA15B5FF84092447", 9 | "tampered": false 10 | }, 11 | { 12 | "algo": "aes-128-gcm", 13 | "key": "6970787039613669314d623455536234", 14 | "iv": "583673497131313748307652", 15 | "plain": "Hello World!", 16 | "ct": "4BE13896F64DFA2C2D0F2C76", 17 | "aad": "000000FF", 18 | "tag": "BA2479F66275665A88CB7B15F43EB005", 19 | "tampered": false 20 | }, 21 | { 22 | "algo": "aes-128-gcm", 23 | "key": "6970787039613669314d623455536234", 24 | "iv": "583673497131313748307652", 25 | "plain": "Hello World!", 26 | "ct": "4BE13596F64DFA2C2D0FAC76", 27 | "tag": "272B422F62EB545EAA15B5FF84092447", 28 | "tampered": true 29 | }, 30 | { 31 | "algo": "aes-256-gcm", 32 | "key": "337a54767a7233703637564336316a6d56353472495975313534357834546c59", 33 | "iv": "36306950306836764a6f4561", 34 | "plain": "Hello node.js world!", 35 | "ct": "58E62CFE7B1D274111A82267EBB93866E72B6C2A", 36 | "tag": "9BB44F663BADABACAE9720881FB1EC7A", 37 | "tampered": false 38 | }, 39 | { 40 | "algo": "aes-256-gcm", 41 | "key": "337a54767a7233703637564336316a6d56353472495975313534357834546c59", 42 | "iv": "36306950306836764a6f4561", 43 | "plain": "Hello node.js world!", 44 | "ct": "58E62CFF7B1D274011A82267EBB93866E72B6C2B", 45 | "tag": "9BB44F663BADABACAE9720881FB1EC7A", 46 | "tampered": true 47 | }, 48 | { 49 | "algo": "aes-192-gcm", 50 | "key": "1ed2233fa2223ef5d7df08546049406c7305220bca40d4c9", 51 | "iv": "0e1791e9db3bd21a9122c416", 52 | "plain": "Hello node.js world!", 53 | "password": "very bad password", 54 | "aad": "63616c76696e", 55 | "ct": "DDA53A4059AA17B88756984995F7BBA3C636CC44", 56 | "tag": "D2A35E5C611E5E3D2258360241C5B045", 57 | "tampered": false 58 | } 59 | ] 60 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var Buffer = require('safe-buffer').Buffer 2 | var test = require('tape') 3 | var fixtures = require('./fixtures.json') 4 | var fixtures2 = require('./extra.json') 5 | var _crypto = require('crypto') 6 | var crypto = require('../browser.js') 7 | var modes = require('../modes') 8 | var CIPHERS = Object.keys(modes) 9 | var ebtk = require('evp_bytestokey') 10 | 11 | function isGCM (cipher) { 12 | return modes[cipher].mode === 'GCM' 13 | } 14 | 15 | function isNode10 () { 16 | return process.version && process.version.split('.').length === 3 && parseInt(process.version.split('.')[1], 10) <= 10 17 | } 18 | 19 | fixtures.forEach(function (f, i) { 20 | CIPHERS.forEach(function (cipher) { 21 | if (isGCM(cipher)) return 22 | 23 | test('fixture ' + i + ' ' + cipher, function (t) { 24 | t.plan(1) 25 | var suite = crypto.createCipher(cipher, Buffer.from(f.password)) 26 | var buf = Buffer.alloc(0) 27 | suite.on('data', function (d) { 28 | buf = Buffer.concat([buf, d]) 29 | }) 30 | suite.on('error', function (e) { 31 | console.log(e) 32 | }) 33 | suite.on('end', function () { 34 | // console.log(f.text) 35 | // decriptNoPadding(cipher, Buffer.from(f.password), buf.toString('hex'), 'a') 36 | // decriptNoPadding(cipher, Buffer.from(f.password), f.results.ciphers[cipher], 'b') 37 | t.equals(buf.toString('hex'), f.results.ciphers[cipher]) 38 | }) 39 | suite.write(Buffer.from(f.text)) 40 | suite.end() 41 | }) 42 | 43 | test('fixture ' + i + ' ' + cipher + '-legacy', function (t) { 44 | t.plan(3) 45 | var suite = crypto.createCipher(cipher, Buffer.from(f.password)) 46 | var buf = Buffer.alloc(0) 47 | var suite2 = _crypto.createCipher(cipher, Buffer.from(f.password)) 48 | var buf2 = Buffer.alloc(0) 49 | var inbuf = Buffer.from(f.text) 50 | var mid = ~~(inbuf.length / 2) 51 | buf = Buffer.concat([buf, suite.update(inbuf.slice(0, mid))]) 52 | buf2 = Buffer.concat([buf2, suite2.update(inbuf.slice(0, mid))]) 53 | t.equals(buf.toString('hex'), buf2.toString('hex'), 'intermediate') 54 | buf = Buffer.concat([buf, suite.update(inbuf.slice(mid))]) 55 | buf2 = Buffer.concat([buf2, suite2.update(inbuf.slice(mid))]) 56 | t.equals(buf.toString('hex'), buf2.toString('hex'), 'intermediate 2') 57 | buf = Buffer.concat([buf, suite.final()]) 58 | buf2 = Buffer.concat([buf2, suite2.final()]) 59 | t.equals(buf.toString('hex'), buf2.toString('hex'), 'final') 60 | }) 61 | 62 | test('fixture ' + i + ' ' + cipher + '-decrypt', function (t) { 63 | t.plan(1) 64 | var suite = crypto.createDecipher(cipher, Buffer.from(f.password)) 65 | var buf = Buffer.alloc(0) 66 | suite.on('data', function (d) { 67 | buf = Buffer.concat([buf, d]) 68 | }) 69 | suite.on('error', function (e) { 70 | console.log(e) 71 | }) 72 | suite.on('end', function () { 73 | // console.log(f.text) 74 | // decriptNoPadding(cipher, Buffer.from(f.password), buf.toString('hex'), 'a') 75 | // decriptNoPadding(cipher, Buffer.from(f.password), f.results.ciphers[cipher], 'b') 76 | t.equals(buf.toString('utf8'), f.text) 77 | }) 78 | suite.write(Buffer.from(f.results.ciphers[cipher], 'hex')) 79 | suite.end() 80 | }) 81 | 82 | test('fixture ' + i + ' ' + cipher + '-decrypt-legacy', function (t) { 83 | t.plan(4) 84 | var suite = crypto.createDecipher(cipher, Buffer.from(f.password)) 85 | var buf = Buffer.alloc(0) 86 | var suite2 = _crypto.createDecipher(cipher, Buffer.from(f.password)) 87 | var buf2 = Buffer.alloc(0) 88 | var inbuf = Buffer.from(f.results.ciphers[cipher], 'hex') 89 | var mid = ~~(inbuf.length / 2) 90 | buf = Buffer.concat([buf, suite.update(inbuf.slice(0, mid))]) 91 | buf2 = Buffer.concat([buf2, suite2.update(inbuf.slice(0, mid))]) 92 | t.equals(buf.toString('utf8'), buf2.toString('utf8'), 'intermediate') 93 | buf = Buffer.concat([buf, suite.update(inbuf.slice(mid))]) 94 | buf2 = Buffer.concat([buf2, suite2.update(inbuf.slice(mid))]) 95 | t.equals(buf.toString('utf8'), buf2.toString('utf8'), 'intermediate 2') 96 | buf = Buffer.concat([buf, suite.final()]) 97 | buf2 = Buffer.concat([buf2, suite2.final()]) 98 | t.equals(buf.toString('utf8'), f.text) 99 | t.equals(buf.toString('utf8'), buf2.toString('utf8'), 'final') 100 | }) 101 | }) 102 | 103 | CIPHERS.forEach(function (cipher) { 104 | if (modes[cipher].mode === 'ECB') return 105 | if (isGCM(cipher) && isNode10()) return 106 | 107 | test('fixture ' + i + ' ' + cipher + '-iv', function (t) { 108 | t.plan(isGCM(cipher) ? 4 : 2) 109 | 110 | var suite = crypto.createCipheriv(cipher, ebtk(f.password, false, modes[cipher].key).key, isGCM(cipher) ? (Buffer.from(f.iv, 'hex').slice(0, 12)) : (Buffer.from(f.iv, 'hex'))) 111 | var suite2 = _crypto.createCipheriv(cipher, ebtk(f.password, false, modes[cipher].key).key, isGCM(cipher) ? (Buffer.from(f.iv, 'hex').slice(0, 12)) : (Buffer.from(f.iv, 'hex'))) 112 | var buf = Buffer.alloc(0) 113 | var buf2 = Buffer.alloc(0) 114 | 115 | suite.on('data', function (d) { 116 | buf = Buffer.concat([buf, d]) 117 | }) 118 | 119 | suite.on('error', function (e) { 120 | console.log(e) 121 | }) 122 | 123 | suite2.on('data', function (d) { 124 | buf2 = Buffer.concat([buf2, d]) 125 | }) 126 | 127 | suite2.on('error', function (e) { 128 | console.log(e) 129 | }) 130 | 131 | suite.on('end', function () { 132 | t.equals(buf.toString('hex'), f.results.cipherivs[cipher], 'vs fixture') 133 | t.equals(buf.toString('hex'), buf2.toString('hex'), 'vs node') 134 | if (isGCM(cipher)) { 135 | t.equals(suite.getAuthTag().toString('hex'), f.authtag[cipher], 'authtag vs fixture') 136 | t.equals(suite.getAuthTag().toString('hex'), suite2.getAuthTag().toString('hex'), 'authtag vs node') 137 | } 138 | }) 139 | 140 | if (isGCM(cipher)) { 141 | suite.setAAD(Buffer.from(f.aad, 'hex')) 142 | suite2.setAAD(Buffer.from(f.aad, 'hex')) 143 | } 144 | 145 | suite2.write(Buffer.from(f.text)) 146 | suite2.end() 147 | suite.write(Buffer.from(f.text)) 148 | suite.end() 149 | }) 150 | 151 | test('fixture ' + i + ' ' + cipher + '-legacy-iv', function (t) { 152 | t.plan(isGCM(cipher) ? 6 : 4) 153 | 154 | var suite = crypto.createCipheriv(cipher, ebtk(f.password, false, modes[cipher].key).key, isGCM(cipher) ? (Buffer.from(f.iv, 'hex').slice(0, 12)) : (Buffer.from(f.iv, 'hex'))) 155 | var suite2 = _crypto.createCipheriv(cipher, ebtk(f.password, false, modes[cipher].key).key, isGCM(cipher) ? (Buffer.from(f.iv, 'hex').slice(0, 12)) : (Buffer.from(f.iv, 'hex'))) 156 | var buf = Buffer.alloc(0) 157 | var buf2 = Buffer.alloc(0) 158 | var inbuf = Buffer.from(f.text) 159 | var mid = ~~(inbuf.length / 2) 160 | if (isGCM(cipher)) { 161 | suite.setAAD(Buffer.from(f.aad, 'hex')) 162 | suite2.setAAD(Buffer.from(f.aad, 'hex')) 163 | } 164 | 165 | buf = Buffer.concat([buf, suite.update(inbuf.slice(0, mid))]) 166 | buf2 = Buffer.concat([buf2, suite2.update(inbuf.slice(0, mid))]) 167 | t.equals(buf.toString('hex'), buf2.toString('hex'), 'intermediate') 168 | buf = Buffer.concat([buf, suite.update(inbuf.slice(mid))]) 169 | buf2 = Buffer.concat([buf2, suite2.update(inbuf.slice(mid))]) 170 | t.equals(buf.toString('hex'), buf2.toString('hex'), 'intermediate 2') 171 | buf = Buffer.concat([buf, suite.final()]) 172 | buf2 = Buffer.concat([buf2, suite2.final()]) 173 | t.equals(buf.toString('hex'), f.results.cipherivs[cipher]) 174 | t.equals(buf.toString('hex'), buf2.toString('hex'), 'final') 175 | if (isGCM(cipher)) { 176 | t.equals(suite.getAuthTag().toString('hex'), f.authtag[cipher], 'authtag vs fixture') 177 | t.equals(suite.getAuthTag().toString('hex'), suite2.getAuthTag().toString('hex'), 'authtag vs node') 178 | } 179 | }) 180 | 181 | test('fixture ' + i + ' ' + cipher + '-iv-decrypt', function (t) { 182 | t.plan(2) 183 | 184 | var suite = crypto.createDecipheriv(cipher, ebtk(f.password, false, modes[cipher].key).key, isGCM(cipher) ? (Buffer.from(f.iv, 'hex').slice(0, 12)) : (Buffer.from(f.iv, 'hex'))) 185 | var buf = Buffer.alloc(0) 186 | var suite2 = _crypto.createDecipheriv(cipher, ebtk(f.password, false, modes[cipher].key).key, isGCM(cipher) ? (Buffer.from(f.iv, 'hex').slice(0, 12)) : (Buffer.from(f.iv, 'hex'))) 187 | var buf2 = Buffer.alloc(0) 188 | 189 | suite.on('data', function (d) { 190 | buf = Buffer.concat([buf, d]) 191 | }) 192 | 193 | suite.on('error', function (e) { 194 | t.notOk(e) 195 | }) 196 | 197 | suite2.on('data', function (d) { 198 | buf2 = Buffer.concat([buf2, d]) 199 | }) 200 | 201 | suite2.on('error', function (e) { 202 | t.notOk(e) 203 | }) 204 | 205 | suite.on('end', function () { 206 | t.equals(buf.toString('utf8'), f.text, 'correct text vs fixture') 207 | t.equals(buf.toString('utf8'), buf2.toString('utf8'), 'correct text vs node') 208 | }) 209 | 210 | if (isGCM(cipher)) { 211 | suite.setAuthTag(Buffer.from(f.authtag[cipher], 'hex')) 212 | suite2.setAuthTag(Buffer.from(f.authtag[cipher], 'hex')) 213 | suite.setAAD(Buffer.from(f.aad, 'hex')) 214 | suite2.setAAD(Buffer.from(f.aad, 'hex')) 215 | } 216 | 217 | suite2.write(Buffer.from(f.results.cipherivs[cipher], 'hex')) 218 | suite.write(Buffer.from(f.results.cipherivs[cipher], 'hex')) 219 | suite2.end() 220 | suite.end() 221 | }) 222 | test('fixture ' + i + ' ' + cipher + '-decrypt-legacy', function (t) { 223 | t.plan(4) 224 | var suite = crypto.createDecipheriv(cipher, ebtk(f.password, false, modes[cipher].key).key, isGCM(cipher) ? (Buffer.from(f.iv, 'hex').slice(0, 12)) : (Buffer.from(f.iv, 'hex'))) 225 | var buf = Buffer.alloc(0) 226 | var suite2 = _crypto.createDecipheriv(cipher, ebtk(f.password, false, modes[cipher].key).key, isGCM(cipher) ? (Buffer.from(f.iv, 'hex').slice(0, 12)) : (Buffer.from(f.iv, 'hex'))) 227 | var buf2 = Buffer.alloc(0) 228 | var inbuf = Buffer.from(f.results.cipherivs[cipher], 'hex') 229 | var mid = ~~(inbuf.length / 2) 230 | if (isGCM(cipher)) { 231 | suite.setAAD(Buffer.from(f.aad, 'hex')) 232 | suite2.setAAD(Buffer.from(f.aad, 'hex')) 233 | suite.setAuthTag(Buffer.from(f.authtag[cipher], 'hex')) 234 | suite2.setAuthTag(Buffer.from(f.authtag[cipher], 'hex')) 235 | } 236 | buf = Buffer.concat([buf, suite.update(inbuf.slice(0, mid))]) 237 | buf2 = Buffer.concat([buf2, suite2.update(inbuf.slice(0, mid))]) 238 | 239 | t.equals(buf.toString('utf8'), buf2.toString('utf8'), 'intermediate') 240 | buf = Buffer.concat([buf, suite.update(inbuf.slice(mid))]) 241 | buf2 = Buffer.concat([buf2, suite2.update(inbuf.slice(mid))]) 242 | t.equals(buf.toString('utf8'), buf2.toString('utf8'), 'intermediate 2') 243 | buf = Buffer.concat([buf, suite.final()]) 244 | buf2 = Buffer.concat([buf2, suite2.final()]) 245 | t.equals(buf.toString('utf8'), f.text) 246 | t.equals(buf.toString('utf8'), buf2.toString('utf8'), 'final') 247 | }) 248 | }) 249 | }) 250 | 251 | fixtures2.forEach((f, i) => { 252 | test('test case ' + i, function (t) { 253 | if (CIPHERS.indexOf(f.algo) === -1) { 254 | console.log('skipping unsupported ' + f.algo + ' test') 255 | return 256 | } 257 | 258 | (function () { 259 | var encrypt = crypto.createCipheriv(f.algo, 260 | Buffer.from(f.key, 'hex'), Buffer.from(f.iv, 'hex')) 261 | if (f.aad) encrypt.setAAD(Buffer.from(f.aad, 'hex')) 262 | 263 | var hex = encrypt.update(f.plain, 'ascii', 'hex') 264 | hex += encrypt.final('hex') 265 | var authTag = encrypt.getAuthTag() 266 | 267 | // only test basic encryption run if output is marked as tampered. 268 | if (!f.tampered) { 269 | t.equal(hex.toUpperCase(), f.ct) 270 | t.equal(authTag.toString('hex').toUpperCase(), f.tag) 271 | } 272 | })() 273 | 274 | ;(function () { 275 | var decrypt = crypto.createDecipheriv(f.algo, 276 | Buffer.from(f.key, 'hex'), Buffer.from(f.iv, 'hex')) 277 | decrypt.setAuthTag(Buffer.from(f.tag, 'hex')) 278 | if (f.aad) decrypt.setAAD(Buffer.from(f.aad, 'hex')) 279 | var msg = decrypt.update(f.ct, 'hex', 'ascii') 280 | if (!f.tampered) { 281 | msg += decrypt.final('ascii') 282 | t.equal(msg, f.plain) 283 | } else { 284 | // assert that final throws if input data could not be verified! 285 | t.throws(function () { decrypt.final('ascii') }, / auth/) 286 | } 287 | })() 288 | 289 | ;(function () { 290 | if (!f.password) return 291 | var encrypt = crypto.createCipher(f.algo, f.password) 292 | if (f.aad) encrypt.setAAD(Buffer.from(f.aad, 'hex')) 293 | var hex = encrypt.update(f.plain, 'ascii', 'hex') 294 | hex += encrypt.final('hex') 295 | var authTag = encrypt.getAuthTag() 296 | // only test basic encryption run if output is marked as tampered. 297 | if (!f.tampered) { 298 | t.equal(hex.toUpperCase(), f.ct) 299 | t.equal(authTag.toString('hex').toUpperCase(), f.tag) 300 | } 301 | })() 302 | 303 | ;(function () { 304 | if (!f.password) return 305 | var decrypt = crypto.createDecipher(f.algo, f.password) 306 | decrypt.setAuthTag(Buffer.from(f.tag, 'hex')) 307 | if (f.aad) decrypt.setAAD(Buffer.from(f.aad, 'hex')) 308 | var msg = decrypt.update(f.ct, 'hex', 'ascii') 309 | if (!f.tampered) { 310 | msg += decrypt.final('ascii') 311 | t.equal(msg, f.plain) 312 | } else { 313 | // assert that final throws if input data could not be verified! 314 | t.throws(function () { decrypt.final('ascii') }, / auth/) 315 | } 316 | })() 317 | 318 | // after normal operation, test some incorrect ways of calling the API: 319 | // it's most certainly enough to run these tests with one algorithm only. 320 | if (i !== 0) { 321 | t.end() 322 | return 323 | } 324 | 325 | (function () { 326 | // non-authenticating mode: 327 | var encrypt = crypto.createCipheriv('aes-128-cbc', 328 | 'ipxp9a6i1Mb4USb4', '6fKjEjR3Vl30EUYC') 329 | encrypt.update('blah', 'ascii') 330 | encrypt.final() 331 | t.throws(function () { encrypt.getAuthTag() }) 332 | t.throws(function () { 333 | encrypt.setAAD(Buffer.from('123', 'ascii')) 334 | }) 335 | })() 336 | 337 | ;(function () { 338 | // trying to get tag before inputting all data: 339 | var encrypt = crypto.createCipheriv(f.algo, 340 | Buffer.from(f.key, 'hex'), Buffer.from(f.iv, 'hex')) 341 | encrypt.update('blah', 'ascii') 342 | t.throws(function () { encrypt.getAuthTag() }, / state/) 343 | })() 344 | 345 | ;(function () { 346 | // trying to set tag on encryption object: 347 | var encrypt = crypto.createCipheriv(f.algo, 348 | Buffer.from(f.key, 'hex'), Buffer.from(f.iv, 'hex')) 349 | t.throws(function () { 350 | encrypt.setAuthTag(Buffer.from(f.tag, 'hex')) 351 | }, / state/) 352 | })() 353 | 354 | ;(function () { 355 | // trying to read tag from decryption object: 356 | var decrypt = crypto.createDecipheriv(f.algo, 357 | Buffer.from(f.key, 'hex'), Buffer.from(f.iv, 'hex')) 358 | t.throws(function () { decrypt.getAuthTag() }, / state/) 359 | })() 360 | t.end() 361 | }) 362 | }) 363 | 364 | test('autopadding false decipher', function (t) { 365 | t.plan(2) 366 | var mycipher = crypto.createCipher('AES-128-ECB', Buffer.from('password')) 367 | var nodecipher = _crypto.createCipher('AES-128-ECB', Buffer.from('password')) 368 | var myEnc = mycipher.final() 369 | var nodeEnc = nodecipher.final() 370 | t.equals(myEnc.toString('hex'), nodeEnc.toString('hex'), 'same encryption') 371 | var decipher = crypto.createDecipher('aes-128-ecb', Buffer.from('password')) 372 | decipher.setAutoPadding(false) 373 | var decipher2 = _crypto.createDecipher('aes-128-ecb', Buffer.from('password')) 374 | decipher2.setAutoPadding(false) 375 | t.equals(decipher.update(myEnc).toString('hex'), decipher2.update(nodeEnc).toString('hex'), 'same decryption') 376 | }) 377 | 378 | test('autopadding false cipher throws', function (t) { 379 | t.plan(2) 380 | 381 | var mycipher = crypto.createCipher('aes-128-ecb', Buffer.from('password')) 382 | mycipher.setAutoPadding(false) 383 | var nodecipher = _crypto.createCipher('aes-128-ecb', Buffer.from('password')) 384 | nodecipher.setAutoPadding(false) 385 | mycipher.update('foo') 386 | nodecipher.update('foo') 387 | t.throws(function () { 388 | mycipher.final() 389 | }, /data not multiple of block length/) 390 | t.throws(function () { 391 | nodecipher.final() 392 | }, /./) 393 | }) 394 | 395 | test('getCiphers works', function (t) { 396 | t.plan(1) 397 | t.ok(crypto.getCiphers().length, 'get some ciphers') 398 | }) 399 | 400 | test('correctly handle incremental base64 output', function (t) { 401 | t.plan(2) 402 | 403 | var encoding = 'base64' 404 | function encrypt (data, key, algorithm) { 405 | algorithm = algorithm || 'aes256' 406 | var cipher = crypto.createCipher(algorithm, key) 407 | var part1 = cipher.update(data, 'utf8', encoding) 408 | var part2 = cipher.final(encoding) 409 | return part1 + part2 410 | } 411 | 412 | function encryptNode (data, key, algorithm) { 413 | algorithm = algorithm || 'aes256' 414 | var cipher = _crypto.createCipher(algorithm, key) 415 | var part1 = cipher.update(data, 'utf8', encoding) 416 | var part2 = cipher.final(encoding) 417 | return part1 + part2 418 | } 419 | 420 | function decrypt (data, key, algorithm) { 421 | algorithm = algorithm || 'aes256' 422 | var decipher = crypto.createDecipher(algorithm, key) 423 | return decipher.update(data, encoding, 'utf8') + decipher.final('utf8') 424 | } 425 | 426 | var key = 'this is a very secure key' 427 | var data = 'The quick brown fox jumps over the lazy dog.' 428 | var encrypted = encrypt(data, key) 429 | t.equals(encrypted, encryptNode(data, key), 'encrypt correctly') 430 | var decrypted = decrypt(encrypted, key) 431 | t.equals(data, decrypted, 'round trips') 432 | }) 433 | 434 | var gcmTest = [ 435 | { 436 | key: '68d010dad5295e1f4f485f35cff46c35d423797bf4cd536d4943d787e00f6f07', 437 | length: 8, 438 | answer: '44d0f292', 439 | tag: '1f21c63664fc5262827b9624dee894bd', 440 | ivFill: 9 441 | }, 442 | { 443 | key: '9ba693ec61afc9b7950f9177780b3533126af40a7596c662e26e6d6bbf536030', 444 | length: 16, 445 | answer: '1c8f8783', 446 | tag: '2d2b33f509153a8afc973cf9fc983800', 447 | ivFill: 1 448 | }, 449 | { 450 | key: 'dad2a11c52614e4402f0f126028d5e55b50b3a9d6d006cfbee79b77e4a4ee7b9', 451 | length: 21, 452 | ivFill: 2, 453 | answer: '1a8dd3ed', 454 | tag: '68ce0e40ee335388c0468813b8e5eb4b' 455 | }, 456 | { 457 | key: '4c062c7bd7566bec4c509e3bf0c9cc2acb75a863403b04fdce025ba26b6a6ca2', 458 | length: 43, 459 | ivFill: 5, 460 | answer: '5f6ccc8c', 461 | tag: '9a0d845168a1491e17217a20a75defb0' 462 | } 463 | ] 464 | function testIV (t, length, answer, tag, key, ivFill) { 465 | t.test('key length ' + length, function (t) { 466 | t.plan(3) 467 | var iv = Buffer.alloc(length, ivFill) 468 | var cipher = crypto.createCipheriv('aes-256-gcm', key, iv) 469 | var out = cipher.update('fooo').toString('hex') 470 | t.equals(out, answer) 471 | cipher.final() 472 | t.equals(tag, cipher.getAuthTag().toString('hex')) 473 | var decipher = crypto.createDecipheriv('aes-256-gcm', key, iv) 474 | decipher.setAuthTag(Buffer.from(tag, 'hex')) 475 | var decrypted = decipher.update(Buffer.from(answer, 'hex')) 476 | t.equals(decrypted.toString(), 'fooo') 477 | }) 478 | } 479 | test('different IV lengths work for GCM', function (t) { 480 | gcmTest.forEach(function (item) { 481 | testIV(t, item.length, item.answer, item.tag, Buffer.from(item.key, 'hex'), item.ivFill) 482 | }) 483 | }) 484 | test('handle long uft8 plaintexts', function (t) { 485 | t.plan(1) 486 | var salt = Buffer.alloc(32, 0) 487 | 488 | function encrypt (txt) { 489 | var cipher = crypto.createCipher('aes-256-cbc', salt) 490 | return cipher.update(txt, 'utf8', 'base64') + cipher.final('base64') 491 | } 492 | 493 | function decrypt (enc) { 494 | var decipher = crypto.createDecipher('aes-256-cbc', salt) 495 | return decipher.update(enc, 'base64', 'utf8') + decipher.final('utf8') 496 | } 497 | 498 | var input = 'ふっかつ あきる すぶり はやい つける まゆげ たんさん みんぞく ねほりはほり せまい たいまつばな ひはん' 499 | var enc = encrypt(input, 'a') 500 | 501 | var dec = decrypt(enc, 'a') 502 | t.equals(dec, input) 503 | }) 504 | 505 | test('mix and match encoding', function (t) { 506 | t.plan(2) 507 | var cipher = crypto.createCipher('aes-256-cbc', 'a') 508 | cipher.update('foo', 'utf8', 'utf8') 509 | t.throws(function () { 510 | cipher.update('foo', 'utf8', 'base64') 511 | }) 512 | cipher = crypto.createCipher('aes-256-cbc', 'a') 513 | cipher.update('foo', 'utf8', 'base64') 514 | t.doesNotThrow(function () { 515 | cipher.update('foo', 'utf8') 516 | cipher.final('base64') 517 | }) 518 | }) 519 | 520 | function corectPaddingWords (padding, result) { 521 | test('correct padding ' + padding.toString('hex'), function (t) { 522 | t.plan(1) 523 | var block1 = Buffer.alloc(16, 4) 524 | result = block1.toString('hex') + result.toString('hex') 525 | var cipher = _crypto.createCipher('aes128', Buffer.from('password')) 526 | cipher.setAutoPadding(false) 527 | var decipher = crypto.createDecipher('aes128', Buffer.from('password')) 528 | var out = Buffer.alloc(0) 529 | out = Buffer.concat([out, cipher.update(block1)]) 530 | out = Buffer.concat([out, cipher.update(padding)]) 531 | var deciphered = decipher.update(out) 532 | deciphered = Buffer.concat([deciphered, decipher.final()]) 533 | t.equals(deciphered.toString('hex'), result) 534 | }) 535 | } 536 | 537 | function incorectPaddingthrows (padding) { 538 | test('incorrect padding ' + padding.toString('hex'), function (t) { 539 | t.plan(2) 540 | var block1 = Buffer.alloc(16, 4) 541 | var cipher = crypto.createCipher('aes128', Buffer.from('password')) 542 | cipher.setAutoPadding(false) 543 | var decipher = crypto.createDecipher('aes128', Buffer.from('password')) 544 | var decipher2 = _crypto.createDecipher('aes128', Buffer.from('password')) 545 | var out = Buffer.alloc(0) 546 | out = Buffer.concat([out, cipher.update(block1)]) 547 | out = Buffer.concat([out, cipher.update(padding)]) 548 | decipher.update(out) 549 | decipher2.update(out) 550 | t.throws(function () { 551 | decipher.final() 552 | }, 'mine') 553 | t.throws(function () { 554 | decipher2.final() 555 | }, 'node') 556 | }) 557 | } 558 | 559 | function incorectPaddingDoesNotThrow (padding) { 560 | test('stream incorrect padding ' + padding.toString('hex'), function (t) { 561 | t.plan(2) 562 | var block1 = Buffer.alloc(16, 4) 563 | var cipher = crypto.createCipher('aes128', Buffer.from('password')) 564 | cipher.setAutoPadding(false) 565 | var decipher = crypto.createDecipher('aes128', Buffer.from('password')) 566 | var decipher2 = _crypto.createDecipher('aes128', Buffer.from('password')) 567 | cipher.pipe(decipher) 568 | cipher.pipe(decipher2) 569 | cipher.write(block1) 570 | cipher.write(padding) 571 | decipher.on('error', function (e) { 572 | t.ok(e, 'mine') 573 | }) 574 | decipher2.on('error', function (e) { 575 | t.ok(e, 'node') 576 | }) 577 | cipher.end() 578 | }) 579 | } 580 | 581 | var sixteens = Buffer.alloc(16, 16) 582 | var fifteens = Buffer.alloc(16, 15) 583 | fifteens[0] = 5 584 | var one = _crypto.randomBytes(16) 585 | one[15] = 1 586 | var sixteens2 = Buffer.alloc(16, 16) 587 | sixteens2[3] = 5 588 | var fifteens2 = Buffer.alloc(16, 15) 589 | fifteens2[0] = 5 590 | fifteens2[1] = 6 591 | var two = _crypto.randomBytes(16) 592 | two[15] = 2 593 | two[14] = 1 594 | var zeroes = Buffer.alloc(16) 595 | var seventeens = Buffer.alloc(16, 17) 596 | var ff = Buffer.alloc(16, 0xff) 597 | 598 | corectPaddingWords(sixteens, Buffer.alloc(0)) 599 | corectPaddingWords(fifteens, Buffer.from([5])) 600 | corectPaddingWords(one, one.slice(0, -1)) 601 | ;[sixteens2, fifteens2, two, zeroes, seventeens, ff].forEach((x) => { 602 | incorectPaddingthrows(x) 603 | incorectPaddingDoesNotThrow(x) 604 | }) 605 | --------------------------------------------------------------------------------