@didcid/gatekeeper 0.3.6 → 0.4.0

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.
@@ -38283,7 +38283,7 @@ const toU8 = (a, len) => abytes$1(isStr(a) ? hexToBytes(a) : u8fr(abytes$1(a)),
38283
38283
  const cr = () => globalThis?.crypto; // WebCrypto is available in all modern environments
38284
38284
  const subtle = () => cr()?.subtle ?? err('crypto.subtle must be defined');
38285
38285
  // prettier-ignore
38286
- const concatBytes$1 = (...arrs) => {
38286
+ const concatBytes$2 = (...arrs) => {
38287
38287
  const r = u8n(arrs.reduce((sum, a) => sum + abytes$1(a).length, 0)); // create u8a of summed length
38288
38288
  let pad = 0; // walk through each array,
38289
38289
  arrs.forEach(a => { r.set(a, pad); pad += a.length; }); // ensure they have proper type
@@ -38524,8 +38524,8 @@ class Point {
38524
38524
  const { x, y } = this.assertValidity().toAffine();
38525
38525
  const x32b = numTo32b(x);
38526
38526
  if (isCompressed)
38527
- return concatBytes$1(getPrefix(y), x32b);
38528
- return concatBytes$1(u8of(0x04), x32b, numTo32b(y));
38527
+ return concatBytes$2(getPrefix(y), x32b);
38528
+ return concatBytes$2(u8of(0x04), x32b, numTo32b(y));
38529
38529
  }
38530
38530
  /** Create 3d xyz point from 2d xy. (0, 0) => (0, 1, 0), not (0, 0, 1) */
38531
38531
  static fromAffine(ap) {
@@ -38599,7 +38599,7 @@ class Signature {
38599
38599
  }
38600
38600
  toBytes() {
38601
38601
  const { r, s } = this;
38602
- return concatBytes$1(numTo32b(r), numTo32b(s));
38602
+ return concatBytes$2(numTo32b(r), numTo32b(s));
38603
38603
  }
38604
38604
  /** Copy signature, with newly added recovery bit. */
38605
38605
  addRecoveryBit(bit) {
@@ -38691,7 +38691,7 @@ const prepSig = (msgh, priv, opts = signOpts) => {
38691
38691
  }
38692
38692
  return new Signature(r, normS, recovery); // use normS, not s
38693
38693
  };
38694
- return { seed: concatBytes$1(...seed), k2sig };
38694
+ return { seed: concatBytes$2(...seed), k2sig };
38695
38695
  };
38696
38696
  // HMAC-DRBG from NIST 800-90. Minimal, non-full-spec - used for RFC6979 signatures.
38697
38697
  const hmacDrbg = (asynchronous) => {
@@ -38809,7 +38809,7 @@ const recoverPublicKey = (sig, msgh) => {
38809
38809
  const radj = recovery === 2 || recovery === 3 ? r + N : r;
38810
38810
  afield(radj); // ensure q.x is still a field element
38811
38811
  const head = getPrefix(big(recovery)); // head is 0x02 or 0x03
38812
- const Rb = concatBytes$1(head, numTo32b(radj)); // concat head + r
38812
+ const Rb = concatBytes$2(head, numTo32b(radj)); // concat head + r
38813
38813
  const R = Point.fromBytes(Rb);
38814
38814
  const ir = invert(radj, N); // r^-1
38815
38815
  const u1 = modN(-h * ir); // -hr^-1
@@ -38842,7 +38842,7 @@ const _sha = 'SHA-256';
38842
38842
  const etc = {
38843
38843
  hexToBytes: hexToBytes,
38844
38844
  bytesToHex: bytesToHex,
38845
- concatBytes: concatBytes$1,
38845
+ concatBytes: concatBytes$2,
38846
38846
  bytesToNumberBE: bytesToNumBE,
38847
38847
  numberToBytesBE: numTo32b,
38848
38848
  mod: M,
@@ -38851,7 +38851,7 @@ const etc = {
38851
38851
  const s = subtle();
38852
38852
  const name = 'HMAC';
38853
38853
  const k = await s.importKey('raw', key, { name, hash: { name: _sha } }, false, ['sign']);
38854
- return u8n(await s.sign(name, k, concatBytes$1(...msgs)));
38854
+ return u8n(await s.sign(name, k, concatBytes$2(...msgs)));
38855
38855
  },
38856
38856
  hmacSha256Sync: undefined, // For TypeScript. Actual logic is below
38857
38857
  hashToPrivateKey: hashToPrivateKey,
@@ -39387,6 +39387,7 @@ const sha256$1 = sha256$2;
39387
39387
 
39388
39388
  /*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
39389
39389
  // Cast array to different type
39390
+ const u8 = (arr) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
39390
39391
  const u32 = (arr) => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
39391
39392
  function isBytes$1(a) {
39392
39393
  return (a instanceof Uint8Array ||
@@ -39427,7 +39428,7 @@ function toBytes(data) {
39427
39428
  /**
39428
39429
  * Copies several Uint8Arrays into one.
39429
39430
  */
39430
- function concatBytes(...arrays) {
39431
+ function concatBytes$1(...arrays) {
39431
39432
  let sum = 0;
39432
39433
  for (let i = 0; i < arrays.length; i++) {
39433
39434
  const a = arrays[i];
@@ -39479,8 +39480,8 @@ function setBigUint64(view, byteOffset, value, isLE) {
39479
39480
  const _u32_max = BigInt(0xffffffff);
39480
39481
  const wh = Number((value >> _32n) & _u32_max);
39481
39482
  const wl = Number(value & _u32_max);
39482
- const h = 4 ;
39483
- const l = 0 ;
39483
+ const h = isLE ? 4 : 0;
39484
+ const l = isLE ? 0 : 4;
39484
39485
  view.setUint32(byteOffset + h, wh, isLE);
39485
39486
  view.setUint32(byteOffset + l, wl, isLE);
39486
39487
  }
@@ -39771,7 +39772,7 @@ class Poly1305 {
39771
39772
  return res;
39772
39773
  }
39773
39774
  }
39774
- function wrapConstructorWithKey(hashCons) {
39775
+ function wrapConstructorWithKey$1(hashCons) {
39775
39776
  const hashC = (msg, key) => hashCons(key).update(toBytes(msg)).digest();
39776
39777
  const tmp = hashCons(new Uint8Array(32));
39777
39778
  hashC.outputLen = tmp.outputLen;
@@ -39779,7 +39780,7 @@ function wrapConstructorWithKey(hashCons) {
39779
39780
  hashC.create = (key) => hashCons(key);
39780
39781
  return hashC;
39781
39782
  }
39782
- const poly1305 = wrapConstructorWithKey((key) => new Poly1305(key));
39783
+ const poly1305 = wrapConstructorWithKey$1((key) => new Poly1305(key));
39783
39784
 
39784
39785
  // Basic utils for ARX (add-rotate-xor) salsa and chacha ciphers.
39785
39786
  /*
@@ -40140,17 +40141,17 @@ const xchacha20 = /* @__PURE__ */ createCipher(chachaCore, {
40140
40141
  extendNonceFn: hchacha,
40141
40142
  allowShortKeys: false,
40142
40143
  });
40143
- const ZEROS16 = /* @__PURE__ */ new Uint8Array(16);
40144
+ const ZEROS16$1 = /* @__PURE__ */ new Uint8Array(16);
40144
40145
  // Pad to digest size with zeros
40145
40146
  const updatePadded = (h, msg) => {
40146
40147
  h.update(msg);
40147
40148
  const left = msg.length % 16;
40148
40149
  if (left)
40149
- h.update(ZEROS16.subarray(left));
40150
+ h.update(ZEROS16$1.subarray(left));
40150
40151
  };
40151
- const ZEROS32 = /* @__PURE__ */ new Uint8Array(32);
40152
- function computeTag(fn, key, nonce, data, AAD) {
40153
- const authKey = fn(key, nonce, ZEROS32);
40152
+ const ZEROS32$1 = /* @__PURE__ */ new Uint8Array(32);
40153
+ function computeTag$1(fn, key, nonce, data, AAD) {
40154
+ const authKey = fn(key, nonce, ZEROS32$1);
40154
40155
  const h = poly1305.create(authKey);
40155
40156
  if (AAD)
40156
40157
  updatePadded(h, AAD);
@@ -40188,7 +40189,7 @@ const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
40188
40189
  output = new Uint8Array(clength);
40189
40190
  }
40190
40191
  xorStream(key, nonce, plaintext, output, 1);
40191
- const tag = computeTag(xorStream, key, nonce, output.subarray(0, -tagLength), AAD);
40192
+ const tag = computeTag$1(xorStream, key, nonce, output.subarray(0, -tagLength), AAD);
40192
40193
  output.set(tag, plength); // append tag
40193
40194
  return output;
40194
40195
  },
@@ -40205,7 +40206,7 @@ const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
40205
40206
  }
40206
40207
  const data = ciphertext.subarray(0, -tagLength);
40207
40208
  const passedTag = ciphertext.subarray(-tagLength);
40208
- const tag = computeTag(xorStream, key, nonce, data, AAD);
40209
+ const tag = computeTag$1(xorStream, key, nonce, data, AAD);
40209
40210
  if (!equalBytes(passedTag, tag))
40210
40211
  throw new Error('invalid tag');
40211
40212
  xorStream(key, nonce, data, output, 1);
@@ -40245,7 +40246,7 @@ function managedNonce(fn) {
40245
40246
  const { nonceLength } = fn;
40246
40247
  const nonce = randomBytes(nonceLength);
40247
40248
  const ciphertext = fn(key, nonce, ...args).encrypt(plaintext, ...argsEnc);
40248
- const out = concatBytes(nonce, ciphertext);
40249
+ const out = concatBytes$1(nonce, ciphertext);
40249
40250
  ciphertext.fill(0);
40250
40251
  return out;
40251
40252
  },
@@ -40272,6 +40273,979 @@ function managedNonce(fn) {
40272
40273
  // const wcbc2 = managedNonce(managedNonce(cbc));
40273
40274
  // const wecb = managedNonce(ecb);
40274
40275
 
40276
+ // GHash from AES-GCM and its little-endian "mirror image" Polyval from AES-SIV.
40277
+ // Implemented in terms of GHash with conversion function for keys
40278
+ // GCM GHASH from NIST SP800-38d, SIV from RFC 8452.
40279
+ // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
40280
+ // GHASH modulo: x^128 + x^7 + x^2 + x + 1
40281
+ // POLYVAL modulo: x^128 + x^127 + x^126 + x^121 + 1
40282
+ const BLOCK_SIZE$1 = 16;
40283
+ // TODO: rewrite
40284
+ // temporary padding buffer
40285
+ const ZEROS16 = /* @__PURE__ */ new Uint8Array(16);
40286
+ const ZEROS32 = u32(ZEROS16);
40287
+ const POLY$1 = 0xe1; // v = 2*v % POLY
40288
+ // v = 2*v % POLY
40289
+ // NOTE: because x + x = 0 (add/sub is same), mul2(x) != x+x
40290
+ // We can multiply any number using montgomery ladder and this function (works as double, add is simple xor)
40291
+ const mul2$1 = (s0, s1, s2, s3) => {
40292
+ const hiBit = s3 & 1;
40293
+ return {
40294
+ s3: (s2 << 31) | (s3 >>> 1),
40295
+ s2: (s1 << 31) | (s2 >>> 1),
40296
+ s1: (s0 << 31) | (s1 >>> 1),
40297
+ s0: (s0 >>> 1) ^ ((POLY$1 << 24) & -(hiBit & 1)), // reduce % poly
40298
+ };
40299
+ };
40300
+ const swapLE = (n) => (((n >>> 0) & 0xff) << 24) |
40301
+ (((n >>> 8) & 0xff) << 16) |
40302
+ (((n >>> 16) & 0xff) << 8) |
40303
+ ((n >>> 24) & 0xff) |
40304
+ 0;
40305
+ /**
40306
+ * `mulX_POLYVAL(ByteReverse(H))` from spec
40307
+ * @param k mutated in place
40308
+ */
40309
+ function _toGHASHKey(k) {
40310
+ k.reverse();
40311
+ const hiBit = k[15] & 1;
40312
+ // k >>= 1
40313
+ let carry = 0;
40314
+ for (let i = 0; i < k.length; i++) {
40315
+ const t = k[i];
40316
+ k[i] = (t >>> 1) | carry;
40317
+ carry = (t & 1) << 7;
40318
+ }
40319
+ k[0] ^= -hiBit & 0xe1; // if (hiBit) n ^= 0xe1000000000000000000000000000000;
40320
+ return k;
40321
+ }
40322
+ const estimateWindow = (bytes) => {
40323
+ if (bytes > 64 * 1024)
40324
+ return 8;
40325
+ if (bytes > 1024)
40326
+ return 4;
40327
+ return 2;
40328
+ };
40329
+ class GHASH {
40330
+ // We select bits per window adaptively based on expectedLength
40331
+ constructor(key, expectedLength) {
40332
+ this.blockLen = BLOCK_SIZE$1;
40333
+ this.outputLen = BLOCK_SIZE$1;
40334
+ this.s0 = 0;
40335
+ this.s1 = 0;
40336
+ this.s2 = 0;
40337
+ this.s3 = 0;
40338
+ this.finished = false;
40339
+ key = toBytes(key);
40340
+ ensureBytes(key, 16);
40341
+ const kView = createView(key);
40342
+ let k0 = kView.getUint32(0, false);
40343
+ let k1 = kView.getUint32(4, false);
40344
+ let k2 = kView.getUint32(8, false);
40345
+ let k3 = kView.getUint32(12, false);
40346
+ // generate table of doubled keys (half of montgomery ladder)
40347
+ const doubles = [];
40348
+ for (let i = 0; i < 128; i++) {
40349
+ doubles.push({ s0: swapLE(k0), s1: swapLE(k1), s2: swapLE(k2), s3: swapLE(k3) });
40350
+ ({ s0: k0, s1: k1, s2: k2, s3: k3 } = mul2$1(k0, k1, k2, k3));
40351
+ }
40352
+ const W = estimateWindow(expectedLength || 1024);
40353
+ if (![1, 2, 4, 8].includes(W))
40354
+ throw new Error(`ghash: wrong window size=${W}, should be 2, 4 or 8`);
40355
+ this.W = W;
40356
+ const bits = 128; // always 128 bits;
40357
+ const windows = bits / W;
40358
+ const windowSize = (this.windowSize = 2 ** W);
40359
+ const items = [];
40360
+ // Create precompute table for window of W bits
40361
+ for (let w = 0; w < windows; w++) {
40362
+ // truth table: 00, 01, 10, 11
40363
+ for (let byte = 0; byte < windowSize; byte++) {
40364
+ // prettier-ignore
40365
+ let s0 = 0, s1 = 0, s2 = 0, s3 = 0;
40366
+ for (let j = 0; j < W; j++) {
40367
+ const bit = (byte >>> (W - j - 1)) & 1;
40368
+ if (!bit)
40369
+ continue;
40370
+ const { s0: d0, s1: d1, s2: d2, s3: d3 } = doubles[W * w + j];
40371
+ (s0 ^= d0), (s1 ^= d1), (s2 ^= d2), (s3 ^= d3);
40372
+ }
40373
+ items.push({ s0, s1, s2, s3 });
40374
+ }
40375
+ }
40376
+ this.t = items;
40377
+ }
40378
+ _updateBlock(s0, s1, s2, s3) {
40379
+ (s0 ^= this.s0), (s1 ^= this.s1), (s2 ^= this.s2), (s3 ^= this.s3);
40380
+ const { W, t, windowSize } = this;
40381
+ // prettier-ignore
40382
+ let o0 = 0, o1 = 0, o2 = 0, o3 = 0;
40383
+ const mask = (1 << W) - 1; // 2**W will kill performance.
40384
+ let w = 0;
40385
+ for (const num of [s0, s1, s2, s3]) {
40386
+ for (let bytePos = 0; bytePos < 4; bytePos++) {
40387
+ const byte = (num >>> (8 * bytePos)) & 0xff;
40388
+ for (let bitPos = 8 / W - 1; bitPos >= 0; bitPos--) {
40389
+ const bit = (byte >>> (W * bitPos)) & mask;
40390
+ const { s0: e0, s1: e1, s2: e2, s3: e3 } = t[w * windowSize + bit];
40391
+ (o0 ^= e0), (o1 ^= e1), (o2 ^= e2), (o3 ^= e3);
40392
+ w += 1;
40393
+ }
40394
+ }
40395
+ }
40396
+ this.s0 = o0;
40397
+ this.s1 = o1;
40398
+ this.s2 = o2;
40399
+ this.s3 = o3;
40400
+ }
40401
+ update(data) {
40402
+ data = toBytes(data);
40403
+ exists(this);
40404
+ const b32 = u32(data);
40405
+ const blocks = Math.floor(data.length / BLOCK_SIZE$1);
40406
+ const left = data.length % BLOCK_SIZE$1;
40407
+ for (let i = 0; i < blocks; i++) {
40408
+ this._updateBlock(b32[i * 4 + 0], b32[i * 4 + 1], b32[i * 4 + 2], b32[i * 4 + 3]);
40409
+ }
40410
+ if (left) {
40411
+ ZEROS16.set(data.subarray(blocks * BLOCK_SIZE$1));
40412
+ this._updateBlock(ZEROS32[0], ZEROS32[1], ZEROS32[2], ZEROS32[3]);
40413
+ ZEROS32.fill(0); // clean tmp buffer
40414
+ }
40415
+ return this;
40416
+ }
40417
+ destroy() {
40418
+ const { t } = this;
40419
+ // clean precompute table
40420
+ for (const elm of t) {
40421
+ (elm.s0 = 0), (elm.s1 = 0), (elm.s2 = 0), (elm.s3 = 0);
40422
+ }
40423
+ }
40424
+ digestInto(out) {
40425
+ exists(this);
40426
+ output(out, this);
40427
+ this.finished = true;
40428
+ const { s0, s1, s2, s3 } = this;
40429
+ const o32 = u32(out);
40430
+ o32[0] = s0;
40431
+ o32[1] = s1;
40432
+ o32[2] = s2;
40433
+ o32[3] = s3;
40434
+ return out;
40435
+ }
40436
+ digest() {
40437
+ const res = new Uint8Array(BLOCK_SIZE$1);
40438
+ this.digestInto(res);
40439
+ this.destroy();
40440
+ return res;
40441
+ }
40442
+ }
40443
+ class Polyval extends GHASH {
40444
+ constructor(key, expectedLength) {
40445
+ key = toBytes(key);
40446
+ const ghKey = _toGHASHKey(key.slice());
40447
+ super(ghKey, expectedLength);
40448
+ ghKey.fill(0);
40449
+ }
40450
+ update(data) {
40451
+ data = toBytes(data);
40452
+ exists(this);
40453
+ const b32 = u32(data);
40454
+ const left = data.length % BLOCK_SIZE$1;
40455
+ const blocks = Math.floor(data.length / BLOCK_SIZE$1);
40456
+ for (let i = 0; i < blocks; i++) {
40457
+ this._updateBlock(swapLE(b32[i * 4 + 3]), swapLE(b32[i * 4 + 2]), swapLE(b32[i * 4 + 1]), swapLE(b32[i * 4 + 0]));
40458
+ }
40459
+ if (left) {
40460
+ ZEROS16.set(data.subarray(blocks * BLOCK_SIZE$1));
40461
+ this._updateBlock(swapLE(ZEROS32[3]), swapLE(ZEROS32[2]), swapLE(ZEROS32[1]), swapLE(ZEROS32[0]));
40462
+ ZEROS32.fill(0); // clean tmp buffer
40463
+ }
40464
+ return this;
40465
+ }
40466
+ digestInto(out) {
40467
+ exists(this);
40468
+ output(out, this);
40469
+ this.finished = true;
40470
+ // tmp ugly hack
40471
+ const { s0, s1, s2, s3 } = this;
40472
+ const o32 = u32(out);
40473
+ o32[0] = s0;
40474
+ o32[1] = s1;
40475
+ o32[2] = s2;
40476
+ o32[3] = s3;
40477
+ return out.reverse();
40478
+ }
40479
+ }
40480
+ function wrapConstructorWithKey(hashCons) {
40481
+ const hashC = (msg, key) => hashCons(key, msg.length).update(toBytes(msg)).digest();
40482
+ const tmp = hashCons(new Uint8Array(16), 0);
40483
+ hashC.outputLen = tmp.outputLen;
40484
+ hashC.blockLen = tmp.blockLen;
40485
+ hashC.create = (key, expectedLength) => hashCons(key, expectedLength);
40486
+ return hashC;
40487
+ }
40488
+ const ghash = wrapConstructorWithKey((key, expectedLength) => new GHASH(key, expectedLength));
40489
+ const polyval = wrapConstructorWithKey((key, expectedLength) => new Polyval(key, expectedLength));
40490
+
40491
+ // AES (Advanced Encryption Standard) aka Rijndael block cipher.
40492
+ //
40493
+ // Data is split into 128-bit blocks. Encrypted in 10/12/14 rounds (128/192/256bit). Every round:
40494
+ // 1. **S-box**, table substitution
40495
+ // 2. **Shift rows**, cyclic shift left of all rows of data array
40496
+ // 3. **Mix columns**, multiplying every column by fixed polynomial
40497
+ // 4. **Add round key**, round_key xor i-th column of array
40498
+ //
40499
+ // Resources:
40500
+ // - FIPS-197 https://csrc.nist.gov/files/pubs/fips/197/final/docs/fips-197.pdf
40501
+ // - Original proposal: https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/aes-development/rijndael-ammended.pdf
40502
+ const BLOCK_SIZE = 16;
40503
+ const BLOCK_SIZE32 = 4;
40504
+ const EMPTY_BLOCK = new Uint8Array(BLOCK_SIZE);
40505
+ const POLY = 0x11b; // 1 + x + x**3 + x**4 + x**8
40506
+ // TODO: remove multiplication, binary ops only
40507
+ function mul2(n) {
40508
+ return (n << 1) ^ (POLY & -(n >> 7));
40509
+ }
40510
+ function mul(a, b) {
40511
+ let res = 0;
40512
+ for (; b > 0; b >>= 1) {
40513
+ // Montgomery ladder
40514
+ res ^= a & -(b & 1); // if (b&1) res ^=a (but const-time).
40515
+ a = mul2(a); // a = 2*a
40516
+ }
40517
+ return res;
40518
+ }
40519
+ // AES S-box is generated using finite field inversion,
40520
+ // an affine transform, and xor of a constant 0x63.
40521
+ const _sbox = /* @__PURE__ */ (() => {
40522
+ let t = new Uint8Array(256);
40523
+ for (let i = 0, x = 1; i < 256; i++, x ^= mul2(x))
40524
+ t[i] = x;
40525
+ const sbox = new Uint8Array(256);
40526
+ sbox[0] = 0x63; // first elm
40527
+ for (let i = 0; i < 255; i++) {
40528
+ let x = t[255 - i];
40529
+ x |= x << 8;
40530
+ sbox[t[i]] = (x ^ (x >> 4) ^ (x >> 5) ^ (x >> 6) ^ (x >> 7) ^ 0x63) & 0xff;
40531
+ }
40532
+ return sbox;
40533
+ })();
40534
+ // Inverted S-box
40535
+ const _inv_sbox = /* @__PURE__ */ _sbox.map((_, j) => _sbox.indexOf(j));
40536
+ // Rotate u32 by 8
40537
+ const rotr32_8 = (n) => (n << 24) | (n >>> 8);
40538
+ const rotl32_8 = (n) => (n << 8) | (n >>> 24);
40539
+ // T-table is optimization suggested in 5.2 of original proposal (missed from FIPS-197). Changes:
40540
+ // - LE instead of BE
40541
+ // - bigger tables: T0 and T1 are merged into T01 table and T2 & T3 into T23;
40542
+ // so index is u16, instead of u8. This speeds up things, unexpectedly
40543
+ function genTtable(sbox, fn) {
40544
+ if (sbox.length !== 256)
40545
+ throw new Error('Wrong sbox length');
40546
+ const T0 = new Uint32Array(256).map((_, j) => fn(sbox[j]));
40547
+ const T1 = T0.map(rotl32_8);
40548
+ const T2 = T1.map(rotl32_8);
40549
+ const T3 = T2.map(rotl32_8);
40550
+ const T01 = new Uint32Array(256 * 256);
40551
+ const T23 = new Uint32Array(256 * 256);
40552
+ const sbox2 = new Uint16Array(256 * 256);
40553
+ for (let i = 0; i < 256; i++) {
40554
+ for (let j = 0; j < 256; j++) {
40555
+ const idx = i * 256 + j;
40556
+ T01[idx] = T0[i] ^ T1[j];
40557
+ T23[idx] = T2[i] ^ T3[j];
40558
+ sbox2[idx] = (sbox[i] << 8) | sbox[j];
40559
+ }
40560
+ }
40561
+ return { sbox, sbox2, T0, T1, T2, T3, T01, T23 };
40562
+ }
40563
+ const TABLE_ENC = /* @__PURE__ */ genTtable(_sbox, (s) => (mul(s, 3) << 24) | (s << 16) | (s << 8) | mul(s, 2));
40564
+ const TABLE_DEC = /* @__PURE__ */ genTtable(_inv_sbox, (s) => (mul(s, 11) << 24) | (mul(s, 13) << 16) | (mul(s, 9) << 8) | mul(s, 14));
40565
+ const POWX = /* @__PURE__ */ (() => {
40566
+ const p = new Uint8Array(16);
40567
+ for (let i = 0, x = 1; i < 16; i++, x = mul2(x))
40568
+ p[i] = x;
40569
+ return p;
40570
+ })();
40571
+ function expandKeyLE(key) {
40572
+ ensureBytes(key);
40573
+ const len = key.length;
40574
+ if (![16, 24, 32].includes(len))
40575
+ throw new Error(`aes: wrong key size: should be 16, 24 or 32, got: ${len}`);
40576
+ const { sbox2 } = TABLE_ENC;
40577
+ const k32 = u32(key);
40578
+ const Nk = k32.length;
40579
+ const subByte = (n) => applySbox(sbox2, n, n, n, n);
40580
+ const xk = new Uint32Array(len + 28); // expanded key
40581
+ xk.set(k32);
40582
+ // 4.3.1 Key expansion
40583
+ for (let i = Nk; i < xk.length; i++) {
40584
+ let t = xk[i - 1];
40585
+ if (i % Nk === 0)
40586
+ t = subByte(rotr32_8(t)) ^ POWX[i / Nk - 1];
40587
+ else if (Nk > 6 && i % Nk === 4)
40588
+ t = subByte(t);
40589
+ xk[i] = xk[i - Nk] ^ t;
40590
+ }
40591
+ return xk;
40592
+ }
40593
+ function expandKeyDecLE(key) {
40594
+ const encKey = expandKeyLE(key);
40595
+ const xk = encKey.slice();
40596
+ const Nk = encKey.length;
40597
+ const { sbox2 } = TABLE_ENC;
40598
+ const { T0, T1, T2, T3 } = TABLE_DEC;
40599
+ // Inverse key by chunks of 4 (rounds)
40600
+ for (let i = 0; i < Nk; i += 4) {
40601
+ for (let j = 0; j < 4; j++)
40602
+ xk[i + j] = encKey[Nk - i - 4 + j];
40603
+ }
40604
+ encKey.fill(0);
40605
+ // apply InvMixColumn except first & last round
40606
+ for (let i = 4; i < Nk - 4; i++) {
40607
+ const x = xk[i];
40608
+ const w = applySbox(sbox2, x, x, x, x);
40609
+ xk[i] = T0[w & 0xff] ^ T1[(w >>> 8) & 0xff] ^ T2[(w >>> 16) & 0xff] ^ T3[w >>> 24];
40610
+ }
40611
+ return xk;
40612
+ }
40613
+ // Apply tables
40614
+ function apply0123(T01, T23, s0, s1, s2, s3) {
40615
+ return (T01[((s0 << 8) & 0xff00) | ((s1 >>> 8) & 0xff)] ^
40616
+ T23[((s2 >>> 8) & 0xff00) | ((s3 >>> 24) & 0xff)]);
40617
+ }
40618
+ function applySbox(sbox2, s0, s1, s2, s3) {
40619
+ return (sbox2[(s0 & 0xff) | (s1 & 0xff00)] |
40620
+ (sbox2[((s2 >>> 16) & 0xff) | ((s3 >>> 16) & 0xff00)] << 16));
40621
+ }
40622
+ function encrypt(xk, s0, s1, s2, s3) {
40623
+ const { sbox2, T01, T23 } = TABLE_ENC;
40624
+ let k = 0;
40625
+ (s0 ^= xk[k++]), (s1 ^= xk[k++]), (s2 ^= xk[k++]), (s3 ^= xk[k++]);
40626
+ const rounds = xk.length / 4 - 2;
40627
+ for (let i = 0; i < rounds; i++) {
40628
+ const t0 = xk[k++] ^ apply0123(T01, T23, s0, s1, s2, s3);
40629
+ const t1 = xk[k++] ^ apply0123(T01, T23, s1, s2, s3, s0);
40630
+ const t2 = xk[k++] ^ apply0123(T01, T23, s2, s3, s0, s1);
40631
+ const t3 = xk[k++] ^ apply0123(T01, T23, s3, s0, s1, s2);
40632
+ (s0 = t0), (s1 = t1), (s2 = t2), (s3 = t3);
40633
+ }
40634
+ // last round (without mixcolumns, so using SBOX2 table)
40635
+ const t0 = xk[k++] ^ applySbox(sbox2, s0, s1, s2, s3);
40636
+ const t1 = xk[k++] ^ applySbox(sbox2, s1, s2, s3, s0);
40637
+ const t2 = xk[k++] ^ applySbox(sbox2, s2, s3, s0, s1);
40638
+ const t3 = xk[k++] ^ applySbox(sbox2, s3, s0, s1, s2);
40639
+ return { s0: t0, s1: t1, s2: t2, s3: t3 };
40640
+ }
40641
+ function decrypt(xk, s0, s1, s2, s3) {
40642
+ const { sbox2, T01, T23 } = TABLE_DEC;
40643
+ let k = 0;
40644
+ (s0 ^= xk[k++]), (s1 ^= xk[k++]), (s2 ^= xk[k++]), (s3 ^= xk[k++]);
40645
+ const rounds = xk.length / 4 - 2;
40646
+ for (let i = 0; i < rounds; i++) {
40647
+ const t0 = xk[k++] ^ apply0123(T01, T23, s0, s3, s2, s1);
40648
+ const t1 = xk[k++] ^ apply0123(T01, T23, s1, s0, s3, s2);
40649
+ const t2 = xk[k++] ^ apply0123(T01, T23, s2, s1, s0, s3);
40650
+ const t3 = xk[k++] ^ apply0123(T01, T23, s3, s2, s1, s0);
40651
+ (s0 = t0), (s1 = t1), (s2 = t2), (s3 = t3);
40652
+ }
40653
+ // Last round
40654
+ const t0 = xk[k++] ^ applySbox(sbox2, s0, s3, s2, s1);
40655
+ const t1 = xk[k++] ^ applySbox(sbox2, s1, s0, s3, s2);
40656
+ const t2 = xk[k++] ^ applySbox(sbox2, s2, s1, s0, s3);
40657
+ const t3 = xk[k++] ^ applySbox(sbox2, s3, s2, s1, s0);
40658
+ return { s0: t0, s1: t1, s2: t2, s3: t3 };
40659
+ }
40660
+ function getDst(len, dst) {
40661
+ if (!dst)
40662
+ return new Uint8Array(len);
40663
+ ensureBytes(dst);
40664
+ if (dst.length < len)
40665
+ throw new Error(`aes: wrong destination length, expected at least ${len}, got: ${dst.length}`);
40666
+ return dst;
40667
+ }
40668
+ // TODO: investigate merging with ctr32
40669
+ function ctrCounter(xk, nonce, src, dst) {
40670
+ ensureBytes(nonce, BLOCK_SIZE);
40671
+ ensureBytes(src);
40672
+ const srcLen = src.length;
40673
+ dst = getDst(srcLen, dst);
40674
+ const ctr = nonce;
40675
+ const c32 = u32(ctr);
40676
+ // Fill block (empty, ctr=0)
40677
+ let { s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]);
40678
+ const src32 = u32(src);
40679
+ const dst32 = u32(dst);
40680
+ // process blocks
40681
+ for (let i = 0; i + 4 <= src32.length; i += 4) {
40682
+ dst32[i + 0] = src32[i + 0] ^ s0;
40683
+ dst32[i + 1] = src32[i + 1] ^ s1;
40684
+ dst32[i + 2] = src32[i + 2] ^ s2;
40685
+ dst32[i + 3] = src32[i + 3] ^ s3;
40686
+ // Full 128 bit counter with wrap around
40687
+ let carry = 1;
40688
+ for (let i = ctr.length - 1; i >= 0; i--) {
40689
+ carry = (carry + (ctr[i] & 0xff)) | 0;
40690
+ ctr[i] = carry & 0xff;
40691
+ carry >>>= 8;
40692
+ }
40693
+ ({ s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]));
40694
+ }
40695
+ // leftovers (less than block)
40696
+ // It's possible to handle > u32 fast, but is it worth it?
40697
+ const start = BLOCK_SIZE * Math.floor(src32.length / BLOCK_SIZE32);
40698
+ if (start < srcLen) {
40699
+ const b32 = new Uint32Array([s0, s1, s2, s3]);
40700
+ const buf = u8(b32);
40701
+ for (let i = start, pos = 0; i < srcLen; i++, pos++)
40702
+ dst[i] = src[i] ^ buf[pos];
40703
+ }
40704
+ return dst;
40705
+ }
40706
+ // AES CTR with overflowing 32 bit counter
40707
+ // It's possible to do 32le significantly simpler (and probably faster) by using u32.
40708
+ // But, we need both, and perf bottleneck is in ghash anyway.
40709
+ function ctr32(xk, isLE, nonce, src, dst) {
40710
+ ensureBytes(nonce, BLOCK_SIZE);
40711
+ ensureBytes(src);
40712
+ dst = getDst(src.length, dst);
40713
+ const ctr = nonce; // write new value to nonce, so it can be re-used
40714
+ const c32 = u32(ctr);
40715
+ const view = createView(ctr);
40716
+ const src32 = u32(src);
40717
+ const dst32 = u32(dst);
40718
+ const ctrPos = isLE ? 0 : 12;
40719
+ const srcLen = src.length;
40720
+ // Fill block (empty, ctr=0)
40721
+ let ctrNum = view.getUint32(ctrPos, isLE); // read current counter value
40722
+ let { s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]);
40723
+ // process blocks
40724
+ for (let i = 0; i + 4 <= src32.length; i += 4) {
40725
+ dst32[i + 0] = src32[i + 0] ^ s0;
40726
+ dst32[i + 1] = src32[i + 1] ^ s1;
40727
+ dst32[i + 2] = src32[i + 2] ^ s2;
40728
+ dst32[i + 3] = src32[i + 3] ^ s3;
40729
+ ctrNum = (ctrNum + 1) >>> 0; // u32 wrap
40730
+ view.setUint32(ctrPos, ctrNum, isLE);
40731
+ ({ s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]));
40732
+ }
40733
+ // leftovers (less than a block)
40734
+ const start = BLOCK_SIZE * Math.floor(src32.length / BLOCK_SIZE32);
40735
+ if (start < srcLen) {
40736
+ const b32 = new Uint32Array([s0, s1, s2, s3]);
40737
+ const buf = u8(b32);
40738
+ for (let i = start, pos = 0; i < srcLen; i++, pos++)
40739
+ dst[i] = src[i] ^ buf[pos];
40740
+ }
40741
+ return dst;
40742
+ }
40743
+ /**
40744
+ * CTR: counter mode. Creates stream cipher.
40745
+ * Requires good IV. Parallelizable. OK, but no MAC.
40746
+ */
40747
+ wrapCipher({ blockSize: 16, nonceLength: 16 }, function ctr(key, nonce) {
40748
+ ensureBytes(key);
40749
+ ensureBytes(nonce, BLOCK_SIZE);
40750
+ function processCtr(buf, dst) {
40751
+ const xk = expandKeyLE(key);
40752
+ const n = nonce.slice();
40753
+ const out = ctrCounter(xk, n, buf, dst);
40754
+ xk.fill(0);
40755
+ n.fill(0);
40756
+ return out;
40757
+ }
40758
+ return {
40759
+ encrypt: (plaintext, dst) => processCtr(plaintext, dst),
40760
+ decrypt: (ciphertext, dst) => processCtr(ciphertext, dst),
40761
+ };
40762
+ });
40763
+ function validateBlockDecrypt(data) {
40764
+ ensureBytes(data);
40765
+ if (data.length % BLOCK_SIZE !== 0) {
40766
+ throw new Error(`aes/(cbc-ecb).decrypt ciphertext should consist of blocks with size ${BLOCK_SIZE}`);
40767
+ }
40768
+ }
40769
+ function validateBlockEncrypt(plaintext, pcks5, dst) {
40770
+ let outLen = plaintext.length;
40771
+ const remaining = outLen % BLOCK_SIZE;
40772
+ if (!pcks5 && remaining !== 0)
40773
+ throw new Error('aec/(cbc-ecb): unpadded plaintext with disabled padding');
40774
+ const b = u32(plaintext);
40775
+ if (pcks5) {
40776
+ let left = BLOCK_SIZE - remaining;
40777
+ if (!left)
40778
+ left = BLOCK_SIZE; // if no bytes left, create empty padding block
40779
+ outLen = outLen + left;
40780
+ }
40781
+ const out = getDst(outLen, dst);
40782
+ const o = u32(out);
40783
+ return { b, o, out };
40784
+ }
40785
+ function validatePCKS(data, pcks5) {
40786
+ if (!pcks5)
40787
+ return data;
40788
+ const len = data.length;
40789
+ if (!len)
40790
+ throw new Error(`aes/pcks5: empty ciphertext not allowed`);
40791
+ const lastByte = data[len - 1];
40792
+ if (lastByte <= 0 || lastByte > 16)
40793
+ throw new Error(`aes/pcks5: wrong padding byte: ${lastByte}`);
40794
+ const out = data.subarray(0, -lastByte);
40795
+ for (let i = 0; i < lastByte; i++)
40796
+ if (data[len - i - 1] !== lastByte)
40797
+ throw new Error(`aes/pcks5: wrong padding`);
40798
+ return out;
40799
+ }
40800
+ function padPCKS(left) {
40801
+ const tmp = new Uint8Array(16);
40802
+ const tmp32 = u32(tmp);
40803
+ tmp.set(left);
40804
+ const paddingByte = BLOCK_SIZE - left.length;
40805
+ for (let i = BLOCK_SIZE - paddingByte; i < BLOCK_SIZE; i++)
40806
+ tmp[i] = paddingByte;
40807
+ return tmp32;
40808
+ }
40809
+ /**
40810
+ * ECB: Electronic CodeBook. Simple deterministic replacement.
40811
+ * Dangerous: always map x to y. See [AES Penguin](https://words.filippo.io/the-ecb-penguin/).
40812
+ */
40813
+ wrapCipher({ blockSize: 16 }, function ecb(key, opts = {}) {
40814
+ ensureBytes(key);
40815
+ const pcks5 = !opts.disablePadding;
40816
+ return {
40817
+ encrypt: (plaintext, dst) => {
40818
+ ensureBytes(plaintext);
40819
+ const { b, o, out: _out } = validateBlockEncrypt(plaintext, pcks5, dst);
40820
+ const xk = expandKeyLE(key);
40821
+ let i = 0;
40822
+ for (; i + 4 <= b.length;) {
40823
+ const { s0, s1, s2, s3 } = encrypt(xk, b[i + 0], b[i + 1], b[i + 2], b[i + 3]);
40824
+ (o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
40825
+ }
40826
+ if (pcks5) {
40827
+ const tmp32 = padPCKS(plaintext.subarray(i * 4));
40828
+ const { s0, s1, s2, s3 } = encrypt(xk, tmp32[0], tmp32[1], tmp32[2], tmp32[3]);
40829
+ (o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
40830
+ }
40831
+ xk.fill(0);
40832
+ return _out;
40833
+ },
40834
+ decrypt: (ciphertext, dst) => {
40835
+ validateBlockDecrypt(ciphertext);
40836
+ const xk = expandKeyDecLE(key);
40837
+ const out = getDst(ciphertext.length, dst);
40838
+ const b = u32(ciphertext);
40839
+ const o = u32(out);
40840
+ for (let i = 0; i + 4 <= b.length;) {
40841
+ const { s0, s1, s2, s3 } = decrypt(xk, b[i + 0], b[i + 1], b[i + 2], b[i + 3]);
40842
+ (o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
40843
+ }
40844
+ xk.fill(0);
40845
+ return validatePCKS(out, pcks5);
40846
+ },
40847
+ };
40848
+ });
40849
+ /**
40850
+ * CBC: Cipher-Block-Chaining. Key is previous round’s block.
40851
+ * Fragile: needs proper padding. Unauthenticated: needs MAC.
40852
+ */
40853
+ wrapCipher({ blockSize: 16, nonceLength: 16 }, function cbc(key, iv, opts = {}) {
40854
+ ensureBytes(key);
40855
+ ensureBytes(iv, 16);
40856
+ const pcks5 = !opts.disablePadding;
40857
+ return {
40858
+ encrypt: (plaintext, dst) => {
40859
+ const xk = expandKeyLE(key);
40860
+ const { b, o, out: _out } = validateBlockEncrypt(plaintext, pcks5, dst);
40861
+ const n32 = u32(iv);
40862
+ // prettier-ignore
40863
+ let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3];
40864
+ let i = 0;
40865
+ for (; i + 4 <= b.length;) {
40866
+ (s0 ^= b[i + 0]), (s1 ^= b[i + 1]), (s2 ^= b[i + 2]), (s3 ^= b[i + 3]);
40867
+ ({ s0, s1, s2, s3 } = encrypt(xk, s0, s1, s2, s3));
40868
+ (o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
40869
+ }
40870
+ if (pcks5) {
40871
+ const tmp32 = padPCKS(plaintext.subarray(i * 4));
40872
+ (s0 ^= tmp32[0]), (s1 ^= tmp32[1]), (s2 ^= tmp32[2]), (s3 ^= tmp32[3]);
40873
+ ({ s0, s1, s2, s3 } = encrypt(xk, s0, s1, s2, s3));
40874
+ (o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
40875
+ }
40876
+ xk.fill(0);
40877
+ return _out;
40878
+ },
40879
+ decrypt: (ciphertext, dst) => {
40880
+ validateBlockDecrypt(ciphertext);
40881
+ const xk = expandKeyDecLE(key);
40882
+ const n32 = u32(iv);
40883
+ const out = getDst(ciphertext.length, dst);
40884
+ const b = u32(ciphertext);
40885
+ const o = u32(out);
40886
+ // prettier-ignore
40887
+ let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3];
40888
+ for (let i = 0; i + 4 <= b.length;) {
40889
+ // prettier-ignore
40890
+ const ps0 = s0, ps1 = s1, ps2 = s2, ps3 = s3;
40891
+ (s0 = b[i + 0]), (s1 = b[i + 1]), (s2 = b[i + 2]), (s3 = b[i + 3]);
40892
+ const { s0: o0, s1: o1, s2: o2, s3: o3 } = decrypt(xk, s0, s1, s2, s3);
40893
+ (o[i++] = o0 ^ ps0), (o[i++] = o1 ^ ps1), (o[i++] = o2 ^ ps2), (o[i++] = o3 ^ ps3);
40894
+ }
40895
+ xk.fill(0);
40896
+ return validatePCKS(out, pcks5);
40897
+ },
40898
+ };
40899
+ });
40900
+ // TODO: merge with chacha, however gcm has bitLen while chacha has byteLen
40901
+ function computeTag(fn, isLE, key, data, AAD) {
40902
+ const h = fn.create(key, data.length + (AAD?.length || 0));
40903
+ if (AAD)
40904
+ h.update(AAD);
40905
+ h.update(data);
40906
+ const num = new Uint8Array(16);
40907
+ const view = createView(num);
40908
+ if (AAD)
40909
+ setBigUint64(view, 0, BigInt(AAD.length * 8), isLE);
40910
+ setBigUint64(view, 8, BigInt(data.length * 8), isLE);
40911
+ h.update(num);
40912
+ return h.digest();
40913
+ }
40914
+ /**
40915
+ * GCM: Galois/Counter Mode.
40916
+ * Good, modern version of CTR, parallel, with MAC.
40917
+ * Be careful: MACs can be forged.
40918
+ */
40919
+ const gcm = wrapCipher({ blockSize: 16, nonceLength: 12, tagLength: 16 }, function gcm(key, nonce, AAD) {
40920
+ ensureBytes(nonce);
40921
+ // Nonce can be pretty much anything (even 1 byte). But smaller nonces less secure.
40922
+ if (nonce.length === 0)
40923
+ throw new Error('aes/gcm: empty nonce');
40924
+ const tagLength = 16;
40925
+ function _computeTag(authKey, tagMask, data) {
40926
+ const tag = computeTag(ghash, false, authKey, data, AAD);
40927
+ for (let i = 0; i < tagMask.length; i++)
40928
+ tag[i] ^= tagMask[i];
40929
+ return tag;
40930
+ }
40931
+ function deriveKeys() {
40932
+ const xk = expandKeyLE(key);
40933
+ const authKey = EMPTY_BLOCK.slice();
40934
+ const counter = EMPTY_BLOCK.slice();
40935
+ ctr32(xk, false, counter, counter, authKey);
40936
+ if (nonce.length === 12) {
40937
+ counter.set(nonce);
40938
+ }
40939
+ else {
40940
+ // Spec (NIST 800-38d) supports variable size nonce.
40941
+ // Not supported for now, but can be useful.
40942
+ const nonceLen = EMPTY_BLOCK.slice();
40943
+ const view = createView(nonceLen);
40944
+ setBigUint64(view, 8, BigInt(nonce.length * 8), false);
40945
+ // ghash(nonce || u64be(0) || u64be(nonceLen*8))
40946
+ ghash.create(authKey).update(nonce).update(nonceLen).digestInto(counter);
40947
+ }
40948
+ const tagMask = ctr32(xk, false, counter, EMPTY_BLOCK);
40949
+ return { xk, authKey, counter, tagMask };
40950
+ }
40951
+ return {
40952
+ encrypt: (plaintext) => {
40953
+ ensureBytes(plaintext);
40954
+ const { xk, authKey, counter, tagMask } = deriveKeys();
40955
+ const out = new Uint8Array(plaintext.length + tagLength);
40956
+ ctr32(xk, false, counter, plaintext, out);
40957
+ const tag = _computeTag(authKey, tagMask, out.subarray(0, out.length - tagLength));
40958
+ out.set(tag, plaintext.length);
40959
+ xk.fill(0);
40960
+ return out;
40961
+ },
40962
+ decrypt: (ciphertext) => {
40963
+ ensureBytes(ciphertext);
40964
+ if (ciphertext.length < tagLength)
40965
+ throw new Error(`aes/gcm: ciphertext less than tagLen (${tagLength})`);
40966
+ const { xk, authKey, counter, tagMask } = deriveKeys();
40967
+ const data = ciphertext.subarray(0, -tagLength);
40968
+ const passedTag = ciphertext.subarray(-tagLength);
40969
+ const tag = _computeTag(authKey, tagMask, data);
40970
+ if (!equalBytes(tag, passedTag))
40971
+ throw new Error('aes/gcm: invalid ghash tag');
40972
+ const out = ctr32(xk, false, counter, data);
40973
+ authKey.fill(0);
40974
+ tagMask.fill(0);
40975
+ xk.fill(0);
40976
+ return out;
40977
+ },
40978
+ };
40979
+ });
40980
+ const limit = (name, min, max) => (value) => {
40981
+ if (!Number.isSafeInteger(value) || min > value || value > max)
40982
+ throw new Error(`${name}: invalid value=${value}, must be [${min}..${max}]`);
40983
+ };
40984
+ /**
40985
+ * AES-GCM-SIV: classic AES-GCM with nonce-misuse resistance.
40986
+ * Guarantees that, when a nonce is repeated, the only security loss is that identical
40987
+ * plaintexts will produce identical ciphertexts.
40988
+ * RFC 8452, https://datatracker.ietf.org/doc/html/rfc8452
40989
+ */
40990
+ wrapCipher({ blockSize: 16, nonceLength: 12, tagLength: 16 }, function siv(key, nonce, AAD) {
40991
+ const tagLength = 16;
40992
+ // From RFC 8452: Section 6
40993
+ const AAD_LIMIT = limit('AAD', 0, 2 ** 36);
40994
+ const PLAIN_LIMIT = limit('plaintext', 0, 2 ** 36);
40995
+ const NONCE_LIMIT = limit('nonce', 12, 12);
40996
+ const CIPHER_LIMIT = limit('ciphertext', 16, 2 ** 36 + 16);
40997
+ ensureBytes(nonce);
40998
+ NONCE_LIMIT(nonce.length);
40999
+ if (AAD) {
41000
+ ensureBytes(AAD);
41001
+ AAD_LIMIT(AAD.length);
41002
+ }
41003
+ function deriveKeys() {
41004
+ const len = key.length;
41005
+ if (len !== 16 && len !== 24 && len !== 32)
41006
+ throw new Error(`key length must be 16, 24 or 32 bytes, got: ${len} bytes`);
41007
+ const xk = expandKeyLE(key);
41008
+ const encKey = new Uint8Array(len);
41009
+ const authKey = new Uint8Array(16);
41010
+ const n32 = u32(nonce);
41011
+ // prettier-ignore
41012
+ let s0 = 0, s1 = n32[0], s2 = n32[1], s3 = n32[2];
41013
+ let counter = 0;
41014
+ for (const derivedKey of [authKey, encKey].map(u32)) {
41015
+ const d32 = u32(derivedKey);
41016
+ for (let i = 0; i < d32.length; i += 2) {
41017
+ // aes(u32le(0) || nonce)[:8] || aes(u32le(1) || nonce)[:8] ...
41018
+ const { s0: o0, s1: o1 } = encrypt(xk, s0, s1, s2, s3);
41019
+ d32[i + 0] = o0;
41020
+ d32[i + 1] = o1;
41021
+ s0 = ++counter; // increment counter inside state
41022
+ }
41023
+ }
41024
+ xk.fill(0);
41025
+ return { authKey, encKey: expandKeyLE(encKey) };
41026
+ }
41027
+ function _computeTag(encKey, authKey, data) {
41028
+ const tag = computeTag(polyval, true, authKey, data, AAD);
41029
+ // Compute the expected tag by XORing S_s and the nonce, clearing the
41030
+ // most significant bit of the last byte and encrypting with the
41031
+ // message-encryption key.
41032
+ for (let i = 0; i < 12; i++)
41033
+ tag[i] ^= nonce[i];
41034
+ tag[15] &= 0x7f; // Clear the highest bit
41035
+ // encrypt tag as block
41036
+ const t32 = u32(tag);
41037
+ // prettier-ignore
41038
+ let s0 = t32[0], s1 = t32[1], s2 = t32[2], s3 = t32[3];
41039
+ ({ s0, s1, s2, s3 } = encrypt(encKey, s0, s1, s2, s3));
41040
+ (t32[0] = s0), (t32[1] = s1), (t32[2] = s2), (t32[3] = s3);
41041
+ return tag;
41042
+ }
41043
+ // actual decrypt/encrypt of message.
41044
+ function processSiv(encKey, tag, input) {
41045
+ let block = tag.slice();
41046
+ block[15] |= 0x80; // Force highest bit
41047
+ return ctr32(encKey, true, block, input);
41048
+ }
41049
+ return {
41050
+ encrypt: (plaintext) => {
41051
+ ensureBytes(plaintext);
41052
+ PLAIN_LIMIT(plaintext.length);
41053
+ const { encKey, authKey } = deriveKeys();
41054
+ const tag = _computeTag(encKey, authKey, plaintext);
41055
+ const out = new Uint8Array(plaintext.length + tagLength);
41056
+ out.set(tag, plaintext.length);
41057
+ out.set(processSiv(encKey, tag, plaintext));
41058
+ encKey.fill(0);
41059
+ authKey.fill(0);
41060
+ return out;
41061
+ },
41062
+ decrypt: (ciphertext) => {
41063
+ ensureBytes(ciphertext);
41064
+ CIPHER_LIMIT(ciphertext.length);
41065
+ const tag = ciphertext.subarray(-tagLength);
41066
+ const { encKey, authKey } = deriveKeys();
41067
+ const plaintext = processSiv(encKey, tag, ciphertext.subarray(0, -tagLength));
41068
+ const expectedTag = _computeTag(encKey, authKey, plaintext);
41069
+ encKey.fill(0);
41070
+ authKey.fill(0);
41071
+ if (!equalBytes(tag, expectedTag))
41072
+ throw new Error('invalid polyval tag');
41073
+ return plaintext;
41074
+ },
41075
+ };
41076
+ });
41077
+
41078
+ /**
41079
+ * Concat KDF (Single Step KDF) per NIST SP 800-56A and RFC 7518 §4.6.2.
41080
+ *
41081
+ * Derives a symmetric key from an ECDH shared secret for use in JWE.
41082
+ *
41083
+ * @param sharedSecret - The raw ECDH shared secret (Z)
41084
+ * @param keyBitLength - Desired key length in bits (e.g. 128, 256)
41085
+ * @param algorithmId - The "enc" value for ECDH-ES or "alg" for ECDH-ES+A*KW
41086
+ * @param apu - Agreement PartyUInfo (typically empty)
41087
+ * @param apv - Agreement PartyVInfo (typically empty)
41088
+ * @returns Derived key as Uint8Array
41089
+ */
41090
+ function concatKdf(sharedSecret, keyBitLength, algorithmId, apu = new Uint8Array(0), apv = new Uint8Array(0)) {
41091
+ const algIdBytes = new TextEncoder().encode(algorithmId);
41092
+ // Build otherInfo per RFC 7518 §4.6.2:
41093
+ // AlgorithmID = len(algId) || algId
41094
+ // PartyUInfo = len(apu) || apu
41095
+ // PartyVInfo = len(apv) || apv
41096
+ // SuppPubInfo = keydatalen (32-bit BE, in bits)
41097
+ const otherInfo = concatBytes(uint32BE(algIdBytes.length), algIdBytes, uint32BE(apu.length), apu, uint32BE(apv.length), apv, uint32BE(keyBitLength));
41098
+ const hashLength = 256; // SHA-256 output in bits
41099
+ const reps = Math.ceil(keyBitLength / hashLength);
41100
+ const result = new Uint8Array(reps * 32);
41101
+ for (let counter = 1; counter <= reps; counter++) {
41102
+ const input = concatBytes(uint32BE(counter), sharedSecret, otherInfo);
41103
+ const digest = sha256$1(input);
41104
+ result.set(digest, (counter - 1) * 32);
41105
+ }
41106
+ return result.slice(0, keyBitLength / 8);
41107
+ }
41108
+ function uint32BE(value) {
41109
+ const buf = new Uint8Array(4);
41110
+ buf[0] = (value >>> 24) & 0xff;
41111
+ buf[1] = (value >>> 16) & 0xff;
41112
+ buf[2] = (value >>> 8) & 0xff;
41113
+ buf[3] = value & 0xff;
41114
+ return buf;
41115
+ }
41116
+ function concatBytes(...arrays) {
41117
+ let totalLength = 0;
41118
+ for (const arr of arrays)
41119
+ totalLength += arr.length;
41120
+ const result = new Uint8Array(totalLength);
41121
+ let offset = 0;
41122
+ for (const arr of arrays) {
41123
+ result.set(arr, offset);
41124
+ offset += arr.length;
41125
+ }
41126
+ return result;
41127
+ }
41128
+
41129
+ const ENCODER = new TextEncoder();
41130
+ const DECODER = new TextDecoder();
41131
+ /**
41132
+ * Build a JWE Compact Serialization string using ECDH-ES + A256GCM.
41133
+ *
41134
+ * Uses an ephemeral secp256k1 keypair for key agreement.
41135
+ * The sender's identity key is NOT involved — only the recipient's public key.
41136
+ *
41137
+ * @param recipientPubKey - Recipient's public key (secp256k1 JWK)
41138
+ * @param plaintext - Data to encrypt
41139
+ * @returns JWE Compact string: header.encryptedKey.iv.ciphertext.tag
41140
+ */
41141
+ function buildJweCompact(recipientPubKey, plaintext) {
41142
+ // 1. Generate ephemeral keypair
41143
+ const ephemeralPrivKey = utils.randomPrivateKey();
41144
+ const ephemeralPubKeyBytes = getPublicKey(ephemeralPrivKey);
41145
+ // Get uncompressed public key coordinates for the JWE header
41146
+ const ephemeralPubHex = etc.bytesToHex(ephemeralPubKeyBytes);
41147
+ const curvePoints = Point.fromHex(ephemeralPubHex);
41148
+ const uncompressed = curvePoints.toRawBytes(false);
41149
+ const epkX = base64url.baseEncode(uncompressed.subarray(1, 33));
41150
+ const epkY = base64url.baseEncode(uncompressed.subarray(33, 65));
41151
+ // 2. Build protected header
41152
+ const header = {
41153
+ alg: 'ECDH-ES',
41154
+ enc: 'A256GCM',
41155
+ epk: {
41156
+ kty: 'EC',
41157
+ crv: 'secp256k1',
41158
+ x: epkX,
41159
+ y: epkY,
41160
+ },
41161
+ };
41162
+ const headerJson = JSON.stringify(header);
41163
+ const headerB64 = base64url.baseEncode(ENCODER.encode(headerJson));
41164
+ // 3. ECDH key agreement: ephemeral private + recipient public
41165
+ const recipientPubBytes = jwkToCompressedBytes(recipientPubKey);
41166
+ const sharedSecret = getSharedSecret(ephemeralPrivKey, recipientPubBytes);
41167
+ // 4. Derive CEK via Concat KDF (RFC 7518 §4.6.2)
41168
+ // For ECDH-ES (direct), algorithmId = enc value
41169
+ const cek = concatKdf(sharedSecret.slice(1), 256, 'A256GCM');
41170
+ // 5. Generate random 96-bit IV
41171
+ const iv = utils.randomPrivateKey().slice(0, 12);
41172
+ // 6. Encrypt with AES-256-GCM
41173
+ // AAD = ASCII bytes of the base64url-encoded protected header
41174
+ const aad = ENCODER.encode(headerB64);
41175
+ const cipher = gcm(cek, iv, aad);
41176
+ const encrypted = cipher.encrypt(plaintext); // returns ciphertext || tag
41177
+ // 7. Split ciphertext and tag (tag is last 16 bytes)
41178
+ const ciphertext = encrypted.slice(0, encrypted.length - 16);
41179
+ const tag = encrypted.slice(encrypted.length - 16);
41180
+ // 8. Assemble JWE Compact: header.encryptedKey.iv.ciphertext.tag
41181
+ // For ECDH-ES (direct), encrypted key is empty
41182
+ return [
41183
+ headerB64,
41184
+ '', // empty encrypted key
41185
+ base64url.baseEncode(iv),
41186
+ base64url.baseEncode(ciphertext),
41187
+ base64url.baseEncode(tag),
41188
+ ].join('.');
41189
+ }
41190
+ /**
41191
+ * Parse and decrypt a JWE Compact Serialization string.
41192
+ *
41193
+ * @param recipientPrivKey - Recipient's private key (secp256k1 JWK)
41194
+ * @param jweCompact - The JWE Compact string to decrypt
41195
+ * @returns Decrypted plaintext
41196
+ */
41197
+ function parseJweCompact(recipientPrivKey, jweCompact) {
41198
+ // 1. Split into 5 parts
41199
+ const parts = jweCompact.split('.');
41200
+ if (parts.length !== 5) {
41201
+ throw new Error('Invalid JWE Compact: expected 5 segments');
41202
+ }
41203
+ const [headerB64, , ivB64, ciphertextB64, tagB64] = parts;
41204
+ // 2. Parse protected header
41205
+ const headerJson = DECODER.decode(base64url.baseDecode(headerB64));
41206
+ const header = JSON.parse(headerJson);
41207
+ if (header.alg !== 'ECDH-ES') {
41208
+ throw new Error(`Unsupported JWE alg: ${header.alg}`);
41209
+ }
41210
+ if (header.enc !== 'A256GCM') {
41211
+ throw new Error(`Unsupported JWE enc: ${header.enc}`);
41212
+ }
41213
+ // 3. Reconstruct ephemeral public key from header
41214
+ const epk = header.epk;
41215
+ const ephemeralPubBytes = jwkToCompressedBytes(epk);
41216
+ // 4. ECDH key agreement: recipient private + ephemeral public
41217
+ const recipientPrivBytes = base64url.baseDecode(recipientPrivKey.d);
41218
+ const sharedSecret = getSharedSecret(recipientPrivBytes, ephemeralPubBytes);
41219
+ // 5. Derive CEK via Concat KDF
41220
+ const cek = concatKdf(sharedSecret.slice(1), 256, 'A256GCM');
41221
+ // 6. Decrypt with AES-256-GCM
41222
+ const iv = base64url.baseDecode(ivB64);
41223
+ const ciphertext = base64url.baseDecode(ciphertextB64);
41224
+ const tag = base64url.baseDecode(tagB64);
41225
+ // Reassemble ciphertext || tag for @noble/ciphers GCM
41226
+ const encrypted = new Uint8Array(ciphertext.length + tag.length);
41227
+ encrypted.set(ciphertext, 0);
41228
+ encrypted.set(tag, ciphertext.length);
41229
+ const aad = ENCODER.encode(headerB64);
41230
+ const cipher = gcm(cek, iv, aad);
41231
+ return cipher.decrypt(encrypted);
41232
+ }
41233
+ /**
41234
+ * Detect whether a string is a JWE Compact Serialization.
41235
+ */
41236
+ function isJweCompact(ciphertext) {
41237
+ return ciphertext.startsWith('eyJ') && ciphertext.split('.').length === 5;
41238
+ }
41239
+ /**
41240
+ * Convert a JWK public key to compressed secp256k1 bytes.
41241
+ */
41242
+ function jwkToCompressedBytes(jwk) {
41243
+ const xBytes = base64url.baseDecode(jwk.x);
41244
+ const yBytes = base64url.baseDecode(jwk.y);
41245
+ const prefix = yBytes[yBytes.length - 1] % 2 === 0 ? 0x02 : 0x03;
41246
+ return new Uint8Array([prefix, ...xBytes]);
41247
+ }
41248
+
40275
41249
  const canonicalize = canonicalizeModule;
40276
41250
  // Polyfill for synchronous signatures
40277
41251
  etc.hmacSha256Sync = (k, ...m) => hmac(sha256$1, k, etc.concatBytes(...m));
@@ -40326,16 +41300,27 @@ class CipherBase {
40326
41300
  const signature = Signature.fromCompact(sigHex);
40327
41301
  return verify(signature, msgHash, compressedPublicKeyBytes);
40328
41302
  }
40329
- encryptBytes(pubKey, privKey, data) {
40330
- const priv = base64url.baseDecode(privKey.d);
40331
- const pub = this.convertJwkToCompressedBytes(pubKey);
40332
- const ss = getSharedSecret(priv, pub);
40333
- const key = ss.slice(0, 32);
40334
- const chacha = managedNonce(xchacha20poly1305)(key);
40335
- const ciphertext = chacha.encrypt(data);
40336
- return base64url.baseEncode(ciphertext);
41303
+ encryptBytes(recipientPubKey, data) {
41304
+ return buildJweCompact(recipientPubKey, data);
41305
+ }
41306
+ decryptBytes(recipientPrivKey, ciphertext, legacyPubKey) {
41307
+ if (isJweCompact(ciphertext)) {
41308
+ return parseJweCompact(recipientPrivKey, ciphertext);
41309
+ }
41310
+ if (legacyPubKey) {
41311
+ return this.decryptBytesLegacy(legacyPubKey, recipientPrivKey, ciphertext);
41312
+ }
41313
+ throw new Error('Cannot decrypt: not a JWE and no legacy public key provided. Pass legacyPubKey as the third argument for old ciphertext.');
40337
41314
  }
40338
- decryptBytes(pubKey, privKey, ciphertext) {
41315
+ encryptMessage(recipientPubKey, message) {
41316
+ const data = utf8ToBytes(message);
41317
+ return this.encryptBytes(recipientPubKey, data);
41318
+ }
41319
+ decryptMessage(recipientPrivKey, ciphertext, legacyPubKey) {
41320
+ const data = this.decryptBytes(recipientPrivKey, ciphertext, legacyPubKey);
41321
+ return bytesToUtf8(data);
41322
+ }
41323
+ decryptBytesLegacy(pubKey, privKey, ciphertext) {
40339
41324
  const priv = base64url.baseDecode(privKey.d);
40340
41325
  const pub = this.convertJwkToCompressedBytes(pubKey);
40341
41326
  const ss = getSharedSecret(priv, pub);
@@ -40344,12 +41329,8 @@ class CipherBase {
40344
41329
  const cipherdata = base64url.baseDecode(ciphertext);
40345
41330
  return chacha.decrypt(cipherdata);
40346
41331
  }
40347
- encryptMessage(pubKey, privKey, message) {
40348
- const data = utf8ToBytes(message);
40349
- return this.encryptBytes(pubKey, privKey, data);
40350
- }
40351
- decryptMessage(pubKey, privKey, ciphertext) {
40352
- const data = this.decryptBytes(pubKey, privKey, ciphertext);
41332
+ decryptMessageLegacy(pubKey, privKey, ciphertext) {
41333
+ const data = this.decryptBytesLegacy(pubKey, privKey, ciphertext);
40353
41334
  return bytesToUtf8(data);
40354
41335
  }
40355
41336
  hasLeadingZeroBits(hexHash, bits) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@didcid/gatekeeper",
3
- "version": "0.3.6",
3
+ "version": "0.4.0",
4
4
  "description": "Archon Gatekeeper",
5
5
  "type": "module",
6
6
  "module": "./dist/esm/index.js",
@@ -106,7 +106,7 @@
106
106
  "author": "David McFadzean <davidmc@gmail.com>",
107
107
  "license": "MIT",
108
108
  "dependencies": {
109
- "@didcid/cipher": "^0.1.3",
109
+ "@didcid/cipher": "^0.2.0",
110
110
  "@didcid/common": "^0.1.3",
111
111
  "@didcid/ipfs": "^0.1.3",
112
112
  "axios": "^1.7.7",
@@ -124,5 +124,5 @@
124
124
  "devDependencies": {
125
125
  "@rollup/plugin-json": "^6.1.0"
126
126
  },
127
- "gitHead": "2f8305e87026fc82787fabb06d6dab419e77679b"
127
+ "gitHead": "4692cc18e1377cafc96aaf7a55cd969a0b09b77c"
128
128
  }