forked from calvinmetcalf/chacha20poly1305
-
Notifications
You must be signed in to change notification settings - Fork 1
/
chacha20.js
106 lines (98 loc) · 2.8 KB
/
chacha20.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
function ROTATE(v, c) {
return (v << c) | (v >>> (32 - c));
}
var constants = new Buffer('expand 32-byte k');
module.exports = Chacha20;
function Chacha20(key, nonce) {
this.input = new Uint32Array(16);
// https://tools.ietf.org/html/draft-irtf-cfrg-chacha20-poly1305-01#section-2.3
this.input[0] = constants.readUInt32LE(0);
this.input[1] = constants.readUInt32LE(4);
this.input[2] = constants.readUInt32LE(8);
this.input[3] = constants.readUInt32LE(12);
this.input[4] = key.readUInt32LE(0);
this.input[5] = key.readUInt32LE(4);
this.input[6] = key.readUInt32LE(8);
this.input[7] = key.readUInt32LE(12);
this.input[8] = key.readUInt32LE(16);
this.input[9] = key.readUInt32LE(20);
this.input[10] = key.readUInt32LE(24);
this.input[11] = key.readUInt32LE(28);
this.input[12] = 0;
this.input[13] = nonce.readUInt32LE(0);
this.input[14] = nonce.readUInt32LE(4);
this.input[15] = nonce.readUInt32LE(8);
this.cachePos = 64;
this.buffer = new Uint32Array(16);
this.output = new Buffer(64);
}
Chacha20.prototype.quarterRound = function(a, b, c, d) {
var x = this.buffer;
x[a] += x[b]; x[d] = ROTATE(x[d] ^ x[a], 16);
x[c] += x[d]; x[b] = ROTATE(x[b] ^ x[c], 12);
x[a] += x[b]; x[d] = ROTATE(x[d] ^ x[a], 8);
x[c] += x[d]; x[b] = ROTATE(x[b] ^ x[c], 7);
};
Chacha20.prototype.makeBlock = function (output, start) {
var i = -1;
// copy input into working buffer
while (++i < 16) {
this.buffer[i] = this.input[i];
}
i = -1;
while (++i < 10) {
// straight round
this.quarterRound(0, 4, 8,12);
this.quarterRound(1, 5, 9,13);
this.quarterRound(2, 6,10,14);
this.quarterRound(3, 7,11,15);
//diaganle round
this.quarterRound(0, 5,10,15);
this.quarterRound(1, 6,11,12);
this.quarterRound(2, 7, 8,13);
this.quarterRound(3, 4, 9,14);
}
i = -1;
// copy working buffer into output
while (++i < 16) {
this.buffer[i] += this.input[i];
output.writeUInt32LE(this.buffer[i], start);
start += 4;
}
this.input[12]++;
if (!this.input[12]) {
throw new Error('counter is exausted');
}
};
Chacha20.prototype.getBytes = function(len) {
var dpos = 0;
var dst = new Buffer(len);
var cacheLen = 64 - this.cachePos;
if (cacheLen) {
if (cacheLen >= len) {
this.output.copy(dst, 0, this.cachePos, 64);
this.cachePos += len;
return dst;
} else {
this.output.copy(dst, 0, this.cachePos, 64);
len -= cacheLen;
dpos += cacheLen;
this.cachePos = 64;
}
}
while (len > 0 ) {
if (len <= 64) {
this.makeBlock(this.output, 0);
this.output.copy(dst, dpos, 0, len);
if (len < 64) {
this.cachePos = len;
}
return dst;
} else {
this.makeBlock(dst, dpos);
}
len -= 64;
dpos += 64;
}
throw new Error('something bad happended');
};