@didcid/cipher 0.1.3
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/LICENSE +21 -0
- package/README.md +38 -0
- package/dist/cjs/cipher-base-D2GtcXrx.cjs +1695 -0
- package/dist/cjs/cipher-node.cjs +44 -0
- package/dist/cjs/cipher-web.cjs +53 -0
- package/dist/esm/cipher-base.js +127 -0
- package/dist/esm/cipher-base.js.map +1 -0
- package/dist/esm/cipher-node.js +18 -0
- package/dist/esm/cipher-node.js.map +1 -0
- package/dist/esm/cipher-web.js +27 -0
- package/dist/esm/cipher-web.js.map +1 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/types/cipher-base.d.ts +22 -0
- package/dist/types/cipher-node.d.ts +8 -0
- package/dist/types/cipher-web.d.ts +8 -0
- package/dist/types/types.d.ts +46 -0
- package/package.json +85 -0
|
@@ -0,0 +1,1695 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var bip39 = require('bip39');
|
|
4
|
+
var secp = require('@noble/secp256k1');
|
|
5
|
+
var canonicalizeModule = require('canonicalize');
|
|
6
|
+
|
|
7
|
+
function _interopNamespaceDefault(e) {
|
|
8
|
+
var n = Object.create(null);
|
|
9
|
+
if (e) {
|
|
10
|
+
Object.keys(e).forEach(function (k) {
|
|
11
|
+
if (k !== 'default') {
|
|
12
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: function () { return e[k]; }
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var bip39__namespace = /*#__PURE__*/_interopNamespaceDefault(bip39);
|
|
25
|
+
var secp__namespace = /*#__PURE__*/_interopNamespaceDefault(secp);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Class represents both BaseEncoder and MultibaseEncoder meaning it
|
|
29
|
+
* can be used to encode to multibase or base encode without multibase
|
|
30
|
+
* prefix.
|
|
31
|
+
*/
|
|
32
|
+
class Encoder {
|
|
33
|
+
name;
|
|
34
|
+
prefix;
|
|
35
|
+
baseEncode;
|
|
36
|
+
constructor(name, prefix, baseEncode) {
|
|
37
|
+
this.name = name;
|
|
38
|
+
this.prefix = prefix;
|
|
39
|
+
this.baseEncode = baseEncode;
|
|
40
|
+
}
|
|
41
|
+
encode(bytes) {
|
|
42
|
+
if (bytes instanceof Uint8Array) {
|
|
43
|
+
return `${this.prefix}${this.baseEncode(bytes)}`;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
throw Error('Unknown type, must be binary type');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Class represents both BaseDecoder and MultibaseDecoder so it could be used
|
|
52
|
+
* to decode multibases (with matching prefix) or just base decode strings
|
|
53
|
+
* with corresponding base encoding.
|
|
54
|
+
*/
|
|
55
|
+
class Decoder {
|
|
56
|
+
name;
|
|
57
|
+
prefix;
|
|
58
|
+
baseDecode;
|
|
59
|
+
prefixCodePoint;
|
|
60
|
+
constructor(name, prefix, baseDecode) {
|
|
61
|
+
this.name = name;
|
|
62
|
+
this.prefix = prefix;
|
|
63
|
+
const prefixCodePoint = prefix.codePointAt(0);
|
|
64
|
+
/* c8 ignore next 3 */
|
|
65
|
+
if (prefixCodePoint === undefined) {
|
|
66
|
+
throw new Error('Invalid prefix character');
|
|
67
|
+
}
|
|
68
|
+
this.prefixCodePoint = prefixCodePoint;
|
|
69
|
+
this.baseDecode = baseDecode;
|
|
70
|
+
}
|
|
71
|
+
decode(text) {
|
|
72
|
+
if (typeof text === 'string') {
|
|
73
|
+
if (text.codePointAt(0) !== this.prefixCodePoint) {
|
|
74
|
+
throw Error(`Unable to decode multibase string ${JSON.stringify(text)}, ${this.name} decoder only supports inputs prefixed with ${this.prefix}`);
|
|
75
|
+
}
|
|
76
|
+
return this.baseDecode(text.slice(this.prefix.length));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
throw Error('Can only multibase decode strings');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
or(decoder) {
|
|
83
|
+
return or(this, decoder);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
class ComposedDecoder {
|
|
87
|
+
decoders;
|
|
88
|
+
constructor(decoders) {
|
|
89
|
+
this.decoders = decoders;
|
|
90
|
+
}
|
|
91
|
+
or(decoder) {
|
|
92
|
+
return or(this, decoder);
|
|
93
|
+
}
|
|
94
|
+
decode(input) {
|
|
95
|
+
const prefix = input[0];
|
|
96
|
+
const decoder = this.decoders[prefix];
|
|
97
|
+
if (decoder != null) {
|
|
98
|
+
return decoder.decode(input);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
throw RangeError(`Unable to decode multibase string ${JSON.stringify(input)}, only inputs prefixed with ${Object.keys(this.decoders)} are supported`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function or(left, right) {
|
|
106
|
+
return new ComposedDecoder({
|
|
107
|
+
...(left.decoders ?? { [left.prefix]: left }),
|
|
108
|
+
...(right.decoders ?? { [right.prefix]: right })
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
class Codec {
|
|
112
|
+
name;
|
|
113
|
+
prefix;
|
|
114
|
+
baseEncode;
|
|
115
|
+
baseDecode;
|
|
116
|
+
encoder;
|
|
117
|
+
decoder;
|
|
118
|
+
constructor(name, prefix, baseEncode, baseDecode) {
|
|
119
|
+
this.name = name;
|
|
120
|
+
this.prefix = prefix;
|
|
121
|
+
this.baseEncode = baseEncode;
|
|
122
|
+
this.baseDecode = baseDecode;
|
|
123
|
+
this.encoder = new Encoder(name, prefix, baseEncode);
|
|
124
|
+
this.decoder = new Decoder(name, prefix, baseDecode);
|
|
125
|
+
}
|
|
126
|
+
encode(input) {
|
|
127
|
+
return this.encoder.encode(input);
|
|
128
|
+
}
|
|
129
|
+
decode(input) {
|
|
130
|
+
return this.decoder.decode(input);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function from({ name, prefix, encode, decode }) {
|
|
134
|
+
return new Codec(name, prefix, encode, decode);
|
|
135
|
+
}
|
|
136
|
+
function decode(string, alphabetIdx, bitsPerChar, name) {
|
|
137
|
+
// Count the padding bytes:
|
|
138
|
+
let end = string.length;
|
|
139
|
+
while (string[end - 1] === '=') {
|
|
140
|
+
--end;
|
|
141
|
+
}
|
|
142
|
+
// Allocate the output:
|
|
143
|
+
const out = new Uint8Array((end * bitsPerChar / 8) | 0);
|
|
144
|
+
// Parse the data:
|
|
145
|
+
let bits = 0; // Number of bits currently in the buffer
|
|
146
|
+
let buffer = 0; // Bits waiting to be written out, MSB first
|
|
147
|
+
let written = 0; // Next byte to write
|
|
148
|
+
for (let i = 0; i < end; ++i) {
|
|
149
|
+
// Read one character from the string:
|
|
150
|
+
const value = alphabetIdx[string[i]];
|
|
151
|
+
if (value === undefined) {
|
|
152
|
+
throw new SyntaxError(`Non-${name} character`);
|
|
153
|
+
}
|
|
154
|
+
// Append the bits to the buffer:
|
|
155
|
+
buffer = (buffer << bitsPerChar) | value;
|
|
156
|
+
bits += bitsPerChar;
|
|
157
|
+
// Write out some bits if the buffer has a byte's worth:
|
|
158
|
+
if (bits >= 8) {
|
|
159
|
+
bits -= 8;
|
|
160
|
+
out[written++] = 0xff & (buffer >> bits);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Verify that we have received just enough bits:
|
|
164
|
+
if (bits >= bitsPerChar || (0xff & (buffer << (8 - bits))) !== 0) {
|
|
165
|
+
throw new SyntaxError('Unexpected end of data');
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
function encode(data, alphabet, bitsPerChar) {
|
|
170
|
+
const pad = alphabet[alphabet.length - 1] === '=';
|
|
171
|
+
const mask = (1 << bitsPerChar) - 1;
|
|
172
|
+
let out = '';
|
|
173
|
+
let bits = 0; // Number of bits currently in the buffer
|
|
174
|
+
let buffer = 0; // Bits waiting to be written out, MSB first
|
|
175
|
+
for (let i = 0; i < data.length; ++i) {
|
|
176
|
+
// Slurp data into the buffer:
|
|
177
|
+
buffer = (buffer << 8) | data[i];
|
|
178
|
+
bits += 8;
|
|
179
|
+
// Write out as much as we can:
|
|
180
|
+
while (bits > bitsPerChar) {
|
|
181
|
+
bits -= bitsPerChar;
|
|
182
|
+
out += alphabet[mask & (buffer >> bits)];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Partial character:
|
|
186
|
+
if (bits !== 0) {
|
|
187
|
+
out += alphabet[mask & (buffer << (bitsPerChar - bits))];
|
|
188
|
+
}
|
|
189
|
+
// Add padding characters until we hit a byte boundary:
|
|
190
|
+
if (pad) {
|
|
191
|
+
while (((out.length * bitsPerChar) & 7) !== 0) {
|
|
192
|
+
out += '=';
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
function createAlphabetIdx(alphabet) {
|
|
198
|
+
// Build the character lookup table:
|
|
199
|
+
const alphabetIdx = {};
|
|
200
|
+
for (let i = 0; i < alphabet.length; ++i) {
|
|
201
|
+
alphabetIdx[alphabet[i]] = i;
|
|
202
|
+
}
|
|
203
|
+
return alphabetIdx;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* RFC4648 Factory
|
|
207
|
+
*/
|
|
208
|
+
function rfc4648({ name, prefix, bitsPerChar, alphabet }) {
|
|
209
|
+
const alphabetIdx = createAlphabetIdx(alphabet);
|
|
210
|
+
return from({
|
|
211
|
+
prefix,
|
|
212
|
+
name,
|
|
213
|
+
encode(input) {
|
|
214
|
+
return encode(input, alphabet, bitsPerChar);
|
|
215
|
+
},
|
|
216
|
+
decode(input) {
|
|
217
|
+
return decode(input, alphabetIdx, bitsPerChar, name);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
rfc4648({
|
|
223
|
+
prefix: 'm',
|
|
224
|
+
name: 'base64',
|
|
225
|
+
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
|
|
226
|
+
bitsPerChar: 6
|
|
227
|
+
});
|
|
228
|
+
rfc4648({
|
|
229
|
+
prefix: 'M',
|
|
230
|
+
name: 'base64pad',
|
|
231
|
+
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
|
|
232
|
+
bitsPerChar: 6
|
|
233
|
+
});
|
|
234
|
+
const base64url = rfc4648({
|
|
235
|
+
prefix: 'u',
|
|
236
|
+
name: 'base64url',
|
|
237
|
+
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
|
|
238
|
+
bitsPerChar: 6
|
|
239
|
+
});
|
|
240
|
+
rfc4648({
|
|
241
|
+
prefix: 'U',
|
|
242
|
+
name: 'base64urlpad',
|
|
243
|
+
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=',
|
|
244
|
+
bitsPerChar: 6
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Utilities for hex, bytes, CSPRNG.
|
|
249
|
+
* @module
|
|
250
|
+
*/
|
|
251
|
+
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
252
|
+
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
|
|
253
|
+
// node.js versions earlier than v19 don't declare it in global scope.
|
|
254
|
+
// For node.js, package.json#exports field mapping rewrites import
|
|
255
|
+
// from `crypto` to `cryptoNode`, which imports native module.
|
|
256
|
+
// Makes the utils un-importable in browsers without a bundler.
|
|
257
|
+
// Once node.js 18 is deprecated (2025-04-30), we can just drop the import.
|
|
258
|
+
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
|
|
259
|
+
function isBytes$2(a) {
|
|
260
|
+
return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
|
|
261
|
+
}
|
|
262
|
+
/** Asserts something is positive integer. */
|
|
263
|
+
function anumber(n) {
|
|
264
|
+
if (!Number.isSafeInteger(n) || n < 0)
|
|
265
|
+
throw new Error('positive integer expected, got ' + n);
|
|
266
|
+
}
|
|
267
|
+
/** Asserts something is Uint8Array. */
|
|
268
|
+
function abytes(b, ...lengths) {
|
|
269
|
+
if (!isBytes$2(b))
|
|
270
|
+
throw new Error('Uint8Array expected');
|
|
271
|
+
if (lengths.length > 0 && !lengths.includes(b.length))
|
|
272
|
+
throw new Error('Uint8Array expected of length ' + lengths + ', got length=' + b.length);
|
|
273
|
+
}
|
|
274
|
+
/** Asserts something is hash */
|
|
275
|
+
function ahash(h) {
|
|
276
|
+
if (typeof h !== 'function' || typeof h.create !== 'function')
|
|
277
|
+
throw new Error('Hash should be wrapped by utils.createHasher');
|
|
278
|
+
anumber(h.outputLen);
|
|
279
|
+
anumber(h.blockLen);
|
|
280
|
+
}
|
|
281
|
+
/** Asserts a hash instance has not been destroyed / finished */
|
|
282
|
+
function aexists(instance, checkFinished = true) {
|
|
283
|
+
if (instance.destroyed)
|
|
284
|
+
throw new Error('Hash instance has been destroyed');
|
|
285
|
+
if (checkFinished && instance.finished)
|
|
286
|
+
throw new Error('Hash#digest() has already been called');
|
|
287
|
+
}
|
|
288
|
+
/** Asserts output is properly-sized byte array */
|
|
289
|
+
function aoutput(out, instance) {
|
|
290
|
+
abytes(out);
|
|
291
|
+
const min = instance.outputLen;
|
|
292
|
+
if (out.length < min) {
|
|
293
|
+
throw new Error('digestInto() expects output buffer of length at least ' + min);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/** Zeroize a byte array. Warning: JS provides no guarantees. */
|
|
297
|
+
function clean(...arrays) {
|
|
298
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
299
|
+
arrays[i].fill(0);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/** Create DataView of an array for easy byte-level manipulation. */
|
|
303
|
+
function createView$1(arr) {
|
|
304
|
+
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
305
|
+
}
|
|
306
|
+
/** The rotate right (circular right shift) operation for uint32 */
|
|
307
|
+
function rotr(word, shift) {
|
|
308
|
+
return (word << (32 - shift)) | (word >>> shift);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Converts string to bytes using UTF8 encoding.
|
|
312
|
+
* @example utf8ToBytes('abc') // Uint8Array.from([97, 98, 99])
|
|
313
|
+
*/
|
|
314
|
+
function utf8ToBytes$1(str) {
|
|
315
|
+
if (typeof str !== 'string')
|
|
316
|
+
throw new Error('string expected');
|
|
317
|
+
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
|
|
321
|
+
* Warning: when Uint8Array is passed, it would NOT get copied.
|
|
322
|
+
* Keep in mind for future mutable operations.
|
|
323
|
+
*/
|
|
324
|
+
function toBytes$1(data) {
|
|
325
|
+
if (typeof data === 'string')
|
|
326
|
+
data = utf8ToBytes$1(data);
|
|
327
|
+
abytes(data);
|
|
328
|
+
return data;
|
|
329
|
+
}
|
|
330
|
+
/** For runtime check if class implements interface */
|
|
331
|
+
class Hash {
|
|
332
|
+
}
|
|
333
|
+
/** Wraps hash function, creating an interface on top of it */
|
|
334
|
+
function createHasher(hashCons) {
|
|
335
|
+
const hashC = (msg) => hashCons().update(toBytes$1(msg)).digest();
|
|
336
|
+
const tmp = hashCons();
|
|
337
|
+
hashC.outputLen = tmp.outputLen;
|
|
338
|
+
hashC.blockLen = tmp.blockLen;
|
|
339
|
+
hashC.create = () => hashCons();
|
|
340
|
+
return hashC;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* HMAC: RFC2104 message authentication code.
|
|
345
|
+
* @module
|
|
346
|
+
*/
|
|
347
|
+
class HMAC extends Hash {
|
|
348
|
+
constructor(hash, _key) {
|
|
349
|
+
super();
|
|
350
|
+
this.finished = false;
|
|
351
|
+
this.destroyed = false;
|
|
352
|
+
ahash(hash);
|
|
353
|
+
const key = toBytes$1(_key);
|
|
354
|
+
this.iHash = hash.create();
|
|
355
|
+
if (typeof this.iHash.update !== 'function')
|
|
356
|
+
throw new Error('Expected instance of class which extends utils.Hash');
|
|
357
|
+
this.blockLen = this.iHash.blockLen;
|
|
358
|
+
this.outputLen = this.iHash.outputLen;
|
|
359
|
+
const blockLen = this.blockLen;
|
|
360
|
+
const pad = new Uint8Array(blockLen);
|
|
361
|
+
// blockLen can be bigger than outputLen
|
|
362
|
+
pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);
|
|
363
|
+
for (let i = 0; i < pad.length; i++)
|
|
364
|
+
pad[i] ^= 0x36;
|
|
365
|
+
this.iHash.update(pad);
|
|
366
|
+
// By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone
|
|
367
|
+
this.oHash = hash.create();
|
|
368
|
+
// Undo internal XOR && apply outer XOR
|
|
369
|
+
for (let i = 0; i < pad.length; i++)
|
|
370
|
+
pad[i] ^= 0x36 ^ 0x5c;
|
|
371
|
+
this.oHash.update(pad);
|
|
372
|
+
clean(pad);
|
|
373
|
+
}
|
|
374
|
+
update(buf) {
|
|
375
|
+
aexists(this);
|
|
376
|
+
this.iHash.update(buf);
|
|
377
|
+
return this;
|
|
378
|
+
}
|
|
379
|
+
digestInto(out) {
|
|
380
|
+
aexists(this);
|
|
381
|
+
abytes(out, this.outputLen);
|
|
382
|
+
this.finished = true;
|
|
383
|
+
this.iHash.digestInto(out);
|
|
384
|
+
this.oHash.update(out);
|
|
385
|
+
this.oHash.digestInto(out);
|
|
386
|
+
this.destroy();
|
|
387
|
+
}
|
|
388
|
+
digest() {
|
|
389
|
+
const out = new Uint8Array(this.oHash.outputLen);
|
|
390
|
+
this.digestInto(out);
|
|
391
|
+
return out;
|
|
392
|
+
}
|
|
393
|
+
_cloneInto(to) {
|
|
394
|
+
// Create new instance without calling constructor since key already in state and we don't know it.
|
|
395
|
+
to || (to = Object.create(Object.getPrototypeOf(this), {}));
|
|
396
|
+
const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
|
|
397
|
+
to = to;
|
|
398
|
+
to.finished = finished;
|
|
399
|
+
to.destroyed = destroyed;
|
|
400
|
+
to.blockLen = blockLen;
|
|
401
|
+
to.outputLen = outputLen;
|
|
402
|
+
to.oHash = oHash._cloneInto(to.oHash);
|
|
403
|
+
to.iHash = iHash._cloneInto(to.iHash);
|
|
404
|
+
return to;
|
|
405
|
+
}
|
|
406
|
+
clone() {
|
|
407
|
+
return this._cloneInto();
|
|
408
|
+
}
|
|
409
|
+
destroy() {
|
|
410
|
+
this.destroyed = true;
|
|
411
|
+
this.oHash.destroy();
|
|
412
|
+
this.iHash.destroy();
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* HMAC: RFC2104 message authentication code.
|
|
417
|
+
* @param hash - function that would be used e.g. sha256
|
|
418
|
+
* @param key - message key
|
|
419
|
+
* @param message - message data
|
|
420
|
+
* @example
|
|
421
|
+
* import { hmac } from '@noble/hashes/hmac';
|
|
422
|
+
* import { sha256 } from '@noble/hashes/sha2';
|
|
423
|
+
* const mac1 = hmac(sha256, 'key', 'message');
|
|
424
|
+
*/
|
|
425
|
+
const hmac = (hash, key, message) => new HMAC(hash, key).update(message).digest();
|
|
426
|
+
hmac.create = (hash, key) => new HMAC(hash, key);
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Internal Merkle-Damgard hash utils.
|
|
430
|
+
* @module
|
|
431
|
+
*/
|
|
432
|
+
/** Polyfill for Safari 14. https://caniuse.com/mdn-javascript_builtins_dataview_setbiguint64 */
|
|
433
|
+
function setBigUint64$1(view, byteOffset, value, isLE) {
|
|
434
|
+
if (typeof view.setBigUint64 === 'function')
|
|
435
|
+
return view.setBigUint64(byteOffset, value, isLE);
|
|
436
|
+
const _32n = BigInt(32);
|
|
437
|
+
const _u32_max = BigInt(0xffffffff);
|
|
438
|
+
const wh = Number((value >> _32n) & _u32_max);
|
|
439
|
+
const wl = Number(value & _u32_max);
|
|
440
|
+
const h = isLE ? 4 : 0;
|
|
441
|
+
const l = isLE ? 0 : 4;
|
|
442
|
+
view.setUint32(byteOffset + h, wh, isLE);
|
|
443
|
+
view.setUint32(byteOffset + l, wl, isLE);
|
|
444
|
+
}
|
|
445
|
+
/** Choice: a ? b : c */
|
|
446
|
+
function Chi(a, b, c) {
|
|
447
|
+
return (a & b) ^ (~a & c);
|
|
448
|
+
}
|
|
449
|
+
/** Majority function, true if any two inputs is true. */
|
|
450
|
+
function Maj(a, b, c) {
|
|
451
|
+
return (a & b) ^ (a & c) ^ (b & c);
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Merkle-Damgard hash construction base class.
|
|
455
|
+
* Could be used to create MD5, RIPEMD, SHA1, SHA2.
|
|
456
|
+
*/
|
|
457
|
+
class HashMD extends Hash {
|
|
458
|
+
constructor(blockLen, outputLen, padOffset, isLE) {
|
|
459
|
+
super();
|
|
460
|
+
this.finished = false;
|
|
461
|
+
this.length = 0;
|
|
462
|
+
this.pos = 0;
|
|
463
|
+
this.destroyed = false;
|
|
464
|
+
this.blockLen = blockLen;
|
|
465
|
+
this.outputLen = outputLen;
|
|
466
|
+
this.padOffset = padOffset;
|
|
467
|
+
this.isLE = isLE;
|
|
468
|
+
this.buffer = new Uint8Array(blockLen);
|
|
469
|
+
this.view = createView$1(this.buffer);
|
|
470
|
+
}
|
|
471
|
+
update(data) {
|
|
472
|
+
aexists(this);
|
|
473
|
+
data = toBytes$1(data);
|
|
474
|
+
abytes(data);
|
|
475
|
+
const { view, buffer, blockLen } = this;
|
|
476
|
+
const len = data.length;
|
|
477
|
+
for (let pos = 0; pos < len;) {
|
|
478
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
479
|
+
// Fast path: we have at least one block in input, cast it to view and process
|
|
480
|
+
if (take === blockLen) {
|
|
481
|
+
const dataView = createView$1(data);
|
|
482
|
+
for (; blockLen <= len - pos; pos += blockLen)
|
|
483
|
+
this.process(dataView, pos);
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|
487
|
+
this.pos += take;
|
|
488
|
+
pos += take;
|
|
489
|
+
if (this.pos === blockLen) {
|
|
490
|
+
this.process(view, 0);
|
|
491
|
+
this.pos = 0;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
this.length += data.length;
|
|
495
|
+
this.roundClean();
|
|
496
|
+
return this;
|
|
497
|
+
}
|
|
498
|
+
digestInto(out) {
|
|
499
|
+
aexists(this);
|
|
500
|
+
aoutput(out, this);
|
|
501
|
+
this.finished = true;
|
|
502
|
+
// Padding
|
|
503
|
+
// We can avoid allocation of buffer for padding completely if it
|
|
504
|
+
// was previously not allocated here. But it won't change performance.
|
|
505
|
+
const { buffer, view, blockLen, isLE } = this;
|
|
506
|
+
let { pos } = this;
|
|
507
|
+
// append the bit '1' to the message
|
|
508
|
+
buffer[pos++] = 0b10000000;
|
|
509
|
+
clean(this.buffer.subarray(pos));
|
|
510
|
+
// we have less than padOffset left in buffer, so we cannot put length in
|
|
511
|
+
// current block, need process it and pad again
|
|
512
|
+
if (this.padOffset > blockLen - pos) {
|
|
513
|
+
this.process(view, 0);
|
|
514
|
+
pos = 0;
|
|
515
|
+
}
|
|
516
|
+
// Pad until full block byte with zeros
|
|
517
|
+
for (let i = pos; i < blockLen; i++)
|
|
518
|
+
buffer[i] = 0;
|
|
519
|
+
// Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that
|
|
520
|
+
// You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.
|
|
521
|
+
// So we just write lowest 64 bits of that value.
|
|
522
|
+
setBigUint64$1(view, blockLen - 8, BigInt(this.length * 8), isLE);
|
|
523
|
+
this.process(view, 0);
|
|
524
|
+
const oview = createView$1(out);
|
|
525
|
+
const len = this.outputLen;
|
|
526
|
+
// NOTE: we do division by 4 later, which should be fused in single op with modulo by JIT
|
|
527
|
+
if (len % 4)
|
|
528
|
+
throw new Error('_sha2: outputLen should be aligned to 32bit');
|
|
529
|
+
const outLen = len / 4;
|
|
530
|
+
const state = this.get();
|
|
531
|
+
if (outLen > state.length)
|
|
532
|
+
throw new Error('_sha2: outputLen bigger than state');
|
|
533
|
+
for (let i = 0; i < outLen; i++)
|
|
534
|
+
oview.setUint32(4 * i, state[i], isLE);
|
|
535
|
+
}
|
|
536
|
+
digest() {
|
|
537
|
+
const { buffer, outputLen } = this;
|
|
538
|
+
this.digestInto(buffer);
|
|
539
|
+
const res = buffer.slice(0, outputLen);
|
|
540
|
+
this.destroy();
|
|
541
|
+
return res;
|
|
542
|
+
}
|
|
543
|
+
_cloneInto(to) {
|
|
544
|
+
to || (to = new this.constructor());
|
|
545
|
+
to.set(...this.get());
|
|
546
|
+
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
|
547
|
+
to.destroyed = destroyed;
|
|
548
|
+
to.finished = finished;
|
|
549
|
+
to.length = length;
|
|
550
|
+
to.pos = pos;
|
|
551
|
+
if (length % blockLen)
|
|
552
|
+
to.buffer.set(buffer);
|
|
553
|
+
return to;
|
|
554
|
+
}
|
|
555
|
+
clone() {
|
|
556
|
+
return this._cloneInto();
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.
|
|
561
|
+
* Check out `test/misc/sha2-gen-iv.js` for recomputation guide.
|
|
562
|
+
*/
|
|
563
|
+
/** Initial SHA256 state. Bits 0..32 of frac part of sqrt of primes 2..19 */
|
|
564
|
+
const SHA256_IV = /* @__PURE__ */ Uint32Array.from([
|
|
565
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
|
566
|
+
]);
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.
|
|
570
|
+
* SHA256 is the fastest hash implementable in JS, even faster than Blake3.
|
|
571
|
+
* Check out [RFC 4634](https://datatracker.ietf.org/doc/html/rfc4634) and
|
|
572
|
+
* [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
|
|
573
|
+
* @module
|
|
574
|
+
*/
|
|
575
|
+
/**
|
|
576
|
+
* Round constants:
|
|
577
|
+
* First 32 bits of fractional parts of the cube roots of the first 64 primes 2..311)
|
|
578
|
+
*/
|
|
579
|
+
// prettier-ignore
|
|
580
|
+
const SHA256_K = /* @__PURE__ */ Uint32Array.from([
|
|
581
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
582
|
+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
583
|
+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
584
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
585
|
+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
586
|
+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
587
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
588
|
+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
|
589
|
+
]);
|
|
590
|
+
/** Reusable temporary buffer. "W" comes straight from spec. */
|
|
591
|
+
const SHA256_W = /* @__PURE__ */ new Uint32Array(64);
|
|
592
|
+
class SHA256 extends HashMD {
|
|
593
|
+
constructor(outputLen = 32) {
|
|
594
|
+
super(64, outputLen, 8, false);
|
|
595
|
+
// We cannot use array here since array allows indexing by variable
|
|
596
|
+
// which means optimizer/compiler cannot use registers.
|
|
597
|
+
this.A = SHA256_IV[0] | 0;
|
|
598
|
+
this.B = SHA256_IV[1] | 0;
|
|
599
|
+
this.C = SHA256_IV[2] | 0;
|
|
600
|
+
this.D = SHA256_IV[3] | 0;
|
|
601
|
+
this.E = SHA256_IV[4] | 0;
|
|
602
|
+
this.F = SHA256_IV[5] | 0;
|
|
603
|
+
this.G = SHA256_IV[6] | 0;
|
|
604
|
+
this.H = SHA256_IV[7] | 0;
|
|
605
|
+
}
|
|
606
|
+
get() {
|
|
607
|
+
const { A, B, C, D, E, F, G, H } = this;
|
|
608
|
+
return [A, B, C, D, E, F, G, H];
|
|
609
|
+
}
|
|
610
|
+
// prettier-ignore
|
|
611
|
+
set(A, B, C, D, E, F, G, H) {
|
|
612
|
+
this.A = A | 0;
|
|
613
|
+
this.B = B | 0;
|
|
614
|
+
this.C = C | 0;
|
|
615
|
+
this.D = D | 0;
|
|
616
|
+
this.E = E | 0;
|
|
617
|
+
this.F = F | 0;
|
|
618
|
+
this.G = G | 0;
|
|
619
|
+
this.H = H | 0;
|
|
620
|
+
}
|
|
621
|
+
process(view, offset) {
|
|
622
|
+
// Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array
|
|
623
|
+
for (let i = 0; i < 16; i++, offset += 4)
|
|
624
|
+
SHA256_W[i] = view.getUint32(offset, false);
|
|
625
|
+
for (let i = 16; i < 64; i++) {
|
|
626
|
+
const W15 = SHA256_W[i - 15];
|
|
627
|
+
const W2 = SHA256_W[i - 2];
|
|
628
|
+
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);
|
|
629
|
+
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);
|
|
630
|
+
SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;
|
|
631
|
+
}
|
|
632
|
+
// Compression function main loop, 64 rounds
|
|
633
|
+
let { A, B, C, D, E, F, G, H } = this;
|
|
634
|
+
for (let i = 0; i < 64; i++) {
|
|
635
|
+
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
|
|
636
|
+
const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;
|
|
637
|
+
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
|
|
638
|
+
const T2 = (sigma0 + Maj(A, B, C)) | 0;
|
|
639
|
+
H = G;
|
|
640
|
+
G = F;
|
|
641
|
+
F = E;
|
|
642
|
+
E = (D + T1) | 0;
|
|
643
|
+
D = C;
|
|
644
|
+
C = B;
|
|
645
|
+
B = A;
|
|
646
|
+
A = (T1 + T2) | 0;
|
|
647
|
+
}
|
|
648
|
+
// Add the compressed chunk to the current hash value
|
|
649
|
+
A = (A + this.A) | 0;
|
|
650
|
+
B = (B + this.B) | 0;
|
|
651
|
+
C = (C + this.C) | 0;
|
|
652
|
+
D = (D + this.D) | 0;
|
|
653
|
+
E = (E + this.E) | 0;
|
|
654
|
+
F = (F + this.F) | 0;
|
|
655
|
+
G = (G + this.G) | 0;
|
|
656
|
+
H = (H + this.H) | 0;
|
|
657
|
+
this.set(A, B, C, D, E, F, G, H);
|
|
658
|
+
}
|
|
659
|
+
roundClean() {
|
|
660
|
+
clean(SHA256_W);
|
|
661
|
+
}
|
|
662
|
+
destroy() {
|
|
663
|
+
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
|
664
|
+
clean(this.buffer);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* SHA2-256 hash function from RFC 4634.
|
|
669
|
+
*
|
|
670
|
+
* It is the fastest JS hash, even faster than Blake3.
|
|
671
|
+
* To break sha256 using birthday attack, attackers need to try 2^128 hashes.
|
|
672
|
+
* BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.
|
|
673
|
+
*/
|
|
674
|
+
const sha256$1 = /* @__PURE__ */ createHasher(() => new SHA256());
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* SHA2-256 a.k.a. sha256. In JS, it is the fastest hash, even faster than Blake3.
|
|
678
|
+
*
|
|
679
|
+
* To break sha256 using birthday attack, attackers need to try 2^128 hashes.
|
|
680
|
+
* BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.
|
|
681
|
+
*
|
|
682
|
+
* Check out [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
|
|
683
|
+
* @module
|
|
684
|
+
* @deprecated
|
|
685
|
+
*/
|
|
686
|
+
/** @deprecated Use import from `noble/hashes/sha2` module */
|
|
687
|
+
const sha256 = sha256$1;
|
|
688
|
+
|
|
689
|
+
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
|
|
690
|
+
// Cast array to different type
|
|
691
|
+
const u32 = (arr) => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
|
692
|
+
function isBytes$1(a) {
|
|
693
|
+
return (a instanceof Uint8Array ||
|
|
694
|
+
(a != null && typeof a === 'object' && a.constructor.name === 'Uint8Array'));
|
|
695
|
+
}
|
|
696
|
+
// Cast array to view
|
|
697
|
+
const createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
698
|
+
// big-endian hardware is rare. Just in case someone still decides to run ciphers:
|
|
699
|
+
// early-throw an error because we don't support BE yet.
|
|
700
|
+
const isLE = new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44;
|
|
701
|
+
if (!isLE)
|
|
702
|
+
throw new Error('Non little-endian hardware is not supported');
|
|
703
|
+
/**
|
|
704
|
+
* @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])
|
|
705
|
+
*/
|
|
706
|
+
function utf8ToBytes(str) {
|
|
707
|
+
if (typeof str !== 'string')
|
|
708
|
+
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
|
709
|
+
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
|
|
710
|
+
}
|
|
711
|
+
function bytesToUtf8(bytes) {
|
|
712
|
+
return new TextDecoder().decode(bytes);
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
|
|
716
|
+
* Warning: when Uint8Array is passed, it would NOT get copied.
|
|
717
|
+
* Keep in mind for future mutable operations.
|
|
718
|
+
*/
|
|
719
|
+
function toBytes(data) {
|
|
720
|
+
if (typeof data === 'string')
|
|
721
|
+
data = utf8ToBytes(data);
|
|
722
|
+
else if (isBytes$1(data))
|
|
723
|
+
data = data.slice();
|
|
724
|
+
else
|
|
725
|
+
throw new Error(`expected Uint8Array, got ${typeof data}`);
|
|
726
|
+
return data;
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Copies several Uint8Arrays into one.
|
|
730
|
+
*/
|
|
731
|
+
function concatBytes(...arrays) {
|
|
732
|
+
let sum = 0;
|
|
733
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
734
|
+
const a = arrays[i];
|
|
735
|
+
if (!isBytes$1(a))
|
|
736
|
+
throw new Error('Uint8Array expected');
|
|
737
|
+
sum += a.length;
|
|
738
|
+
}
|
|
739
|
+
const res = new Uint8Array(sum);
|
|
740
|
+
for (let i = 0, pad = 0; i < arrays.length; i++) {
|
|
741
|
+
const a = arrays[i];
|
|
742
|
+
res.set(a, pad);
|
|
743
|
+
pad += a.length;
|
|
744
|
+
}
|
|
745
|
+
return res;
|
|
746
|
+
}
|
|
747
|
+
// Check if object doens't have custom constructor (like Uint8Array/Array)
|
|
748
|
+
const isPlainObject = (obj) => Object.prototype.toString.call(obj) === '[object Object]' && obj.constructor === Object;
|
|
749
|
+
function checkOpts(defaults, opts) {
|
|
750
|
+
if (opts !== undefined && (typeof opts !== 'object' || !isPlainObject(opts)))
|
|
751
|
+
throw new Error('options must be object or undefined');
|
|
752
|
+
const merged = Object.assign(defaults, opts);
|
|
753
|
+
return merged;
|
|
754
|
+
}
|
|
755
|
+
function ensureBytes(b, len) {
|
|
756
|
+
if (!isBytes$1(b))
|
|
757
|
+
throw new Error('Uint8Array expected');
|
|
758
|
+
if (typeof len === 'number')
|
|
759
|
+
if (b.length !== len)
|
|
760
|
+
throw new Error(`Uint8Array length ${len} expected`);
|
|
761
|
+
}
|
|
762
|
+
// Compares 2 u8a-s in kinda constant time
|
|
763
|
+
function equalBytes(a, b) {
|
|
764
|
+
if (a.length !== b.length)
|
|
765
|
+
return false;
|
|
766
|
+
let diff = 0;
|
|
767
|
+
for (let i = 0; i < a.length; i++)
|
|
768
|
+
diff |= a[i] ^ b[i];
|
|
769
|
+
return diff === 0;
|
|
770
|
+
}
|
|
771
|
+
const wrapCipher = (params, c) => {
|
|
772
|
+
Object.assign(c, params);
|
|
773
|
+
return c;
|
|
774
|
+
};
|
|
775
|
+
// Polyfill for Safari 14
|
|
776
|
+
function setBigUint64(view, byteOffset, value, isLE) {
|
|
777
|
+
if (typeof view.setBigUint64 === 'function')
|
|
778
|
+
return view.setBigUint64(byteOffset, value, isLE);
|
|
779
|
+
const _32n = BigInt(32);
|
|
780
|
+
const _u32_max = BigInt(0xffffffff);
|
|
781
|
+
const wh = Number((value >> _32n) & _u32_max);
|
|
782
|
+
const wl = Number(value & _u32_max);
|
|
783
|
+
const h = 4 ;
|
|
784
|
+
const l = 0 ;
|
|
785
|
+
view.setUint32(byteOffset + h, wh, isLE);
|
|
786
|
+
view.setUint32(byteOffset + l, wl, isLE);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
function number(n) {
|
|
790
|
+
if (!Number.isSafeInteger(n) || n < 0)
|
|
791
|
+
throw new Error(`wrong positive integer: ${n}`);
|
|
792
|
+
}
|
|
793
|
+
function bool(b) {
|
|
794
|
+
if (typeof b !== 'boolean')
|
|
795
|
+
throw new Error(`boolean expected, not ${b}`);
|
|
796
|
+
}
|
|
797
|
+
// TODO: merge with utils
|
|
798
|
+
function isBytes(a) {
|
|
799
|
+
return (a != null &&
|
|
800
|
+
typeof a === 'object' &&
|
|
801
|
+
(a instanceof Uint8Array || a.constructor.name === 'Uint8Array'));
|
|
802
|
+
}
|
|
803
|
+
function bytes(b, ...lengths) {
|
|
804
|
+
if (!isBytes(b))
|
|
805
|
+
throw new Error('Uint8Array expected');
|
|
806
|
+
if (lengths.length > 0 && !lengths.includes(b.length))
|
|
807
|
+
throw new Error(`Uint8Array expected of length ${lengths}, not of length=${b.length}`);
|
|
808
|
+
}
|
|
809
|
+
function exists(instance, checkFinished = true) {
|
|
810
|
+
if (instance.destroyed)
|
|
811
|
+
throw new Error('Hash instance has been destroyed');
|
|
812
|
+
if (checkFinished && instance.finished)
|
|
813
|
+
throw new Error('Hash#digest() has already been called');
|
|
814
|
+
}
|
|
815
|
+
function output(out, instance) {
|
|
816
|
+
bytes(out);
|
|
817
|
+
const min = instance.outputLen;
|
|
818
|
+
if (out.length < min) {
|
|
819
|
+
throw new Error(`digestInto() expects output buffer of length at least ${min}`);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// Poly1305 is a fast and parallel secret-key message-authentication code.
|
|
824
|
+
// https://cr.yp.to/mac.html, https://cr.yp.to/mac/poly1305-20050329.pdf
|
|
825
|
+
// https://datatracker.ietf.org/doc/html/rfc8439
|
|
826
|
+
// Based on Public Domain poly1305-donna https://github.com/floodyberry/poly1305-donna
|
|
827
|
+
const u8to16 = (a, i) => (a[i++] & 0xff) | ((a[i++] & 0xff) << 8);
|
|
828
|
+
class Poly1305 {
|
|
829
|
+
constructor(key) {
|
|
830
|
+
this.blockLen = 16;
|
|
831
|
+
this.outputLen = 16;
|
|
832
|
+
this.buffer = new Uint8Array(16);
|
|
833
|
+
this.r = new Uint16Array(10);
|
|
834
|
+
this.h = new Uint16Array(10);
|
|
835
|
+
this.pad = new Uint16Array(8);
|
|
836
|
+
this.pos = 0;
|
|
837
|
+
this.finished = false;
|
|
838
|
+
key = toBytes(key);
|
|
839
|
+
ensureBytes(key, 32);
|
|
840
|
+
const t0 = u8to16(key, 0);
|
|
841
|
+
const t1 = u8to16(key, 2);
|
|
842
|
+
const t2 = u8to16(key, 4);
|
|
843
|
+
const t3 = u8to16(key, 6);
|
|
844
|
+
const t4 = u8to16(key, 8);
|
|
845
|
+
const t5 = u8to16(key, 10);
|
|
846
|
+
const t6 = u8to16(key, 12);
|
|
847
|
+
const t7 = u8to16(key, 14);
|
|
848
|
+
// https://github.com/floodyberry/poly1305-donna/blob/e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781/poly1305-donna-16.h#L47
|
|
849
|
+
this.r[0] = t0 & 0x1fff;
|
|
850
|
+
this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
|
|
851
|
+
this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03;
|
|
852
|
+
this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
|
|
853
|
+
this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff;
|
|
854
|
+
this.r[5] = (t4 >>> 1) & 0x1ffe;
|
|
855
|
+
this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
|
|
856
|
+
this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81;
|
|
857
|
+
this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
|
|
858
|
+
this.r[9] = (t7 >>> 5) & 0x007f;
|
|
859
|
+
for (let i = 0; i < 8; i++)
|
|
860
|
+
this.pad[i] = u8to16(key, 16 + 2 * i);
|
|
861
|
+
}
|
|
862
|
+
process(data, offset, isLast = false) {
|
|
863
|
+
const hibit = isLast ? 0 : 1 << 11;
|
|
864
|
+
const { h, r } = this;
|
|
865
|
+
const r0 = r[0];
|
|
866
|
+
const r1 = r[1];
|
|
867
|
+
const r2 = r[2];
|
|
868
|
+
const r3 = r[3];
|
|
869
|
+
const r4 = r[4];
|
|
870
|
+
const r5 = r[5];
|
|
871
|
+
const r6 = r[6];
|
|
872
|
+
const r7 = r[7];
|
|
873
|
+
const r8 = r[8];
|
|
874
|
+
const r9 = r[9];
|
|
875
|
+
const t0 = u8to16(data, offset + 0);
|
|
876
|
+
const t1 = u8to16(data, offset + 2);
|
|
877
|
+
const t2 = u8to16(data, offset + 4);
|
|
878
|
+
const t3 = u8to16(data, offset + 6);
|
|
879
|
+
const t4 = u8to16(data, offset + 8);
|
|
880
|
+
const t5 = u8to16(data, offset + 10);
|
|
881
|
+
const t6 = u8to16(data, offset + 12);
|
|
882
|
+
const t7 = u8to16(data, offset + 14);
|
|
883
|
+
let h0 = h[0] + (t0 & 0x1fff);
|
|
884
|
+
let h1 = h[1] + (((t0 >>> 13) | (t1 << 3)) & 0x1fff);
|
|
885
|
+
let h2 = h[2] + (((t1 >>> 10) | (t2 << 6)) & 0x1fff);
|
|
886
|
+
let h3 = h[3] + (((t2 >>> 7) | (t3 << 9)) & 0x1fff);
|
|
887
|
+
let h4 = h[4] + (((t3 >>> 4) | (t4 << 12)) & 0x1fff);
|
|
888
|
+
let h5 = h[5] + ((t4 >>> 1) & 0x1fff);
|
|
889
|
+
let h6 = h[6] + (((t4 >>> 14) | (t5 << 2)) & 0x1fff);
|
|
890
|
+
let h7 = h[7] + (((t5 >>> 11) | (t6 << 5)) & 0x1fff);
|
|
891
|
+
let h8 = h[8] + (((t6 >>> 8) | (t7 << 8)) & 0x1fff);
|
|
892
|
+
let h9 = h[9] + ((t7 >>> 5) | hibit);
|
|
893
|
+
let c = 0;
|
|
894
|
+
let d0 = c + h0 * r0 + h1 * (5 * r9) + h2 * (5 * r8) + h3 * (5 * r7) + h4 * (5 * r6);
|
|
895
|
+
c = d0 >>> 13;
|
|
896
|
+
d0 &= 0x1fff;
|
|
897
|
+
d0 += h5 * (5 * r5) + h6 * (5 * r4) + h7 * (5 * r3) + h8 * (5 * r2) + h9 * (5 * r1);
|
|
898
|
+
c += d0 >>> 13;
|
|
899
|
+
d0 &= 0x1fff;
|
|
900
|
+
let d1 = c + h0 * r1 + h1 * r0 + h2 * (5 * r9) + h3 * (5 * r8) + h4 * (5 * r7);
|
|
901
|
+
c = d1 >>> 13;
|
|
902
|
+
d1 &= 0x1fff;
|
|
903
|
+
d1 += h5 * (5 * r6) + h6 * (5 * r5) + h7 * (5 * r4) + h8 * (5 * r3) + h9 * (5 * r2);
|
|
904
|
+
c += d1 >>> 13;
|
|
905
|
+
d1 &= 0x1fff;
|
|
906
|
+
let d2 = c + h0 * r2 + h1 * r1 + h2 * r0 + h3 * (5 * r9) + h4 * (5 * r8);
|
|
907
|
+
c = d2 >>> 13;
|
|
908
|
+
d2 &= 0x1fff;
|
|
909
|
+
d2 += h5 * (5 * r7) + h6 * (5 * r6) + h7 * (5 * r5) + h8 * (5 * r4) + h9 * (5 * r3);
|
|
910
|
+
c += d2 >>> 13;
|
|
911
|
+
d2 &= 0x1fff;
|
|
912
|
+
let d3 = c + h0 * r3 + h1 * r2 + h2 * r1 + h3 * r0 + h4 * (5 * r9);
|
|
913
|
+
c = d3 >>> 13;
|
|
914
|
+
d3 &= 0x1fff;
|
|
915
|
+
d3 += h5 * (5 * r8) + h6 * (5 * r7) + h7 * (5 * r6) + h8 * (5 * r5) + h9 * (5 * r4);
|
|
916
|
+
c += d3 >>> 13;
|
|
917
|
+
d3 &= 0x1fff;
|
|
918
|
+
let d4 = c + h0 * r4 + h1 * r3 + h2 * r2 + h3 * r1 + h4 * r0;
|
|
919
|
+
c = d4 >>> 13;
|
|
920
|
+
d4 &= 0x1fff;
|
|
921
|
+
d4 += h5 * (5 * r9) + h6 * (5 * r8) + h7 * (5 * r7) + h8 * (5 * r6) + h9 * (5 * r5);
|
|
922
|
+
c += d4 >>> 13;
|
|
923
|
+
d4 &= 0x1fff;
|
|
924
|
+
let d5 = c + h0 * r5 + h1 * r4 + h2 * r3 + h3 * r2 + h4 * r1;
|
|
925
|
+
c = d5 >>> 13;
|
|
926
|
+
d5 &= 0x1fff;
|
|
927
|
+
d5 += h5 * r0 + h6 * (5 * r9) + h7 * (5 * r8) + h8 * (5 * r7) + h9 * (5 * r6);
|
|
928
|
+
c += d5 >>> 13;
|
|
929
|
+
d5 &= 0x1fff;
|
|
930
|
+
let d6 = c + h0 * r6 + h1 * r5 + h2 * r4 + h3 * r3 + h4 * r2;
|
|
931
|
+
c = d6 >>> 13;
|
|
932
|
+
d6 &= 0x1fff;
|
|
933
|
+
d6 += h5 * r1 + h6 * r0 + h7 * (5 * r9) + h8 * (5 * r8) + h9 * (5 * r7);
|
|
934
|
+
c += d6 >>> 13;
|
|
935
|
+
d6 &= 0x1fff;
|
|
936
|
+
let d7 = c + h0 * r7 + h1 * r6 + h2 * r5 + h3 * r4 + h4 * r3;
|
|
937
|
+
c = d7 >>> 13;
|
|
938
|
+
d7 &= 0x1fff;
|
|
939
|
+
d7 += h5 * r2 + h6 * r1 + h7 * r0 + h8 * (5 * r9) + h9 * (5 * r8);
|
|
940
|
+
c += d7 >>> 13;
|
|
941
|
+
d7 &= 0x1fff;
|
|
942
|
+
let d8 = c + h0 * r8 + h1 * r7 + h2 * r6 + h3 * r5 + h4 * r4;
|
|
943
|
+
c = d8 >>> 13;
|
|
944
|
+
d8 &= 0x1fff;
|
|
945
|
+
d8 += h5 * r3 + h6 * r2 + h7 * r1 + h8 * r0 + h9 * (5 * r9);
|
|
946
|
+
c += d8 >>> 13;
|
|
947
|
+
d8 &= 0x1fff;
|
|
948
|
+
let d9 = c + h0 * r9 + h1 * r8 + h2 * r7 + h3 * r6 + h4 * r5;
|
|
949
|
+
c = d9 >>> 13;
|
|
950
|
+
d9 &= 0x1fff;
|
|
951
|
+
d9 += h5 * r4 + h6 * r3 + h7 * r2 + h8 * r1 + h9 * r0;
|
|
952
|
+
c += d9 >>> 13;
|
|
953
|
+
d9 &= 0x1fff;
|
|
954
|
+
c = ((c << 2) + c) | 0;
|
|
955
|
+
c = (c + d0) | 0;
|
|
956
|
+
d0 = c & 0x1fff;
|
|
957
|
+
c = c >>> 13;
|
|
958
|
+
d1 += c;
|
|
959
|
+
h[0] = d0;
|
|
960
|
+
h[1] = d1;
|
|
961
|
+
h[2] = d2;
|
|
962
|
+
h[3] = d3;
|
|
963
|
+
h[4] = d4;
|
|
964
|
+
h[5] = d5;
|
|
965
|
+
h[6] = d6;
|
|
966
|
+
h[7] = d7;
|
|
967
|
+
h[8] = d8;
|
|
968
|
+
h[9] = d9;
|
|
969
|
+
}
|
|
970
|
+
finalize() {
|
|
971
|
+
const { h, pad } = this;
|
|
972
|
+
const g = new Uint16Array(10);
|
|
973
|
+
let c = h[1] >>> 13;
|
|
974
|
+
h[1] &= 0x1fff;
|
|
975
|
+
for (let i = 2; i < 10; i++) {
|
|
976
|
+
h[i] += c;
|
|
977
|
+
c = h[i] >>> 13;
|
|
978
|
+
h[i] &= 0x1fff;
|
|
979
|
+
}
|
|
980
|
+
h[0] += c * 5;
|
|
981
|
+
c = h[0] >>> 13;
|
|
982
|
+
h[0] &= 0x1fff;
|
|
983
|
+
h[1] += c;
|
|
984
|
+
c = h[1] >>> 13;
|
|
985
|
+
h[1] &= 0x1fff;
|
|
986
|
+
h[2] += c;
|
|
987
|
+
g[0] = h[0] + 5;
|
|
988
|
+
c = g[0] >>> 13;
|
|
989
|
+
g[0] &= 0x1fff;
|
|
990
|
+
for (let i = 1; i < 10; i++) {
|
|
991
|
+
g[i] = h[i] + c;
|
|
992
|
+
c = g[i] >>> 13;
|
|
993
|
+
g[i] &= 0x1fff;
|
|
994
|
+
}
|
|
995
|
+
g[9] -= 1 << 13;
|
|
996
|
+
let mask = (c ^ 1) - 1;
|
|
997
|
+
for (let i = 0; i < 10; i++)
|
|
998
|
+
g[i] &= mask;
|
|
999
|
+
mask = ~mask;
|
|
1000
|
+
for (let i = 0; i < 10; i++)
|
|
1001
|
+
h[i] = (h[i] & mask) | g[i];
|
|
1002
|
+
h[0] = (h[0] | (h[1] << 13)) & 0xffff;
|
|
1003
|
+
h[1] = ((h[1] >>> 3) | (h[2] << 10)) & 0xffff;
|
|
1004
|
+
h[2] = ((h[2] >>> 6) | (h[3] << 7)) & 0xffff;
|
|
1005
|
+
h[3] = ((h[3] >>> 9) | (h[4] << 4)) & 0xffff;
|
|
1006
|
+
h[4] = ((h[4] >>> 12) | (h[5] << 1) | (h[6] << 14)) & 0xffff;
|
|
1007
|
+
h[5] = ((h[6] >>> 2) | (h[7] << 11)) & 0xffff;
|
|
1008
|
+
h[6] = ((h[7] >>> 5) | (h[8] << 8)) & 0xffff;
|
|
1009
|
+
h[7] = ((h[8] >>> 8) | (h[9] << 5)) & 0xffff;
|
|
1010
|
+
let f = h[0] + pad[0];
|
|
1011
|
+
h[0] = f & 0xffff;
|
|
1012
|
+
for (let i = 1; i < 8; i++) {
|
|
1013
|
+
f = (((h[i] + pad[i]) | 0) + (f >>> 16)) | 0;
|
|
1014
|
+
h[i] = f & 0xffff;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
update(data) {
|
|
1018
|
+
exists(this);
|
|
1019
|
+
const { buffer, blockLen } = this;
|
|
1020
|
+
data = toBytes(data);
|
|
1021
|
+
const len = data.length;
|
|
1022
|
+
for (let pos = 0; pos < len;) {
|
|
1023
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
1024
|
+
// Fast path: we have at least one block in input
|
|
1025
|
+
if (take === blockLen) {
|
|
1026
|
+
for (; blockLen <= len - pos; pos += blockLen)
|
|
1027
|
+
this.process(data, pos);
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|
1031
|
+
this.pos += take;
|
|
1032
|
+
pos += take;
|
|
1033
|
+
if (this.pos === blockLen) {
|
|
1034
|
+
this.process(buffer, 0, false);
|
|
1035
|
+
this.pos = 0;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
return this;
|
|
1039
|
+
}
|
|
1040
|
+
destroy() {
|
|
1041
|
+
this.h.fill(0);
|
|
1042
|
+
this.r.fill(0);
|
|
1043
|
+
this.buffer.fill(0);
|
|
1044
|
+
this.pad.fill(0);
|
|
1045
|
+
}
|
|
1046
|
+
digestInto(out) {
|
|
1047
|
+
exists(this);
|
|
1048
|
+
output(out, this);
|
|
1049
|
+
this.finished = true;
|
|
1050
|
+
const { buffer, h } = this;
|
|
1051
|
+
let { pos } = this;
|
|
1052
|
+
if (pos) {
|
|
1053
|
+
buffer[pos++] = 1;
|
|
1054
|
+
// buffer.subarray(pos).fill(0);
|
|
1055
|
+
for (; pos < 16; pos++)
|
|
1056
|
+
buffer[pos] = 0;
|
|
1057
|
+
this.process(buffer, 0, true);
|
|
1058
|
+
}
|
|
1059
|
+
this.finalize();
|
|
1060
|
+
let opos = 0;
|
|
1061
|
+
for (let i = 0; i < 8; i++) {
|
|
1062
|
+
out[opos++] = h[i] >>> 0;
|
|
1063
|
+
out[opos++] = h[i] >>> 8;
|
|
1064
|
+
}
|
|
1065
|
+
return out;
|
|
1066
|
+
}
|
|
1067
|
+
digest() {
|
|
1068
|
+
const { buffer, outputLen } = this;
|
|
1069
|
+
this.digestInto(buffer);
|
|
1070
|
+
const res = buffer.slice(0, outputLen);
|
|
1071
|
+
this.destroy();
|
|
1072
|
+
return res;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
function wrapConstructorWithKey(hashCons) {
|
|
1076
|
+
const hashC = (msg, key) => hashCons(key).update(toBytes(msg)).digest();
|
|
1077
|
+
const tmp = hashCons(new Uint8Array(32));
|
|
1078
|
+
hashC.outputLen = tmp.outputLen;
|
|
1079
|
+
hashC.blockLen = tmp.blockLen;
|
|
1080
|
+
hashC.create = (key) => hashCons(key);
|
|
1081
|
+
return hashC;
|
|
1082
|
+
}
|
|
1083
|
+
const poly1305 = wrapConstructorWithKey((key) => new Poly1305(key));
|
|
1084
|
+
|
|
1085
|
+
// Basic utils for ARX (add-rotate-xor) salsa and chacha ciphers.
|
|
1086
|
+
/*
|
|
1087
|
+
RFC8439 requires multi-step cipher stream, where
|
|
1088
|
+
authKey starts with counter: 0, actual msg with counter: 1.
|
|
1089
|
+
|
|
1090
|
+
For this, we need a way to re-use nonce / counter:
|
|
1091
|
+
|
|
1092
|
+
const counter = new Uint8Array(4);
|
|
1093
|
+
chacha(..., counter, ...); // counter is now 1
|
|
1094
|
+
chacha(..., counter, ...); // counter is now 2
|
|
1095
|
+
|
|
1096
|
+
This is complicated:
|
|
1097
|
+
|
|
1098
|
+
- 32-bit counters are enough, no need for 64-bit: max ArrayBuffer size in JS is 4GB
|
|
1099
|
+
- Original papers don't allow mutating counters
|
|
1100
|
+
- Counter overflow is undefined [^1]
|
|
1101
|
+
- Idea A: allow providing (nonce | counter) instead of just nonce, re-use it
|
|
1102
|
+
- Caveat: Cannot be re-used through all cases:
|
|
1103
|
+
- * chacha has (counter | nonce)
|
|
1104
|
+
- * xchacha has (nonce16 | counter | nonce16)
|
|
1105
|
+
- Idea B: separate nonce / counter and provide separate API for counter re-use
|
|
1106
|
+
- Caveat: there are different counter sizes depending on an algorithm.
|
|
1107
|
+
- salsa & chacha also differ in structures of key & sigma:
|
|
1108
|
+
salsa20: s[0] | k(4) | s[1] | nonce(2) | ctr(2) | s[2] | k(4) | s[3]
|
|
1109
|
+
chacha: s(4) | k(8) | ctr(1) | nonce(3)
|
|
1110
|
+
chacha20orig: s(4) | k(8) | ctr(2) | nonce(2)
|
|
1111
|
+
- Idea C: helper method such as `setSalsaState(key, nonce, sigma, data)`
|
|
1112
|
+
- Caveat: we can't re-use counter array
|
|
1113
|
+
|
|
1114
|
+
xchacha [^2] uses the subkey and remaining 8 byte nonce with ChaCha20 as normal
|
|
1115
|
+
(prefixed by 4 NUL bytes, since [RFC8439] specifies a 12-byte nonce).
|
|
1116
|
+
|
|
1117
|
+
[^1]: https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU/
|
|
1118
|
+
[^2]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#appendix-A.2
|
|
1119
|
+
*/
|
|
1120
|
+
const sigma16 = utf8ToBytes('expand 16-byte k');
|
|
1121
|
+
const sigma32 = utf8ToBytes('expand 32-byte k');
|
|
1122
|
+
const sigma16_32 = u32(sigma16);
|
|
1123
|
+
const sigma32_32 = u32(sigma32);
|
|
1124
|
+
function rotl(a, b) {
|
|
1125
|
+
return (a << b) | (a >>> (32 - b));
|
|
1126
|
+
}
|
|
1127
|
+
// Is byte array aligned to 4 byte offset (u32)?
|
|
1128
|
+
function isAligned32(b) {
|
|
1129
|
+
return b.byteOffset % 4 === 0;
|
|
1130
|
+
}
|
|
1131
|
+
// Salsa and Chacha block length is always 512-bit
|
|
1132
|
+
const BLOCK_LEN = 64;
|
|
1133
|
+
const BLOCK_LEN32 = 16;
|
|
1134
|
+
// new Uint32Array([2**32]) // => Uint32Array(1) [ 0 ]
|
|
1135
|
+
// new Uint32Array([2**32-1]) // => Uint32Array(1) [ 4294967295 ]
|
|
1136
|
+
const MAX_COUNTER = 2 ** 32 - 1;
|
|
1137
|
+
const U32_EMPTY = new Uint32Array();
|
|
1138
|
+
function runCipher(core, sigma, key, nonce, data, output, counter, rounds) {
|
|
1139
|
+
const len = data.length;
|
|
1140
|
+
const block = new Uint8Array(BLOCK_LEN);
|
|
1141
|
+
const b32 = u32(block);
|
|
1142
|
+
// Make sure that buffers aligned to 4 bytes
|
|
1143
|
+
const isAligned = isAligned32(data) && isAligned32(output);
|
|
1144
|
+
const d32 = isAligned ? u32(data) : U32_EMPTY;
|
|
1145
|
+
const o32 = isAligned ? u32(output) : U32_EMPTY;
|
|
1146
|
+
for (let pos = 0; pos < len; counter++) {
|
|
1147
|
+
core(sigma, key, nonce, b32, counter, rounds);
|
|
1148
|
+
if (counter >= MAX_COUNTER)
|
|
1149
|
+
throw new Error('arx: counter overflow');
|
|
1150
|
+
const take = Math.min(BLOCK_LEN, len - pos);
|
|
1151
|
+
// aligned to 4 bytes
|
|
1152
|
+
if (isAligned && take === BLOCK_LEN) {
|
|
1153
|
+
const pos32 = pos / 4;
|
|
1154
|
+
if (pos % 4 !== 0)
|
|
1155
|
+
throw new Error('arx: invalid block position');
|
|
1156
|
+
for (let j = 0, posj; j < BLOCK_LEN32; j++) {
|
|
1157
|
+
posj = pos32 + j;
|
|
1158
|
+
o32[posj] = d32[posj] ^ b32[j];
|
|
1159
|
+
}
|
|
1160
|
+
pos += BLOCK_LEN;
|
|
1161
|
+
continue;
|
|
1162
|
+
}
|
|
1163
|
+
for (let j = 0, posj; j < take; j++) {
|
|
1164
|
+
posj = pos + j;
|
|
1165
|
+
output[posj] = data[posj] ^ block[j];
|
|
1166
|
+
}
|
|
1167
|
+
pos += take;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
function createCipher(core, opts) {
|
|
1171
|
+
const { allowShortKeys, extendNonceFn, counterLength, counterRight, rounds } = checkOpts({ allowShortKeys: false, counterLength: 8, counterRight: false, rounds: 20 }, opts);
|
|
1172
|
+
if (typeof core !== 'function')
|
|
1173
|
+
throw new Error('core must be a function');
|
|
1174
|
+
number(counterLength);
|
|
1175
|
+
number(rounds);
|
|
1176
|
+
bool(counterRight);
|
|
1177
|
+
bool(allowShortKeys);
|
|
1178
|
+
return (key, nonce, data, output, counter = 0) => {
|
|
1179
|
+
bytes(key);
|
|
1180
|
+
bytes(nonce);
|
|
1181
|
+
bytes(data);
|
|
1182
|
+
const len = data.length;
|
|
1183
|
+
if (!output)
|
|
1184
|
+
output = new Uint8Array(len);
|
|
1185
|
+
bytes(output);
|
|
1186
|
+
number(counter);
|
|
1187
|
+
if (counter < 0 || counter >= MAX_COUNTER)
|
|
1188
|
+
throw new Error('arx: counter overflow');
|
|
1189
|
+
if (output.length < len)
|
|
1190
|
+
throw new Error(`arx: output (${output.length}) is shorter than data (${len})`);
|
|
1191
|
+
const toClean = [];
|
|
1192
|
+
// Key & sigma
|
|
1193
|
+
// key=16 -> sigma16, k=key|key
|
|
1194
|
+
// key=32 -> sigma32, k=key
|
|
1195
|
+
let l = key.length, k, sigma;
|
|
1196
|
+
if (l === 32) {
|
|
1197
|
+
k = key.slice();
|
|
1198
|
+
toClean.push(k);
|
|
1199
|
+
sigma = sigma32_32;
|
|
1200
|
+
}
|
|
1201
|
+
else if (l === 16 && allowShortKeys) {
|
|
1202
|
+
k = new Uint8Array(32);
|
|
1203
|
+
k.set(key);
|
|
1204
|
+
k.set(key, 16);
|
|
1205
|
+
sigma = sigma16_32;
|
|
1206
|
+
toClean.push(k);
|
|
1207
|
+
}
|
|
1208
|
+
else {
|
|
1209
|
+
throw new Error(`arx: invalid 32-byte key, got length=${l}`);
|
|
1210
|
+
}
|
|
1211
|
+
// Nonce
|
|
1212
|
+
// salsa20: 8 (8-byte counter)
|
|
1213
|
+
// chacha20orig: 8 (8-byte counter)
|
|
1214
|
+
// chacha20: 12 (4-byte counter)
|
|
1215
|
+
// xsalsa20: 24 (16 -> hsalsa, 8 -> old nonce)
|
|
1216
|
+
// xchacha20: 24 (16 -> hchacha, 8 -> old nonce)
|
|
1217
|
+
// Align nonce to 4 bytes
|
|
1218
|
+
if (!isAligned32(nonce)) {
|
|
1219
|
+
nonce = nonce.slice();
|
|
1220
|
+
toClean.push(nonce);
|
|
1221
|
+
}
|
|
1222
|
+
const k32 = u32(k);
|
|
1223
|
+
// hsalsa & hchacha: handle extended nonce
|
|
1224
|
+
if (extendNonceFn) {
|
|
1225
|
+
if (nonce.length !== 24)
|
|
1226
|
+
throw new Error(`arx: extended nonce must be 24 bytes`);
|
|
1227
|
+
extendNonceFn(sigma, k32, u32(nonce.subarray(0, 16)), k32);
|
|
1228
|
+
nonce = nonce.subarray(16);
|
|
1229
|
+
}
|
|
1230
|
+
// Handle nonce counter
|
|
1231
|
+
const nonceNcLen = 16 - counterLength;
|
|
1232
|
+
if (nonceNcLen !== nonce.length)
|
|
1233
|
+
throw new Error(`arx: nonce must be ${nonceNcLen} or 16 bytes`);
|
|
1234
|
+
// Pad counter when nonce is 64 bit
|
|
1235
|
+
if (nonceNcLen !== 12) {
|
|
1236
|
+
const nc = new Uint8Array(12);
|
|
1237
|
+
nc.set(nonce, counterRight ? 0 : 12 - nonce.length);
|
|
1238
|
+
nonce = nc;
|
|
1239
|
+
toClean.push(nonce);
|
|
1240
|
+
}
|
|
1241
|
+
const n32 = u32(nonce);
|
|
1242
|
+
runCipher(core, sigma, k32, n32, data, output, counter, rounds);
|
|
1243
|
+
while (toClean.length > 0)
|
|
1244
|
+
toClean.pop().fill(0);
|
|
1245
|
+
return output;
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// ChaCha20 stream cipher was released in 2008. ChaCha aims to increase
|
|
1250
|
+
// the diffusion per round, but had slightly less cryptanalysis.
|
|
1251
|
+
// https://cr.yp.to/chacha.html, http://cr.yp.to/chacha/chacha-20080128.pdf
|
|
1252
|
+
/**
|
|
1253
|
+
* ChaCha core function.
|
|
1254
|
+
*/
|
|
1255
|
+
// prettier-ignore
|
|
1256
|
+
function chachaCore(s, k, n, out, cnt, rounds = 20) {
|
|
1257
|
+
let y00 = s[0], y01 = s[1], y02 = s[2], y03 = s[3], // "expa" "nd 3" "2-by" "te k"
|
|
1258
|
+
y04 = k[0], y05 = k[1], y06 = k[2], y07 = k[3], // Key Key Key Key
|
|
1259
|
+
y08 = k[4], y09 = k[5], y10 = k[6], y11 = k[7], // Key Key Key Key
|
|
1260
|
+
y12 = cnt, y13 = n[0], y14 = n[1], y15 = n[2]; // Counter Counter Nonce Nonce
|
|
1261
|
+
// Save state to temporary variables
|
|
1262
|
+
let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15;
|
|
1263
|
+
for (let r = 0; r < rounds; r += 2) {
|
|
1264
|
+
x00 = (x00 + x04) | 0;
|
|
1265
|
+
x12 = rotl(x12 ^ x00, 16);
|
|
1266
|
+
x08 = (x08 + x12) | 0;
|
|
1267
|
+
x04 = rotl(x04 ^ x08, 12);
|
|
1268
|
+
x00 = (x00 + x04) | 0;
|
|
1269
|
+
x12 = rotl(x12 ^ x00, 8);
|
|
1270
|
+
x08 = (x08 + x12) | 0;
|
|
1271
|
+
x04 = rotl(x04 ^ x08, 7);
|
|
1272
|
+
x01 = (x01 + x05) | 0;
|
|
1273
|
+
x13 = rotl(x13 ^ x01, 16);
|
|
1274
|
+
x09 = (x09 + x13) | 0;
|
|
1275
|
+
x05 = rotl(x05 ^ x09, 12);
|
|
1276
|
+
x01 = (x01 + x05) | 0;
|
|
1277
|
+
x13 = rotl(x13 ^ x01, 8);
|
|
1278
|
+
x09 = (x09 + x13) | 0;
|
|
1279
|
+
x05 = rotl(x05 ^ x09, 7);
|
|
1280
|
+
x02 = (x02 + x06) | 0;
|
|
1281
|
+
x14 = rotl(x14 ^ x02, 16);
|
|
1282
|
+
x10 = (x10 + x14) | 0;
|
|
1283
|
+
x06 = rotl(x06 ^ x10, 12);
|
|
1284
|
+
x02 = (x02 + x06) | 0;
|
|
1285
|
+
x14 = rotl(x14 ^ x02, 8);
|
|
1286
|
+
x10 = (x10 + x14) | 0;
|
|
1287
|
+
x06 = rotl(x06 ^ x10, 7);
|
|
1288
|
+
x03 = (x03 + x07) | 0;
|
|
1289
|
+
x15 = rotl(x15 ^ x03, 16);
|
|
1290
|
+
x11 = (x11 + x15) | 0;
|
|
1291
|
+
x07 = rotl(x07 ^ x11, 12);
|
|
1292
|
+
x03 = (x03 + x07) | 0;
|
|
1293
|
+
x15 = rotl(x15 ^ x03, 8);
|
|
1294
|
+
x11 = (x11 + x15) | 0;
|
|
1295
|
+
x07 = rotl(x07 ^ x11, 7);
|
|
1296
|
+
x00 = (x00 + x05) | 0;
|
|
1297
|
+
x15 = rotl(x15 ^ x00, 16);
|
|
1298
|
+
x10 = (x10 + x15) | 0;
|
|
1299
|
+
x05 = rotl(x05 ^ x10, 12);
|
|
1300
|
+
x00 = (x00 + x05) | 0;
|
|
1301
|
+
x15 = rotl(x15 ^ x00, 8);
|
|
1302
|
+
x10 = (x10 + x15) | 0;
|
|
1303
|
+
x05 = rotl(x05 ^ x10, 7);
|
|
1304
|
+
x01 = (x01 + x06) | 0;
|
|
1305
|
+
x12 = rotl(x12 ^ x01, 16);
|
|
1306
|
+
x11 = (x11 + x12) | 0;
|
|
1307
|
+
x06 = rotl(x06 ^ x11, 12);
|
|
1308
|
+
x01 = (x01 + x06) | 0;
|
|
1309
|
+
x12 = rotl(x12 ^ x01, 8);
|
|
1310
|
+
x11 = (x11 + x12) | 0;
|
|
1311
|
+
x06 = rotl(x06 ^ x11, 7);
|
|
1312
|
+
x02 = (x02 + x07) | 0;
|
|
1313
|
+
x13 = rotl(x13 ^ x02, 16);
|
|
1314
|
+
x08 = (x08 + x13) | 0;
|
|
1315
|
+
x07 = rotl(x07 ^ x08, 12);
|
|
1316
|
+
x02 = (x02 + x07) | 0;
|
|
1317
|
+
x13 = rotl(x13 ^ x02, 8);
|
|
1318
|
+
x08 = (x08 + x13) | 0;
|
|
1319
|
+
x07 = rotl(x07 ^ x08, 7);
|
|
1320
|
+
x03 = (x03 + x04) | 0;
|
|
1321
|
+
x14 = rotl(x14 ^ x03, 16);
|
|
1322
|
+
x09 = (x09 + x14) | 0;
|
|
1323
|
+
x04 = rotl(x04 ^ x09, 12);
|
|
1324
|
+
x03 = (x03 + x04) | 0;
|
|
1325
|
+
x14 = rotl(x14 ^ x03, 8);
|
|
1326
|
+
x09 = (x09 + x14) | 0;
|
|
1327
|
+
x04 = rotl(x04 ^ x09, 7);
|
|
1328
|
+
}
|
|
1329
|
+
// Write output
|
|
1330
|
+
let oi = 0;
|
|
1331
|
+
out[oi++] = (y00 + x00) | 0;
|
|
1332
|
+
out[oi++] = (y01 + x01) | 0;
|
|
1333
|
+
out[oi++] = (y02 + x02) | 0;
|
|
1334
|
+
out[oi++] = (y03 + x03) | 0;
|
|
1335
|
+
out[oi++] = (y04 + x04) | 0;
|
|
1336
|
+
out[oi++] = (y05 + x05) | 0;
|
|
1337
|
+
out[oi++] = (y06 + x06) | 0;
|
|
1338
|
+
out[oi++] = (y07 + x07) | 0;
|
|
1339
|
+
out[oi++] = (y08 + x08) | 0;
|
|
1340
|
+
out[oi++] = (y09 + x09) | 0;
|
|
1341
|
+
out[oi++] = (y10 + x10) | 0;
|
|
1342
|
+
out[oi++] = (y11 + x11) | 0;
|
|
1343
|
+
out[oi++] = (y12 + x12) | 0;
|
|
1344
|
+
out[oi++] = (y13 + x13) | 0;
|
|
1345
|
+
out[oi++] = (y14 + x14) | 0;
|
|
1346
|
+
out[oi++] = (y15 + x15) | 0;
|
|
1347
|
+
}
|
|
1348
|
+
/**
|
|
1349
|
+
* hchacha helper method, used primarily in xchacha, to hash
|
|
1350
|
+
* key and nonce into key' and nonce'.
|
|
1351
|
+
* Same as chachaCore, but there doesn't seem to be a way to move the block
|
|
1352
|
+
* out without 25% performance hit.
|
|
1353
|
+
*/
|
|
1354
|
+
// prettier-ignore
|
|
1355
|
+
function hchacha(s, k, i, o32) {
|
|
1356
|
+
let x00 = s[0], x01 = s[1], x02 = s[2], x03 = s[3], x04 = k[0], x05 = k[1], x06 = k[2], x07 = k[3], x08 = k[4], x09 = k[5], x10 = k[6], x11 = k[7], x12 = i[0], x13 = i[1], x14 = i[2], x15 = i[3];
|
|
1357
|
+
for (let r = 0; r < 20; r += 2) {
|
|
1358
|
+
x00 = (x00 + x04) | 0;
|
|
1359
|
+
x12 = rotl(x12 ^ x00, 16);
|
|
1360
|
+
x08 = (x08 + x12) | 0;
|
|
1361
|
+
x04 = rotl(x04 ^ x08, 12);
|
|
1362
|
+
x00 = (x00 + x04) | 0;
|
|
1363
|
+
x12 = rotl(x12 ^ x00, 8);
|
|
1364
|
+
x08 = (x08 + x12) | 0;
|
|
1365
|
+
x04 = rotl(x04 ^ x08, 7);
|
|
1366
|
+
x01 = (x01 + x05) | 0;
|
|
1367
|
+
x13 = rotl(x13 ^ x01, 16);
|
|
1368
|
+
x09 = (x09 + x13) | 0;
|
|
1369
|
+
x05 = rotl(x05 ^ x09, 12);
|
|
1370
|
+
x01 = (x01 + x05) | 0;
|
|
1371
|
+
x13 = rotl(x13 ^ x01, 8);
|
|
1372
|
+
x09 = (x09 + x13) | 0;
|
|
1373
|
+
x05 = rotl(x05 ^ x09, 7);
|
|
1374
|
+
x02 = (x02 + x06) | 0;
|
|
1375
|
+
x14 = rotl(x14 ^ x02, 16);
|
|
1376
|
+
x10 = (x10 + x14) | 0;
|
|
1377
|
+
x06 = rotl(x06 ^ x10, 12);
|
|
1378
|
+
x02 = (x02 + x06) | 0;
|
|
1379
|
+
x14 = rotl(x14 ^ x02, 8);
|
|
1380
|
+
x10 = (x10 + x14) | 0;
|
|
1381
|
+
x06 = rotl(x06 ^ x10, 7);
|
|
1382
|
+
x03 = (x03 + x07) | 0;
|
|
1383
|
+
x15 = rotl(x15 ^ x03, 16);
|
|
1384
|
+
x11 = (x11 + x15) | 0;
|
|
1385
|
+
x07 = rotl(x07 ^ x11, 12);
|
|
1386
|
+
x03 = (x03 + x07) | 0;
|
|
1387
|
+
x15 = rotl(x15 ^ x03, 8);
|
|
1388
|
+
x11 = (x11 + x15) | 0;
|
|
1389
|
+
x07 = rotl(x07 ^ x11, 7);
|
|
1390
|
+
x00 = (x00 + x05) | 0;
|
|
1391
|
+
x15 = rotl(x15 ^ x00, 16);
|
|
1392
|
+
x10 = (x10 + x15) | 0;
|
|
1393
|
+
x05 = rotl(x05 ^ x10, 12);
|
|
1394
|
+
x00 = (x00 + x05) | 0;
|
|
1395
|
+
x15 = rotl(x15 ^ x00, 8);
|
|
1396
|
+
x10 = (x10 + x15) | 0;
|
|
1397
|
+
x05 = rotl(x05 ^ x10, 7);
|
|
1398
|
+
x01 = (x01 + x06) | 0;
|
|
1399
|
+
x12 = rotl(x12 ^ x01, 16);
|
|
1400
|
+
x11 = (x11 + x12) | 0;
|
|
1401
|
+
x06 = rotl(x06 ^ x11, 12);
|
|
1402
|
+
x01 = (x01 + x06) | 0;
|
|
1403
|
+
x12 = rotl(x12 ^ x01, 8);
|
|
1404
|
+
x11 = (x11 + x12) | 0;
|
|
1405
|
+
x06 = rotl(x06 ^ x11, 7);
|
|
1406
|
+
x02 = (x02 + x07) | 0;
|
|
1407
|
+
x13 = rotl(x13 ^ x02, 16);
|
|
1408
|
+
x08 = (x08 + x13) | 0;
|
|
1409
|
+
x07 = rotl(x07 ^ x08, 12);
|
|
1410
|
+
x02 = (x02 + x07) | 0;
|
|
1411
|
+
x13 = rotl(x13 ^ x02, 8);
|
|
1412
|
+
x08 = (x08 + x13) | 0;
|
|
1413
|
+
x07 = rotl(x07 ^ x08, 7);
|
|
1414
|
+
x03 = (x03 + x04) | 0;
|
|
1415
|
+
x14 = rotl(x14 ^ x03, 16);
|
|
1416
|
+
x09 = (x09 + x14) | 0;
|
|
1417
|
+
x04 = rotl(x04 ^ x09, 12);
|
|
1418
|
+
x03 = (x03 + x04) | 0;
|
|
1419
|
+
x14 = rotl(x14 ^ x03, 8);
|
|
1420
|
+
x09 = (x09 + x14) | 0;
|
|
1421
|
+
x04 = rotl(x04 ^ x09, 7);
|
|
1422
|
+
}
|
|
1423
|
+
let oi = 0;
|
|
1424
|
+
o32[oi++] = x00;
|
|
1425
|
+
o32[oi++] = x01;
|
|
1426
|
+
o32[oi++] = x02;
|
|
1427
|
+
o32[oi++] = x03;
|
|
1428
|
+
o32[oi++] = x12;
|
|
1429
|
+
o32[oi++] = x13;
|
|
1430
|
+
o32[oi++] = x14;
|
|
1431
|
+
o32[oi++] = x15;
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* XChaCha eXtended-nonce ChaCha. 24-byte nonce.
|
|
1435
|
+
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
|
1436
|
+
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
|
1437
|
+
*/
|
|
1438
|
+
const xchacha20 = /* @__PURE__ */ createCipher(chachaCore, {
|
|
1439
|
+
counterRight: false,
|
|
1440
|
+
counterLength: 8,
|
|
1441
|
+
extendNonceFn: hchacha,
|
|
1442
|
+
allowShortKeys: false,
|
|
1443
|
+
});
|
|
1444
|
+
const ZEROS16 = /* @__PURE__ */ new Uint8Array(16);
|
|
1445
|
+
// Pad to digest size with zeros
|
|
1446
|
+
const updatePadded = (h, msg) => {
|
|
1447
|
+
h.update(msg);
|
|
1448
|
+
const left = msg.length % 16;
|
|
1449
|
+
if (left)
|
|
1450
|
+
h.update(ZEROS16.subarray(left));
|
|
1451
|
+
};
|
|
1452
|
+
const ZEROS32 = /* @__PURE__ */ new Uint8Array(32);
|
|
1453
|
+
function computeTag(fn, key, nonce, data, AAD) {
|
|
1454
|
+
const authKey = fn(key, nonce, ZEROS32);
|
|
1455
|
+
const h = poly1305.create(authKey);
|
|
1456
|
+
if (AAD)
|
|
1457
|
+
updatePadded(h, AAD);
|
|
1458
|
+
updatePadded(h, data);
|
|
1459
|
+
const num = new Uint8Array(16);
|
|
1460
|
+
const view = createView(num);
|
|
1461
|
+
setBigUint64(view, 0, BigInt(AAD ? AAD.length : 0), true);
|
|
1462
|
+
setBigUint64(view, 8, BigInt(data.length), true);
|
|
1463
|
+
h.update(num);
|
|
1464
|
+
const res = h.digest();
|
|
1465
|
+
authKey.fill(0);
|
|
1466
|
+
return res;
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* AEAD algorithm from RFC 8439.
|
|
1470
|
+
* Salsa20 and chacha (RFC 8439) use poly1305 differently.
|
|
1471
|
+
* We could have composed them similar to:
|
|
1472
|
+
* https://github.com/paulmillr/scure-base/blob/b266c73dde977b1dd7ef40ef7a23cc15aab526b3/index.ts#L250
|
|
1473
|
+
* But it's hard because of authKey:
|
|
1474
|
+
* In salsa20, authKey changes position in salsa stream.
|
|
1475
|
+
* In chacha, authKey can't be computed inside computeTag, it modifies the counter.
|
|
1476
|
+
*/
|
|
1477
|
+
const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
|
|
1478
|
+
const tagLength = 16;
|
|
1479
|
+
ensureBytes(key, 32);
|
|
1480
|
+
ensureBytes(nonce);
|
|
1481
|
+
return {
|
|
1482
|
+
encrypt: (plaintext, output) => {
|
|
1483
|
+
const plength = plaintext.length;
|
|
1484
|
+
const clength = plength + tagLength;
|
|
1485
|
+
if (output) {
|
|
1486
|
+
ensureBytes(output, clength);
|
|
1487
|
+
}
|
|
1488
|
+
else {
|
|
1489
|
+
output = new Uint8Array(clength);
|
|
1490
|
+
}
|
|
1491
|
+
xorStream(key, nonce, plaintext, output, 1);
|
|
1492
|
+
const tag = computeTag(xorStream, key, nonce, output.subarray(0, -tagLength), AAD);
|
|
1493
|
+
output.set(tag, plength); // append tag
|
|
1494
|
+
return output;
|
|
1495
|
+
},
|
|
1496
|
+
decrypt: (ciphertext, output) => {
|
|
1497
|
+
const clength = ciphertext.length;
|
|
1498
|
+
const plength = clength - tagLength;
|
|
1499
|
+
if (clength < tagLength)
|
|
1500
|
+
throw new Error(`encrypted data must be at least ${tagLength} bytes`);
|
|
1501
|
+
if (output) {
|
|
1502
|
+
ensureBytes(output, plength);
|
|
1503
|
+
}
|
|
1504
|
+
else {
|
|
1505
|
+
output = new Uint8Array(plength);
|
|
1506
|
+
}
|
|
1507
|
+
const data = ciphertext.subarray(0, -tagLength);
|
|
1508
|
+
const passedTag = ciphertext.subarray(-tagLength);
|
|
1509
|
+
const tag = computeTag(xorStream, key, nonce, data, AAD);
|
|
1510
|
+
if (!equalBytes(passedTag, tag))
|
|
1511
|
+
throw new Error('invalid tag');
|
|
1512
|
+
xorStream(key, nonce, data, output, 1);
|
|
1513
|
+
return output;
|
|
1514
|
+
},
|
|
1515
|
+
};
|
|
1516
|
+
};
|
|
1517
|
+
/**
|
|
1518
|
+
* XChaCha20-Poly1305 extended-nonce chacha.
|
|
1519
|
+
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
|
1520
|
+
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
|
1521
|
+
*/
|
|
1522
|
+
const xchacha20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 24, tagLength: 16 }, _poly1305_aead(xchacha20));
|
|
1523
|
+
|
|
1524
|
+
const crypto = typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
|
|
1525
|
+
|
|
1526
|
+
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
|
|
1527
|
+
// node.js versions earlier than v19 don't declare it in global scope.
|
|
1528
|
+
// For node.js, package.js on#exports field mapping rewrites import
|
|
1529
|
+
// from `crypto` to `cryptoNode`, which imports native module.
|
|
1530
|
+
// Makes the utils un-importable in browsers without a bundler.
|
|
1531
|
+
// Once node.js 18 is deprecated, we can just drop the import.
|
|
1532
|
+
/**
|
|
1533
|
+
* Secure PRNG. Uses `crypto.getRandomValues`, which defers to OS.
|
|
1534
|
+
*/
|
|
1535
|
+
function randomBytes(bytesLength = 32) {
|
|
1536
|
+
if (crypto && typeof crypto.getRandomValues === 'function') {
|
|
1537
|
+
return crypto.getRandomValues(new Uint8Array(bytesLength));
|
|
1538
|
+
}
|
|
1539
|
+
throw new Error('crypto.getRandomValues must be defined');
|
|
1540
|
+
}
|
|
1541
|
+
// Uses CSPRG for nonce, nonce injected in ciphertext
|
|
1542
|
+
function managedNonce(fn) {
|
|
1543
|
+
number(fn.nonceLength);
|
|
1544
|
+
return ((key, ...args) => ({
|
|
1545
|
+
encrypt: (plaintext, ...argsEnc) => {
|
|
1546
|
+
const { nonceLength } = fn;
|
|
1547
|
+
const nonce = randomBytes(nonceLength);
|
|
1548
|
+
const ciphertext = fn(key, nonce, ...args).encrypt(plaintext, ...argsEnc);
|
|
1549
|
+
const out = concatBytes(nonce, ciphertext);
|
|
1550
|
+
ciphertext.fill(0);
|
|
1551
|
+
return out;
|
|
1552
|
+
},
|
|
1553
|
+
decrypt: (ciphertext, ...argsDec) => {
|
|
1554
|
+
const { nonceLength } = fn;
|
|
1555
|
+
const nonce = ciphertext.subarray(0, nonceLength);
|
|
1556
|
+
const data = ciphertext.subarray(nonceLength);
|
|
1557
|
+
return fn(key, nonce, ...args).decrypt(data, ...argsDec);
|
|
1558
|
+
},
|
|
1559
|
+
}));
|
|
1560
|
+
}
|
|
1561
|
+
// // Type tests
|
|
1562
|
+
// import { siv, gcm, ctr, ecb, cbc } from '../aes.js';
|
|
1563
|
+
// import { xsalsa20poly1305 } from '../salsa.js';
|
|
1564
|
+
// import { chacha20poly1305, xchacha20poly1305 } from '../chacha.js';
|
|
1565
|
+
// const wsiv = managedNonce(siv);
|
|
1566
|
+
// const wgcm = managedNonce(gcm);
|
|
1567
|
+
// const wctr = managedNonce(ctr);
|
|
1568
|
+
// const wcbc = managedNonce(cbc);
|
|
1569
|
+
// const wsalsapoly = managedNonce(xsalsa20poly1305);
|
|
1570
|
+
// const wchacha = managedNonce(chacha20poly1305);
|
|
1571
|
+
// const wxchacha = managedNonce(xchacha20poly1305);
|
|
1572
|
+
// // should fail
|
|
1573
|
+
// const wcbc2 = managedNonce(managedNonce(cbc));
|
|
1574
|
+
// const wecb = managedNonce(ecb);
|
|
1575
|
+
|
|
1576
|
+
const canonicalize = canonicalizeModule;
|
|
1577
|
+
// Polyfill for synchronous signatures
|
|
1578
|
+
secp__namespace.etc.hmacSha256Sync = (k, ...m) => hmac(sha256, k, secp__namespace.etc.concatBytes(...m));
|
|
1579
|
+
class CipherBase {
|
|
1580
|
+
generateMnemonic() {
|
|
1581
|
+
return bip39__namespace.generateMnemonic();
|
|
1582
|
+
}
|
|
1583
|
+
generateJwk(privateKeyBytes) {
|
|
1584
|
+
const compressedPublicKeyBytes = secp__namespace.getPublicKey(privateKeyBytes);
|
|
1585
|
+
const compressedPublicKeyHex = secp__namespace.etc.bytesToHex(compressedPublicKeyBytes);
|
|
1586
|
+
const curvePoints = secp__namespace.ProjectivePoint.fromHex(compressedPublicKeyHex);
|
|
1587
|
+
const uncompressedPublicKeyBytes = curvePoints.toRawBytes(false);
|
|
1588
|
+
const d = base64url.baseEncode(privateKeyBytes);
|
|
1589
|
+
const x = base64url.baseEncode(uncompressedPublicKeyBytes.subarray(1, 33));
|
|
1590
|
+
const y = base64url.baseEncode(uncompressedPublicKeyBytes.subarray(33, 65));
|
|
1591
|
+
const publicJwk = {
|
|
1592
|
+
kty: 'EC',
|
|
1593
|
+
crv: 'secp256k1',
|
|
1594
|
+
x,
|
|
1595
|
+
y
|
|
1596
|
+
};
|
|
1597
|
+
const privateJwk = { ...publicJwk, d };
|
|
1598
|
+
return { publicJwk, privateJwk };
|
|
1599
|
+
}
|
|
1600
|
+
generateRandomJwk() {
|
|
1601
|
+
const privKey = secp__namespace.utils.randomPrivateKey();
|
|
1602
|
+
return this.generateJwk(privKey);
|
|
1603
|
+
}
|
|
1604
|
+
convertJwkToCompressedBytes(jwk) {
|
|
1605
|
+
const xBytes = base64url.baseDecode(jwk.x);
|
|
1606
|
+
const yBytes = base64url.baseDecode(jwk.y);
|
|
1607
|
+
const prefix = yBytes[yBytes.length - 1] % 2 === 0 ? 0x02 : 0x03;
|
|
1608
|
+
return new Uint8Array([prefix, ...xBytes]);
|
|
1609
|
+
}
|
|
1610
|
+
hashMessage(msg) {
|
|
1611
|
+
const hash = sha256(msg);
|
|
1612
|
+
return Buffer.from(hash).toString('hex');
|
|
1613
|
+
}
|
|
1614
|
+
canonicalizeJSON(json) {
|
|
1615
|
+
return canonicalize(json);
|
|
1616
|
+
}
|
|
1617
|
+
hashJSON(json) {
|
|
1618
|
+
return this.hashMessage(this.canonicalizeJSON(json));
|
|
1619
|
+
}
|
|
1620
|
+
signHash(msgHash, privateJwk) {
|
|
1621
|
+
const privKey = base64url.baseDecode(privateJwk.d);
|
|
1622
|
+
const signature = secp__namespace.sign(msgHash, privKey);
|
|
1623
|
+
return signature.toCompactHex();
|
|
1624
|
+
}
|
|
1625
|
+
verifySig(msgHash, sigHex, publicJwk) {
|
|
1626
|
+
const compressedPublicKeyBytes = this.convertJwkToCompressedBytes(publicJwk);
|
|
1627
|
+
const signature = secp__namespace.Signature.fromCompact(sigHex);
|
|
1628
|
+
return secp__namespace.verify(signature, msgHash, compressedPublicKeyBytes);
|
|
1629
|
+
}
|
|
1630
|
+
encryptBytes(pubKey, privKey, data) {
|
|
1631
|
+
const priv = base64url.baseDecode(privKey.d);
|
|
1632
|
+
const pub = this.convertJwkToCompressedBytes(pubKey);
|
|
1633
|
+
const ss = secp__namespace.getSharedSecret(priv, pub);
|
|
1634
|
+
const key = ss.slice(0, 32);
|
|
1635
|
+
const chacha = managedNonce(xchacha20poly1305)(key);
|
|
1636
|
+
const ciphertext = chacha.encrypt(data);
|
|
1637
|
+
return base64url.baseEncode(ciphertext);
|
|
1638
|
+
}
|
|
1639
|
+
decryptBytes(pubKey, privKey, ciphertext) {
|
|
1640
|
+
const priv = base64url.baseDecode(privKey.d);
|
|
1641
|
+
const pub = this.convertJwkToCompressedBytes(pubKey);
|
|
1642
|
+
const ss = secp__namespace.getSharedSecret(priv, pub);
|
|
1643
|
+
const key = ss.slice(0, 32);
|
|
1644
|
+
const chacha = managedNonce(xchacha20poly1305)(key);
|
|
1645
|
+
const cipherdata = base64url.baseDecode(ciphertext);
|
|
1646
|
+
return chacha.decrypt(cipherdata);
|
|
1647
|
+
}
|
|
1648
|
+
encryptMessage(pubKey, privKey, message) {
|
|
1649
|
+
const data = utf8ToBytes(message);
|
|
1650
|
+
return this.encryptBytes(pubKey, privKey, data);
|
|
1651
|
+
}
|
|
1652
|
+
decryptMessage(pubKey, privKey, ciphertext) {
|
|
1653
|
+
const data = this.decryptBytes(pubKey, privKey, ciphertext);
|
|
1654
|
+
return bytesToUtf8(data);
|
|
1655
|
+
}
|
|
1656
|
+
hasLeadingZeroBits(hexHash, bits) {
|
|
1657
|
+
const binary = BigInt('0x' + hexHash).toString(2).padStart(hexHash.length * 4, '0');
|
|
1658
|
+
return binary.startsWith('0'.repeat(bits));
|
|
1659
|
+
}
|
|
1660
|
+
addProofOfWork(obj, difficulty) {
|
|
1661
|
+
if (!Number.isInteger(difficulty) || difficulty < 0 || difficulty > 256) {
|
|
1662
|
+
throw new Error('Invalid difficulty: must be an integer between 0 and 256.');
|
|
1663
|
+
}
|
|
1664
|
+
let nonce = 0;
|
|
1665
|
+
while (true) {
|
|
1666
|
+
const candidate = {
|
|
1667
|
+
...obj,
|
|
1668
|
+
pow: {
|
|
1669
|
+
nonce: nonce.toString(16),
|
|
1670
|
+
difficulty,
|
|
1671
|
+
}
|
|
1672
|
+
};
|
|
1673
|
+
const hash = this.hashJSON(candidate);
|
|
1674
|
+
if (this.hasLeadingZeroBits(hash, difficulty)) {
|
|
1675
|
+
return candidate;
|
|
1676
|
+
}
|
|
1677
|
+
nonce++;
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
checkProofOfWork(obj) {
|
|
1681
|
+
if (!obj ||
|
|
1682
|
+
typeof obj !== 'object' ||
|
|
1683
|
+
!('pow' in obj) ||
|
|
1684
|
+
typeof obj.pow !== 'object' ||
|
|
1685
|
+
typeof obj.pow.nonce !== 'string' ||
|
|
1686
|
+
typeof obj.pow.difficulty !== 'number') {
|
|
1687
|
+
return false;
|
|
1688
|
+
}
|
|
1689
|
+
const hash = this.hashJSON(obj);
|
|
1690
|
+
return this.hasLeadingZeroBits(hash, obj.pow.difficulty);
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
exports.CipherBase = CipherBase;
|
|
1695
|
+
exports.base64url = base64url;
|