@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
package/dist/ot.d.ts ADDED
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Oblivious transfer — the real cryptographic root of the no-dealer offline phase.
3
+ *
4
+ * A 1-out-of-2 OT lets a RECEIVER with a choice bit `c` obtain exactly message `m_c` from a SENDER's
5
+ * pair `(m_0, m_1)`, such that the sender learns nothing about `c` and the receiver learns nothing
6
+ * about `m_{1−c}`. That single primitive is enough to multiply two parties' secret shares without a
7
+ * dealer (Gilboa's trick, see `mascot.ts`), which is enough to generate Beaver triples and SPDZ MACs
8
+ * with no trusted party. This module provides OT two ways:
9
+ *
10
+ * 1. **Base OT — MALICIOUSLY-secure Chou–Orlandi over the Ed25519 subgroup (`ec.ts`).** The core is
11
+ * "simplest OT": sender samples `y`, publishes `S = y·B`, holds `T = y·S`; receiver with choice `c`
12
+ * samples `x`, publishes `R = x·B + c·S`, derives pad `k = H(x·S)`; sender derives `k_b = H(y·R − b·T)`
13
+ * for `b∈{0,1}`; since `y·R = x·(y·B) + c·T`, `k_c = k` while `k_{1−c}` is `H` of a point the receiver
14
+ * can't compute (it would need the discrete log). **Receiver-choice privacy is information-theoretic
15
+ * in the transcript:** `R = x·B + c·S` with `x` uniform is a uniform subgroup element whatever `c` is
16
+ * — for every observed `R` there is one `x` consistent with `c=0` and one with `c=1` (`x' = x − y`),
17
+ * so the sender's view is identically distributed for both choices. Two additions upgrade this from
18
+ * semi-honest to **malicious-secure against both a cheating sender AND a cheating receiver** (docs
19
+ * §7.1 item a):
20
+ * • **the sender proves knowledge of `y` with a Schnorr NIZK on `S = y·B`** (`ec.ts`), so a sender
21
+ * cannot use an `S` it cannot open (e.g. one crafted to leak the receiver's choice) — the
22
+ * receiver verifies the proof and ABORTS before forming `R`;
23
+ * • **every received point is validated** (on-curve + prime-order-subgroup cofactor check +
24
+ * non-identity): the receiver validates `S`, the sender validates every `R_i`, so neither party
25
+ * can inject an off-curve / small-order / mixed-order point for a small-subgroup attack.
26
+ * The IKNP setup consumes this as a **random OT** (`malBaseRandomOT`): the two transferred seeds ARE
27
+ * the RO pads `(H(y·R), H(y·R − T))`, so there are **no sender-chosen ciphertexts** and hence no
28
+ * selective-failure surface — the only abort (bad `S`/proof/`R_i`) is independent of the receiver's
29
+ * selector, which is exactly what malicious security of the role-inverted base OTs requires. The
30
+ * chosen-message `baseOT` wrapper (random OT + one-time-pad + a commit-to-message check) is used by
31
+ * the direct OT tests; its commitment catches a sender that encrypts inconsistently with its commitment.
32
+ *
33
+ * 2. **OT extension — IKNP + the KOS consistency check (MALICIOUS-secure).** Base OT needs public-key
34
+ * ops per transfer; the offline phase needs hundreds of thousands of OTs. IKNP turns `κ = 128` base
35
+ * OTs (done once) into arbitrarily many OTs using only a hash/PRG. Roles invert for the base OTs:
36
+ * the extension SENDER plays base-receiver with a secret selector `s ∈ {0,1}^κ`; the extension
37
+ * RECEIVER plays base-sender with `κ` seed pairs. Thereafter each extension round of `m` OTs costs
38
+ * `κ` PRG expansions plus `~3·m` hashes. Correctness: the receiver recovers exactly `m_{c_j}` for
39
+ * every `j`; the sender's `s` (hidden by the base OTs) and the correlation-robust hash keep the
40
+ * unchosen messages hidden.
41
+ *
42
+ * Plain IKNP is only SEMI-HONEST: a cheating RECEIVER can send an extension matrix `u` that encodes
43
+ * *different* choice vectors in different columns, which breaks the row correlation `q_j = t_j ⊕
44
+ * (b_j·s)` and lets the receiver learn bits of the sender's selector `s` (and thus the unchosen
45
+ * messages). The **KOS correlation check** (Keller–Orsini–Scholl, "Actively Secure OT Extension
46
+ * with Optimal Overhead") closes exactly this: after `u` is on the wire, the parties draw public
47
+ * random weights `χ_j ∈ GF(2^κ)` (one per row, bound to `u` by a Fiat–Shamir hash here), the
48
+ * receiver reveals `x = Σ_j b_j·χ_j` and `t = Σ_j χ_j·t_j`, and the sender checks
49
+ * `Σ_j χ_j·q_j == t ⊕ x·s` in GF(2^κ). Honestly this always holds (`q_j = t_j ⊕ b_j·s`); a receiver
50
+ * who deviated in `u` satisfies it with probability ≤ `2^{-κ} = 2^{-128}`, so a deviation ABORTS.
51
+ * The check is run over `m + κ + SSEC` rows — the extra `κ + SSEC` random "sacrifice" OT rows mask
52
+ * the revealed combination (receiver privacy) and give statistical soundness. This makes the OT
53
+ * extension **malicious-secure**, not just semi-honest.
54
+ *
55
+ * In-process model: both parties run in one process and the functions take *both* parties' inputs and
56
+ * return the receiver's outputs plus an inspectable transcript (the receiver's wire message `R`, the
57
+ * sender's ciphertexts, the IKNP `u` matrix). That is exactly what the tests assert over: receiver
58
+ * correctness, sender-side choice-hiding from the transcript, and the KOS check aborting on a tampered
59
+ * `u`. See docs §7.1 for the remaining boundary (real async transport, constant-time, CSPRNG).
60
+ */
61
+ import { type Point, type SchnorrProof } from './ec';
62
+ /** IKNP security parameter (number of base OTs / width of the selector s). */
63
+ export declare const KAPPA = 128;
64
+ /**
65
+ * KOS statistical-security parameter: the number of EXTRA random check-OT rows (beyond the `κ` that
66
+ * mask the revealed combination) appended to every extension round. Together `κ + SSEC` padding rows
67
+ * give the correlation check its receiver-privacy and statistical soundness margin.
68
+ */
69
+ export declare const SSEC = 64;
70
+ /** Raised when the KOS correlation check fails: a cheating OT-extension receiver is detected → ABORT. */
71
+ export declare class MalOtAbort extends Error {
72
+ constructor(message: string);
73
+ }
74
+ /** A deterministic source of scalars/seeds; wraps the module FieldRng-style stream for OT needs. */
75
+ export interface OtRandom {
76
+ /** A uniform scalar in [1, L) (for EC blinding) — never 0. */
77
+ scalar(): bigint;
78
+ /** `n` random bytes (for seeds). */
79
+ bytes(n: number): Uint8Array;
80
+ }
81
+ /** A raw SplitMix64 64-bit stream (deterministic). Production uses a CSPRNG with a secret seed. */
82
+ export declare function splitMix64(seed: bigint): () => bigint;
83
+ /** Build an OtRandom from a SplitMix64-style 64-bit stream (deterministic; CSPRNG in production). */
84
+ export declare function otRandom(next64: () => bigint): OtRandom;
85
+ /** Raised when a base OT aborts: a cheating sender (bad proof / inconsistent ciphertext) is detected. */
86
+ export declare class BaseOtAbort extends Error {
87
+ constructor(message: string);
88
+ }
89
+ /**
90
+ * TEST-ONLY injection points modelling a malicious SENDER or RECEIVER in the base OT. Each hook mutates
91
+ * or replaces a value exactly where it would cross the wire, so the honest party's validation runs
92
+ * against the adversarial value — which is what the malicious-security checks must catch.
93
+ */
94
+ export interface BaseOtTamper {
95
+ /** Malicious SENDER: replace `S` on the wire (the receiver still validates it + the proof). */
96
+ tamperS?: (S: Point) => Point;
97
+ /** Malicious SENDER: forge the Schnorr proof on the wire. */
98
+ tamperProof?: (p: SchnorrProof) => SchnorrProof;
99
+ /** Malicious RECEIVER: mutate the `R_i` points on the wire (the sender still validates each). */
100
+ tamperR?: (R: Point[]) => void;
101
+ /** Malicious SENDER: corrupt the ciphertexts on the wire (inconsistent with the commitment). */
102
+ tamperCiphertext?: (ct: Array<[Uint8Array, Uint8Array]>) => void;
103
+ }
104
+ /** The base-OT transcript: the sender's `S` + proof-of-knowledge, the receiver's `R_i`, and the ciphertexts/commitments. */
105
+ export interface BaseOtTranscript {
106
+ /** Sender's public point S = y·B. */
107
+ S: Point;
108
+ /** Schnorr NIZK proving the sender knows `y` with `S = y·B` (malicious-sender hardening). */
109
+ proof: SchnorrProof;
110
+ /** Receiver's points R_i = x_i·B + c_i·S (uniform subgroup elements; independent of c_i). */
111
+ R: Point[];
112
+ /** Sender ciphertexts: for each i, [E_{i,0}, E_{i,1}] = [m_{i,0} ⊕ pad_{i,0}, m_{i,1} ⊕ pad_{i,1}]. */
113
+ ct: Array<[Uint8Array, Uint8Array]>;
114
+ /** Per-message RO commitments com_{i,b} = H(i ‖ b ‖ m_{i,b} ‖ pad_{i,b}); the receiver checks com_{i,c}. */
115
+ com: Array<[Uint8Array, Uint8Array]>;
116
+ }
117
+ export interface BaseOtResult {
118
+ /** Receiver's recovered messages m_{i, c_i}. */
119
+ received: Uint8Array[];
120
+ transcript: BaseOtTranscript;
121
+ }
122
+ /**
123
+ * A pluggable chosen-message base OT: `count = choices.length` 1-of-2 OTs returning the receiver's
124
+ * recovered messages. The EC `baseOT` above satisfies it, and so does the post-quantum `kemBaseOT`
125
+ * (`kem-ot.ts`) — letting `OtChannel` run its κ setup OTs over either base without touching the
126
+ * IKNP/KOS layer. Only `received` is required (the EC-specific `transcript` is not part of the contract,
127
+ * since a non-EC base has a different transcript shape).
128
+ */
129
+ export type BaseOtFn = (messages: Array<[Uint8Array, Uint8Array]>, choices: number[], rng: OtRandom) => {
130
+ received: Uint8Array[];
131
+ };
132
+ /** The output of the malicious-secure RANDOM base OT: validated points + the two RO pads per index. */
133
+ export interface MalRandomOtResult {
134
+ /** Sender's public point (as seen on the wire by the receiver). */
135
+ S: Point;
136
+ /** Sender's proof of knowledge of the discrete log of `S`. */
137
+ proof: SchnorrProof;
138
+ /** Receiver's points (as seen on the wire by the sender). */
139
+ R: Point[];
140
+ /** Sender's pad pairs: senderPads[i] = [H(y·R_i), H(y·R_i − T)]. */
141
+ senderPads: Array<[Uint8Array, Uint8Array]>;
142
+ /** Receiver's recovered pads: recvPads[i] = H(x_i·S) = senderPads[i][c_i] (honest run). */
143
+ recvPads: Uint8Array[];
144
+ }
145
+ /**
146
+ * The MALICIOUS-secure RANDOM base OT: `count = choices.length` 1-of-2 random OTs. The sender publishes
147
+ * `S = y·B` with a Schnorr proof of knowledge of `y`; the receiver validates `S` + the proof and
148
+ * ABORTS on failure, then forms its `R_i`; the sender validates every `R_i` and derives the pad pairs.
149
+ * No messages are transferred — the pads ARE the random output. `tamper` injects malicious behaviour
150
+ * for tests. Both parties are simulated in-process.
151
+ */
152
+ export declare function malBaseRandomOT(choices: number[], rng?: OtRandom, tamper?: BaseOtTamper): MalRandomOtResult;
153
+ /**
154
+ * Run `count` MALICIOUS-secure base OTs in one batch (chosen-message, over the random OT above).
155
+ * `messages[i] = [m0, m1]` (SEED_BYTES each), the sender's pair; `choices[i] ∈ {0,1}` the receiver's
156
+ * choice. The sender one-time-pads each message under the random-OT pads and commits to each message;
157
+ * the receiver recovers `m_{i,c_i}` and checks the commitment, aborting (`BaseOtAbort`) on any
158
+ * inconsistency. Returns the receiver's recovered messages and the full transcript.
159
+ */
160
+ export declare function baseOT(messages: Array<[Uint8Array, Uint8Array]>, choices: number[], rng?: OtRandom, tamper?: BaseOtTamper): BaseOtResult;
161
+ /**
162
+ * An IKNP OT-extension channel between a fixed RECEIVER (choice bits) and SENDER (message pairs). The
163
+ * κ base OTs run lazily on the first `extend`; each later `extend(m)` is a cheap PRG+hash round. The
164
+ * channel is stateful (advances per-seed PRG counters) so many rounds compose into one amortized setup.
165
+ */
166
+ export declare class OtChannel {
167
+ private readonly rng;
168
+ /** Optional post-quantum (or otherwise custom) base OT for the κ setup OTs; defaults to EC `baseOT`. */
169
+ private readonly baseOtFn?;
170
+ private ready;
171
+ private s;
172
+ private sBits;
173
+ private senderSeeds;
174
+ private senderBlk;
175
+ private recvSeed0;
176
+ private recvSeed1;
177
+ private recvBlk0;
178
+ private recvBlk1;
179
+ /** The base-OT transcript (available after setup) — exposed for inspection/tests. */
180
+ baseTranscript?: BaseOtTranscript;
181
+ /**
182
+ * TEST-ONLY injection modelling a MALICIOUS extension RECEIVER: if set, it is called with the honest
183
+ * `u` matrix (κ columns, each `mExt` bits) just before it goes "on the wire", and may mutate it (e.g.
184
+ * XOR a delta into one or more columns) to encode inconsistent choices. The KOS check is computed
185
+ * over the tampered `u` (what the sender sees) while the receiver's revealed `(x, t)` stay honest, so
186
+ * a non-trivial tamper is caught — the real malicious-receiver attack that KOS defeats.
187
+ */
188
+ tamperU?: (u: bigint[], round: number) => void;
189
+ private round;
190
+ /**
191
+ * `rng` defaults to the CSPRNG (`secureOtRandom`, `node:crypto`) — production randomness. Pass a
192
+ * deterministic `otRandom(splitMix64(seed))` ONLY to make a test bit-reproducible (docs §7.1 item c).
193
+ */
194
+ constructor(rng?: OtRandom, opts?: {
195
+ baseOtFn?: BaseOtFn;
196
+ });
197
+ private setup;
198
+ /** Selector s as a κ-bit bigint (sender-side secret; exposed only for tests). */
199
+ get selector(): bigint;
200
+ /**
201
+ * One MALICIOUS-secure extension round: `choiceBits[j] ∈ {0,1}` (receiver), `msg0[j]/msg1[j]`
202
+ * field-element messages (sender). Returns the receiver's chosen messages `msg_{choiceBits[j]}[j]`
203
+ * for the `m` real rows. The round transparently appends `κ + SSEC` random check-OT rows and runs the
204
+ * **KOS correlation check** over all `m + κ + SSEC` rows; a cheating receiver (tampered `u`) is caught
205
+ * there and the round throws `MalOtAbort`. `uMatrix` (the receiver→sender wire message, now
206
+ * `mExt`-bit) is returned for tests.
207
+ */
208
+ extend(choiceBits: number[], msg0: bigint[], msg1: bigint[]): {
209
+ received: bigint[];
210
+ uMatrix: bigint[];
211
+ };
212
+ }