123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- var aes = require('./aes')
- var Buffer = require('safe-buffer').Buffer
- var Transform = require('cipher-base')
- var inherits = require('inherits')
- var GHASH = require('./ghash')
- var xor = require('buffer-xor')
- var incr32 = require('./incr32')
-
- function xorTest (a, b) {
- var out = 0
- if (a.length !== b.length) out++
-
- var len = Math.min(a.length, b.length)
- for (var i = 0; i < len; ++i) {
- out += (a[i] ^ b[i])
- }
-
- return out
- }
-
- function calcIv (self, iv, ck) {
- if (iv.length === 12) {
- self._finID = Buffer.concat([iv, Buffer.from([0, 0, 0, 1])])
- return Buffer.concat([iv, Buffer.from([0, 0, 0, 2])])
- }
- var ghash = new GHASH(ck)
- var len = iv.length
- var toPad = len % 16
- ghash.update(iv)
- if (toPad) {
- toPad = 16 - toPad
- ghash.update(Buffer.alloc(toPad, 0))
- }
- ghash.update(Buffer.alloc(8, 0))
- var ivBits = len * 8
- var tail = Buffer.alloc(8)
- tail.writeUIntBE(ivBits, 0, 8)
- ghash.update(tail)
- self._finID = ghash.state
- var out = Buffer.from(self._finID)
- incr32(out)
- return out
- }
- function StreamCipher (mode, key, iv, decrypt) {
- Transform.call(this)
-
- var h = Buffer.alloc(4, 0)
-
- this._cipher = new aes.AES(key)
- var ck = this._cipher.encryptBlock(h)
- this._ghash = new GHASH(ck)
- iv = calcIv(this, iv, ck)
-
- this._prev = Buffer.from(iv)
- this._cache = Buffer.allocUnsafe(0)
- this._secCache = Buffer.allocUnsafe(0)
- this._decrypt = decrypt
- this._alen = 0
- this._len = 0
- this._mode = mode
-
- this._authTag = null
- this._called = false
- }
-
- inherits(StreamCipher, Transform)
-
- StreamCipher.prototype._update = function (chunk) {
- if (!this._called && this._alen) {
- var rump = 16 - (this._alen % 16)
- if (rump < 16) {
- rump = Buffer.alloc(rump, 0)
- this._ghash.update(rump)
- }
- }
-
- this._called = true
- var out = this._mode.encrypt(this, chunk)
- if (this._decrypt) {
- this._ghash.update(chunk)
- } else {
- this._ghash.update(out)
- }
- this._len += chunk.length
- return out
- }
-
- StreamCipher.prototype._final = function () {
- if (this._decrypt && !this._authTag) throw new Error('Unsupported state or unable to authenticate data')
-
- var tag = xor(this._ghash.final(this._alen * 8, this._len * 8), this._cipher.encryptBlock(this._finID))
- if (this._decrypt && xorTest(tag, this._authTag)) throw new Error('Unsupported state or unable to authenticate data')
-
- this._authTag = tag
- this._cipher.scrub()
- }
-
- StreamCipher.prototype.getAuthTag = function getAuthTag () {
- if (this._decrypt || !Buffer.isBuffer(this._authTag)) throw new Error('Attempting to get auth tag in unsupported state')
-
- return this._authTag
- }
-
- StreamCipher.prototype.setAuthTag = function setAuthTag (tag) {
- if (!this._decrypt) throw new Error('Attempting to set auth tag in unsupported state')
-
- this._authTag = tag
- }
-
- StreamCipher.prototype.setAAD = function setAAD (buf) {
- if (this._called) throw new Error('Attempting to set AAD in unsupported state')
-
- this._ghash.update(buf)
- this._alen += buf.length
- }
-
- module.exports = StreamCipher
|