@didcid/cipher 0.1.3 → 0.2.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/dist/cjs/aes-B3jnKULi.cjs +1652 -0
- package/dist/cjs/{cipher-base-D2GtcXrx.cjs → cipher-base-CsSWgNEp.cjs} +235 -619
- package/dist/cjs/cipher-node.cjs +2 -1
- package/dist/cjs/cipher-web.cjs +2 -1
- package/dist/cjs/passphrase.cjs +118 -0
- package/dist/esm/cipher-base.js +23 -15
- package/dist/esm/cipher-base.js.map +1 -1
- package/dist/esm/concat-kdf.js +52 -0
- package/dist/esm/concat-kdf.js.map +1 -0
- package/dist/esm/jwe.js +124 -0
- package/dist/esm/jwe.js.map +1 -0
- package/dist/esm/passphrase.js +44 -0
- package/dist/esm/passphrase.js.map +1 -0
- package/dist/types/cipher-base.d.ts +6 -4
- package/dist/types/concat-kdf.d.ts +13 -0
- package/dist/types/jwe.d.ts +24 -0
- package/dist/types/passphrase.d.ts +10 -0
- package/dist/types/types.d.ts +6 -4
- package/package.json +10 -2
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var bip39 = require('bip39');
|
|
4
4
|
var secp = require('@noble/secp256k1');
|
|
5
|
+
var aes = require('./aes-B3jnKULi.cjs');
|
|
5
6
|
var canonicalizeModule = require('canonicalize');
|
|
6
7
|
|
|
7
8
|
function _interopNamespaceDefault(e) {
|
|
@@ -244,435 +245,6 @@ rfc4648({
|
|
|
244
245
|
bitsPerChar: 6
|
|
245
246
|
});
|
|
246
247
|
|
|
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
248
|
/**
|
|
677
249
|
* SHA2-256 a.k.a. sha256. In JS, it is the fastest hash, even faster than Blake3.
|
|
678
250
|
*
|
|
@@ -684,141 +256,7 @@ const sha256$1 = /* @__PURE__ */ createHasher(() => new SHA256());
|
|
|
684
256
|
* @deprecated
|
|
685
257
|
*/
|
|
686
258
|
/** @deprecated Use import from `noble/hashes/sha2` module */
|
|
687
|
-
const sha256 = sha256
|
|
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
|
-
}
|
|
259
|
+
const sha256 = aes.sha256;
|
|
822
260
|
|
|
823
261
|
// Poly1305 is a fast and parallel secret-key message-authentication code.
|
|
824
262
|
// https://cr.yp.to/mac.html, https://cr.yp.to/mac/poly1305-20050329.pdf
|
|
@@ -835,8 +273,8 @@ class Poly1305 {
|
|
|
835
273
|
this.pad = new Uint16Array(8);
|
|
836
274
|
this.pos = 0;
|
|
837
275
|
this.finished = false;
|
|
838
|
-
key = toBytes(key);
|
|
839
|
-
ensureBytes(key, 32);
|
|
276
|
+
key = aes.toBytes(key);
|
|
277
|
+
aes.ensureBytes(key, 32);
|
|
840
278
|
const t0 = u8to16(key, 0);
|
|
841
279
|
const t1 = u8to16(key, 2);
|
|
842
280
|
const t2 = u8to16(key, 4);
|
|
@@ -1015,9 +453,9 @@ class Poly1305 {
|
|
|
1015
453
|
}
|
|
1016
454
|
}
|
|
1017
455
|
update(data) {
|
|
1018
|
-
exists(this);
|
|
456
|
+
aes.exists(this);
|
|
1019
457
|
const { buffer, blockLen } = this;
|
|
1020
|
-
data = toBytes(data);
|
|
458
|
+
data = aes.toBytes(data);
|
|
1021
459
|
const len = data.length;
|
|
1022
460
|
for (let pos = 0; pos < len;) {
|
|
1023
461
|
const take = Math.min(blockLen - this.pos, len - pos);
|
|
@@ -1044,8 +482,8 @@ class Poly1305 {
|
|
|
1044
482
|
this.pad.fill(0);
|
|
1045
483
|
}
|
|
1046
484
|
digestInto(out) {
|
|
1047
|
-
exists(this);
|
|
1048
|
-
output(out, this);
|
|
485
|
+
aes.exists(this);
|
|
486
|
+
aes.output(out, this);
|
|
1049
487
|
this.finished = true;
|
|
1050
488
|
const { buffer, h } = this;
|
|
1051
489
|
let { pos } = this;
|
|
@@ -1073,7 +511,7 @@ class Poly1305 {
|
|
|
1073
511
|
}
|
|
1074
512
|
}
|
|
1075
513
|
function wrapConstructorWithKey(hashCons) {
|
|
1076
|
-
const hashC = (msg, key) => hashCons(key).update(toBytes(msg)).digest();
|
|
514
|
+
const hashC = (msg, key) => hashCons(key).update(aes.toBytes(msg)).digest();
|
|
1077
515
|
const tmp = hashCons(new Uint8Array(32));
|
|
1078
516
|
hashC.outputLen = tmp.outputLen;
|
|
1079
517
|
hashC.blockLen = tmp.blockLen;
|
|
@@ -1117,10 +555,10 @@ xchacha [^2] uses the subkey and remaining 8 byte nonce with ChaCha20 as normal
|
|
|
1117
555
|
[^1]: https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU/
|
|
1118
556
|
[^2]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#appendix-A.2
|
|
1119
557
|
*/
|
|
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);
|
|
558
|
+
const sigma16 = aes.utf8ToBytes('expand 16-byte k');
|
|
559
|
+
const sigma32 = aes.utf8ToBytes('expand 32-byte k');
|
|
560
|
+
const sigma16_32 = aes.u32(sigma16);
|
|
561
|
+
const sigma32_32 = aes.u32(sigma32);
|
|
1124
562
|
function rotl(a, b) {
|
|
1125
563
|
return (a << b) | (a >>> (32 - b));
|
|
1126
564
|
}
|
|
@@ -1138,11 +576,11 @@ const U32_EMPTY = new Uint32Array();
|
|
|
1138
576
|
function runCipher(core, sigma, key, nonce, data, output, counter, rounds) {
|
|
1139
577
|
const len = data.length;
|
|
1140
578
|
const block = new Uint8Array(BLOCK_LEN);
|
|
1141
|
-
const b32 = u32(block);
|
|
579
|
+
const b32 = aes.u32(block);
|
|
1142
580
|
// Make sure that buffers aligned to 4 bytes
|
|
1143
581
|
const isAligned = isAligned32(data) && isAligned32(output);
|
|
1144
|
-
const d32 = isAligned ? u32(data) : U32_EMPTY;
|
|
1145
|
-
const o32 = isAligned ? u32(output) : U32_EMPTY;
|
|
582
|
+
const d32 = isAligned ? aes.u32(data) : U32_EMPTY;
|
|
583
|
+
const o32 = isAligned ? aes.u32(output) : U32_EMPTY;
|
|
1146
584
|
for (let pos = 0; pos < len; counter++) {
|
|
1147
585
|
core(sigma, key, nonce, b32, counter, rounds);
|
|
1148
586
|
if (counter >= MAX_COUNTER)
|
|
@@ -1168,22 +606,22 @@ function runCipher(core, sigma, key, nonce, data, output, counter, rounds) {
|
|
|
1168
606
|
}
|
|
1169
607
|
}
|
|
1170
608
|
function createCipher(core, opts) {
|
|
1171
|
-
const { allowShortKeys, extendNonceFn, counterLength, counterRight, rounds } = checkOpts({ allowShortKeys: false, counterLength: 8, counterRight: false, rounds: 20 }, opts);
|
|
609
|
+
const { allowShortKeys, extendNonceFn, counterLength, counterRight, rounds } = aes.checkOpts({ allowShortKeys: false, counterLength: 8, counterRight: false, rounds: 20 }, opts);
|
|
1172
610
|
if (typeof core !== 'function')
|
|
1173
611
|
throw new Error('core must be a function');
|
|
1174
|
-
number(counterLength);
|
|
1175
|
-
number(rounds);
|
|
1176
|
-
bool(counterRight);
|
|
1177
|
-
bool(allowShortKeys);
|
|
612
|
+
aes.number(counterLength);
|
|
613
|
+
aes.number(rounds);
|
|
614
|
+
aes.bool(counterRight);
|
|
615
|
+
aes.bool(allowShortKeys);
|
|
1178
616
|
return (key, nonce, data, output, counter = 0) => {
|
|
1179
|
-
bytes(key);
|
|
1180
|
-
bytes(nonce);
|
|
1181
|
-
bytes(data);
|
|
617
|
+
aes.bytes(key);
|
|
618
|
+
aes.bytes(nonce);
|
|
619
|
+
aes.bytes(data);
|
|
1182
620
|
const len = data.length;
|
|
1183
621
|
if (!output)
|
|
1184
622
|
output = new Uint8Array(len);
|
|
1185
|
-
bytes(output);
|
|
1186
|
-
number(counter);
|
|
623
|
+
aes.bytes(output);
|
|
624
|
+
aes.number(counter);
|
|
1187
625
|
if (counter < 0 || counter >= MAX_COUNTER)
|
|
1188
626
|
throw new Error('arx: counter overflow');
|
|
1189
627
|
if (output.length < len)
|
|
@@ -1219,12 +657,12 @@ function createCipher(core, opts) {
|
|
|
1219
657
|
nonce = nonce.slice();
|
|
1220
658
|
toClean.push(nonce);
|
|
1221
659
|
}
|
|
1222
|
-
const k32 = u32(k);
|
|
660
|
+
const k32 = aes.u32(k);
|
|
1223
661
|
// hsalsa & hchacha: handle extended nonce
|
|
1224
662
|
if (extendNonceFn) {
|
|
1225
663
|
if (nonce.length !== 24)
|
|
1226
664
|
throw new Error(`arx: extended nonce must be 24 bytes`);
|
|
1227
|
-
extendNonceFn(sigma, k32, u32(nonce.subarray(0, 16)), k32);
|
|
665
|
+
extendNonceFn(sigma, k32, aes.u32(nonce.subarray(0, 16)), k32);
|
|
1228
666
|
nonce = nonce.subarray(16);
|
|
1229
667
|
}
|
|
1230
668
|
// Handle nonce counter
|
|
@@ -1238,7 +676,7 @@ function createCipher(core, opts) {
|
|
|
1238
676
|
nonce = nc;
|
|
1239
677
|
toClean.push(nonce);
|
|
1240
678
|
}
|
|
1241
|
-
const n32 = u32(nonce);
|
|
679
|
+
const n32 = aes.u32(nonce);
|
|
1242
680
|
runCipher(core, sigma, k32, n32, data, output, counter, rounds);
|
|
1243
681
|
while (toClean.length > 0)
|
|
1244
682
|
toClean.pop().fill(0);
|
|
@@ -1457,9 +895,9 @@ function computeTag(fn, key, nonce, data, AAD) {
|
|
|
1457
895
|
updatePadded(h, AAD);
|
|
1458
896
|
updatePadded(h, data);
|
|
1459
897
|
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);
|
|
898
|
+
const view = aes.createView(num);
|
|
899
|
+
aes.setBigUint64(view, 0, BigInt(AAD ? AAD.length : 0), true);
|
|
900
|
+
aes.setBigUint64(view, 8, BigInt(data.length), true);
|
|
1463
901
|
h.update(num);
|
|
1464
902
|
const res = h.digest();
|
|
1465
903
|
authKey.fill(0);
|
|
@@ -1476,14 +914,14 @@ function computeTag(fn, key, nonce, data, AAD) {
|
|
|
1476
914
|
*/
|
|
1477
915
|
const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
|
|
1478
916
|
const tagLength = 16;
|
|
1479
|
-
ensureBytes(key, 32);
|
|
1480
|
-
ensureBytes(nonce);
|
|
917
|
+
aes.ensureBytes(key, 32);
|
|
918
|
+
aes.ensureBytes(nonce);
|
|
1481
919
|
return {
|
|
1482
920
|
encrypt: (plaintext, output) => {
|
|
1483
921
|
const plength = plaintext.length;
|
|
1484
922
|
const clength = plength + tagLength;
|
|
1485
923
|
if (output) {
|
|
1486
|
-
ensureBytes(output, clength);
|
|
924
|
+
aes.ensureBytes(output, clength);
|
|
1487
925
|
}
|
|
1488
926
|
else {
|
|
1489
927
|
output = new Uint8Array(clength);
|
|
@@ -1499,7 +937,7 @@ const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
|
|
|
1499
937
|
if (clength < tagLength)
|
|
1500
938
|
throw new Error(`encrypted data must be at least ${tagLength} bytes`);
|
|
1501
939
|
if (output) {
|
|
1502
|
-
ensureBytes(output, plength);
|
|
940
|
+
aes.ensureBytes(output, plength);
|
|
1503
941
|
}
|
|
1504
942
|
else {
|
|
1505
943
|
output = new Uint8Array(plength);
|
|
@@ -1507,7 +945,7 @@ const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
|
|
|
1507
945
|
const data = ciphertext.subarray(0, -tagLength);
|
|
1508
946
|
const passedTag = ciphertext.subarray(-tagLength);
|
|
1509
947
|
const tag = computeTag(xorStream, key, nonce, data, AAD);
|
|
1510
|
-
if (!equalBytes(passedTag, tag))
|
|
948
|
+
if (!aes.equalBytes(passedTag, tag))
|
|
1511
949
|
throw new Error('invalid tag');
|
|
1512
950
|
xorStream(key, nonce, data, output, 1);
|
|
1513
951
|
return output;
|
|
@@ -1519,7 +957,7 @@ const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
|
|
|
1519
957
|
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
|
|
1520
958
|
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
|
|
1521
959
|
*/
|
|
1522
|
-
const xchacha20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 24, tagLength: 16 }, _poly1305_aead(xchacha20));
|
|
960
|
+
const xchacha20poly1305 = /* @__PURE__ */ aes.wrapCipher({ blockSize: 64, nonceLength: 24, tagLength: 16 }, _poly1305_aead(xchacha20));
|
|
1523
961
|
|
|
1524
962
|
const crypto = typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
|
|
1525
963
|
|
|
@@ -1540,13 +978,13 @@ function randomBytes(bytesLength = 32) {
|
|
|
1540
978
|
}
|
|
1541
979
|
// Uses CSPRG for nonce, nonce injected in ciphertext
|
|
1542
980
|
function managedNonce(fn) {
|
|
1543
|
-
number(fn.nonceLength);
|
|
981
|
+
aes.number(fn.nonceLength);
|
|
1544
982
|
return ((key, ...args) => ({
|
|
1545
983
|
encrypt: (plaintext, ...argsEnc) => {
|
|
1546
984
|
const { nonceLength } = fn;
|
|
1547
985
|
const nonce = randomBytes(nonceLength);
|
|
1548
986
|
const ciphertext = fn(key, nonce, ...args).encrypt(plaintext, ...argsEnc);
|
|
1549
|
-
const out = concatBytes(nonce, ciphertext);
|
|
987
|
+
const out = aes.concatBytes(nonce, ciphertext);
|
|
1550
988
|
ciphertext.fill(0);
|
|
1551
989
|
return out;
|
|
1552
990
|
},
|
|
@@ -1573,9 +1011,180 @@ function managedNonce(fn) {
|
|
|
1573
1011
|
// const wcbc2 = managedNonce(managedNonce(cbc));
|
|
1574
1012
|
// const wecb = managedNonce(ecb);
|
|
1575
1013
|
|
|
1014
|
+
/**
|
|
1015
|
+
* Concat KDF (Single Step KDF) per NIST SP 800-56A and RFC 7518 §4.6.2.
|
|
1016
|
+
*
|
|
1017
|
+
* Derives a symmetric key from an ECDH shared secret for use in JWE.
|
|
1018
|
+
*
|
|
1019
|
+
* @param sharedSecret - The raw ECDH shared secret (Z)
|
|
1020
|
+
* @param keyBitLength - Desired key length in bits (e.g. 128, 256)
|
|
1021
|
+
* @param algorithmId - The "enc" value for ECDH-ES or "alg" for ECDH-ES+A*KW
|
|
1022
|
+
* @param apu - Agreement PartyUInfo (typically empty)
|
|
1023
|
+
* @param apv - Agreement PartyVInfo (typically empty)
|
|
1024
|
+
* @returns Derived key as Uint8Array
|
|
1025
|
+
*/
|
|
1026
|
+
function concatKdf(sharedSecret, keyBitLength, algorithmId, apu = new Uint8Array(0), apv = new Uint8Array(0)) {
|
|
1027
|
+
const algIdBytes = new TextEncoder().encode(algorithmId);
|
|
1028
|
+
// Build otherInfo per RFC 7518 §4.6.2:
|
|
1029
|
+
// AlgorithmID = len(algId) || algId
|
|
1030
|
+
// PartyUInfo = len(apu) || apu
|
|
1031
|
+
// PartyVInfo = len(apv) || apv
|
|
1032
|
+
// SuppPubInfo = keydatalen (32-bit BE, in bits)
|
|
1033
|
+
const otherInfo = concatBytes(uint32BE(algIdBytes.length), algIdBytes, uint32BE(apu.length), apu, uint32BE(apv.length), apv, uint32BE(keyBitLength));
|
|
1034
|
+
const hashLength = 256; // SHA-256 output in bits
|
|
1035
|
+
const reps = Math.ceil(keyBitLength / hashLength);
|
|
1036
|
+
const result = new Uint8Array(reps * 32);
|
|
1037
|
+
for (let counter = 1; counter <= reps; counter++) {
|
|
1038
|
+
const input = concatBytes(uint32BE(counter), sharedSecret, otherInfo);
|
|
1039
|
+
const digest = sha256(input);
|
|
1040
|
+
result.set(digest, (counter - 1) * 32);
|
|
1041
|
+
}
|
|
1042
|
+
return result.slice(0, keyBitLength / 8);
|
|
1043
|
+
}
|
|
1044
|
+
function uint32BE(value) {
|
|
1045
|
+
const buf = new Uint8Array(4);
|
|
1046
|
+
buf[0] = (value >>> 24) & 0xff;
|
|
1047
|
+
buf[1] = (value >>> 16) & 0xff;
|
|
1048
|
+
buf[2] = (value >>> 8) & 0xff;
|
|
1049
|
+
buf[3] = value & 0xff;
|
|
1050
|
+
return buf;
|
|
1051
|
+
}
|
|
1052
|
+
function concatBytes(...arrays) {
|
|
1053
|
+
let totalLength = 0;
|
|
1054
|
+
for (const arr of arrays)
|
|
1055
|
+
totalLength += arr.length;
|
|
1056
|
+
const result = new Uint8Array(totalLength);
|
|
1057
|
+
let offset = 0;
|
|
1058
|
+
for (const arr of arrays) {
|
|
1059
|
+
result.set(arr, offset);
|
|
1060
|
+
offset += arr.length;
|
|
1061
|
+
}
|
|
1062
|
+
return result;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
const ENCODER = new TextEncoder();
|
|
1066
|
+
const DECODER = new TextDecoder();
|
|
1067
|
+
/**
|
|
1068
|
+
* Build a JWE Compact Serialization string using ECDH-ES + A256GCM.
|
|
1069
|
+
*
|
|
1070
|
+
* Uses an ephemeral secp256k1 keypair for key agreement.
|
|
1071
|
+
* The sender's identity key is NOT involved — only the recipient's public key.
|
|
1072
|
+
*
|
|
1073
|
+
* @param recipientPubKey - Recipient's public key (secp256k1 JWK)
|
|
1074
|
+
* @param plaintext - Data to encrypt
|
|
1075
|
+
* @returns JWE Compact string: header.encryptedKey.iv.ciphertext.tag
|
|
1076
|
+
*/
|
|
1077
|
+
function buildJweCompact(recipientPubKey, plaintext) {
|
|
1078
|
+
// 1. Generate ephemeral keypair
|
|
1079
|
+
const ephemeralPrivKey = secp__namespace.utils.randomPrivateKey();
|
|
1080
|
+
const ephemeralPubKeyBytes = secp__namespace.getPublicKey(ephemeralPrivKey);
|
|
1081
|
+
// Get uncompressed public key coordinates for the JWE header
|
|
1082
|
+
const ephemeralPubHex = secp__namespace.etc.bytesToHex(ephemeralPubKeyBytes);
|
|
1083
|
+
const curvePoints = secp__namespace.ProjectivePoint.fromHex(ephemeralPubHex);
|
|
1084
|
+
const uncompressed = curvePoints.toRawBytes(false);
|
|
1085
|
+
const epkX = base64url.baseEncode(uncompressed.subarray(1, 33));
|
|
1086
|
+
const epkY = base64url.baseEncode(uncompressed.subarray(33, 65));
|
|
1087
|
+
// 2. Build protected header
|
|
1088
|
+
const header = {
|
|
1089
|
+
alg: 'ECDH-ES',
|
|
1090
|
+
enc: 'A256GCM',
|
|
1091
|
+
epk: {
|
|
1092
|
+
kty: 'EC',
|
|
1093
|
+
crv: 'secp256k1',
|
|
1094
|
+
x: epkX,
|
|
1095
|
+
y: epkY,
|
|
1096
|
+
},
|
|
1097
|
+
};
|
|
1098
|
+
const headerJson = JSON.stringify(header);
|
|
1099
|
+
const headerB64 = base64url.baseEncode(ENCODER.encode(headerJson));
|
|
1100
|
+
// 3. ECDH key agreement: ephemeral private + recipient public
|
|
1101
|
+
const recipientPubBytes = jwkToCompressedBytes(recipientPubKey);
|
|
1102
|
+
const sharedSecret = secp__namespace.getSharedSecret(ephemeralPrivKey, recipientPubBytes);
|
|
1103
|
+
// 4. Derive CEK via Concat KDF (RFC 7518 §4.6.2)
|
|
1104
|
+
// For ECDH-ES (direct), algorithmId = enc value
|
|
1105
|
+
const cek = concatKdf(sharedSecret.slice(1), 256, 'A256GCM');
|
|
1106
|
+
// 5. Generate random 96-bit IV
|
|
1107
|
+
const iv = secp__namespace.utils.randomPrivateKey().slice(0, 12);
|
|
1108
|
+
// 6. Encrypt with AES-256-GCM
|
|
1109
|
+
// AAD = ASCII bytes of the base64url-encoded protected header
|
|
1110
|
+
const aad = ENCODER.encode(headerB64);
|
|
1111
|
+
const cipher = aes.gcm(cek, iv, aad);
|
|
1112
|
+
const encrypted = cipher.encrypt(plaintext); // returns ciphertext || tag
|
|
1113
|
+
// 7. Split ciphertext and tag (tag is last 16 bytes)
|
|
1114
|
+
const ciphertext = encrypted.slice(0, encrypted.length - 16);
|
|
1115
|
+
const tag = encrypted.slice(encrypted.length - 16);
|
|
1116
|
+
// 8. Assemble JWE Compact: header.encryptedKey.iv.ciphertext.tag
|
|
1117
|
+
// For ECDH-ES (direct), encrypted key is empty
|
|
1118
|
+
return [
|
|
1119
|
+
headerB64,
|
|
1120
|
+
'', // empty encrypted key
|
|
1121
|
+
base64url.baseEncode(iv),
|
|
1122
|
+
base64url.baseEncode(ciphertext),
|
|
1123
|
+
base64url.baseEncode(tag),
|
|
1124
|
+
].join('.');
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Parse and decrypt a JWE Compact Serialization string.
|
|
1128
|
+
*
|
|
1129
|
+
* @param recipientPrivKey - Recipient's private key (secp256k1 JWK)
|
|
1130
|
+
* @param jweCompact - The JWE Compact string to decrypt
|
|
1131
|
+
* @returns Decrypted plaintext
|
|
1132
|
+
*/
|
|
1133
|
+
function parseJweCompact(recipientPrivKey, jweCompact) {
|
|
1134
|
+
// 1. Split into 5 parts
|
|
1135
|
+
const parts = jweCompact.split('.');
|
|
1136
|
+
if (parts.length !== 5) {
|
|
1137
|
+
throw new Error('Invalid JWE Compact: expected 5 segments');
|
|
1138
|
+
}
|
|
1139
|
+
const [headerB64, , ivB64, ciphertextB64, tagB64] = parts;
|
|
1140
|
+
// 2. Parse protected header
|
|
1141
|
+
const headerJson = DECODER.decode(base64url.baseDecode(headerB64));
|
|
1142
|
+
const header = JSON.parse(headerJson);
|
|
1143
|
+
if (header.alg !== 'ECDH-ES') {
|
|
1144
|
+
throw new Error(`Unsupported JWE alg: ${header.alg}`);
|
|
1145
|
+
}
|
|
1146
|
+
if (header.enc !== 'A256GCM') {
|
|
1147
|
+
throw new Error(`Unsupported JWE enc: ${header.enc}`);
|
|
1148
|
+
}
|
|
1149
|
+
// 3. Reconstruct ephemeral public key from header
|
|
1150
|
+
const epk = header.epk;
|
|
1151
|
+
const ephemeralPubBytes = jwkToCompressedBytes(epk);
|
|
1152
|
+
// 4. ECDH key agreement: recipient private + ephemeral public
|
|
1153
|
+
const recipientPrivBytes = base64url.baseDecode(recipientPrivKey.d);
|
|
1154
|
+
const sharedSecret = secp__namespace.getSharedSecret(recipientPrivBytes, ephemeralPubBytes);
|
|
1155
|
+
// 5. Derive CEK via Concat KDF
|
|
1156
|
+
const cek = concatKdf(sharedSecret.slice(1), 256, 'A256GCM');
|
|
1157
|
+
// 6. Decrypt with AES-256-GCM
|
|
1158
|
+
const iv = base64url.baseDecode(ivB64);
|
|
1159
|
+
const ciphertext = base64url.baseDecode(ciphertextB64);
|
|
1160
|
+
const tag = base64url.baseDecode(tagB64);
|
|
1161
|
+
// Reassemble ciphertext || tag for @noble/ciphers GCM
|
|
1162
|
+
const encrypted = new Uint8Array(ciphertext.length + tag.length);
|
|
1163
|
+
encrypted.set(ciphertext, 0);
|
|
1164
|
+
encrypted.set(tag, ciphertext.length);
|
|
1165
|
+
const aad = ENCODER.encode(headerB64);
|
|
1166
|
+
const cipher = aes.gcm(cek, iv, aad);
|
|
1167
|
+
return cipher.decrypt(encrypted);
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Detect whether a string is a JWE Compact Serialization.
|
|
1171
|
+
*/
|
|
1172
|
+
function isJweCompact(ciphertext) {
|
|
1173
|
+
return ciphertext.startsWith('eyJ') && ciphertext.split('.').length === 5;
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Convert a JWK public key to compressed secp256k1 bytes.
|
|
1177
|
+
*/
|
|
1178
|
+
function jwkToCompressedBytes(jwk) {
|
|
1179
|
+
const xBytes = base64url.baseDecode(jwk.x);
|
|
1180
|
+
const yBytes = base64url.baseDecode(jwk.y);
|
|
1181
|
+
const prefix = yBytes[yBytes.length - 1] % 2 === 0 ? 0x02 : 0x03;
|
|
1182
|
+
return new Uint8Array([prefix, ...xBytes]);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1576
1185
|
const canonicalize = canonicalizeModule;
|
|
1577
1186
|
// Polyfill for synchronous signatures
|
|
1578
|
-
secp__namespace.etc.hmacSha256Sync = (k, ...m) => hmac(sha256, k, secp__namespace.etc.concatBytes(...m));
|
|
1187
|
+
secp__namespace.etc.hmacSha256Sync = (k, ...m) => aes.hmac(sha256, k, secp__namespace.etc.concatBytes(...m));
|
|
1579
1188
|
class CipherBase {
|
|
1580
1189
|
generateMnemonic() {
|
|
1581
1190
|
return bip39__namespace.generateMnemonic();
|
|
@@ -1627,16 +1236,27 @@ class CipherBase {
|
|
|
1627
1236
|
const signature = secp__namespace.Signature.fromCompact(sigHex);
|
|
1628
1237
|
return secp__namespace.verify(signature, msgHash, compressedPublicKeyBytes);
|
|
1629
1238
|
}
|
|
1630
|
-
encryptBytes(
|
|
1631
|
-
|
|
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);
|
|
1239
|
+
encryptBytes(recipientPubKey, data) {
|
|
1240
|
+
return buildJweCompact(recipientPubKey, data);
|
|
1638
1241
|
}
|
|
1639
|
-
decryptBytes(
|
|
1242
|
+
decryptBytes(recipientPrivKey, ciphertext, legacyPubKey) {
|
|
1243
|
+
if (isJweCompact(ciphertext)) {
|
|
1244
|
+
return parseJweCompact(recipientPrivKey, ciphertext);
|
|
1245
|
+
}
|
|
1246
|
+
if (legacyPubKey) {
|
|
1247
|
+
return this.decryptBytesLegacy(legacyPubKey, recipientPrivKey, ciphertext);
|
|
1248
|
+
}
|
|
1249
|
+
throw new Error('Cannot decrypt: not a JWE and no legacy public key provided. Pass legacyPubKey as the third argument for old ciphertext.');
|
|
1250
|
+
}
|
|
1251
|
+
encryptMessage(recipientPubKey, message) {
|
|
1252
|
+
const data = aes.utf8ToBytes(message);
|
|
1253
|
+
return this.encryptBytes(recipientPubKey, data);
|
|
1254
|
+
}
|
|
1255
|
+
decryptMessage(recipientPrivKey, ciphertext, legacyPubKey) {
|
|
1256
|
+
const data = this.decryptBytes(recipientPrivKey, ciphertext, legacyPubKey);
|
|
1257
|
+
return aes.bytesToUtf8(data);
|
|
1258
|
+
}
|
|
1259
|
+
decryptBytesLegacy(pubKey, privKey, ciphertext) {
|
|
1640
1260
|
const priv = base64url.baseDecode(privKey.d);
|
|
1641
1261
|
const pub = this.convertJwkToCompressedBytes(pubKey);
|
|
1642
1262
|
const ss = secp__namespace.getSharedSecret(priv, pub);
|
|
@@ -1645,13 +1265,9 @@ class CipherBase {
|
|
|
1645
1265
|
const cipherdata = base64url.baseDecode(ciphertext);
|
|
1646
1266
|
return chacha.decrypt(cipherdata);
|
|
1647
1267
|
}
|
|
1648
|
-
|
|
1649
|
-
const data =
|
|
1650
|
-
return
|
|
1651
|
-
}
|
|
1652
|
-
decryptMessage(pubKey, privKey, ciphertext) {
|
|
1653
|
-
const data = this.decryptBytes(pubKey, privKey, ciphertext);
|
|
1654
|
-
return bytesToUtf8(data);
|
|
1268
|
+
decryptMessageLegacy(pubKey, privKey, ciphertext) {
|
|
1269
|
+
const data = this.decryptBytesLegacy(pubKey, privKey, ciphertext);
|
|
1270
|
+
return aes.bytesToUtf8(data);
|
|
1655
1271
|
}
|
|
1656
1272
|
hasLeadingZeroBits(hexHash, bits) {
|
|
1657
1273
|
const binary = BigInt('0x' + hexHash).toString(2).padStart(hexHash.length * 4, '0');
|