UNPKG

2.74 kBJavaScriptView Raw
1var aes = require('./aes');
2var Transform = require('./cipherBase');
3var inherits = require('inherits');
4var GHASH = require('./ghash');
5var xor = require('./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._transform = function (chunk, _, next) {
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 next(null, out);
49};
50StreamCipher.prototype._flush = function (next) {
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 next();
64};
65StreamCipher.prototype.getAuthTag = function getAuthTag () {
66 if (!this._decrypt && Buffer.isBuffer(this._authTag)) {
67 return this._authTag;
68 } else {
69 throw new Error('Attempting to get auth tag in unsupported state');
70 }
71};
72StreamCipher.prototype.setAuthTag = function setAuthTag (tag) {
73 if (this._decrypt) {
74 this._authTag = tag;
75 } else {
76 throw new Error('Attempting to set auth tag in unsupported state');
77 }
78};
79StreamCipher.prototype.setAAD = function setAAD (buf) {
80 if (!this._called) {
81 this._ghash.update(buf);
82 this._alen += buf.length;
83 } else {
84 throw new Error('Attempting to set AAD in unsupported state');
85 }
86};
87function xorTest(a, b) {
88 var out = 0;
89 if (a.length !== b.length) {
90 out++;
91 }
92 var len = Math.min(a.length, b.length);
93 var i = -1;
94 while (++i < len) {
95 out += (a[i] ^ b[i]);
96 }
97 return out;
98}
99
100