@atlasauth/pca-mpc 0.0.0-stage → 0.2.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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +65 -2
  3. package/dist/beaver.d.ts +49 -0
  4. package/dist/beaver.js +72 -0
  5. package/dist/beaver.js.map +1 -0
  6. package/dist/compose.d.ts +21 -0
  7. package/dist/compose.js +33 -0
  8. package/dist/compose.js.map +1 -0
  9. package/dist/csprng.d.ts +46 -0
  10. package/dist/csprng.js +86 -0
  11. package/dist/csprng.js.map +1 -0
  12. package/dist/ec.d.ts +130 -0
  13. package/dist/ec.js +475 -0
  14. package/dist/ec.js.map +1 -0
  15. package/dist/endemic-ot.d.ts +209 -0
  16. package/dist/endemic-ot.js +405 -0
  17. package/dist/endemic-ot.js.map +1 -0
  18. package/dist/field.d.ts +34 -0
  19. package/dist/field.js +65 -0
  20. package/dist/field.js.map +1 -0
  21. package/dist/gf128.d.ts +23 -0
  22. package/dist/gf128.js +59 -0
  23. package/dist/gf128.js.map +1 -0
  24. package/dist/hybrid-ot.d.ts +153 -0
  25. package/dist/hybrid-ot.js +241 -0
  26. package/dist/hybrid-ot.js.map +1 -0
  27. package/dist/index.d.ts +29 -0
  28. package/dist/index.js +46 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/kem-ot.d.ts +292 -0
  31. package/dist/kem-ot.js +559 -0
  32. package/dist/kem-ot.js.map +1 -0
  33. package/dist/mascot.d.ts +189 -0
  34. package/dist/mascot.js +322 -0
  35. package/dist/mascot.js.map +1 -0
  36. package/dist/ot.d.ts +212 -0
  37. package/dist/ot.js +536 -0
  38. package/dist/ot.js.map +1 -0
  39. package/dist/party.d.ts +38 -0
  40. package/dist/party.js +40 -0
  41. package/dist/party.js.map +1 -0
  42. package/dist/runner.d.ts +56 -0
  43. package/dist/runner.js +97 -0
  44. package/dist/runner.js.map +1 -0
  45. package/dist/sharing.d.ts +40 -0
  46. package/dist/sharing.js +97 -0
  47. package/dist/sharing.js.map +1 -0
  48. package/dist/spdz-runner.d.ts +50 -0
  49. package/dist/spdz-runner.js +107 -0
  50. package/dist/spdz-runner.js.map +1 -0
  51. package/dist/spdz.d.ts +136 -0
  52. package/dist/spdz.js +243 -0
  53. package/dist/spdz.js.map +1 -0
  54. package/dist/thermometer.d.ts +22 -0
  55. package/dist/thermometer.js +41 -0
  56. package/dist/thermometer.js.map +1 -0
  57. package/package.json +58 -3
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Arithmetic in the prime field F_p used for additive secret sharing.
3
+ *
4
+ * p = 2^61 - 1 (the Mersenne prime M61). It is wider than any value the composition ever shares
5
+ * (bits, thermometer bits, small sums), so no secret ever wraps; sharing randomness lives in the
6
+ * full field so that any single share is (information-theoretically) uniform and independent of the
7
+ * secret. All arithmetic is BigInt and exact — never floating point — so the protocol is
8
+ * bit-deterministic and verifier-reproducible.
9
+ *
10
+ * RANDOMNESS (docs §7.1 item "CSPRNG — done"): the `FieldRng` below is a deterministic SplitMix64, kept
11
+ * ONLY for bit-reproducible tests behind an explicit seed. It is NOT the default: the protocol draws
12
+ * all sharing/triple/MAC randomness from the CSPRNG `SecureFieldRng` / `secureOtRandom` in `csprng.ts`
13
+ * (`node:crypto.randomBytes`) unless a test explicitly asks for the seeded stream. `SecureFieldRng`
14
+ * extends this class and overrides `next()`, so it is a drop-in wherever a `FieldRng` is accepted.
15
+ */
16
+ export declare const PRIME: bigint;
17
+ /** Canonical representative in [0, p). */
18
+ export declare function mod(x: bigint): bigint;
19
+ export declare const fadd: (a: bigint, b: bigint) => bigint;
20
+ export declare const fsub: (a: bigint, b: bigint) => bigint;
21
+ export declare const fmul: (a: bigint, b: bigint) => bigint;
22
+ export declare const fneg: (a: bigint) => bigint;
23
+ /**
24
+ * Deterministic SplitMix64, drawing UNBIASED field elements by rejection sampling (reject the top
25
+ * 2^64 mod p = 8 residues so the modulo is exactly uniform). Deterministic for reproducible tests;
26
+ * see the security note above for production.
27
+ */
28
+ export declare class FieldRng {
29
+ private s;
30
+ constructor(seed?: bigint);
31
+ private nextU64;
32
+ /** Uniform element of F_p (unbiased). */
33
+ next(): bigint;
34
+ }
package/dist/field.js ADDED
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ /**
3
+ * Arithmetic in the prime field F_p used for additive secret sharing.
4
+ *
5
+ * p = 2^61 - 1 (the Mersenne prime M61). It is wider than any value the composition ever shares
6
+ * (bits, thermometer bits, small sums), so no secret ever wraps; sharing randomness lives in the
7
+ * full field so that any single share is (information-theoretically) uniform and independent of the
8
+ * secret. All arithmetic is BigInt and exact — never floating point — so the protocol is
9
+ * bit-deterministic and verifier-reproducible.
10
+ *
11
+ * RANDOMNESS (docs §7.1 item "CSPRNG — done"): the `FieldRng` below is a deterministic SplitMix64, kept
12
+ * ONLY for bit-reproducible tests behind an explicit seed. It is NOT the default: the protocol draws
13
+ * all sharing/triple/MAC randomness from the CSPRNG `SecureFieldRng` / `secureOtRandom` in `csprng.ts`
14
+ * (`node:crypto.randomBytes`) unless a test explicitly asks for the seeded stream. `SecureFieldRng`
15
+ * extends this class and overrides `next()`, so it is a drop-in wherever a `FieldRng` is accepted.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.FieldRng = exports.fneg = exports.fmul = exports.fsub = exports.fadd = exports.PRIME = void 0;
19
+ exports.mod = mod;
20
+ exports.PRIME = 2n ** 61n - 1n;
21
+ const MASK64 = (1n << 64n) - 1n;
22
+ /** Canonical representative in [0, p). */
23
+ function mod(x) {
24
+ const r = x % exports.PRIME;
25
+ return r >= 0n ? r : r + exports.PRIME;
26
+ }
27
+ const fadd = (a, b) => mod(a + b);
28
+ exports.fadd = fadd;
29
+ const fsub = (a, b) => mod(a - b);
30
+ exports.fsub = fsub;
31
+ const fmul = (a, b) => mod(a * b);
32
+ exports.fmul = fmul;
33
+ const fneg = (a) => mod(-a);
34
+ exports.fneg = fneg;
35
+ /**
36
+ * Deterministic SplitMix64, drawing UNBIASED field elements by rejection sampling (reject the top
37
+ * 2^64 mod p = 8 residues so the modulo is exactly uniform). Deterministic for reproducible tests;
38
+ * see the security note above for production.
39
+ */
40
+ class FieldRng {
41
+ s;
42
+ constructor(seed = 0x243f6a8885a308d3n) {
43
+ this.s = seed & MASK64;
44
+ }
45
+ nextU64() {
46
+ this.s = (this.s + 0x9e3779b97f4a7c15n) & MASK64;
47
+ let z = this.s;
48
+ z = ((z ^ (z >> 30n)) * 0xbf58476d1ce4e5b9n) & MASK64;
49
+ z = ((z ^ (z >> 27n)) * 0x94d049bb133111ebn) & MASK64;
50
+ z = (z ^ (z >> 31n)) & MASK64;
51
+ return z;
52
+ }
53
+ /** Uniform element of F_p (unbiased). */
54
+ next() {
55
+ // Largest multiple of p that fits in 64 bits; drop anything at or above it so `% PRIME` is uniform.
56
+ const limit = ((MASK64 + 1n) / exports.PRIME) * exports.PRIME;
57
+ for (;;) {
58
+ const u = this.nextU64();
59
+ if (u < limit)
60
+ return u % exports.PRIME;
61
+ }
62
+ }
63
+ }
64
+ exports.FieldRng = FieldRng;
65
+ //# sourceMappingURL=field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field.js","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AAOH,kBAGC;AARY,QAAA,KAAK,GAAG,EAAE,IAAI,GAAG,GAAG,EAAE,CAAC;AAEpC,MAAM,MAAM,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;AAEhC,0CAA0C;AAC1C,SAAgB,GAAG,CAAC,CAAS;IAC3B,MAAM,CAAC,GAAG,CAAC,GAAG,aAAK,CAAC;IACpB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,aAAK,CAAC;AACjC,CAAC;AAEM,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,CAAS,EAAU,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAApD,QAAA,IAAI,QAAgD;AAC1D,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,CAAS,EAAU,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAApD,QAAA,IAAI,QAAgD;AAC1D,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,CAAS,EAAU,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAApD,QAAA,IAAI,QAAgD;AAC1D,MAAM,IAAI,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAAtC,QAAA,IAAI,QAAkC;AAEnD;;;;GAIG;AACH,MAAa,QAAQ;IACX,CAAC,CAAS;IAElB,YAAY,OAAe,mBAAmB;QAC5C,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,MAAM,CAAC;IACzB,CAAC;IAEO,OAAO;QACb,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,mBAAmB,CAAC,GAAG,MAAM,CAAC;QACjD,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACf,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,mBAAmB,CAAC,GAAG,MAAM,CAAC;QACtD,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,mBAAmB,CAAC,GAAG,MAAM,CAAC;QACtD,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC;QAC9B,OAAO,CAAC,CAAC;IACX,CAAC;IAED,yCAAyC;IACzC,IAAI;QACF,oGAAoG;QACpG,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,aAAK,CAAC,GAAG,aAAK,CAAC;QAC9C,SAAS,CAAC;YACR,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;YACzB,IAAI,CAAC,GAAG,KAAK;gBAAE,OAAO,CAAC,GAAG,aAAK,CAAC;QAClC,CAAC;IACH,CAAC;CACF;AAzBD,4BAyBC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * GF(2^128) arithmetic — the field the KOS correlation check lives in (`ot.ts`).
3
+ *
4
+ * The malicious-secure OT-extension consistency check (KOS) folds every row of the extension matrix
5
+ * into a single random linear combination over GF(2^κ) with κ = 128, and verifies one field equation.
6
+ * A cheating receiver who deviates in the extension matrix passes that equation with probability at
7
+ * most `2^{-128}` (the field size), which is what upgrades IKNP from semi-honest to malicious-secure.
8
+ *
9
+ * We use the standard GF(2^128) with reduction polynomial `x^128 + x^7 + x^2 + x + 1` (the GCM /
10
+ * "0x87" polynomial). Elements are 128-bit `bigint`s, bit `i` = coefficient of `x^i`. Addition is XOR.
11
+ * Multiplication is schoolbook carryless multiply with reduction; `mulByXi` (multiply by `x^i`) is the
12
+ * repeated-shift form used by the column-domain check so the hot path pays no full 128×128 multiply
13
+ * per row. This is a textbook field (BigInt, not constant-time) — sound for the in-process prototype;
14
+ * see docs §7.1 for the constant-time boundary.
15
+ */
16
+ /** Field addition in GF(2^128): XOR (masked to 128 bits). */
17
+ export declare function gf128Add(a: bigint, b: bigint): bigint;
18
+ /** Multiply by `x` (one step of the LFSR): shift left 1, reduce if it overflowed bit 127. */
19
+ export declare function gf128MulX(v: bigint): bigint;
20
+ /** Multiply by `x^i` (i ≥ 0): i LFSR steps. Used by the column-domain KOS fold. */
21
+ export declare function gf128MulByXi(v: bigint, i: number): bigint;
22
+ /** Full GF(2^128) multiplication (carryless multiply + reduction), right-to-left over the bits of `b`. */
23
+ export declare function gf128Mul(a: bigint, b: bigint): bigint;
package/dist/gf128.js ADDED
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ /**
3
+ * GF(2^128) arithmetic — the field the KOS correlation check lives in (`ot.ts`).
4
+ *
5
+ * The malicious-secure OT-extension consistency check (KOS) folds every row of the extension matrix
6
+ * into a single random linear combination over GF(2^κ) with κ = 128, and verifies one field equation.
7
+ * A cheating receiver who deviates in the extension matrix passes that equation with probability at
8
+ * most `2^{-128}` (the field size), which is what upgrades IKNP from semi-honest to malicious-secure.
9
+ *
10
+ * We use the standard GF(2^128) with reduction polynomial `x^128 + x^7 + x^2 + x + 1` (the GCM /
11
+ * "0x87" polynomial). Elements are 128-bit `bigint`s, bit `i` = coefficient of `x^i`. Addition is XOR.
12
+ * Multiplication is schoolbook carryless multiply with reduction; `mulByXi` (multiply by `x^i`) is the
13
+ * repeated-shift form used by the column-domain check so the hot path pays no full 128×128 multiply
14
+ * per row. This is a textbook field (BigInt, not constant-time) — sound for the in-process prototype;
15
+ * see docs §7.1 for the constant-time boundary.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.gf128Add = gf128Add;
19
+ exports.gf128MulX = gf128MulX;
20
+ exports.gf128MulByXi = gf128MulByXi;
21
+ exports.gf128Mul = gf128Mul;
22
+ /** 128-bit mask. */
23
+ const MASK128 = (1n << 128n) - 1n;
24
+ /**
25
+ * The reduction tail for `x^128 + x^7 + x^2 + x + 1`: when a shift pushes a 1 past bit 127, we have
26
+ * `x^128 ≡ x^7 + x^2 + x + 1 = 0x87`, so we XOR `0x87` back in.
27
+ */
28
+ const GF128_RED = (1n << 7n) | (1n << 2n) | (1n << 1n) | 1n; // 0x87
29
+ /** Field addition in GF(2^128): XOR (masked to 128 bits). */
30
+ function gf128Add(a, b) {
31
+ return (a ^ b) & MASK128;
32
+ }
33
+ /** Multiply by `x` (one step of the LFSR): shift left 1, reduce if it overflowed bit 127. */
34
+ function gf128MulX(v) {
35
+ const carry = (v >> 127n) & 1n;
36
+ let r = (v << 1n) & MASK128;
37
+ if (carry)
38
+ r ^= GF128_RED;
39
+ return r;
40
+ }
41
+ /** Multiply by `x^i` (i ≥ 0): i LFSR steps. Used by the column-domain KOS fold. */
42
+ function gf128MulByXi(v, i) {
43
+ let r = v & MASK128;
44
+ for (let k = 0; k < i; k++)
45
+ r = gf128MulX(r);
46
+ return r;
47
+ }
48
+ /** Full GF(2^128) multiplication (carryless multiply + reduction), right-to-left over the bits of `b`. */
49
+ function gf128Mul(a, b) {
50
+ let res = 0n;
51
+ let v = a & MASK128;
52
+ for (let i = 0; i < 128; i++) {
53
+ if ((b >> BigInt(i)) & 1n)
54
+ res ^= v;
55
+ v = gf128MulX(v);
56
+ }
57
+ return res & MASK128;
58
+ }
59
+ //# sourceMappingURL=gf128.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gf128.js","sourceRoot":"","sources":["../src/gf128.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AAYH,4BAEC;AAGD,8BAKC;AAGD,oCAIC;AAGD,4BAQC;AAtCD,oBAAoB;AACpB,MAAM,OAAO,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;AAElC;;;GAGG;AACH,MAAM,SAAS,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO;AAEpE,6DAA6D;AAC7D,SAAgB,QAAQ,CAAC,CAAS,EAAE,CAAS;IAC3C,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC;AAC3B,CAAC;AAED,6FAA6F;AAC7F,SAAgB,SAAS,CAAC,CAAS;IACjC,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IAC/B,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC;IAC5B,IAAI,KAAK;QAAE,CAAC,IAAI,SAAS,CAAC;IAC1B,OAAO,CAAC,CAAC;AACX,CAAC;AAED,mFAAmF;AACnF,SAAgB,YAAY,CAAC,CAAS,EAAE,CAAS;IAC/C,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;QAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,CAAC;AACX,CAAC;AAED,0GAA0G;AAC1G,SAAgB,QAAQ,CAAC,CAAS,EAAE,CAAS;IAC3C,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE;YAAE,GAAG,IAAI,CAAC,CAAC;QACpC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,GAAG,OAAO,CAAC;AACvB,CAAC"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Hybrid base OT — a SOUND combiner of the malicious-secure EC base OT (`ot.ts`/`ec.ts`) and the
3
+ * post-quantum committed ML-KEM base OT (`kem-ot.ts`), run together so the stack inherits the BEST
4
+ * guarantee of each half instead of the weakest.
5
+ *
6
+ * ─── WHY A COMBINER ────────────────────────────────────────────────────────────────────────────────
7
+ * The two base OTs this package ships have complementary weaknesses:
8
+ * • The EC base OT (`malBaseRandomOT`) is MALICIOUS-secure against both roles — a Schnorr NIZK proof
9
+ * of knowledge of `y` on `S = y·B`, full prime-order-subgroup validation of every transmitted
10
+ * point, and a random-OT core with no sender-chosen ciphertexts — but its hardness is the curve25519
11
+ * discrete log, which a QUANTUM adversary breaks with Shor.
12
+ * • The ML-KEM base OT (`kemBaseOtMalRandom`) is POST-QUANTUM (Module-LWE) and has unconditional,
13
+ * even-against-a-quantum-sender choice privacy, but against an actively-malicious receiver it has an
14
+ * IRREDUCIBLE residue from the opaque primitive: a receiver that runs keygen twice and keeps BOTH
15
+ * secret keys learns both messages, and the decisional-MLWE indistinguishability that gives choice
16
+ * privacy makes that cheat undetectable to the sender (see `kem-ot.ts` header).
17
+ *
18
+ * The combiner runs BOTH base OTs on the SAME choice bit and the SAME session, then derives each
19
+ * transfer key from BOTH halves' shared secrets:
20
+ *
21
+ * k_b = H( DOMAIN_SEP ‖ sid ‖ i ‖ b ‖ ss_ec_b ‖ ss_kem_b ) for b ∈ {0, 1}
22
+ *
23
+ * where `ss_ec_b` is the EC random-OT pad (`malBaseRandomOT`) and `ss_kem_b` the committed ML-KEM
24
+ * random-OT secret (`kemBaseOtMalRandom`, the hardened variant — never the semi-honest one). The sender
25
+ * one-time-pads `m_b` under `k_b` and commits to it (`com_b`); the honest receiver reconstructs only
26
+ * `k_c` (it holds only `ss_ec_c` and `ss_kem_c`) and checks `com_c`.
27
+ *
28
+ * ─── WHAT THIS ACHIEVES (precise) ───────────────────────────────────────────────────────────────────
29
+ * To recover `m_{1−c}` an adversary needs `k_{1−c}`, hence BOTH `ss_ec_{1−c}` AND `ss_kem_{1−c}` (the
30
+ * hash is a random oracle; missing either input leaves the pad pseudorandom). Therefore:
31
+ * • vs a CLASSICAL malicious receiver — MALICIOUS-secure. Even a receiver that defeats the KEM half
32
+ * entirely by keeping both ML-KEM secrets (so it holds `ss_kem_{1−c}`) still cannot obtain
33
+ * `ss_ec_{1−c}`: the EC base OT is malicious-secure and `ss_ec_{1−c} = H(y·R − T)` is a point the
34
+ * receiver cannot compute without the discrete log of `S`. No `ss_ec_{1−c}` ⇒ no `k_{1−c}` ⇒ `m_{1−c}`
35
+ * stays hidden. This is the KILLER property the tests prove.
36
+ * • SENDER PRIVACY vs a SEMI-HONEST QUANTUM receiver — holds via the KEM half (Module-LWE): a quantum
37
+ * receiver that follows the protocol (discards the non-chosen material) cannot get `ss_kem_{1−c}`,
38
+ * so again `k_{1−c}` is unobtainable even though it could break the EC half with Shor.
39
+ * • CHOICE PRIVACY — POST-QUANTUM. The KEM half's choice privacy is unconditional (two i.i.d. real
40
+ * ML-KEM public keys on the wire); the EC half's is information-theoretic in its transcript (`R` is a
41
+ * uniform subgroup element whatever `c` is). Neither leaks `c`, against any adversary.
42
+ * • SESSION BINDING — both halves are bound to a COMMON `sid` derived from BOTH sub-protocols'
43
+ * session material (the KEM `(rn, sn)` nonces and the EC point `S`), and `sid` is hashed into every
44
+ * transfer key and commitment, so secrets from one session/opened-key-set cannot be replayed into
45
+ * another. The KEM half additionally aborts (`KemBaseOtAbort`) if the receiver opens keys other than
46
+ * the ones it committed to; the EC half aborts (`BaseOtAbort`/`PointValidationError`) on a bad proof
47
+ * or an off-/small-order point; and the combiner aborts (`HybridOtAbort`) if a tampered ciphertext
48
+ * fails its commitment.
49
+ *
50
+ * ─── THE HONEST BOUNDARY (NOT faked) ────────────────────────────────────────────────────────────────
51
+ * The one residue this combiner does NOT close is a receiver that is SIMULTANEOUSLY actively-malicious
52
+ * AND quantum: such a receiver could break the classical EC half with Shor (recovering `ss_ec_{1−c}`
53
+ * from the transcript) WHILE keeping both ML-KEM secrets (recovering `ss_kem_{1−c}`), and so assemble
54
+ * `k_{1−c}`. Closing THAT requires a base OT that is BOTH post-quantum AND malicious-secure in one
55
+ * primitive — i.e. an endemic/UC OT from Module-LWE with a CRS, which needs direct lattice module
56
+ * arithmetic (sampling / adding in ML-KEM's structured public-key space to plant a secretless slot).
57
+ * The opaque `ml_kem768` keygen/encaps/decaps interface this package is restricted to does NOT expose
58
+ * that arithmetic, and hand-rolling it is exactly the lattice crypto the module forbids. So this is the
59
+ * honest limit of what a KEM-opaque + EC combiner can reach; it is stated, not papered over.
60
+ *
61
+ * No hand-rolled crypto: the EC half goes through `malBaseRandomOT` (unchanged), the KEM half through
62
+ * `kemBaseOtMalRandom` (unchanged), and this module only hashes (SHA-256 RO) and XORs their outputs.
63
+ */
64
+ import { type Point } from './ec';
65
+ import { type BaseOtTamper, type OtChannel, type OtRandom } from './ot';
66
+ import { type KemMalTamper } from './kem-ot';
67
+ /** Raised when the hybrid combiner aborts: a sender ciphertext inconsistent with its commitment is caught. */
68
+ export declare class HybridOtAbort extends Error {
69
+ constructor(message: string);
70
+ }
71
+ /**
72
+ * The COMMON session id binding both halves: `H(TAG ‖ kemSid ‖ encode(ecS))`. It folds the KEM
73
+ * sub-protocol's two-nonce session id (`rn, sn`) and the EC sub-protocol's point `S`, so neither party
74
+ * alone fixes it and secrets cannot be mixed across sessions. Both parties derive the same value from
75
+ * the public transcript.
76
+ */
77
+ export declare function hybridSessionId(kemSid: Uint8Array, ecS: Point): Uint8Array;
78
+ /**
79
+ * The hybrid transfer key for one slot: `H(TAG ‖ sid ‖ i ‖ slot ‖ ss_ec ‖ ss_kem)` expanded (SHA-256
80
+ * counter mode) to `len` bytes. Modelled as a random oracle: the key is pseudorandom unless BOTH
81
+ * `ss_ec` and `ss_kem` (for that exact slot/index/session) are supplied — the soundness crux of the
82
+ * combiner.
83
+ */
84
+ export declare function hybridTransferKey(sid: Uint8Array, index: number, slot: number, ssEc: Uint8Array, ssKem: Uint8Array, len: number): Uint8Array;
85
+ /** The sender's wire message of the hybrid combiner: one-time-padded messages + per-message commitments. */
86
+ export interface HybridSenderMessage {
87
+ /** For each transfer i, `[E_0, E_1]` with `E_b = m_b ⊕ k_b`. */
88
+ encrypted: Array<[Uint8Array, Uint8Array]>;
89
+ /** For each transfer i, `[com_0, com_1]` with `com_b = H(sid ‖ i ‖ b ‖ m_b ‖ k_b)`. */
90
+ commitments: Array<[Uint8Array, Uint8Array]>;
91
+ }
92
+ /**
93
+ * SENDER side of the combiner: given BOTH halves' per-slot shared secrets, derive both transfer keys,
94
+ * one-time-pad each message, and commit to each. `ecSecrets[i] = [ss_ec_0, ss_ec_1]` (the EC random-OT
95
+ * `senderPads`), `kemSecrets[i] = [ss_kem_0, ss_kem_1]` (the committed-KEM `senderSecrets`).
96
+ */
97
+ export declare function hybridSenderEncrypt(sid: Uint8Array, messages: Array<[Uint8Array, Uint8Array]>, ecSecrets: Array<[Uint8Array, Uint8Array]>, kemSecrets: Array<[Uint8Array, Uint8Array]>): HybridSenderMessage;
98
+ /**
99
+ * RECEIVER side of the combiner: reconstruct ONLY the chosen transfer key (from the chosen EC and KEM
100
+ * secrets the receiver legitimately holds), unmask the chosen message, and CHECK the commitment.
101
+ * `ecRecv[i] = ss_ec_{choices[i]}` (the EC `recvPads`), `kemRecv[i] = ss_kem_{choices[i]}` (the
102
+ * committed-KEM `recvSecrets`). Aborts (`HybridOtAbort`) if the recovered message fails its commitment.
103
+ */
104
+ export declare function hybridReceiverRecover(sid: Uint8Array, choices: number[], sender: HybridSenderMessage, ecRecv: Uint8Array[], kemRecv: Uint8Array[]): Uint8Array[];
105
+ /**
106
+ * TEST-ONLY injection points modelling malicious behaviour in the hybrid combiner. The `ec` / `kem`
107
+ * bags are forwarded to the respective sub-OTs (so each half's real malicious checks run against the
108
+ * adversarial value); `tamperEncrypted` corrupts the hybrid sender's ciphertexts on the wire (caught by
109
+ * the commitment check).
110
+ */
111
+ export interface HybridBaseOtTamper {
112
+ /** Forwarded to the EC half (`malBaseRandomOT`): malicious sender/receiver on the EC sub-OT. */
113
+ ec?: BaseOtTamper;
114
+ /** Forwarded to the KEM half (`kemBaseOtMalRandom`): malicious receiver on the committed KEM sub-OT. */
115
+ kem?: KemMalTamper;
116
+ /** Malicious hybrid SENDER: corrupt the padded messages on the wire (inconsistent with the commitment). */
117
+ tamperEncrypted?: (encrypted: Array<[Uint8Array, Uint8Array]>) => void;
118
+ }
119
+ /** A full hybrid base-OT transcript, exposed for inspection/tests. */
120
+ export interface HybridBaseOtTranscript {
121
+ /** The common session id both halves are bound to. */
122
+ sid: Uint8Array;
123
+ /** The EC sub-OT's public point `S` (with its Schnorr proof available via the EC half). */
124
+ ecS: Point;
125
+ /** The committed-KEM sub-OT session id `H(rn ‖ sn)`. */
126
+ kemSid: Uint8Array;
127
+ encrypted: Array<[Uint8Array, Uint8Array]>;
128
+ commitments: Array<[Uint8Array, Uint8Array]>;
129
+ }
130
+ export interface HybridBaseOtResult {
131
+ /** Receiver's recovered messages `m_{i, choices[i]}`. */
132
+ received: Uint8Array[];
133
+ transcript: HybridBaseOtTranscript;
134
+ }
135
+ /**
136
+ * In-process combined simulator (both parties in one call), matching the shape of `baseOT`/`kemBaseOT`
137
+ * so it is a drop-in for `OtChannel`'s `baseOtFn` (`withHybridBaseOt`). Runs the malicious-secure EC
138
+ * base OT and the committed ML-KEM base OT on the SAME choices, binds both to a common `sid`, and
139
+ * combines their per-slot secrets into the transfer keys. A cheating party is caught by whichever
140
+ * half's check applies (EC proof/point validation, KEM commitment, or the hybrid ciphertext
141
+ * commitment). `tamper` injects malicious behaviour for tests.
142
+ */
143
+ export declare function hybridBaseOT(messages: Array<[Uint8Array, Uint8Array]>, choices: number[], rng?: OtRandom, tamper?: HybridBaseOtTamper): HybridBaseOtResult;
144
+ /**
145
+ * Wire the HYBRID base OT into the KOS OT-extension: a drop-in for `withKemBaseOt`/`withKemBaseOtMal`
146
+ * that makes `OtChannel` run its κ setup base OTs over the combiner. The extension inherits the
147
+ * combiner's guarantee — malicious security vs a classical receiver, PQ sender-privacy vs a semi-honest
148
+ * quantum one, and PQ choice privacy — with everything above the base OT (IKNP matrix, KOS correlation
149
+ * check, correlation-robust hash) unchanged.
150
+ *
151
+ * const ch = new OtChannel(secureOtRandom(), withHybridBaseOt());
152
+ */
153
+ export declare function withHybridBaseOt(): ConstructorParameters<typeof OtChannel>[1];
@@ -0,0 +1,241 @@
1
+ "use strict";
2
+ /**
3
+ * Hybrid base OT — a SOUND combiner of the malicious-secure EC base OT (`ot.ts`/`ec.ts`) and the
4
+ * post-quantum committed ML-KEM base OT (`kem-ot.ts`), run together so the stack inherits the BEST
5
+ * guarantee of each half instead of the weakest.
6
+ *
7
+ * ─── WHY A COMBINER ────────────────────────────────────────────────────────────────────────────────
8
+ * The two base OTs this package ships have complementary weaknesses:
9
+ * • The EC base OT (`malBaseRandomOT`) is MALICIOUS-secure against both roles — a Schnorr NIZK proof
10
+ * of knowledge of `y` on `S = y·B`, full prime-order-subgroup validation of every transmitted
11
+ * point, and a random-OT core with no sender-chosen ciphertexts — but its hardness is the curve25519
12
+ * discrete log, which a QUANTUM adversary breaks with Shor.
13
+ * • The ML-KEM base OT (`kemBaseOtMalRandom`) is POST-QUANTUM (Module-LWE) and has unconditional,
14
+ * even-against-a-quantum-sender choice privacy, but against an actively-malicious receiver it has an
15
+ * IRREDUCIBLE residue from the opaque primitive: a receiver that runs keygen twice and keeps BOTH
16
+ * secret keys learns both messages, and the decisional-MLWE indistinguishability that gives choice
17
+ * privacy makes that cheat undetectable to the sender (see `kem-ot.ts` header).
18
+ *
19
+ * The combiner runs BOTH base OTs on the SAME choice bit and the SAME session, then derives each
20
+ * transfer key from BOTH halves' shared secrets:
21
+ *
22
+ * k_b = H( DOMAIN_SEP ‖ sid ‖ i ‖ b ‖ ss_ec_b ‖ ss_kem_b ) for b ∈ {0, 1}
23
+ *
24
+ * where `ss_ec_b` is the EC random-OT pad (`malBaseRandomOT`) and `ss_kem_b` the committed ML-KEM
25
+ * random-OT secret (`kemBaseOtMalRandom`, the hardened variant — never the semi-honest one). The sender
26
+ * one-time-pads `m_b` under `k_b` and commits to it (`com_b`); the honest receiver reconstructs only
27
+ * `k_c` (it holds only `ss_ec_c` and `ss_kem_c`) and checks `com_c`.
28
+ *
29
+ * ─── WHAT THIS ACHIEVES (precise) ───────────────────────────────────────────────────────────────────
30
+ * To recover `m_{1−c}` an adversary needs `k_{1−c}`, hence BOTH `ss_ec_{1−c}` AND `ss_kem_{1−c}` (the
31
+ * hash is a random oracle; missing either input leaves the pad pseudorandom). Therefore:
32
+ * • vs a CLASSICAL malicious receiver — MALICIOUS-secure. Even a receiver that defeats the KEM half
33
+ * entirely by keeping both ML-KEM secrets (so it holds `ss_kem_{1−c}`) still cannot obtain
34
+ * `ss_ec_{1−c}`: the EC base OT is malicious-secure and `ss_ec_{1−c} = H(y·R − T)` is a point the
35
+ * receiver cannot compute without the discrete log of `S`. No `ss_ec_{1−c}` ⇒ no `k_{1−c}` ⇒ `m_{1−c}`
36
+ * stays hidden. This is the KILLER property the tests prove.
37
+ * • SENDER PRIVACY vs a SEMI-HONEST QUANTUM receiver — holds via the KEM half (Module-LWE): a quantum
38
+ * receiver that follows the protocol (discards the non-chosen material) cannot get `ss_kem_{1−c}`,
39
+ * so again `k_{1−c}` is unobtainable even though it could break the EC half with Shor.
40
+ * • CHOICE PRIVACY — POST-QUANTUM. The KEM half's choice privacy is unconditional (two i.i.d. real
41
+ * ML-KEM public keys on the wire); the EC half's is information-theoretic in its transcript (`R` is a
42
+ * uniform subgroup element whatever `c` is). Neither leaks `c`, against any adversary.
43
+ * • SESSION BINDING — both halves are bound to a COMMON `sid` derived from BOTH sub-protocols'
44
+ * session material (the KEM `(rn, sn)` nonces and the EC point `S`), and `sid` is hashed into every
45
+ * transfer key and commitment, so secrets from one session/opened-key-set cannot be replayed into
46
+ * another. The KEM half additionally aborts (`KemBaseOtAbort`) if the receiver opens keys other than
47
+ * the ones it committed to; the EC half aborts (`BaseOtAbort`/`PointValidationError`) on a bad proof
48
+ * or an off-/small-order point; and the combiner aborts (`HybridOtAbort`) if a tampered ciphertext
49
+ * fails its commitment.
50
+ *
51
+ * ─── THE HONEST BOUNDARY (NOT faked) ────────────────────────────────────────────────────────────────
52
+ * The one residue this combiner does NOT close is a receiver that is SIMULTANEOUSLY actively-malicious
53
+ * AND quantum: such a receiver could break the classical EC half with Shor (recovering `ss_ec_{1−c}`
54
+ * from the transcript) WHILE keeping both ML-KEM secrets (recovering `ss_kem_{1−c}`), and so assemble
55
+ * `k_{1−c}`. Closing THAT requires a base OT that is BOTH post-quantum AND malicious-secure in one
56
+ * primitive — i.e. an endemic/UC OT from Module-LWE with a CRS, which needs direct lattice module
57
+ * arithmetic (sampling / adding in ML-KEM's structured public-key space to plant a secretless slot).
58
+ * The opaque `ml_kem768` keygen/encaps/decaps interface this package is restricted to does NOT expose
59
+ * that arithmetic, and hand-rolling it is exactly the lattice crypto the module forbids. So this is the
60
+ * honest limit of what a KEM-opaque + EC combiner can reach; it is stated, not papered over.
61
+ *
62
+ * No hand-rolled crypto: the EC half goes through `malBaseRandomOT` (unchanged), the KEM half through
63
+ * `kemBaseOtMalRandom` (unchanged), and this module only hashes (SHA-256 RO) and XORs their outputs.
64
+ */
65
+ Object.defineProperty(exports, "__esModule", { value: true });
66
+ exports.HybridOtAbort = void 0;
67
+ exports.hybridSessionId = hybridSessionId;
68
+ exports.hybridTransferKey = hybridTransferKey;
69
+ exports.hybridSenderEncrypt = hybridSenderEncrypt;
70
+ exports.hybridReceiverRecover = hybridReceiverRecover;
71
+ exports.hybridBaseOT = hybridBaseOT;
72
+ exports.withHybridBaseOt = withHybridBaseOt;
73
+ const node_crypto_1 = require("node:crypto");
74
+ const ec_1 = require("./ec");
75
+ const ot_1 = require("./ot");
76
+ const kem_ot_1 = require("./kem-ot");
77
+ const csprng_1 = require("./csprng");
78
+ const TAG_HYBRID_SID = new TextEncoder().encode('atlas-pca-mpc/hybrid-ot-sid');
79
+ const TAG_HYBRID_KEY = new TextEncoder().encode('atlas-pca-mpc/hybrid-ot-transfer-key');
80
+ const TAG_HYBRID_COMMIT = new TextEncoder().encode('atlas-pca-mpc/hybrid-ot-msg-commit');
81
+ function sha256(...chunks) {
82
+ const h = (0, node_crypto_1.createHash)('sha256');
83
+ for (const c of chunks)
84
+ h.update(c);
85
+ return new Uint8Array(h.digest());
86
+ }
87
+ function u32(x) {
88
+ const b = new Uint8Array(4);
89
+ b[0] = (x >>> 24) & 0xff;
90
+ b[1] = (x >>> 16) & 0xff;
91
+ b[2] = (x >>> 8) & 0xff;
92
+ b[3] = x & 0xff;
93
+ return b;
94
+ }
95
+ function xorBytes(a, b) {
96
+ if (a.length !== b.length)
97
+ throw new Error('hybrid-ot: XOR length mismatch');
98
+ const out = new Uint8Array(a.length);
99
+ for (let i = 0; i < a.length; i++)
100
+ out[i] = a[i] ^ b[i];
101
+ return out;
102
+ }
103
+ function ctEqual(a, b) {
104
+ return a.length === b.length && (0, node_crypto_1.timingSafeEqual)(Buffer.from(a), Buffer.from(b));
105
+ }
106
+ /** Raised when the hybrid combiner aborts: a sender ciphertext inconsistent with its commitment is caught. */
107
+ class HybridOtAbort extends Error {
108
+ constructor(message) {
109
+ super(message);
110
+ this.name = 'HybridOtAbort';
111
+ }
112
+ }
113
+ exports.HybridOtAbort = HybridOtAbort;
114
+ /**
115
+ * The COMMON session id binding both halves: `H(TAG ‖ kemSid ‖ encode(ecS))`. It folds the KEM
116
+ * sub-protocol's two-nonce session id (`rn, sn`) and the EC sub-protocol's point `S`, so neither party
117
+ * alone fixes it and secrets cannot be mixed across sessions. Both parties derive the same value from
118
+ * the public transcript.
119
+ */
120
+ function hybridSessionId(kemSid, ecS) {
121
+ return sha256(TAG_HYBRID_SID, kemSid, (0, ec_1.encodePoint)(ecS));
122
+ }
123
+ /**
124
+ * The hybrid transfer key for one slot: `H(TAG ‖ sid ‖ i ‖ slot ‖ ss_ec ‖ ss_kem)` expanded (SHA-256
125
+ * counter mode) to `len` bytes. Modelled as a random oracle: the key is pseudorandom unless BOTH
126
+ * `ss_ec` and `ss_kem` (for that exact slot/index/session) are supplied — the soundness crux of the
127
+ * combiner.
128
+ */
129
+ function hybridTransferKey(sid, index, slot, ssEc, ssKem, len) {
130
+ const out = new Uint8Array(len);
131
+ let produced = 0;
132
+ let ctr = 0;
133
+ while (produced < len) {
134
+ const block = sha256(TAG_HYBRID_KEY, sid, u32(index), Uint8Array.of(slot & 1), u32(ctr), ssEc, ssKem);
135
+ for (let k = 0; k < block.length && produced < len; k++)
136
+ out[produced++] = block[k];
137
+ ctr++;
138
+ }
139
+ return out;
140
+ }
141
+ /** Per-message RO commitment `com = H(TAG ‖ sid ‖ i ‖ slot ‖ msg ‖ key)`, bound to the session. */
142
+ function hybridCommit(sid, index, slot, msg, key) {
143
+ return sha256(TAG_HYBRID_COMMIT, sid, u32(index), Uint8Array.of(slot & 1), msg, key);
144
+ }
145
+ /**
146
+ * SENDER side of the combiner: given BOTH halves' per-slot shared secrets, derive both transfer keys,
147
+ * one-time-pad each message, and commit to each. `ecSecrets[i] = [ss_ec_0, ss_ec_1]` (the EC random-OT
148
+ * `senderPads`), `kemSecrets[i] = [ss_kem_0, ss_kem_1]` (the committed-KEM `senderSecrets`).
149
+ */
150
+ function hybridSenderEncrypt(sid, messages, ecSecrets, kemSecrets) {
151
+ if (messages.length !== ecSecrets.length || messages.length !== kemSecrets.length) {
152
+ throw new Error('hybridSenderEncrypt: messages/ecSecrets/kemSecrets length mismatch');
153
+ }
154
+ const encrypted = [];
155
+ const commitments = [];
156
+ for (let i = 0; i < messages.length; i++) {
157
+ const [m0, m1] = messages[i];
158
+ if (m0.length !== m1.length) {
159
+ throw new Error(`hybridSenderEncrypt (index ${i}): the two messages must be equal length`);
160
+ }
161
+ const k0 = hybridTransferKey(sid, i, 0, ecSecrets[i][0], kemSecrets[i][0], m0.length);
162
+ const k1 = hybridTransferKey(sid, i, 1, ecSecrets[i][1], kemSecrets[i][1], m1.length);
163
+ encrypted.push([xorBytes(m0, k0), xorBytes(m1, k1)]);
164
+ commitments.push([hybridCommit(sid, i, 0, m0, k0), hybridCommit(sid, i, 1, m1, k1)]);
165
+ }
166
+ return { encrypted, commitments };
167
+ }
168
+ /**
169
+ * RECEIVER side of the combiner: reconstruct ONLY the chosen transfer key (from the chosen EC and KEM
170
+ * secrets the receiver legitimately holds), unmask the chosen message, and CHECK the commitment.
171
+ * `ecRecv[i] = ss_ec_{choices[i]}` (the EC `recvPads`), `kemRecv[i] = ss_kem_{choices[i]}` (the
172
+ * committed-KEM `recvSecrets`). Aborts (`HybridOtAbort`) if the recovered message fails its commitment.
173
+ */
174
+ function hybridReceiverRecover(sid, choices, sender, ecRecv, kemRecv) {
175
+ const { encrypted, commitments } = sender;
176
+ if (encrypted.length !== choices.length ||
177
+ commitments.length !== choices.length ||
178
+ ecRecv.length !== choices.length ||
179
+ kemRecv.length !== choices.length) {
180
+ throw new Error('hybridReceiverRecover: length mismatch');
181
+ }
182
+ const received = [];
183
+ for (let i = 0; i < choices.length; i++) {
184
+ const c = choices[i] & 1;
185
+ const e = encrypted[i][c];
186
+ const key = hybridTransferKey(sid, i, c, ecRecv[i], kemRecv[i], e.length);
187
+ const m = xorBytes(e, key);
188
+ const check = hybridCommit(sid, i, c, m, key);
189
+ if (!ctEqual(check, commitments[i][c])) {
190
+ throw new HybridOtAbort(`hybrid base OT (index ${i}): sender ciphertext inconsistent with its commitment; aborting`);
191
+ }
192
+ received.push(m);
193
+ }
194
+ return received;
195
+ }
196
+ /**
197
+ * In-process combined simulator (both parties in one call), matching the shape of `baseOT`/`kemBaseOT`
198
+ * so it is a drop-in for `OtChannel`'s `baseOtFn` (`withHybridBaseOt`). Runs the malicious-secure EC
199
+ * base OT and the committed ML-KEM base OT on the SAME choices, binds both to a common `sid`, and
200
+ * combines their per-slot secrets into the transfer keys. A cheating party is caught by whichever
201
+ * half's check applies (EC proof/point validation, KEM commitment, or the hybrid ciphertext
202
+ * commitment). `tamper` injects malicious behaviour for tests.
203
+ */
204
+ function hybridBaseOT(messages, choices, rng = (0, csprng_1.secureOtRandom)(), tamper) {
205
+ if (messages.length !== choices.length) {
206
+ throw new Error('hybridBaseOT: messages/choices length mismatch');
207
+ }
208
+ // EC half: malicious-secure random OT with ALL checks ON (Schnorr proof + subgroup validation).
209
+ const ec = (0, ot_1.malBaseRandomOT)(choices, rng, tamper?.ec);
210
+ // KEM half: the committed/session-bound random OT (aborts on an opened-key/commitment mismatch).
211
+ const kem = (0, kem_ot_1.kemBaseOtMalRandom)(choices, rng, tamper?.kem);
212
+ const sid = hybridSessionId(kem.sid, ec.S);
213
+ const sender = hybridSenderEncrypt(sid, messages, ec.senderPads, kem.senderSecrets);
214
+ // A malicious hybrid SENDER may corrupt the ciphertexts on the wire (test-injected).
215
+ if (tamper?.tamperEncrypted)
216
+ tamper.tamperEncrypted(sender.encrypted);
217
+ const received = hybridReceiverRecover(sid, choices, sender, ec.recvPads, kem.recvSecrets);
218
+ return {
219
+ received,
220
+ transcript: {
221
+ sid,
222
+ ecS: ec.S,
223
+ kemSid: kem.sid,
224
+ encrypted: sender.encrypted,
225
+ commitments: sender.commitments,
226
+ },
227
+ };
228
+ }
229
+ /**
230
+ * Wire the HYBRID base OT into the KOS OT-extension: a drop-in for `withKemBaseOt`/`withKemBaseOtMal`
231
+ * that makes `OtChannel` run its κ setup base OTs over the combiner. The extension inherits the
232
+ * combiner's guarantee — malicious security vs a classical receiver, PQ sender-privacy vs a semi-honest
233
+ * quantum one, and PQ choice privacy — with everything above the base OT (IKNP matrix, KOS correlation
234
+ * check, correlation-robust hash) unchanged.
235
+ *
236
+ * const ch = new OtChannel(secureOtRandom(), withHybridBaseOt());
237
+ */
238
+ function withHybridBaseOt() {
239
+ return { baseOtFn: (m, c, r) => ({ received: hybridBaseOT(m, c, r).received }) };
240
+ }
241
+ //# sourceMappingURL=hybrid-ot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hybrid-ot.js","sourceRoot":"","sources":["../src/hybrid-ot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @atlasauth/pca-mpc — PCA evolution B5 reference prototype.
3
+ *
4
+ * A multi-stakeholder Policy VM composed under secure multi-party computation, so user + org +
5
+ * regulator decide allow / threshold jointly WITHOUT any party revealing its own policy. Two layers:
6
+ * - SEMI-HONEST: additive secret sharing over a prime field + Beaver-triple multiplication
7
+ * (`sharing.ts`, `beaver.ts`, `runner.ts`).
8
+ * - MALICIOUS-WITH-ABORT (dishonest-majority): SPDZ-style authenticated sharing with
9
+ * information-theoretic MACs + a commit-then-open MAC-check that ABORTS on any deviation
10
+ * (`spdz.ts`, `spdz-runner.ts`). Result is correct, or an abort — never a silent wrong answer.
11
+ * See docs/specs/pca-mpc-policy-vm.md for the security model and the trusted-dealer / no-dealer boundary.
12
+ */
13
+ export * from './field';
14
+ export * from './sharing';
15
+ export * from './beaver';
16
+ export * from './thermometer';
17
+ export * from './party';
18
+ export * from './compose';
19
+ export * from './runner';
20
+ export * from './spdz';
21
+ export * from './spdz-runner';
22
+ export * from './ec';
23
+ export * from './gf128';
24
+ export * from './csprng';
25
+ export * from './ot';
26
+ export * from './kem-ot';
27
+ export * from './hybrid-ot';
28
+ export * from './mascot';
29
+ export * from './endemic-ot';