UNPKG

2.71 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._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}
98
99