@pathscale/secure-local-storage-chacha20-poly1305 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +16 -32
- package/dist/SecureLocalStorage.d.ts +0 -1
- package/dist/SecureLocalStorage.d.ts.map +1 -1
- package/dist/encryption.d.ts +4 -2
- package/dist/encryption.d.ts.map +1 -1
- package/dist/index.js +1371 -149
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1370 -148
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -6
package/dist/index.js
CHANGED
|
@@ -2,182 +2,1224 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var chacha_js = require('@noble/ciphers/chacha.js');
|
|
6
|
-
var utils_js = require('@noble/ciphers/utils.js');
|
|
7
|
-
var pbkdf2_js = require('@noble/hashes/pbkdf2.js');
|
|
8
|
-
var sha2_js = require('@noble/hashes/sha2.js');
|
|
9
|
-
|
|
10
5
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
6
|
+
// Copyright (C) 2016 Dmitry Chestnykh
|
|
7
|
+
// MIT License. See LICENSE file for details.
|
|
11
8
|
/**
|
|
12
|
-
*
|
|
9
|
+
* Package binary provides functions for encoding and decoding numbers in byte arrays.
|
|
13
10
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Writes 4-byte little-endian representation of 32-bit unsigned
|
|
13
|
+
* value to array starting at offset.
|
|
14
|
+
*
|
|
15
|
+
* If byte array is not given, creates a new 4-byte one.
|
|
16
|
+
*
|
|
17
|
+
* Returns the output byte array.
|
|
18
|
+
*/
|
|
19
|
+
function writeUint32LE(value, out = new Uint8Array(4), offset = 0) {
|
|
20
|
+
out[offset + 0] = value >>> 0;
|
|
21
|
+
out[offset + 1] = value >>> 8;
|
|
22
|
+
out[offset + 2] = value >>> 16;
|
|
23
|
+
out[offset + 3] = value >>> 24;
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Writes 8-byte little-endian representation of 64-bit unsigned
|
|
28
|
+
* value to byte array starting at offset.
|
|
29
|
+
*
|
|
30
|
+
* Due to JavaScript limitation, supports values up to 2^53-1.
|
|
31
|
+
*
|
|
32
|
+
* If byte array is not given, creates a new 8-byte one.
|
|
33
|
+
*
|
|
34
|
+
* Returns the output byte array.
|
|
35
|
+
*/
|
|
36
|
+
function writeUint64LE(value, out = new Uint8Array(8), offset = 0) {
|
|
37
|
+
writeUint32LE(value >>> 0, out, offset);
|
|
38
|
+
writeUint32LE(value / 0x100000000 >>> 0, out, offset + 4);
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Copyright (C) 2016 Dmitry Chestnykh
|
|
43
|
+
// MIT License. See LICENSE file for details.
|
|
44
|
+
/**
|
|
45
|
+
* Sets all values in the given array to zero and returns it.
|
|
46
|
+
*
|
|
47
|
+
* The fact that it sets bytes to zero can be relied on.
|
|
48
|
+
*
|
|
49
|
+
* There is no guarantee that this function makes data disappear from memory,
|
|
50
|
+
* as runtime implementation can, for example, have copying garbage collector
|
|
51
|
+
* that will make copies of sensitive data before we wipe it. Or that an
|
|
52
|
+
* operating system will write our data to swap or sleep image. Another thing
|
|
53
|
+
* is that an optimizing compiler can remove calls to this function or make it
|
|
54
|
+
* no-op. There's nothing we can do with it, so we just do our best and hope
|
|
55
|
+
* that everything will be okay and good will triumph over evil.
|
|
56
|
+
*/
|
|
57
|
+
function wipe(array) {
|
|
58
|
+
// Right now it's similar to array.fill(0). If it turns
|
|
59
|
+
// out that runtimes optimize this call away, maybe
|
|
60
|
+
// we can try something else.
|
|
61
|
+
for (let i = 0; i < array.length; i++) {
|
|
62
|
+
array[i] = 0;
|
|
17
63
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
64
|
+
return array;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Copyright (C) 2016 Dmitry Chestnykh
|
|
68
|
+
// MIT License. See LICENSE file for details.
|
|
69
|
+
/**
|
|
70
|
+
* Package chacha implements ChaCha stream cipher.
|
|
71
|
+
*/
|
|
72
|
+
// Number of ChaCha rounds (ChaCha20).
|
|
73
|
+
const ROUNDS$1 = 20;
|
|
74
|
+
// Applies the ChaCha core function to 16-byte input,
|
|
75
|
+
// 32-byte key key, and puts the result into 64-byte array out.
|
|
76
|
+
function core(out, input, key) {
|
|
77
|
+
let j0 = 0x61707865; // "expa" -- ChaCha's "sigma" constant
|
|
78
|
+
let j1 = 0x3320646E; // "nd 3" for 32-byte keys
|
|
79
|
+
let j2 = 0x79622D32; // "2-by"
|
|
80
|
+
let j3 = 0x6B206574; // "te k"
|
|
81
|
+
let j4 = (key[3] << 24) | (key[2] << 16) | (key[1] << 8) | key[0];
|
|
82
|
+
let j5 = (key[7] << 24) | (key[6] << 16) | (key[5] << 8) | key[4];
|
|
83
|
+
let j6 = (key[11] << 24) | (key[10] << 16) | (key[9] << 8) | key[8];
|
|
84
|
+
let j7 = (key[15] << 24) | (key[14] << 16) | (key[13] << 8) | key[12];
|
|
85
|
+
let j8 = (key[19] << 24) | (key[18] << 16) | (key[17] << 8) | key[16];
|
|
86
|
+
let j9 = (key[23] << 24) | (key[22] << 16) | (key[21] << 8) | key[20];
|
|
87
|
+
let j10 = (key[27] << 24) | (key[26] << 16) | (key[25] << 8) | key[24];
|
|
88
|
+
let j11 = (key[31] << 24) | (key[30] << 16) | (key[29] << 8) | key[28];
|
|
89
|
+
let j12 = (input[3] << 24) | (input[2] << 16) | (input[1] << 8) | input[0];
|
|
90
|
+
let j13 = (input[7] << 24) | (input[6] << 16) | (input[5] << 8) | input[4];
|
|
91
|
+
let j14 = (input[11] << 24) | (input[10] << 16) | (input[9] << 8) | input[8];
|
|
92
|
+
let j15 = (input[15] << 24) | (input[14] << 16) | (input[13] << 8) | input[12];
|
|
93
|
+
let x0 = j0;
|
|
94
|
+
let x1 = j1;
|
|
95
|
+
let x2 = j2;
|
|
96
|
+
let x3 = j3;
|
|
97
|
+
let x4 = j4;
|
|
98
|
+
let x5 = j5;
|
|
99
|
+
let x6 = j6;
|
|
100
|
+
let x7 = j7;
|
|
101
|
+
let x8 = j8;
|
|
102
|
+
let x9 = j9;
|
|
103
|
+
let x10 = j10;
|
|
104
|
+
let x11 = j11;
|
|
105
|
+
let x12 = j12;
|
|
106
|
+
let x13 = j13;
|
|
107
|
+
let x14 = j14;
|
|
108
|
+
let x15 = j15;
|
|
109
|
+
for (let i = 0; i < ROUNDS$1; i += 2) {
|
|
110
|
+
x0 = x0 + x4 | 0;
|
|
111
|
+
x12 ^= x0;
|
|
112
|
+
x12 = x12 >>> (32 - 16) | x12 << 16;
|
|
113
|
+
x8 = x8 + x12 | 0;
|
|
114
|
+
x4 ^= x8;
|
|
115
|
+
x4 = x4 >>> (32 - 12) | x4 << 12;
|
|
116
|
+
x1 = x1 + x5 | 0;
|
|
117
|
+
x13 ^= x1;
|
|
118
|
+
x13 = x13 >>> (32 - 16) | x13 << 16;
|
|
119
|
+
x9 = x9 + x13 | 0;
|
|
120
|
+
x5 ^= x9;
|
|
121
|
+
x5 = x5 >>> (32 - 12) | x5 << 12;
|
|
122
|
+
x2 = x2 + x6 | 0;
|
|
123
|
+
x14 ^= x2;
|
|
124
|
+
x14 = x14 >>> (32 - 16) | x14 << 16;
|
|
125
|
+
x10 = x10 + x14 | 0;
|
|
126
|
+
x6 ^= x10;
|
|
127
|
+
x6 = x6 >>> (32 - 12) | x6 << 12;
|
|
128
|
+
x3 = x3 + x7 | 0;
|
|
129
|
+
x15 ^= x3;
|
|
130
|
+
x15 = x15 >>> (32 - 16) | x15 << 16;
|
|
131
|
+
x11 = x11 + x15 | 0;
|
|
132
|
+
x7 ^= x11;
|
|
133
|
+
x7 = x7 >>> (32 - 12) | x7 << 12;
|
|
134
|
+
x2 = x2 + x6 | 0;
|
|
135
|
+
x14 ^= x2;
|
|
136
|
+
x14 = x14 >>> (32 - 8) | x14 << 8;
|
|
137
|
+
x10 = x10 + x14 | 0;
|
|
138
|
+
x6 ^= x10;
|
|
139
|
+
x6 = x6 >>> (32 - 7) | x6 << 7;
|
|
140
|
+
x3 = x3 + x7 | 0;
|
|
141
|
+
x15 ^= x3;
|
|
142
|
+
x15 = x15 >>> (32 - 8) | x15 << 8;
|
|
143
|
+
x11 = x11 + x15 | 0;
|
|
144
|
+
x7 ^= x11;
|
|
145
|
+
x7 = x7 >>> (32 - 7) | x7 << 7;
|
|
146
|
+
x1 = x1 + x5 | 0;
|
|
147
|
+
x13 ^= x1;
|
|
148
|
+
x13 = x13 >>> (32 - 8) | x13 << 8;
|
|
149
|
+
x9 = x9 + x13 | 0;
|
|
150
|
+
x5 ^= x9;
|
|
151
|
+
x5 = x5 >>> (32 - 7) | x5 << 7;
|
|
152
|
+
x0 = x0 + x4 | 0;
|
|
153
|
+
x12 ^= x0;
|
|
154
|
+
x12 = x12 >>> (32 - 8) | x12 << 8;
|
|
155
|
+
x8 = x8 + x12 | 0;
|
|
156
|
+
x4 ^= x8;
|
|
157
|
+
x4 = x4 >>> (32 - 7) | x4 << 7;
|
|
158
|
+
x0 = x0 + x5 | 0;
|
|
159
|
+
x15 ^= x0;
|
|
160
|
+
x15 = x15 >>> (32 - 16) | x15 << 16;
|
|
161
|
+
x10 = x10 + x15 | 0;
|
|
162
|
+
x5 ^= x10;
|
|
163
|
+
x5 = x5 >>> (32 - 12) | x5 << 12;
|
|
164
|
+
x1 = x1 + x6 | 0;
|
|
165
|
+
x12 ^= x1;
|
|
166
|
+
x12 = x12 >>> (32 - 16) | x12 << 16;
|
|
167
|
+
x11 = x11 + x12 | 0;
|
|
168
|
+
x6 ^= x11;
|
|
169
|
+
x6 = x6 >>> (32 - 12) | x6 << 12;
|
|
170
|
+
x2 = x2 + x7 | 0;
|
|
171
|
+
x13 ^= x2;
|
|
172
|
+
x13 = x13 >>> (32 - 16) | x13 << 16;
|
|
173
|
+
x8 = x8 + x13 | 0;
|
|
174
|
+
x7 ^= x8;
|
|
175
|
+
x7 = x7 >>> (32 - 12) | x7 << 12;
|
|
176
|
+
x3 = x3 + x4 | 0;
|
|
177
|
+
x14 ^= x3;
|
|
178
|
+
x14 = x14 >>> (32 - 16) | x14 << 16;
|
|
179
|
+
x9 = x9 + x14 | 0;
|
|
180
|
+
x4 ^= x9;
|
|
181
|
+
x4 = x4 >>> (32 - 12) | x4 << 12;
|
|
182
|
+
x2 = x2 + x7 | 0;
|
|
183
|
+
x13 ^= x2;
|
|
184
|
+
x13 = x13 >>> (32 - 8) | x13 << 8;
|
|
185
|
+
x8 = x8 + x13 | 0;
|
|
186
|
+
x7 ^= x8;
|
|
187
|
+
x7 = x7 >>> (32 - 7) | x7 << 7;
|
|
188
|
+
x3 = x3 + x4 | 0;
|
|
189
|
+
x14 ^= x3;
|
|
190
|
+
x14 = x14 >>> (32 - 8) | x14 << 8;
|
|
191
|
+
x9 = x9 + x14 | 0;
|
|
192
|
+
x4 ^= x9;
|
|
193
|
+
x4 = x4 >>> (32 - 7) | x4 << 7;
|
|
194
|
+
x1 = x1 + x6 | 0;
|
|
195
|
+
x12 ^= x1;
|
|
196
|
+
x12 = x12 >>> (32 - 8) | x12 << 8;
|
|
197
|
+
x11 = x11 + x12 | 0;
|
|
198
|
+
x6 ^= x11;
|
|
199
|
+
x6 = x6 >>> (32 - 7) | x6 << 7;
|
|
200
|
+
x0 = x0 + x5 | 0;
|
|
201
|
+
x15 ^= x0;
|
|
202
|
+
x15 = x15 >>> (32 - 8) | x15 << 8;
|
|
203
|
+
x10 = x10 + x15 | 0;
|
|
204
|
+
x5 ^= x10;
|
|
205
|
+
x5 = x5 >>> (32 - 7) | x5 << 7;
|
|
36
206
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
207
|
+
writeUint32LE(x0 + j0 | 0, out, 0);
|
|
208
|
+
writeUint32LE(x1 + j1 | 0, out, 4);
|
|
209
|
+
writeUint32LE(x2 + j2 | 0, out, 8);
|
|
210
|
+
writeUint32LE(x3 + j3 | 0, out, 12);
|
|
211
|
+
writeUint32LE(x4 + j4 | 0, out, 16);
|
|
212
|
+
writeUint32LE(x5 + j5 | 0, out, 20);
|
|
213
|
+
writeUint32LE(x6 + j6 | 0, out, 24);
|
|
214
|
+
writeUint32LE(x7 + j7 | 0, out, 28);
|
|
215
|
+
writeUint32LE(x8 + j8 | 0, out, 32);
|
|
216
|
+
writeUint32LE(x9 + j9 | 0, out, 36);
|
|
217
|
+
writeUint32LE(x10 + j10 | 0, out, 40);
|
|
218
|
+
writeUint32LE(x11 + j11 | 0, out, 44);
|
|
219
|
+
writeUint32LE(x12 + j12 | 0, out, 48);
|
|
220
|
+
writeUint32LE(x13 + j13 | 0, out, 52);
|
|
221
|
+
writeUint32LE(x14 + j14 | 0, out, 56);
|
|
222
|
+
writeUint32LE(x15 + j15 | 0, out, 60);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Encrypt src with ChaCha20 stream generated for the given 32-byte key and
|
|
226
|
+
* 8-byte (as in original implementation) or 12-byte (as in RFC7539) nonce and
|
|
227
|
+
* write the result into dst and return it.
|
|
228
|
+
*
|
|
229
|
+
* dst and src may be the same, but otherwise must not overlap.
|
|
230
|
+
*
|
|
231
|
+
* If nonce is 12 bytes, users should not encrypt more than 256 GiB with the
|
|
232
|
+
* same key and nonce, otherwise the stream will repeat. The function will
|
|
233
|
+
* throw error if counter overflows to prevent this.
|
|
234
|
+
*
|
|
235
|
+
* If nonce is 8 bytes, the output is practically unlimited (2^70 bytes, which
|
|
236
|
+
* is more than a million petabytes). However, it is not recommended to
|
|
237
|
+
* generate 8-byte nonces randomly, as the chance of collision is high.
|
|
238
|
+
*
|
|
239
|
+
* Never use the same key and nonce to encrypt more than one message.
|
|
240
|
+
*
|
|
241
|
+
* If nonceInplaceCounterLength is not 0, the nonce is assumed to be a 16-byte
|
|
242
|
+
* array with stream counter in first nonceInplaceCounterLength bytes and nonce
|
|
243
|
+
* in the last remaining bytes. The counter will be incremented inplace for
|
|
244
|
+
* each ChaCha block. This is useful if you need to encrypt one stream of data
|
|
245
|
+
* in chunks.
|
|
246
|
+
*/
|
|
247
|
+
function streamXOR(key, nonce, src, dst, nonceInplaceCounterLength = 0) {
|
|
248
|
+
// We only support 256-bit keys.
|
|
249
|
+
if (key.length !== 32) {
|
|
250
|
+
throw new Error("ChaCha: key size must be 32 bytes");
|
|
44
251
|
}
|
|
45
|
-
|
|
46
|
-
|
|
252
|
+
if (dst.length < src.length) {
|
|
253
|
+
throw new Error("ChaCha: destination is shorter than source");
|
|
47
254
|
}
|
|
48
|
-
|
|
49
|
-
|
|
255
|
+
let nc;
|
|
256
|
+
let counterLength;
|
|
257
|
+
if (nonceInplaceCounterLength === 0) {
|
|
258
|
+
if (nonce.length !== 8 && nonce.length !== 12) {
|
|
259
|
+
throw new Error("ChaCha nonce must be 8 or 12 bytes");
|
|
260
|
+
}
|
|
261
|
+
nc = new Uint8Array(16);
|
|
262
|
+
// First counterLength bytes of nc are counter, starting with zero.
|
|
263
|
+
counterLength = nc.length - nonce.length;
|
|
264
|
+
// Last bytes of nc after counterLength are nonce, set them.
|
|
265
|
+
nc.set(nonce, counterLength);
|
|
50
266
|
}
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
267
|
+
else {
|
|
268
|
+
if (nonce.length !== 16) {
|
|
269
|
+
throw new Error("ChaCha nonce with counter must be 16 bytes");
|
|
270
|
+
}
|
|
271
|
+
// This will update passed nonce with counter inplace.
|
|
272
|
+
nc = nonce;
|
|
273
|
+
counterLength = nonceInplaceCounterLength;
|
|
55
274
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
275
|
+
// Allocate temporary space for ChaCha block.
|
|
276
|
+
const block = new Uint8Array(64);
|
|
277
|
+
for (let i = 0; i < src.length; i += 64) {
|
|
278
|
+
// Generate a block.
|
|
279
|
+
core(block, nc, key);
|
|
280
|
+
// XOR block bytes with src into dst.
|
|
281
|
+
for (let j = i; j < i + 64 && j < src.length; j++) {
|
|
282
|
+
dst[j] = src[j] ^ block[j - i];
|
|
283
|
+
}
|
|
284
|
+
// Increment counter.
|
|
285
|
+
incrementCounter(nc, 0, counterLength);
|
|
64
286
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
287
|
+
// Cleanup temporary space.
|
|
288
|
+
wipe(block);
|
|
289
|
+
if (nonceInplaceCounterLength === 0) {
|
|
290
|
+
// Cleanup counter.
|
|
291
|
+
wipe(nc);
|
|
292
|
+
}
|
|
293
|
+
return dst;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Generate ChaCha20 stream for the given 32-byte key and 8-byte or 12-byte
|
|
297
|
+
* nonce and write it into dst and return it.
|
|
298
|
+
*
|
|
299
|
+
* Never use the same key and nonce to generate more than one stream.
|
|
300
|
+
*
|
|
301
|
+
* If nonceInplaceCounterLength is not 0, it behaves the same with respect to
|
|
302
|
+
* the nonce as described in the streamXOR documentation.
|
|
303
|
+
*
|
|
304
|
+
* stream is like streamXOR with all-zero src.
|
|
305
|
+
*/
|
|
306
|
+
function stream(key, nonce, dst, nonceInplaceCounterLength = 0) {
|
|
307
|
+
wipe(dst);
|
|
308
|
+
return streamXOR(key, nonce, dst, dst, nonceInplaceCounterLength);
|
|
309
|
+
}
|
|
310
|
+
function incrementCounter(counter, pos, len) {
|
|
311
|
+
let carry = 1;
|
|
312
|
+
while (len--) {
|
|
313
|
+
carry = carry + (counter[pos] & 0xff) | 0;
|
|
314
|
+
counter[pos] = carry & 0xff;
|
|
315
|
+
carry >>>= 8;
|
|
316
|
+
pos++;
|
|
317
|
+
}
|
|
318
|
+
if (carry > 0) {
|
|
319
|
+
throw new Error("ChaCha: counter overflow");
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Copyright (C) 2019 Kyle Den Hartog
|
|
324
|
+
// MIT License. See LICENSE file for details.
|
|
325
|
+
/**
|
|
326
|
+
* Package xchacha20 implements XChaCha20 stream cipher.
|
|
327
|
+
*/
|
|
328
|
+
// Number of ChaCha rounds (ChaCha20).
|
|
329
|
+
const ROUNDS = 20;
|
|
330
|
+
/**
|
|
331
|
+
* HChaCha is a one-way function used in XChaCha to extend nonce.
|
|
332
|
+
*
|
|
333
|
+
* It takes 32-byte key and 16-byte src and writes 32-byte result
|
|
334
|
+
* into dst and returns it.
|
|
335
|
+
*/
|
|
336
|
+
function hchacha(key, src, dst) {
|
|
337
|
+
let j0 = 0x61707865; // "expa" -- ChaCha's "sigma" constant
|
|
338
|
+
let j1 = 0x3320646e; // "nd 3" for 32-byte keys
|
|
339
|
+
let j2 = 0x79622d32; // "2-by"
|
|
340
|
+
let j3 = 0x6b206574; // "te k"
|
|
341
|
+
let j4 = (key[3] << 24) | (key[2] << 16) | (key[1] << 8) | key[0];
|
|
342
|
+
let j5 = (key[7] << 24) | (key[6] << 16) | (key[5] << 8) | key[4];
|
|
343
|
+
let j6 = (key[11] << 24) | (key[10] << 16) | (key[9] << 8) | key[8];
|
|
344
|
+
let j7 = (key[15] << 24) | (key[14] << 16) | (key[13] << 8) | key[12];
|
|
345
|
+
let j8 = (key[19] << 24) | (key[18] << 16) | (key[17] << 8) | key[16];
|
|
346
|
+
let j9 = (key[23] << 24) | (key[22] << 16) | (key[21] << 8) | key[20];
|
|
347
|
+
let j10 = (key[27] << 24) | (key[26] << 16) | (key[25] << 8) | key[24];
|
|
348
|
+
let j11 = (key[31] << 24) | (key[30] << 16) | (key[29] << 8) | key[28];
|
|
349
|
+
let j12 = (src[3] << 24) | (src[2] << 16) | (src[1] << 8) | src[0];
|
|
350
|
+
let j13 = (src[7] << 24) | (src[6] << 16) | (src[5] << 8) | src[4];
|
|
351
|
+
let j14 = (src[11] << 24) | (src[10] << 16) | (src[9] << 8) | src[8];
|
|
352
|
+
let j15 = (src[15] << 24) | (src[14] << 16) | (src[13] << 8) | src[12];
|
|
353
|
+
let x0 = j0;
|
|
354
|
+
let x1 = j1;
|
|
355
|
+
let x2 = j2;
|
|
356
|
+
let x3 = j3;
|
|
357
|
+
let x4 = j4;
|
|
358
|
+
let x5 = j5;
|
|
359
|
+
let x6 = j6;
|
|
360
|
+
let x7 = j7;
|
|
361
|
+
let x8 = j8;
|
|
362
|
+
let x9 = j9;
|
|
363
|
+
let x10 = j10;
|
|
364
|
+
let x11 = j11;
|
|
365
|
+
let x12 = j12;
|
|
366
|
+
let x13 = j13;
|
|
367
|
+
let x14 = j14;
|
|
368
|
+
let x15 = j15;
|
|
369
|
+
for (let i = 0; i < ROUNDS; i += 2) {
|
|
370
|
+
x0 = (x0 + x4) | 0;
|
|
371
|
+
x12 ^= x0;
|
|
372
|
+
x12 = (x12 >>> (32 - 16)) | (x12 << 16);
|
|
373
|
+
x8 = (x8 + x12) | 0;
|
|
374
|
+
x4 ^= x8;
|
|
375
|
+
x4 = (x4 >>> (32 - 12)) | (x4 << 12);
|
|
376
|
+
x1 = (x1 + x5) | 0;
|
|
377
|
+
x13 ^= x1;
|
|
378
|
+
x13 = (x13 >>> (32 - 16)) | (x13 << 16);
|
|
379
|
+
x9 = (x9 + x13) | 0;
|
|
380
|
+
x5 ^= x9;
|
|
381
|
+
x5 = (x5 >>> (32 - 12)) | (x5 << 12);
|
|
382
|
+
x2 = (x2 + x6) | 0;
|
|
383
|
+
x14 ^= x2;
|
|
384
|
+
x14 = (x14 >>> (32 - 16)) | (x14 << 16);
|
|
385
|
+
x10 = (x10 + x14) | 0;
|
|
386
|
+
x6 ^= x10;
|
|
387
|
+
x6 = (x6 >>> (32 - 12)) | (x6 << 12);
|
|
388
|
+
x3 = (x3 + x7) | 0;
|
|
389
|
+
x15 ^= x3;
|
|
390
|
+
x15 = (x15 >>> (32 - 16)) | (x15 << 16);
|
|
391
|
+
x11 = (x11 + x15) | 0;
|
|
392
|
+
x7 ^= x11;
|
|
393
|
+
x7 = (x7 >>> (32 - 12)) | (x7 << 12);
|
|
394
|
+
x2 = (x2 + x6) | 0;
|
|
395
|
+
x14 ^= x2;
|
|
396
|
+
x14 = (x14 >>> (32 - 8)) | (x14 << 8);
|
|
397
|
+
x10 = (x10 + x14) | 0;
|
|
398
|
+
x6 ^= x10;
|
|
399
|
+
x6 = (x6 >>> (32 - 7)) | (x6 << 7);
|
|
400
|
+
x3 = (x3 + x7) | 0;
|
|
401
|
+
x15 ^= x3;
|
|
402
|
+
x15 = (x15 >>> (32 - 8)) | (x15 << 8);
|
|
403
|
+
x11 = (x11 + x15) | 0;
|
|
404
|
+
x7 ^= x11;
|
|
405
|
+
x7 = (x7 >>> (32 - 7)) | (x7 << 7);
|
|
406
|
+
x1 = (x1 + x5) | 0;
|
|
407
|
+
x13 ^= x1;
|
|
408
|
+
x13 = (x13 >>> (32 - 8)) | (x13 << 8);
|
|
409
|
+
x9 = (x9 + x13) | 0;
|
|
410
|
+
x5 ^= x9;
|
|
411
|
+
x5 = (x5 >>> (32 - 7)) | (x5 << 7);
|
|
412
|
+
x0 = (x0 + x4) | 0;
|
|
413
|
+
x12 ^= x0;
|
|
414
|
+
x12 = (x12 >>> (32 - 8)) | (x12 << 8);
|
|
415
|
+
x8 = (x8 + x12) | 0;
|
|
416
|
+
x4 ^= x8;
|
|
417
|
+
x4 = (x4 >>> (32 - 7)) | (x4 << 7);
|
|
418
|
+
x0 = (x0 + x5) | 0;
|
|
419
|
+
x15 ^= x0;
|
|
420
|
+
x15 = (x15 >>> (32 - 16)) | (x15 << 16);
|
|
421
|
+
x10 = (x10 + x15) | 0;
|
|
422
|
+
x5 ^= x10;
|
|
423
|
+
x5 = (x5 >>> (32 - 12)) | (x5 << 12);
|
|
424
|
+
x1 = (x1 + x6) | 0;
|
|
425
|
+
x12 ^= x1;
|
|
426
|
+
x12 = (x12 >>> (32 - 16)) | (x12 << 16);
|
|
427
|
+
x11 = (x11 + x12) | 0;
|
|
428
|
+
x6 ^= x11;
|
|
429
|
+
x6 = (x6 >>> (32 - 12)) | (x6 << 12);
|
|
430
|
+
x2 = (x2 + x7) | 0;
|
|
431
|
+
x13 ^= x2;
|
|
432
|
+
x13 = (x13 >>> (32 - 16)) | (x13 << 16);
|
|
433
|
+
x8 = (x8 + x13) | 0;
|
|
434
|
+
x7 ^= x8;
|
|
435
|
+
x7 = (x7 >>> (32 - 12)) | (x7 << 12);
|
|
436
|
+
x3 = (x3 + x4) | 0;
|
|
437
|
+
x14 ^= x3;
|
|
438
|
+
x14 = (x14 >>> (32 - 16)) | (x14 << 16);
|
|
439
|
+
x9 = (x9 + x14) | 0;
|
|
440
|
+
x4 ^= x9;
|
|
441
|
+
x4 = (x4 >>> (32 - 12)) | (x4 << 12);
|
|
442
|
+
x2 = (x2 + x7) | 0;
|
|
443
|
+
x13 ^= x2;
|
|
444
|
+
x13 = (x13 >>> (32 - 8)) | (x13 << 8);
|
|
445
|
+
x8 = (x8 + x13) | 0;
|
|
446
|
+
x7 ^= x8;
|
|
447
|
+
x7 = (x7 >>> (32 - 7)) | (x7 << 7);
|
|
448
|
+
x3 = (x3 + x4) | 0;
|
|
449
|
+
x14 ^= x3;
|
|
450
|
+
x14 = (x14 >>> (32 - 8)) | (x14 << 8);
|
|
451
|
+
x9 = (x9 + x14) | 0;
|
|
452
|
+
x4 ^= x9;
|
|
453
|
+
x4 = (x4 >>> (32 - 7)) | (x4 << 7);
|
|
454
|
+
x1 = (x1 + x6) | 0;
|
|
455
|
+
x12 ^= x1;
|
|
456
|
+
x12 = (x12 >>> (32 - 8)) | (x12 << 8);
|
|
457
|
+
x11 = (x11 + x12) | 0;
|
|
458
|
+
x6 ^= x11;
|
|
459
|
+
x6 = (x6 >>> (32 - 7)) | (x6 << 7);
|
|
460
|
+
x0 = (x0 + x5) | 0;
|
|
461
|
+
x15 ^= x0;
|
|
462
|
+
x15 = (x15 >>> (32 - 8)) | (x15 << 8);
|
|
463
|
+
x10 = (x10 + x15) | 0;
|
|
464
|
+
x5 ^= x10;
|
|
465
|
+
x5 = (x5 >>> (32 - 7)) | (x5 << 7);
|
|
466
|
+
}
|
|
467
|
+
writeUint32LE(x0, dst, 0);
|
|
468
|
+
writeUint32LE(x1, dst, 4);
|
|
469
|
+
writeUint32LE(x2, dst, 8);
|
|
470
|
+
writeUint32LE(x3, dst, 12);
|
|
471
|
+
writeUint32LE(x12, dst, 16);
|
|
472
|
+
writeUint32LE(x13, dst, 20);
|
|
473
|
+
writeUint32LE(x14, dst, 24);
|
|
474
|
+
writeUint32LE(x15, dst, 28);
|
|
475
|
+
return dst;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Copyright (C) 2016 Dmitry Chestnykh
|
|
479
|
+
// MIT License. See LICENSE file for details.
|
|
480
|
+
/**
|
|
481
|
+
* Package constant-time provides functions for performing algorithmically constant-time operations.
|
|
482
|
+
*/
|
|
483
|
+
/**
|
|
484
|
+
* NOTE! Due to the inability to guarantee real constant time evaluation of
|
|
485
|
+
* anything in JavaScript VM, this is module is the best effort.
|
|
486
|
+
*/
|
|
487
|
+
/**
|
|
488
|
+
* Returns resultIfOne if subject is 1, or resultIfZero if subject is 0.
|
|
489
|
+
*
|
|
490
|
+
* Supports only 32-bit integers, so resultIfOne or resultIfZero are not
|
|
491
|
+
* integers, they'll be converted to them with bitwise operations.
|
|
492
|
+
*/
|
|
493
|
+
/**
|
|
494
|
+
* Returns 1 if a and b are of equal length and their contents
|
|
495
|
+
* are equal, or 0 otherwise.
|
|
496
|
+
*
|
|
497
|
+
* Note that unlike in equal(), zero-length inputs are considered
|
|
498
|
+
* the same, so this function will return 1.
|
|
499
|
+
*/
|
|
500
|
+
function compare(a, b) {
|
|
501
|
+
if (a.length !== b.length) {
|
|
502
|
+
return 0;
|
|
503
|
+
}
|
|
504
|
+
let result = 0;
|
|
505
|
+
for (let i = 0; i < a.length; i++) {
|
|
506
|
+
result |= a[i] ^ b[i];
|
|
507
|
+
}
|
|
508
|
+
return (1 & ((result - 1) >>> 8));
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Returns true if a and b are of equal non-zero length,
|
|
512
|
+
* and their contents are equal, or false otherwise.
|
|
513
|
+
*
|
|
514
|
+
* Note that unlike in compare() zero-length inputs are considered
|
|
515
|
+
* _not_ equal, so this function will return false.
|
|
516
|
+
*/
|
|
517
|
+
function equal(a, b) {
|
|
518
|
+
if (a.length === 0 || b.length === 0) {
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
return compare(a, b) !== 0;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Copyright (C) 2016 Dmitry Chestnykh
|
|
525
|
+
// MIT License. See LICENSE file for details.
|
|
526
|
+
/**
|
|
527
|
+
* Package poly1305 implements Poly1305 one-time message authentication algorithm.
|
|
528
|
+
*/
|
|
529
|
+
const DIGEST_LENGTH = 16;
|
|
530
|
+
// Port of Andrew Moon's Poly1305-donna-16. Public domain.
|
|
531
|
+
// https://github.com/floodyberry/poly1305-donna
|
|
532
|
+
/**
|
|
533
|
+
* Poly1305 computes 16-byte authenticator of message using
|
|
534
|
+
* a one-time 32-byte key.
|
|
535
|
+
*
|
|
536
|
+
* Important: key should be used for only one message,
|
|
537
|
+
* it should never repeat.
|
|
538
|
+
*/
|
|
539
|
+
class Poly1305 {
|
|
540
|
+
digestLength = DIGEST_LENGTH;
|
|
541
|
+
_buffer = new Uint8Array(16);
|
|
542
|
+
_r = new Uint16Array(10);
|
|
543
|
+
_h = new Uint16Array(10);
|
|
544
|
+
_pad = new Uint16Array(8);
|
|
545
|
+
_leftover = 0;
|
|
546
|
+
_fin = 0;
|
|
547
|
+
_finished = false;
|
|
548
|
+
constructor(key) {
|
|
549
|
+
let t0 = key[0] | key[1] << 8;
|
|
550
|
+
this._r[0] = (t0) & 0x1fff;
|
|
551
|
+
let t1 = key[2] | key[3] << 8;
|
|
552
|
+
this._r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
|
|
553
|
+
let t2 = key[4] | key[5] << 8;
|
|
554
|
+
this._r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03;
|
|
555
|
+
let t3 = key[6] | key[7] << 8;
|
|
556
|
+
this._r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
|
|
557
|
+
let t4 = key[8] | key[9] << 8;
|
|
558
|
+
this._r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff;
|
|
559
|
+
this._r[5] = ((t4 >>> 1)) & 0x1ffe;
|
|
560
|
+
let t5 = key[10] | key[11] << 8;
|
|
561
|
+
this._r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
|
|
562
|
+
let t6 = key[12] | key[13] << 8;
|
|
563
|
+
this._r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81;
|
|
564
|
+
let t7 = key[14] | key[15] << 8;
|
|
565
|
+
this._r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
|
|
566
|
+
this._r[9] = ((t7 >>> 5)) & 0x007f;
|
|
567
|
+
this._pad[0] = key[16] | key[17] << 8;
|
|
568
|
+
this._pad[1] = key[18] | key[19] << 8;
|
|
569
|
+
this._pad[2] = key[20] | key[21] << 8;
|
|
570
|
+
this._pad[3] = key[22] | key[23] << 8;
|
|
571
|
+
this._pad[4] = key[24] | key[25] << 8;
|
|
572
|
+
this._pad[5] = key[26] | key[27] << 8;
|
|
573
|
+
this._pad[6] = key[28] | key[29] << 8;
|
|
574
|
+
this._pad[7] = key[30] | key[31] << 8;
|
|
575
|
+
}
|
|
576
|
+
_blocks(m, mpos, bytes) {
|
|
577
|
+
let hibit = this._fin ? 0 : 1 << 11;
|
|
578
|
+
let h0 = this._h[0], h1 = this._h[1], h2 = this._h[2], h3 = this._h[3], h4 = this._h[4], h5 = this._h[5], h6 = this._h[6], h7 = this._h[7], h8 = this._h[8], h9 = this._h[9];
|
|
579
|
+
let r0 = this._r[0], r1 = this._r[1], r2 = this._r[2], r3 = this._r[3], r4 = this._r[4], r5 = this._r[5], r6 = this._r[6], r7 = this._r[7], r8 = this._r[8], r9 = this._r[9];
|
|
580
|
+
while (bytes >= 16) {
|
|
581
|
+
let t0 = m[mpos + 0] | m[mpos + 1] << 8;
|
|
582
|
+
h0 += (t0) & 0x1fff;
|
|
583
|
+
let t1 = m[mpos + 2] | m[mpos + 3] << 8;
|
|
584
|
+
h1 += ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
|
|
585
|
+
let t2 = m[mpos + 4] | m[mpos + 5] << 8;
|
|
586
|
+
h2 += ((t1 >>> 10) | (t2 << 6)) & 0x1fff;
|
|
587
|
+
let t3 = m[mpos + 6] | m[mpos + 7] << 8;
|
|
588
|
+
h3 += ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
|
|
589
|
+
let t4 = m[mpos + 8] | m[mpos + 9] << 8;
|
|
590
|
+
h4 += ((t3 >>> 4) | (t4 << 12)) & 0x1fff;
|
|
591
|
+
h5 += ((t4 >>> 1)) & 0x1fff;
|
|
592
|
+
let t5 = m[mpos + 10] | m[mpos + 11] << 8;
|
|
593
|
+
h6 += ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
|
|
594
|
+
let t6 = m[mpos + 12] | m[mpos + 13] << 8;
|
|
595
|
+
h7 += ((t5 >>> 11) | (t6 << 5)) & 0x1fff;
|
|
596
|
+
let t7 = m[mpos + 14] | m[mpos + 15] << 8;
|
|
597
|
+
h8 += ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
|
|
598
|
+
h9 += ((t7 >>> 5)) | hibit;
|
|
599
|
+
let c = 0;
|
|
600
|
+
let d0 = c;
|
|
601
|
+
d0 += h0 * r0;
|
|
602
|
+
d0 += h1 * (5 * r9);
|
|
603
|
+
d0 += h2 * (5 * r8);
|
|
604
|
+
d0 += h3 * (5 * r7);
|
|
605
|
+
d0 += h4 * (5 * r6);
|
|
606
|
+
c = (d0 >>> 13);
|
|
607
|
+
d0 &= 0x1fff;
|
|
608
|
+
d0 += h5 * (5 * r5);
|
|
609
|
+
d0 += h6 * (5 * r4);
|
|
610
|
+
d0 += h7 * (5 * r3);
|
|
611
|
+
d0 += h8 * (5 * r2);
|
|
612
|
+
d0 += h9 * (5 * r1);
|
|
613
|
+
c += (d0 >>> 13);
|
|
614
|
+
d0 &= 0x1fff;
|
|
615
|
+
let d1 = c;
|
|
616
|
+
d1 += h0 * r1;
|
|
617
|
+
d1 += h1 * r0;
|
|
618
|
+
d1 += h2 * (5 * r9);
|
|
619
|
+
d1 += h3 * (5 * r8);
|
|
620
|
+
d1 += h4 * (5 * r7);
|
|
621
|
+
c = (d1 >>> 13);
|
|
622
|
+
d1 &= 0x1fff;
|
|
623
|
+
d1 += h5 * (5 * r6);
|
|
624
|
+
d1 += h6 * (5 * r5);
|
|
625
|
+
d1 += h7 * (5 * r4);
|
|
626
|
+
d1 += h8 * (5 * r3);
|
|
627
|
+
d1 += h9 * (5 * r2);
|
|
628
|
+
c += (d1 >>> 13);
|
|
629
|
+
d1 &= 0x1fff;
|
|
630
|
+
let d2 = c;
|
|
631
|
+
d2 += h0 * r2;
|
|
632
|
+
d2 += h1 * r1;
|
|
633
|
+
d2 += h2 * r0;
|
|
634
|
+
d2 += h3 * (5 * r9);
|
|
635
|
+
d2 += h4 * (5 * r8);
|
|
636
|
+
c = (d2 >>> 13);
|
|
637
|
+
d2 &= 0x1fff;
|
|
638
|
+
d2 += h5 * (5 * r7);
|
|
639
|
+
d2 += h6 * (5 * r6);
|
|
640
|
+
d2 += h7 * (5 * r5);
|
|
641
|
+
d2 += h8 * (5 * r4);
|
|
642
|
+
d2 += h9 * (5 * r3);
|
|
643
|
+
c += (d2 >>> 13);
|
|
644
|
+
d2 &= 0x1fff;
|
|
645
|
+
let d3 = c;
|
|
646
|
+
d3 += h0 * r3;
|
|
647
|
+
d3 += h1 * r2;
|
|
648
|
+
d3 += h2 * r1;
|
|
649
|
+
d3 += h3 * r0;
|
|
650
|
+
d3 += h4 * (5 * r9);
|
|
651
|
+
c = (d3 >>> 13);
|
|
652
|
+
d3 &= 0x1fff;
|
|
653
|
+
d3 += h5 * (5 * r8);
|
|
654
|
+
d3 += h6 * (5 * r7);
|
|
655
|
+
d3 += h7 * (5 * r6);
|
|
656
|
+
d3 += h8 * (5 * r5);
|
|
657
|
+
d3 += h9 * (5 * r4);
|
|
658
|
+
c += (d3 >>> 13);
|
|
659
|
+
d3 &= 0x1fff;
|
|
660
|
+
let d4 = c;
|
|
661
|
+
d4 += h0 * r4;
|
|
662
|
+
d4 += h1 * r3;
|
|
663
|
+
d4 += h2 * r2;
|
|
664
|
+
d4 += h3 * r1;
|
|
665
|
+
d4 += h4 * r0;
|
|
666
|
+
c = (d4 >>> 13);
|
|
667
|
+
d4 &= 0x1fff;
|
|
668
|
+
d4 += h5 * (5 * r9);
|
|
669
|
+
d4 += h6 * (5 * r8);
|
|
670
|
+
d4 += h7 * (5 * r7);
|
|
671
|
+
d4 += h8 * (5 * r6);
|
|
672
|
+
d4 += h9 * (5 * r5);
|
|
673
|
+
c += (d4 >>> 13);
|
|
674
|
+
d4 &= 0x1fff;
|
|
675
|
+
let d5 = c;
|
|
676
|
+
d5 += h0 * r5;
|
|
677
|
+
d5 += h1 * r4;
|
|
678
|
+
d5 += h2 * r3;
|
|
679
|
+
d5 += h3 * r2;
|
|
680
|
+
d5 += h4 * r1;
|
|
681
|
+
c = (d5 >>> 13);
|
|
682
|
+
d5 &= 0x1fff;
|
|
683
|
+
d5 += h5 * r0;
|
|
684
|
+
d5 += h6 * (5 * r9);
|
|
685
|
+
d5 += h7 * (5 * r8);
|
|
686
|
+
d5 += h8 * (5 * r7);
|
|
687
|
+
d5 += h9 * (5 * r6);
|
|
688
|
+
c += (d5 >>> 13);
|
|
689
|
+
d5 &= 0x1fff;
|
|
690
|
+
let d6 = c;
|
|
691
|
+
d6 += h0 * r6;
|
|
692
|
+
d6 += h1 * r5;
|
|
693
|
+
d6 += h2 * r4;
|
|
694
|
+
d6 += h3 * r3;
|
|
695
|
+
d6 += h4 * r2;
|
|
696
|
+
c = (d6 >>> 13);
|
|
697
|
+
d6 &= 0x1fff;
|
|
698
|
+
d6 += h5 * r1;
|
|
699
|
+
d6 += h6 * r0;
|
|
700
|
+
d6 += h7 * (5 * r9);
|
|
701
|
+
d6 += h8 * (5 * r8);
|
|
702
|
+
d6 += h9 * (5 * r7);
|
|
703
|
+
c += (d6 >>> 13);
|
|
704
|
+
d6 &= 0x1fff;
|
|
705
|
+
let d7 = c;
|
|
706
|
+
d7 += h0 * r7;
|
|
707
|
+
d7 += h1 * r6;
|
|
708
|
+
d7 += h2 * r5;
|
|
709
|
+
d7 += h3 * r4;
|
|
710
|
+
d7 += h4 * r3;
|
|
711
|
+
c = (d7 >>> 13);
|
|
712
|
+
d7 &= 0x1fff;
|
|
713
|
+
d7 += h5 * r2;
|
|
714
|
+
d7 += h6 * r1;
|
|
715
|
+
d7 += h7 * r0;
|
|
716
|
+
d7 += h8 * (5 * r9);
|
|
717
|
+
d7 += h9 * (5 * r8);
|
|
718
|
+
c += (d7 >>> 13);
|
|
719
|
+
d7 &= 0x1fff;
|
|
720
|
+
let d8 = c;
|
|
721
|
+
d8 += h0 * r8;
|
|
722
|
+
d8 += h1 * r7;
|
|
723
|
+
d8 += h2 * r6;
|
|
724
|
+
d8 += h3 * r5;
|
|
725
|
+
d8 += h4 * r4;
|
|
726
|
+
c = (d8 >>> 13);
|
|
727
|
+
d8 &= 0x1fff;
|
|
728
|
+
d8 += h5 * r3;
|
|
729
|
+
d8 += h6 * r2;
|
|
730
|
+
d8 += h7 * r1;
|
|
731
|
+
d8 += h8 * r0;
|
|
732
|
+
d8 += h9 * (5 * r9);
|
|
733
|
+
c += (d8 >>> 13);
|
|
734
|
+
d8 &= 0x1fff;
|
|
735
|
+
let d9 = c;
|
|
736
|
+
d9 += h0 * r9;
|
|
737
|
+
d9 += h1 * r8;
|
|
738
|
+
d9 += h2 * r7;
|
|
739
|
+
d9 += h3 * r6;
|
|
740
|
+
d9 += h4 * r5;
|
|
741
|
+
c = (d9 >>> 13);
|
|
742
|
+
d9 &= 0x1fff;
|
|
743
|
+
d9 += h5 * r4;
|
|
744
|
+
d9 += h6 * r3;
|
|
745
|
+
d9 += h7 * r2;
|
|
746
|
+
d9 += h8 * r1;
|
|
747
|
+
d9 += h9 * r0;
|
|
748
|
+
c += (d9 >>> 13);
|
|
749
|
+
d9 &= 0x1fff;
|
|
750
|
+
c = (((c << 2) + c)) | 0;
|
|
751
|
+
c = (c + d0) | 0;
|
|
752
|
+
d0 = c & 0x1fff;
|
|
753
|
+
c = (c >>> 13);
|
|
754
|
+
d1 += c;
|
|
755
|
+
h0 = d0;
|
|
756
|
+
h1 = d1;
|
|
757
|
+
h2 = d2;
|
|
758
|
+
h3 = d3;
|
|
759
|
+
h4 = d4;
|
|
760
|
+
h5 = d5;
|
|
761
|
+
h6 = d6;
|
|
762
|
+
h7 = d7;
|
|
763
|
+
h8 = d8;
|
|
764
|
+
h9 = d9;
|
|
765
|
+
mpos += 16;
|
|
766
|
+
bytes -= 16;
|
|
767
|
+
}
|
|
768
|
+
this._h[0] = h0;
|
|
769
|
+
this._h[1] = h1;
|
|
770
|
+
this._h[2] = h2;
|
|
771
|
+
this._h[3] = h3;
|
|
772
|
+
this._h[4] = h4;
|
|
773
|
+
this._h[5] = h5;
|
|
774
|
+
this._h[6] = h6;
|
|
775
|
+
this._h[7] = h7;
|
|
776
|
+
this._h[8] = h8;
|
|
777
|
+
this._h[9] = h9;
|
|
778
|
+
}
|
|
779
|
+
finish(mac, macpos = 0) {
|
|
780
|
+
const g = new Uint16Array(10);
|
|
781
|
+
let c;
|
|
782
|
+
let mask;
|
|
783
|
+
let f;
|
|
784
|
+
let i;
|
|
785
|
+
if (this._leftover) {
|
|
786
|
+
i = this._leftover;
|
|
787
|
+
this._buffer[i++] = 1;
|
|
788
|
+
for (; i < 16; i++) {
|
|
789
|
+
this._buffer[i] = 0;
|
|
86
790
|
}
|
|
87
|
-
|
|
88
|
-
|
|
791
|
+
this._fin = 1;
|
|
792
|
+
this._blocks(this._buffer, 0, 16);
|
|
793
|
+
}
|
|
794
|
+
c = this._h[1] >>> 13;
|
|
795
|
+
this._h[1] &= 0x1fff;
|
|
796
|
+
for (i = 2; i < 10; i++) {
|
|
797
|
+
this._h[i] += c;
|
|
798
|
+
c = this._h[i] >>> 13;
|
|
799
|
+
this._h[i] &= 0x1fff;
|
|
800
|
+
}
|
|
801
|
+
this._h[0] += (c * 5);
|
|
802
|
+
c = this._h[0] >>> 13;
|
|
803
|
+
this._h[0] &= 0x1fff;
|
|
804
|
+
this._h[1] += c;
|
|
805
|
+
c = this._h[1] >>> 13;
|
|
806
|
+
this._h[1] &= 0x1fff;
|
|
807
|
+
this._h[2] += c;
|
|
808
|
+
g[0] = this._h[0] + 5;
|
|
809
|
+
c = g[0] >>> 13;
|
|
810
|
+
g[0] &= 0x1fff;
|
|
811
|
+
for (i = 1; i < 10; i++) {
|
|
812
|
+
g[i] = this._h[i] + c;
|
|
813
|
+
c = g[i] >>> 13;
|
|
814
|
+
g[i] &= 0x1fff;
|
|
815
|
+
}
|
|
816
|
+
g[9] -= (1 << 13);
|
|
817
|
+
mask = (c ^ 1) - 1;
|
|
818
|
+
for (i = 0; i < 10; i++) {
|
|
819
|
+
g[i] &= mask;
|
|
820
|
+
}
|
|
821
|
+
mask = ~mask;
|
|
822
|
+
for (i = 0; i < 10; i++) {
|
|
823
|
+
this._h[i] = (this._h[i] & mask) | g[i];
|
|
824
|
+
}
|
|
825
|
+
this._h[0] = ((this._h[0]) | (this._h[1] << 13)) & 0xffff;
|
|
826
|
+
this._h[1] = ((this._h[1] >>> 3) | (this._h[2] << 10)) & 0xffff;
|
|
827
|
+
this._h[2] = ((this._h[2] >>> 6) | (this._h[3] << 7)) & 0xffff;
|
|
828
|
+
this._h[3] = ((this._h[3] >>> 9) | (this._h[4] << 4)) & 0xffff;
|
|
829
|
+
this._h[4] = ((this._h[4] >>> 12) | (this._h[5] << 1) | (this._h[6] << 14)) & 0xffff;
|
|
830
|
+
this._h[5] = ((this._h[6] >>> 2) | (this._h[7] << 11)) & 0xffff;
|
|
831
|
+
this._h[6] = ((this._h[7] >>> 5) | (this._h[8] << 8)) & 0xffff;
|
|
832
|
+
this._h[7] = ((this._h[8] >>> 8) | (this._h[9] << 5)) & 0xffff;
|
|
833
|
+
f = this._h[0] + this._pad[0];
|
|
834
|
+
this._h[0] = f & 0xffff;
|
|
835
|
+
for (i = 1; i < 8; i++) {
|
|
836
|
+
f = (((this._h[i] + this._pad[i]) | 0) + (f >>> 16)) | 0;
|
|
837
|
+
this._h[i] = f & 0xffff;
|
|
838
|
+
}
|
|
839
|
+
mac[macpos + 0] = this._h[0] >>> 0;
|
|
840
|
+
mac[macpos + 1] = this._h[0] >>> 8;
|
|
841
|
+
mac[macpos + 2] = this._h[1] >>> 0;
|
|
842
|
+
mac[macpos + 3] = this._h[1] >>> 8;
|
|
843
|
+
mac[macpos + 4] = this._h[2] >>> 0;
|
|
844
|
+
mac[macpos + 5] = this._h[2] >>> 8;
|
|
845
|
+
mac[macpos + 6] = this._h[3] >>> 0;
|
|
846
|
+
mac[macpos + 7] = this._h[3] >>> 8;
|
|
847
|
+
mac[macpos + 8] = this._h[4] >>> 0;
|
|
848
|
+
mac[macpos + 9] = this._h[4] >>> 8;
|
|
849
|
+
mac[macpos + 10] = this._h[5] >>> 0;
|
|
850
|
+
mac[macpos + 11] = this._h[5] >>> 8;
|
|
851
|
+
mac[macpos + 12] = this._h[6] >>> 0;
|
|
852
|
+
mac[macpos + 13] = this._h[6] >>> 8;
|
|
853
|
+
mac[macpos + 14] = this._h[7] >>> 0;
|
|
854
|
+
mac[macpos + 15] = this._h[7] >>> 8;
|
|
855
|
+
this._finished = true;
|
|
856
|
+
return this;
|
|
89
857
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
858
|
+
update(m) {
|
|
859
|
+
let mpos = 0;
|
|
860
|
+
let bytes = m.length;
|
|
861
|
+
let want;
|
|
862
|
+
if (this._leftover) {
|
|
863
|
+
want = (16 - this._leftover);
|
|
864
|
+
if (want > bytes) {
|
|
865
|
+
want = bytes;
|
|
866
|
+
}
|
|
867
|
+
for (let i = 0; i < want; i++) {
|
|
868
|
+
this._buffer[this._leftover + i] = m[mpos + i];
|
|
869
|
+
}
|
|
870
|
+
bytes -= want;
|
|
871
|
+
mpos += want;
|
|
872
|
+
this._leftover += want;
|
|
873
|
+
if (this._leftover < 16) {
|
|
874
|
+
return this;
|
|
875
|
+
}
|
|
876
|
+
this._blocks(this._buffer, 0, 16);
|
|
877
|
+
this._leftover = 0;
|
|
93
878
|
}
|
|
94
|
-
|
|
95
|
-
|
|
879
|
+
if (bytes >= 16) {
|
|
880
|
+
want = bytes - (bytes % 16);
|
|
881
|
+
this._blocks(m, mpos, want);
|
|
882
|
+
mpos += want;
|
|
883
|
+
bytes -= want;
|
|
884
|
+
}
|
|
885
|
+
if (bytes) {
|
|
886
|
+
for (let i = 0; i < bytes; i++) {
|
|
887
|
+
this._buffer[this._leftover + i] = m[mpos + i];
|
|
888
|
+
}
|
|
889
|
+
this._leftover += bytes;
|
|
96
890
|
}
|
|
891
|
+
return this;
|
|
97
892
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
893
|
+
digest() {
|
|
894
|
+
// TODO(dchest): it behaves differently than other hashes/HMAC,
|
|
895
|
+
// because it throws when finished — others just return saved result.
|
|
896
|
+
if (this._finished) {
|
|
897
|
+
throw new Error("Poly1305 was finished");
|
|
101
898
|
}
|
|
102
|
-
|
|
103
|
-
|
|
899
|
+
let mac = new Uint8Array(16);
|
|
900
|
+
this.finish(mac);
|
|
901
|
+
return mac;
|
|
902
|
+
}
|
|
903
|
+
clean() {
|
|
904
|
+
wipe(this._buffer);
|
|
905
|
+
wipe(this._r);
|
|
906
|
+
wipe(this._h);
|
|
907
|
+
wipe(this._pad);
|
|
908
|
+
this._leftover = 0;
|
|
909
|
+
this._fin = 0;
|
|
910
|
+
this._finished = true; // mark as finished even if not
|
|
911
|
+
return this;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// Copyright (C) 2016 Dmitry Chestnykh
|
|
916
|
+
// MIT License. See LICENSE file for details.
|
|
917
|
+
const KEY_LENGTH$1 = 32;
|
|
918
|
+
const NONCE_LENGTH$1 = 12;
|
|
919
|
+
const TAG_LENGTH$1 = 16;
|
|
920
|
+
const ZEROS = new Uint8Array(16);
|
|
921
|
+
/**
|
|
922
|
+
* ChaCha20-Poly1305 Authenticated Encryption with Associated Data.
|
|
923
|
+
*
|
|
924
|
+
* Defined in RFC7539.
|
|
925
|
+
*/
|
|
926
|
+
class ChaCha20Poly1305 {
|
|
927
|
+
nonceLength = NONCE_LENGTH$1;
|
|
928
|
+
tagLength = TAG_LENGTH$1;
|
|
929
|
+
_key;
|
|
930
|
+
/**
|
|
931
|
+
* Creates a new instance with the given 32-byte key.
|
|
932
|
+
*/
|
|
933
|
+
constructor(key) {
|
|
934
|
+
if (key.length !== KEY_LENGTH$1) {
|
|
935
|
+
throw new Error("ChaCha20Poly1305 needs 32-byte key");
|
|
104
936
|
}
|
|
937
|
+
// Copy key.
|
|
938
|
+
this._key = new Uint8Array(key);
|
|
105
939
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
940
|
+
/**
|
|
941
|
+
* Encrypts and authenticates plaintext, authenticates associated data,
|
|
942
|
+
* and returns sealed ciphertext, which includes authentication tag.
|
|
943
|
+
*
|
|
944
|
+
* RFC7539 specifies 12 bytes for nonce. It may be this 12-byte nonce
|
|
945
|
+
* ("IV"), or full 16-byte counter (called "32-bit fixed-common part")
|
|
946
|
+
* and nonce.
|
|
947
|
+
*
|
|
948
|
+
* If dst is given (it must be the size of plaintext + the size of tag
|
|
949
|
+
* length) the result will be put into it. Dst and plaintext must not
|
|
950
|
+
* overlap.
|
|
951
|
+
*/
|
|
952
|
+
seal(nonce, plaintext, associatedData, dst) {
|
|
953
|
+
if (nonce.length > 16) {
|
|
954
|
+
throw new Error("ChaCha20Poly1305: incorrect nonce length");
|
|
109
955
|
}
|
|
110
|
-
|
|
111
|
-
|
|
956
|
+
// Allocate space for counter, and set nonce as last bytes of it.
|
|
957
|
+
const counter = new Uint8Array(16);
|
|
958
|
+
counter.set(nonce, counter.length - nonce.length);
|
|
959
|
+
// Generate authentication key by taking first 32-bytes of stream.
|
|
960
|
+
// We pass full counter, which has 12-byte nonce and 4-byte block counter,
|
|
961
|
+
// and it will get incremented after generating the block, which is
|
|
962
|
+
// exactly what we need: we only use the first 32 bytes of 64-byte
|
|
963
|
+
// ChaCha block and discard the next 32 bytes.
|
|
964
|
+
const authKey = new Uint8Array(32);
|
|
965
|
+
stream(this._key, counter, authKey, 4);
|
|
966
|
+
// Allocate space for sealed ciphertext.
|
|
967
|
+
const resultLength = plaintext.length + this.tagLength;
|
|
968
|
+
let result;
|
|
969
|
+
if (dst) {
|
|
970
|
+
if (dst.length !== resultLength) {
|
|
971
|
+
throw new Error("ChaCha20Poly1305: incorrect destination length");
|
|
972
|
+
}
|
|
973
|
+
result = dst;
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
result = new Uint8Array(resultLength);
|
|
112
977
|
}
|
|
978
|
+
// Encrypt plaintext.
|
|
979
|
+
streamXOR(this._key, counter, plaintext, result, 4);
|
|
980
|
+
// Authenticate.
|
|
981
|
+
// XXX: can "simplify" here: pass full result (which is already padded
|
|
982
|
+
// due to zeroes prepared for tag), and ciphertext length instead of
|
|
983
|
+
// subarray of result.
|
|
984
|
+
this._authenticate(result.subarray(result.length - this.tagLength, result.length), authKey, result.subarray(0, result.length - this.tagLength), associatedData);
|
|
985
|
+
// Cleanup.
|
|
986
|
+
wipe(counter);
|
|
987
|
+
return result;
|
|
113
988
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
989
|
+
/**
|
|
990
|
+
* Authenticates sealed ciphertext (which includes authentication tag) and
|
|
991
|
+
* associated data, decrypts ciphertext and returns decrypted plaintext.
|
|
992
|
+
*
|
|
993
|
+
* RFC7539 specifies 12 bytes for nonce. It may be this 12-byte nonce
|
|
994
|
+
* ("IV"), or full 16-byte counter (called "32-bit fixed-common part")
|
|
995
|
+
* and nonce.
|
|
996
|
+
*
|
|
997
|
+
* If authentication fails, it returns null.
|
|
998
|
+
*
|
|
999
|
+
* If dst is given (it must be of ciphertext length minus tag length),
|
|
1000
|
+
* the result will be put into it. Dst and plaintext must not overlap.
|
|
1001
|
+
*/
|
|
1002
|
+
open(nonce, sealed, associatedData, dst) {
|
|
1003
|
+
if (nonce.length > 16) {
|
|
1004
|
+
throw new Error("ChaCha20Poly1305: incorrect nonce length");
|
|
1005
|
+
}
|
|
1006
|
+
// Sealed ciphertext should at least contain tag.
|
|
1007
|
+
if (sealed.length < this.tagLength) {
|
|
1008
|
+
// TODO(dchest): should we throw here instead?
|
|
1009
|
+
return null;
|
|
1010
|
+
}
|
|
1011
|
+
// Allocate space for counter, and set nonce as last bytes of it.
|
|
1012
|
+
const counter = new Uint8Array(16);
|
|
1013
|
+
counter.set(nonce, counter.length - nonce.length);
|
|
1014
|
+
// Generate authentication key by taking first 32-bytes of stream.
|
|
1015
|
+
const authKey = new Uint8Array(32);
|
|
1016
|
+
stream(this._key, counter, authKey, 4);
|
|
1017
|
+
// Authenticate.
|
|
1018
|
+
// XXX: can simplify and avoid allocation: since authenticate()
|
|
1019
|
+
// already allocates tag (from Poly1305.digest(), it can return)
|
|
1020
|
+
// it instead of copying to calculatedTag. But then in seal()
|
|
1021
|
+
// we'll need to copy it.
|
|
1022
|
+
const calculatedTag = new Uint8Array(this.tagLength);
|
|
1023
|
+
this._authenticate(calculatedTag, authKey, sealed.subarray(0, sealed.length - this.tagLength), associatedData);
|
|
1024
|
+
// Constant-time compare tags and return null if they differ.
|
|
1025
|
+
if (!equal(calculatedTag, sealed.subarray(sealed.length - this.tagLength, sealed.length))) {
|
|
1026
|
+
return null;
|
|
1027
|
+
}
|
|
1028
|
+
// Allocate space for decrypted plaintext.
|
|
1029
|
+
const resultLength = sealed.length - this.tagLength;
|
|
1030
|
+
let result;
|
|
1031
|
+
if (dst) {
|
|
1032
|
+
if (dst.length !== resultLength) {
|
|
1033
|
+
throw new Error("ChaCha20Poly1305: incorrect destination length");
|
|
1034
|
+
}
|
|
1035
|
+
result = dst;
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
result = new Uint8Array(resultLength);
|
|
1039
|
+
}
|
|
1040
|
+
// Decrypt.
|
|
1041
|
+
streamXOR(this._key, counter, sealed.subarray(0, sealed.length - this.tagLength), result, 4);
|
|
1042
|
+
// Cleanup.
|
|
1043
|
+
wipe(counter);
|
|
1044
|
+
return result;
|
|
118
1045
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const extendedNavigator = navigator;
|
|
123
|
-
return extendedNavigator.systemLanguage || navigator.language || '';
|
|
1046
|
+
clean() {
|
|
1047
|
+
wipe(this._key);
|
|
1048
|
+
return this;
|
|
124
1049
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
1050
|
+
_authenticate(tagOut, authKey, ciphertext, associatedData) {
|
|
1051
|
+
// Initialize Poly1305 with authKey.
|
|
1052
|
+
const h = new Poly1305(authKey);
|
|
1053
|
+
// Authenticate padded associated data.
|
|
1054
|
+
if (associatedData) {
|
|
1055
|
+
h.update(associatedData);
|
|
1056
|
+
if (associatedData.length % 16 > 0) {
|
|
1057
|
+
h.update(ZEROS.subarray(associatedData.length % 16));
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
// Authenticate padded ciphertext.
|
|
1061
|
+
h.update(ciphertext);
|
|
1062
|
+
if (ciphertext.length % 16 > 0) {
|
|
1063
|
+
h.update(ZEROS.subarray(ciphertext.length % 16));
|
|
1064
|
+
}
|
|
1065
|
+
// Authenticate length of associated data.
|
|
1066
|
+
// XXX: can avoid allocation here?
|
|
1067
|
+
const length = new Uint8Array(8);
|
|
1068
|
+
if (associatedData) {
|
|
1069
|
+
writeUint64LE(associatedData.length, length);
|
|
1070
|
+
}
|
|
1071
|
+
h.update(length);
|
|
1072
|
+
// Authenticate length of ciphertext.
|
|
1073
|
+
writeUint64LE(ciphertext.length, length);
|
|
1074
|
+
h.update(length);
|
|
1075
|
+
// Get tag and copy it into tagOut.
|
|
1076
|
+
const tag = h.digest();
|
|
1077
|
+
for (let i = 0; i < tag.length; i++) {
|
|
1078
|
+
tagOut[i] = tag[i];
|
|
1079
|
+
}
|
|
1080
|
+
// Cleanup.
|
|
1081
|
+
h.clean();
|
|
1082
|
+
wipe(tag);
|
|
1083
|
+
wipe(length);
|
|
129
1084
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// Copyright (C) 2019 Kyle Den Hartog
|
|
1088
|
+
// MIT License. See LICENSE file for details.
|
|
1089
|
+
const KEY_LENGTH = 32;
|
|
1090
|
+
const NONCE_LENGTH = 24;
|
|
1091
|
+
const TAG_LENGTH = 16;
|
|
1092
|
+
/**
|
|
1093
|
+
* XChaCha20-Poly1305 Authenticated Encryption with Associated Data.
|
|
1094
|
+
*
|
|
1095
|
+
* Defined in draft-irtf-cfrg-xchacha-01.
|
|
1096
|
+
* See https://tools.ietf.org/html/draft-irtf-cfrg-xchacha-01
|
|
1097
|
+
*/
|
|
1098
|
+
class XChaCha20Poly1305 {
|
|
1099
|
+
nonceLength = NONCE_LENGTH;
|
|
1100
|
+
tagLength = TAG_LENGTH;
|
|
1101
|
+
_key;
|
|
1102
|
+
/**
|
|
1103
|
+
* Creates a new instance with the given 32-byte key.
|
|
1104
|
+
*/
|
|
1105
|
+
constructor(key) {
|
|
1106
|
+
if (key.length !== KEY_LENGTH) {
|
|
1107
|
+
throw new Error("ChaCha20Poly1305 needs 32-byte key");
|
|
148
1108
|
}
|
|
149
|
-
|
|
150
|
-
|
|
1109
|
+
// Copy key.
|
|
1110
|
+
this._key = new Uint8Array(key);
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1113
|
+
* Encrypts and authenticates plaintext, authenticates associated data,
|
|
1114
|
+
* and returns sealed ciphertext, which includes authentication tag.
|
|
1115
|
+
*
|
|
1116
|
+
* draft-irtf-cfrg-xchacha-01 defines a 24 byte nonce (192 bits) which
|
|
1117
|
+
* uses the first 16 bytes of the nonce and the secret key with
|
|
1118
|
+
* HChaCha to generate an initial subkey. The last 8 bytes of the nonce
|
|
1119
|
+
* are then prefixed with 4 zero bytes and then provided with the subkey
|
|
1120
|
+
* to the ChaCha20Poly1305 implementation.
|
|
1121
|
+
*
|
|
1122
|
+
* If dst is given (it must be the size of plaintext + the size of tag
|
|
1123
|
+
* length) the result will be put into it. Dst and plaintext must not
|
|
1124
|
+
* overlap.
|
|
1125
|
+
*/
|
|
1126
|
+
seal(nonce, plaintext, associatedData, dst) {
|
|
1127
|
+
if (nonce.length !== 24) {
|
|
1128
|
+
throw new Error("XChaCha20Poly1305: incorrect nonce length");
|
|
151
1129
|
}
|
|
1130
|
+
// Use HSalsa one-way function to transform first 16 bytes of
|
|
1131
|
+
// 24-byte extended nonce and key into a new key for Salsa
|
|
1132
|
+
// stream -- "subkey".
|
|
1133
|
+
const subKey = hchacha(this._key, nonce.subarray(0, 16), new Uint8Array(32));
|
|
1134
|
+
// Use last 8 bytes of 24-byte extended nonce as an actual nonce prefixed by 4 zero bytes,
|
|
1135
|
+
// and a subkey derived in the previous step as key to encrypt.
|
|
1136
|
+
const modifiedNonce = new Uint8Array(12);
|
|
1137
|
+
modifiedNonce.set(nonce.subarray(16), 4);
|
|
1138
|
+
const chaChaPoly = new ChaCha20Poly1305(subKey);
|
|
1139
|
+
const result = chaChaPoly.seal(modifiedNonce, plaintext, associatedData, dst);
|
|
1140
|
+
wipe(subKey);
|
|
1141
|
+
wipe(modifiedNonce);
|
|
1142
|
+
chaChaPoly.clean();
|
|
1143
|
+
return result;
|
|
152
1144
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
1145
|
+
/**
|
|
1146
|
+
* Authenticates sealed ciphertext (which includes authentication tag) and
|
|
1147
|
+
* associated data, decrypts ciphertext and returns decrypted plaintext.
|
|
1148
|
+
*
|
|
1149
|
+
* draft-irtf-cfrg-xchacha-01 defines a 24 byte nonce (192 bits) which
|
|
1150
|
+
* then uses the first 16 bytes of the nonce and the secret key with
|
|
1151
|
+
* Hchacha to generate an initial subkey. The last 8 bytes of the nonce
|
|
1152
|
+
* are then prefixed with 4 zero bytes and then provided with the subkey
|
|
1153
|
+
* to the chacha20poly1305 implementation.
|
|
1154
|
+
*
|
|
1155
|
+
* If authentication fails, it returns null.
|
|
1156
|
+
*
|
|
1157
|
+
* If dst is given (it must be the size of plaintext + the size of tag
|
|
1158
|
+
* length) the result will be put into it. Dst and plaintext must not
|
|
1159
|
+
* overlap.
|
|
1160
|
+
*/
|
|
1161
|
+
open(nonce, sealed, associatedData, dst) {
|
|
1162
|
+
if (nonce.length !== 24) {
|
|
1163
|
+
throw new Error("XChaCha20Poly1305: incorrect nonce length");
|
|
1164
|
+
}
|
|
1165
|
+
// Sealed ciphertext should at least contain tag.
|
|
1166
|
+
if (sealed.length < this.tagLength) {
|
|
1167
|
+
// TODO(dchest): should we throw here instead?
|
|
1168
|
+
return null;
|
|
1169
|
+
}
|
|
1170
|
+
/**
|
|
1171
|
+
* Generate subKey by using HChaCha20 function as defined
|
|
1172
|
+
* in section 2 step 1 of draft-irtf-cfrg-xchacha-01
|
|
1173
|
+
*/
|
|
1174
|
+
const subKey = hchacha(this._key, nonce.subarray(0, 16), new Uint8Array(32));
|
|
1175
|
+
/**
|
|
1176
|
+
* Generate Nonce as defined - remaining 8 bytes of the nonce prefixed with
|
|
1177
|
+
* 4 zero bytes
|
|
1178
|
+
*/
|
|
1179
|
+
const modifiedNonce = new Uint8Array(12);
|
|
1180
|
+
modifiedNonce.set(nonce.subarray(16), 4);
|
|
1181
|
+
/**
|
|
1182
|
+
* Authenticate and decrypt by calling into chacha20poly1305.
|
|
1183
|
+
*/
|
|
1184
|
+
const chaChaPoly = new ChaCha20Poly1305(subKey);
|
|
1185
|
+
const result = chaChaPoly.open(modifiedNonce, sealed, associatedData, dst);
|
|
1186
|
+
wipe(subKey);
|
|
1187
|
+
wipe(modifiedNonce);
|
|
1188
|
+
chaChaPoly.clean();
|
|
1189
|
+
return result;
|
|
1190
|
+
}
|
|
1191
|
+
clean() {
|
|
1192
|
+
wipe(this._key);
|
|
1193
|
+
return this;
|
|
157
1194
|
}
|
|
158
1195
|
}
|
|
159
1196
|
|
|
160
|
-
const ALGORITHM = '
|
|
1197
|
+
const ALGORITHM = 'XChaCha20-Poly1305';
|
|
161
1198
|
const KEY_BYTES = 32;
|
|
162
|
-
const NONCE_BYTES =
|
|
163
|
-
const
|
|
164
|
-
const
|
|
1199
|
+
const NONCE_BYTES = 24;
|
|
1200
|
+
const HEX_KEY_PATTERN = /^[0-9a-f]{64}$/i;
|
|
1201
|
+
const DEFAULT_SECRET_KEY = new Uint8Array([
|
|
1202
|
+
0x70, 0x61, 0x74, 0x68, 0x73, 0x63, 0x61, 0x6c,
|
|
1203
|
+
0x65, 0x2d, 0x73, 0x6c, 0x73, 0x2d, 0x63, 0x68,
|
|
1204
|
+
0x61, 0x63, 0x68, 0x61, 0x32, 0x30, 0x70, 0x6f,
|
|
1205
|
+
0x6c, 0x79, 0x31, 0x33, 0x30, 0x35, 0x6b, 0x31,
|
|
1206
|
+
]);
|
|
165
1207
|
/**
|
|
166
1208
|
* Encryption utility for secure data storage
|
|
167
1209
|
*/
|
|
168
1210
|
class EncryptionManager {
|
|
169
|
-
constructor(secretKey) {
|
|
1211
|
+
constructor(secretKey = '') {
|
|
170
1212
|
this.version = '2.0.0';
|
|
171
1213
|
this.textEncoder = new TextEncoder();
|
|
172
1214
|
this.textDecoder = new TextDecoder();
|
|
173
|
-
this.secretKey = this.
|
|
1215
|
+
this.secretKey = this.loadSecretKey(secretKey);
|
|
174
1216
|
}
|
|
175
1217
|
/**
|
|
176
1218
|
* Update the secret key
|
|
177
1219
|
*/
|
|
178
1220
|
updateSecretKey(newSecretKey) {
|
|
179
1221
|
this.secretKey.fill(0);
|
|
180
|
-
this.secretKey = this.
|
|
1222
|
+
this.secretKey = this.loadSecretKey(newSecretKey);
|
|
181
1223
|
}
|
|
182
1224
|
/**
|
|
183
1225
|
* Encrypt data with type preservation
|
|
@@ -190,9 +1232,11 @@ class EncryptionManager {
|
|
|
190
1232
|
version: this.version,
|
|
191
1233
|
};
|
|
192
1234
|
const serialized = JSON.stringify(item);
|
|
193
|
-
const nonce =
|
|
1235
|
+
const nonce = this.getRandomBytes(NONCE_BYTES);
|
|
194
1236
|
const plaintext = this.textEncoder.encode(serialized);
|
|
195
|
-
const
|
|
1237
|
+
const cipher = new XChaCha20Poly1305(this.secretKey);
|
|
1238
|
+
const ciphertext = cipher.seal(nonce, plaintext);
|
|
1239
|
+
cipher.clean();
|
|
196
1240
|
const envelope = {
|
|
197
1241
|
algorithm: ALGORITHM,
|
|
198
1242
|
nonce: this.encodeBase64(nonce),
|
|
@@ -212,7 +1256,12 @@ class EncryptionManager {
|
|
|
212
1256
|
}
|
|
213
1257
|
const nonce = this.decodeBase64(envelope.nonce);
|
|
214
1258
|
const ciphertext = this.decodeBase64(envelope.ciphertext);
|
|
215
|
-
const
|
|
1259
|
+
const cipher = new XChaCha20Poly1305(this.secretKey);
|
|
1260
|
+
const plaintext = cipher.open(nonce, ciphertext);
|
|
1261
|
+
cipher.clean();
|
|
1262
|
+
if (!plaintext) {
|
|
1263
|
+
throw new Error('Failed to authenticate encrypted data');
|
|
1264
|
+
}
|
|
216
1265
|
const decryptedText = this.textDecoder.decode(plaintext);
|
|
217
1266
|
const item = JSON.parse(decryptedText);
|
|
218
1267
|
return this.deserializeData(item.data, item.type);
|
|
@@ -230,7 +1279,11 @@ class EncryptionManager {
|
|
|
230
1279
|
const envelope = JSON.parse(encryptedData);
|
|
231
1280
|
if (!this.isChachaEnvelope(envelope))
|
|
232
1281
|
return false;
|
|
233
|
-
const
|
|
1282
|
+
const cipher = new XChaCha20Poly1305(this.secretKey);
|
|
1283
|
+
const plaintext = cipher.open(this.decodeBase64(envelope.nonce), this.decodeBase64(envelope.ciphertext));
|
|
1284
|
+
cipher.clean();
|
|
1285
|
+
if (!plaintext)
|
|
1286
|
+
return false;
|
|
234
1287
|
const item = JSON.parse(this.textDecoder.decode(plaintext));
|
|
235
1288
|
return item && typeof item.data !== 'undefined' && typeof item.type === 'string';
|
|
236
1289
|
}
|
|
@@ -238,11 +1291,29 @@ class EncryptionManager {
|
|
|
238
1291
|
return false;
|
|
239
1292
|
}
|
|
240
1293
|
}
|
|
241
|
-
|
|
242
|
-
return
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
1294
|
+
loadSecretKey(key) {
|
|
1295
|
+
return this.tryParseRawKey(key) || DEFAULT_SECRET_KEY.slice();
|
|
1296
|
+
}
|
|
1297
|
+
tryParseRawKey(key) {
|
|
1298
|
+
if (!key)
|
|
1299
|
+
return null;
|
|
1300
|
+
if (HEX_KEY_PATTERN.test(key)) {
|
|
1301
|
+
const bytes = new Uint8Array(KEY_BYTES);
|
|
1302
|
+
for (let index = 0; index < KEY_BYTES; index += 1) {
|
|
1303
|
+
bytes[index] = Number.parseInt(key.slice(index * 2, index * 2 + 2), 16);
|
|
1304
|
+
}
|
|
1305
|
+
return bytes;
|
|
1306
|
+
}
|
|
1307
|
+
try {
|
|
1308
|
+
const decoded = this.decodeBase64(key);
|
|
1309
|
+
if (decoded.length === KEY_BYTES)
|
|
1310
|
+
return decoded;
|
|
1311
|
+
}
|
|
1312
|
+
catch {
|
|
1313
|
+
// Not a base64 key; try raw UTF-8 below.
|
|
1314
|
+
}
|
|
1315
|
+
const raw = this.textEncoder.encode(key);
|
|
1316
|
+
return raw.length === KEY_BYTES ? raw : null;
|
|
246
1317
|
}
|
|
247
1318
|
serializeData(data) {
|
|
248
1319
|
if (data === null || data === undefined) {
|
|
@@ -322,6 +1393,15 @@ class EncryptionManager {
|
|
|
322
1393
|
getGlobalBuffer() {
|
|
323
1394
|
return globalThis.Buffer;
|
|
324
1395
|
}
|
|
1396
|
+
getRandomBytes(length) {
|
|
1397
|
+
const crypto = globalThis.crypto;
|
|
1398
|
+
if (!crypto?.getRandomValues) {
|
|
1399
|
+
throw new Error('No cryptographically secure random source available');
|
|
1400
|
+
}
|
|
1401
|
+
const bytes = new Uint8Array(length);
|
|
1402
|
+
crypto.getRandomValues(bytes);
|
|
1403
|
+
return bytes;
|
|
1404
|
+
}
|
|
325
1405
|
}
|
|
326
1406
|
|
|
327
1407
|
/**
|
|
@@ -407,7 +1487,6 @@ class SecureLocalStorage {
|
|
|
407
1487
|
disabledKeys: config.disabledKeys || envConfig.disabledKeys || [],
|
|
408
1488
|
debug: config.debug || envConfig.debug || false,
|
|
409
1489
|
};
|
|
410
|
-
this.fingerprinting = new BrowserFingerprinting(this.config.disabledKeys);
|
|
411
1490
|
this.storageEngine = this.getStorageEngine();
|
|
412
1491
|
this.prefix = this.config.prefix || 'sls_';
|
|
413
1492
|
this.memoryCache = new Map();
|
|
@@ -565,10 +1644,6 @@ class SecureLocalStorage {
|
|
|
565
1644
|
const newSecretKey = this.generateSecretKey();
|
|
566
1645
|
this.encryption.updateSecretKey(newSecretKey);
|
|
567
1646
|
}
|
|
568
|
-
// Update fingerprinting if disabled keys changed
|
|
569
|
-
if (oldConfig.disabledKeys !== this.config.disabledKeys) {
|
|
570
|
-
this.fingerprinting = new BrowserFingerprinting(this.config.disabledKeys);
|
|
571
|
-
}
|
|
572
1647
|
if (this.config.debug) {
|
|
573
1648
|
console.log('Configuration updated:', this.config);
|
|
574
1649
|
}
|
|
@@ -587,9 +1662,7 @@ class SecureLocalStorage {
|
|
|
587
1662
|
return 'secure-local-storage-default-key';
|
|
588
1663
|
}
|
|
589
1664
|
generateSecretKey() {
|
|
590
|
-
|
|
591
|
-
const fingerprintString = this.fingerprinting.fingerprintToString(fingerprint);
|
|
592
|
-
return `${this.config.hashKey}|${fingerprintString}`;
|
|
1665
|
+
return this.config.hashKey || this.generateDefaultHashKey();
|
|
593
1666
|
}
|
|
594
1667
|
initializeFromStorage() {
|
|
595
1668
|
try {
|
|
@@ -643,6 +1716,155 @@ class MemoryStorage {
|
|
|
643
1716
|
}
|
|
644
1717
|
}
|
|
645
1718
|
|
|
1719
|
+
/**
|
|
1720
|
+
* Browser fingerprinting utility for generating unique browser identifiers
|
|
1721
|
+
*/
|
|
1722
|
+
class BrowserFingerprinting {
|
|
1723
|
+
constructor(disabledKeys = []) {
|
|
1724
|
+
this.disabledKeys = new Set(disabledKeys);
|
|
1725
|
+
}
|
|
1726
|
+
/**
|
|
1727
|
+
* Generate a comprehensive browser fingerprint
|
|
1728
|
+
*/
|
|
1729
|
+
generateFingerprint() {
|
|
1730
|
+
return {
|
|
1731
|
+
userAgent: this.isEnabled('UserAgent') ? this.getUserAgent() : '',
|
|
1732
|
+
screenPrint: this.isEnabled('ScreenPrint') ? this.getScreenPrint() : '',
|
|
1733
|
+
plugins: this.isEnabled('Plugins') ? this.getPlugins() : '',
|
|
1734
|
+
fonts: this.isEnabled('Fonts') ? this.getFonts() : '',
|
|
1735
|
+
localStorage: this.isEnabled('LocalStorage') ? this.hasLocalStorage() : false,
|
|
1736
|
+
sessionStorage: this.isEnabled('SessionStorage') ? this.hasSessionStorage() : false,
|
|
1737
|
+
timeZone: this.isEnabled('TimeZone') ? this.getTimeZone() : '',
|
|
1738
|
+
language: this.isEnabled('Language') ? this.getLanguage() : '',
|
|
1739
|
+
systemLanguage: this.isEnabled('SystemLanguage') ? this.getSystemLanguage() : '',
|
|
1740
|
+
cookie: this.isEnabled('Cookie') ? this.hasCookieSupport() : false,
|
|
1741
|
+
canvas: this.isEnabled('Canvas') ? this.getCanvasFingerprint() : '',
|
|
1742
|
+
hostname: this.isEnabled('Hostname') ? this.getHostname() : '',
|
|
1743
|
+
};
|
|
1744
|
+
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Convert fingerprint to a hash string
|
|
1747
|
+
*/
|
|
1748
|
+
fingerprintToString(fingerprint) {
|
|
1749
|
+
return Object.values(fingerprint)
|
|
1750
|
+
.map(value => String(value))
|
|
1751
|
+
.join('|');
|
|
1752
|
+
}
|
|
1753
|
+
isEnabled(property) {
|
|
1754
|
+
return !this.disabledKeys.has(property);
|
|
1755
|
+
}
|
|
1756
|
+
getUserAgent() {
|
|
1757
|
+
return typeof navigator !== 'undefined' ? navigator.userAgent : '';
|
|
1758
|
+
}
|
|
1759
|
+
getScreenPrint() {
|
|
1760
|
+
if (typeof screen === 'undefined')
|
|
1761
|
+
return '';
|
|
1762
|
+
return `${screen.width}x${screen.height}x${screen.colorDepth}`;
|
|
1763
|
+
}
|
|
1764
|
+
getPlugins() {
|
|
1765
|
+
if (typeof navigator === 'undefined' || !navigator.plugins)
|
|
1766
|
+
return '';
|
|
1767
|
+
const plugins = Array.from(navigator.plugins)
|
|
1768
|
+
.map(plugin => plugin.name)
|
|
1769
|
+
.sort()
|
|
1770
|
+
.join(',');
|
|
1771
|
+
return plugins;
|
|
1772
|
+
}
|
|
1773
|
+
getFonts() {
|
|
1774
|
+
// Basic font detection using canvas
|
|
1775
|
+
if (typeof document === 'undefined')
|
|
1776
|
+
return '';
|
|
1777
|
+
const fonts = [
|
|
1778
|
+
'Arial', 'Helvetica', 'Times New Roman', 'Times', 'Courier New', 'Courier',
|
|
1779
|
+
'Verdana', 'Georgia', 'Palatino', 'Garamond', 'Bookman', 'Comic Sans MS',
|
|
1780
|
+
'Trebuchet MS', 'Arial Black', 'Impact', 'Sans-serif', 'Serif', 'Monospace'
|
|
1781
|
+
];
|
|
1782
|
+
const availableFonts = [];
|
|
1783
|
+
const canvas = document.createElement('canvas');
|
|
1784
|
+
const context = canvas.getContext('2d');
|
|
1785
|
+
if (!context)
|
|
1786
|
+
return '';
|
|
1787
|
+
fonts.forEach(font => {
|
|
1788
|
+
context.font = `12px ${font}, monospace`;
|
|
1789
|
+
const width1 = context.measureText('mmmmmmmmmmlli').width;
|
|
1790
|
+
context.font = `12px ${font}, sans-serif`;
|
|
1791
|
+
const width2 = context.measureText('mmmmmmmmmmlli').width;
|
|
1792
|
+
if (width1 !== width2) {
|
|
1793
|
+
availableFonts.push(font);
|
|
1794
|
+
}
|
|
1795
|
+
});
|
|
1796
|
+
return availableFonts.join(',');
|
|
1797
|
+
}
|
|
1798
|
+
hasLocalStorage() {
|
|
1799
|
+
try {
|
|
1800
|
+
return typeof localStorage !== 'undefined';
|
|
1801
|
+
}
|
|
1802
|
+
catch {
|
|
1803
|
+
return false;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
hasSessionStorage() {
|
|
1807
|
+
try {
|
|
1808
|
+
return typeof sessionStorage !== 'undefined';
|
|
1809
|
+
}
|
|
1810
|
+
catch {
|
|
1811
|
+
return false;
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
getTimeZone() {
|
|
1815
|
+
try {
|
|
1816
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1817
|
+
}
|
|
1818
|
+
catch {
|
|
1819
|
+
return new Date().getTimezoneOffset().toString();
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
getLanguage() {
|
|
1823
|
+
if (typeof navigator === 'undefined')
|
|
1824
|
+
return '';
|
|
1825
|
+
return navigator.language || '';
|
|
1826
|
+
}
|
|
1827
|
+
getSystemLanguage() {
|
|
1828
|
+
if (typeof navigator === 'undefined')
|
|
1829
|
+
return '';
|
|
1830
|
+
const extendedNavigator = navigator;
|
|
1831
|
+
return extendedNavigator.systemLanguage || navigator.language || '';
|
|
1832
|
+
}
|
|
1833
|
+
hasCookieSupport() {
|
|
1834
|
+
if (typeof document === 'undefined')
|
|
1835
|
+
return false;
|
|
1836
|
+
return navigator.cookieEnabled;
|
|
1837
|
+
}
|
|
1838
|
+
getCanvasFingerprint() {
|
|
1839
|
+
if (typeof document === 'undefined')
|
|
1840
|
+
return '';
|
|
1841
|
+
try {
|
|
1842
|
+
const canvas = document.createElement('canvas');
|
|
1843
|
+
const ctx = canvas.getContext('2d');
|
|
1844
|
+
if (!ctx)
|
|
1845
|
+
return '';
|
|
1846
|
+
// Draw some text and shapes to create a unique canvas fingerprint
|
|
1847
|
+
ctx.textBaseline = 'top';
|
|
1848
|
+
ctx.font = '14px Arial';
|
|
1849
|
+
ctx.fillStyle = '#f60';
|
|
1850
|
+
ctx.fillRect(125, 1, 62, 20);
|
|
1851
|
+
ctx.fillStyle = '#069';
|
|
1852
|
+
ctx.fillText('SecureStorage 🔒', 2, 15);
|
|
1853
|
+
ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
|
|
1854
|
+
ctx.fillText('SecureStorage 🔒', 4, 17);
|
|
1855
|
+
return canvas.toDataURL();
|
|
1856
|
+
}
|
|
1857
|
+
catch {
|
|
1858
|
+
return '';
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
getHostname() {
|
|
1862
|
+
if (typeof location === 'undefined')
|
|
1863
|
+
return '';
|
|
1864
|
+
return location.hostname;
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
|
|
646
1868
|
/**
|
|
647
1869
|
* Default secure local storage instance
|
|
648
1870
|
*/
|