UNPKG

2.65 kBJavaScriptView Raw
1var aes = require('./aes')
2var Transform = require('cipher-base')
3var inherits = require('inherits')
4var GHASH = require('./ghash')
5var xor = require('buffer-xor')
6inherits(StreamCipher, Transform)
7module.exports = StreamCipher
8
9function StreamCipher (mode, key, iv, decrypt) {
10 if (!(this instanceof StreamCipher)) {
11 return new StreamCipher(mode, key, iv)
12 }
13 Transform.call(this)
14 this._finID = Buffer.concat([iv, new Buffer([0, 0, 0, 1])])
15 iv = Buffer.concat([iv, new Buffer([0, 0, 0, 2])])
16 this._cipher = new aes.AES(key)
17 this._prev = new Buffer(iv.length)
18 this._cache = new Buffer('')
19 this._secCache = new Buffer('')
20 this._decrypt = decrypt
21 this._alen = 0
22 this._len = 0
23 iv.copy(this._prev)
24 this._mode = mode
25 var h = new Buffer(4)
26 h.fill(0)
27 this._ghash = new GHASH(this._cipher.encryptBlock(h))
28 this._authTag = null
29 this._called = false
30}
31StreamCipher.prototype._update = function (chunk) {
32 if (!this._called && this._alen) {
33 var rump = 16 - (this._alen % 16)
34 if (rump < 16) {
35 rump = new Buffer(rump)
36 rump.fill(0)
37 this._ghash.update(rump)
38 }
39 }
40 this._called = true
41 var out = this._mode.encrypt(this, chunk)
42 if (this._decrypt) {
43 this._ghash.update(chunk)
44 } else {
45 this._ghash.update(out)
46 }
47 this._len += chunk.length
48 return out
49}
50StreamCipher.prototype._final = function () {
51 if (this._decrypt && !this._authTag) {
52 throw new Error('Unsupported state or unable to authenticate data')
53 }
54 var tag = xor(this._ghash.final(this._alen * 8, this._len * 8), this._cipher.encryptBlock(this._finID))
55 if (this._decrypt) {
56 if (xorTest(tag, this._authTag)) {
57 throw new Error('Unsupported state or unable to authenticate data')
58 }
59 } else {
60 this._authTag = tag
61 }
62 this._cipher.scrub()
63}
64StreamCipher.prototype.getAuthTag = function getAuthTag () {
65 if (!this._decrypt && Buffer.isBuffer(this._authTag)) {
66 return this._authTag
67 } else {
68 throw new Error('Attempting to get auth tag in unsupported state')
69 }
70}
71StreamCipher.prototype.setAuthTag = function setAuthTag (tag) {
72 if (this._decrypt) {
73 this._authTag = tag
74 } else {
75 throw new Error('Attempting to set auth tag in unsupported state')
76 }
77}
78StreamCipher.prototype.setAAD = function setAAD (buf) {
79 if (!this._called) {
80 this._ghash.update(buf)
81 this._alen += buf.length
82 } else {
83 throw new Error('Attempting to set AAD in unsupported state')
84 }
85}
86function xorTest (a, b) {
87 var out = 0
88 if (a.length !== b.length) {
89 out++
90 }
91 var len = Math.min(a.length, b.length)
92 var i = -1
93 while (++i < len) {
94 out += (a[i] ^ b[i])
95 }
96 return out
97}