@atlasauth/pca-mpc 0.0.0-stage → 0.1.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 (50) hide show
  1. package/README.md +30 -2
  2. package/dist/beaver.d.ts +49 -0
  3. package/dist/beaver.js +72 -0
  4. package/dist/beaver.js.map +1 -0
  5. package/dist/compose.d.ts +21 -0
  6. package/dist/compose.js +33 -0
  7. package/dist/compose.js.map +1 -0
  8. package/dist/csprng.d.ts +46 -0
  9. package/dist/csprng.js +86 -0
  10. package/dist/csprng.js.map +1 -0
  11. package/dist/ec.d.ts +110 -0
  12. package/dist/ec.js +306 -0
  13. package/dist/ec.js.map +1 -0
  14. package/dist/field.d.ts +34 -0
  15. package/dist/field.js +65 -0
  16. package/dist/field.js.map +1 -0
  17. package/dist/gf128.d.ts +23 -0
  18. package/dist/gf128.js +59 -0
  19. package/dist/gf128.js.map +1 -0
  20. package/dist/index.d.ts +27 -0
  21. package/dist/index.js +44 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/kem-ot.d.ts +137 -0
  24. package/dist/kem-ot.js +224 -0
  25. package/dist/kem-ot.js.map +1 -0
  26. package/dist/mascot.d.ts +189 -0
  27. package/dist/mascot.js +322 -0
  28. package/dist/mascot.js.map +1 -0
  29. package/dist/ot.d.ts +212 -0
  30. package/dist/ot.js +536 -0
  31. package/dist/ot.js.map +1 -0
  32. package/dist/party.d.ts +38 -0
  33. package/dist/party.js +40 -0
  34. package/dist/party.js.map +1 -0
  35. package/dist/runner.d.ts +56 -0
  36. package/dist/runner.js +97 -0
  37. package/dist/runner.js.map +1 -0
  38. package/dist/sharing.d.ts +40 -0
  39. package/dist/sharing.js +97 -0
  40. package/dist/sharing.js.map +1 -0
  41. package/dist/spdz-runner.d.ts +50 -0
  42. package/dist/spdz-runner.js +107 -0
  43. package/dist/spdz-runner.js.map +1 -0
  44. package/dist/spdz.d.ts +136 -0
  45. package/dist/spdz.js +243 -0
  46. package/dist/spdz.js.map +1 -0
  47. package/dist/thermometer.d.ts +22 -0
  48. package/dist/thermometer.js +41 -0
  49. package/dist/thermometer.js.map +1 -0
  50. package/package.json +31 -3
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Post-quantum base OT from ML-KEM — a 1-of-2 oblivious transfer whose hardness rests on Module-LWE
3
+ * (ML-KEM-768, FIPS 203) instead of the curve25519 discrete log. This is the end-game plan's P3
4
+ * ("KEM-based OT from ML-KEM") so the MPC base OT no longer has a classical, quantum-breakable root.
5
+ *
6
+ * ─── THE CONSTRUCTION (be precise and honest) ──────────────────────────────────────────────────────
7
+ * This is the folklore semi-honest 1-of-2 OT from a key-encapsulation mechanism — the "dual-public-key
8
+ * KEM-OT" (closely related to endemic/Masny–Rindal-style OT, but WITHOUT any oblivious-key derivation):
9
+ *
10
+ * RECEIVER, choice bit c, per transfer:
11
+ * • generates TWO *real* ML-KEM keypairs, (pk_c, sk_c) and (pk', sk');
12
+ * • places pk_c at wire index c and pk' at wire index 1−c, and sends the ordered pair (pk_0, pk_1);
13
+ * • KEEPS sk_c and DISCARDS the other secret sk' (this discard is the whole semi-honest assumption).
14
+ * SENDER, messages (m_0, m_1), per transfer:
15
+ * • encapsulates under each received key: (ct_b, ss_b) ← Encaps(pk_b) for b ∈ {0,1};
16
+ * • one-time-pads each message under a random-oracle key: E_b = m_b ⊕ H(b ‖ ss_b), and sends
17
+ * (ct_0, E_0), (ct_1, E_1).
18
+ * RECEIVER recovers ONLY m_c: ss_c ← Decaps(ct_c, sk_c); m_c = E_c ⊕ H(c ‖ ss_c).
19
+ * It cannot recover m_{1−c}: it discarded sk_{1−c}, so ss_{1−c} is hidden by ML-KEM IND-CCA security
20
+ * and H(1−c ‖ ss_{1−c}) is a pseudorandom pad.
21
+ *
22
+ * ─── WHY *THIS* CONSTRUCTION (soundness) ───────────────────────────────────────────────────────────
23
+ * A tempting alternative is an "oblivious public key": derive pk_{1−c} by HASHING some public value so
24
+ * the receiver provably cannot invert it. That is sound for group-based KEMs where a public key is a
25
+ * group element you can hash-to. It is NOT soundly implementable for ML-KEM with the primitive we have:
26
+ * an ML-KEM public key is a structured Module-LWE object (a matrix seed ρ plus t = A·s + e); a hash
27
+ * output is not a valid encapsulation target, and there is no black-box "hash → valid pk whose secret is
28
+ * unknown-to-everyone" for ML-KEM without a trusted setup or a specialised lattice gadget. Rolling one
29
+ * by hand would be exactly the hand-rolled lattice crypto this module forbids. So we use the two-real-
30
+ * keypairs/discard-one-secret variant, which is sound with `ml_kem768` as an opaque IND-CCA KEM.
31
+ *
32
+ * ─── SECURITY MODEL (exactly what is and is not achieved) ──────────────────────────────────────────
33
+ * • Receiver-choice privacy — UNCONDITIONAL, and holds even against a *malicious* sender. The wire
34
+ * message (pk_0, pk_1) is two i.i.d. honest ML-KEM public keys; the choice c only decides which
35
+ * secret the receiver keeps *locally*. The two orderings are therefore identically distributed, so
36
+ * the sender's view is independent of c with probability 1 (no assumption, no abort needed).
37
+ * • Sender privacy (receiver learns nothing about m_{1−c}) — SEMI-HONEST only, under ML-KEM IND-CCA
38
+ * security + H modelled as a random oracle. An honest-but-curious receiver discards sk_{1−c}; given
39
+ * only pk_{1−c} and ct_{1−c} it cannot distinguish ss_{1−c} from random (IND-CCA), so the pad hides
40
+ * m_{1−c}. A MALICIOUS receiver that *keeps both* secrets learns BOTH messages — this base OT does
41
+ * not stop that by itself.
42
+ * • POST-QUANTUM: both guarantees are PQ. Choice privacy is information-theoretic; sender privacy
43
+ * rests on Module-LWE (ML-KEM-768, NIST security level ~3) and a hash in the ROM — no classical
44
+ * discrete-log anywhere, which is the entire point of P3.
45
+ *
46
+ * ─── LIFTING TOWARD MALICIOUS (how this plugs into the stack) ──────────────────────────────────────
47
+ * These base OTs feed the existing KOS OT-extension (`ot.ts`). KOS runs the base OTs with ROLES
48
+ * INVERTED (the extension *sender* is the base *receiver*, holding the secret selector s) and then adds
49
+ * the KOS correlation check over sacrifice rows, which catches an extension receiver that deviates. The
50
+ * malicious surface a semi-honest base OT leaves open — a base receiver keeping both secrets — maps to
51
+ * the extension *sender* learning the extension *receiver*'s seed pairs; KOS's consistency machinery and
52
+ * the correlation-robust hash are what constrain that, exactly as documented for the IKNP/KOS layer.
53
+ * HONEST CAVEAT: a semi-honest base OT does not give a fully malicious base OT on its own; a
54
+ * deployment wanting malicious base security would add a base-OT consistency/commitment round (as the
55
+ * EC `baseOT` wrapper does with its per-message RO commitment). The KOS layer above raises the overall
56
+ * extension to malicious-with-abort against a cheating *extension receiver*, which is the attack IKNP is
57
+ * vulnerable to; see `ot.ts` for that argument.
58
+ *
59
+ * No hand-rolled lattice crypto: all PQ operations go through `ml_kem768` (keygen/encapsulate/
60
+ * decapsulate) as a black box. Randomness follows the module policy — a CSPRNG (`secureOtRandom`) by
61
+ * default, a seeded deterministic stream ONLY when a test passes one.
62
+ */
63
+ import { type OtChannel, type OtRandom } from './ot';
64
+ /**
65
+ * The random-oracle pad for one slot: `H(TAG ‖ i ‖ b ‖ sharedSecret)` expanded (SHA-256 counter mode)
66
+ * to exactly `len` bytes. `i` binds the transfer index and `b` the slot, so the two pads of one transfer
67
+ * — and the pads across transfers — are domain-separated.
68
+ */
69
+ export declare function kemOtPad(index: number, slot: number, sharedSecret: Uint8Array, len: number): Uint8Array;
70
+ /** Receiver round-1 wire message: the ordered public-key pairs the sender encapsulates under. */
71
+ export interface KemOtReceiverMessage {
72
+ /** For each transfer i, [pk_0, pk_1]; the chosen key sits at index `choices[i]`. */
73
+ publicKeys: Array<[Uint8Array, Uint8Array]>;
74
+ }
75
+ /**
76
+ * The receiver's RETAINED state after round 1. An honest receiver keeps ONLY the chosen secret key per
77
+ * transfer (`secretKeys[i]` is `sk_{choices[i]}`); the other secret has been discarded. This is exactly
78
+ * the state the semi-honest model assumes, and the one the tests assert over ("the non-chosen ciphertext
79
+ * does not decrypt under the receiver's state").
80
+ */
81
+ export interface KemOtReceiverState {
82
+ /** The choice bits, one per transfer. */
83
+ choices: number[];
84
+ /** The chosen secret key per transfer (sk for slot `choices[i]`); the non-chosen secret is discarded. */
85
+ secretKeys: Uint8Array[];
86
+ }
87
+ /** Sender round wire message: the two ciphertexts + two padded messages per transfer. */
88
+ export interface KemOtSenderMessage {
89
+ /** For each transfer i, the ML-KEM ciphertexts [ct_0, ct_1] under [pk_0, pk_1]. */
90
+ ciphertexts: Array<[Uint8Array, Uint8Array]>;
91
+ /** For each transfer i, the one-time-padded messages [E_0, E_1] = [m_0 ⊕ H(0‖ss_0), m_1 ⊕ H(1‖ss_1)]. */
92
+ encrypted: Array<[Uint8Array, Uint8Array]>;
93
+ }
94
+ /** A full KEM base-OT transcript (receiver message + sender message), exposed for inspection/tests. */
95
+ export interface KemBaseOtTranscript extends KemOtReceiverMessage, KemOtSenderMessage {
96
+ }
97
+ export interface KemBaseOtResult {
98
+ /** Receiver's recovered messages m_{i, choices[i]}. */
99
+ received: Uint8Array[];
100
+ transcript: KemBaseOtTranscript;
101
+ }
102
+ /**
103
+ * RECEIVER round 1: for each choice bit, generate two real ML-KEM keypairs, send both public keys
104
+ * (chosen one at wire index `c`), and retain only the chosen secret key. Returns the wire message AND
105
+ * the retained receiver state.
106
+ */
107
+ export declare function kemBaseOtReceive(choices: number[], rng?: OtRandom): {
108
+ message: KemOtReceiverMessage;
109
+ state: KemOtReceiverState;
110
+ };
111
+ /**
112
+ * SENDER round: encapsulate under each received public key and one-time-pad each message under
113
+ * `H(slot ‖ sharedSecret)`. `messages[i] = [m_0, m_1]`; the two messages of one transfer must be equal
114
+ * length (padding is derived to that length). Returns the sender wire message.
115
+ */
116
+ export declare function kemBaseOtSend(messages: Array<[Uint8Array, Uint8Array]>, receiverMessage: KemOtReceiverMessage, rng?: OtRandom): KemOtSenderMessage;
117
+ /**
118
+ * RECEIVER recovery: decapsulate ONLY the chosen ciphertext with the retained secret key and unmask the
119
+ * chosen message. The receiver never touches the non-chosen slot (it has no secret for it). Returns
120
+ * `received[i] = m_{i, choices[i]}`.
121
+ */
122
+ export declare function kemBaseOtOpen(state: KemOtReceiverState, senderMessage: KemOtSenderMessage): Uint8Array[];
123
+ /**
124
+ * In-process combined simulator (both parties in one call), matching the shape of `baseOT` in `ot.ts`:
125
+ * run `count = choices.length` KEM base OTs and return the receiver's recovered messages plus the full
126
+ * transcript. This is the drop-in the KOS extension consumes (see `OtChannel`'s `baseOtFn` option).
127
+ */
128
+ export declare function kemBaseOT(messages: Array<[Uint8Array, Uint8Array]>, choices: number[], rng?: OtRandom): KemBaseOtResult;
129
+ /**
130
+ * Wire the ML-KEM base OT into the existing KOS OT-extension: returns the constructor option bag for
131
+ * `OtChannel` that makes it run its κ setup base OTs over `kemBaseOT` (post-quantum) instead of the
132
+ * classical EC Chou–Orlandi `baseOT`. Everything above the base OT (the IKNP matrix, the KOS correlation
133
+ * check, the correlation-robust hash) is unchanged.
134
+ *
135
+ * const ch = new OtChannel(secureOtRandom(), withKemBaseOt());
136
+ */
137
+ export declare function withKemBaseOt(): ConstructorParameters<typeof OtChannel>[1];
package/dist/kem-ot.js ADDED
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ /**
3
+ * Post-quantum base OT from ML-KEM — a 1-of-2 oblivious transfer whose hardness rests on Module-LWE
4
+ * (ML-KEM-768, FIPS 203) instead of the curve25519 discrete log. This is the end-game plan's P3
5
+ * ("KEM-based OT from ML-KEM") so the MPC base OT no longer has a classical, quantum-breakable root.
6
+ *
7
+ * ─── THE CONSTRUCTION (be precise and honest) ──────────────────────────────────────────────────────
8
+ * This is the folklore semi-honest 1-of-2 OT from a key-encapsulation mechanism — the "dual-public-key
9
+ * KEM-OT" (closely related to endemic/Masny–Rindal-style OT, but WITHOUT any oblivious-key derivation):
10
+ *
11
+ * RECEIVER, choice bit c, per transfer:
12
+ * • generates TWO *real* ML-KEM keypairs, (pk_c, sk_c) and (pk', sk');
13
+ * • places pk_c at wire index c and pk' at wire index 1−c, and sends the ordered pair (pk_0, pk_1);
14
+ * • KEEPS sk_c and DISCARDS the other secret sk' (this discard is the whole semi-honest assumption).
15
+ * SENDER, messages (m_0, m_1), per transfer:
16
+ * • encapsulates under each received key: (ct_b, ss_b) ← Encaps(pk_b) for b ∈ {0,1};
17
+ * • one-time-pads each message under a random-oracle key: E_b = m_b ⊕ H(b ‖ ss_b), and sends
18
+ * (ct_0, E_0), (ct_1, E_1).
19
+ * RECEIVER recovers ONLY m_c: ss_c ← Decaps(ct_c, sk_c); m_c = E_c ⊕ H(c ‖ ss_c).
20
+ * It cannot recover m_{1−c}: it discarded sk_{1−c}, so ss_{1−c} is hidden by ML-KEM IND-CCA security
21
+ * and H(1−c ‖ ss_{1−c}) is a pseudorandom pad.
22
+ *
23
+ * ─── WHY *THIS* CONSTRUCTION (soundness) ───────────────────────────────────────────────────────────
24
+ * A tempting alternative is an "oblivious public key": derive pk_{1−c} by HASHING some public value so
25
+ * the receiver provably cannot invert it. That is sound for group-based KEMs where a public key is a
26
+ * group element you can hash-to. It is NOT soundly implementable for ML-KEM with the primitive we have:
27
+ * an ML-KEM public key is a structured Module-LWE object (a matrix seed ρ plus t = A·s + e); a hash
28
+ * output is not a valid encapsulation target, and there is no black-box "hash → valid pk whose secret is
29
+ * unknown-to-everyone" for ML-KEM without a trusted setup or a specialised lattice gadget. Rolling one
30
+ * by hand would be exactly the hand-rolled lattice crypto this module forbids. So we use the two-real-
31
+ * keypairs/discard-one-secret variant, which is sound with `ml_kem768` as an opaque IND-CCA KEM.
32
+ *
33
+ * ─── SECURITY MODEL (exactly what is and is not achieved) ──────────────────────────────────────────
34
+ * • Receiver-choice privacy — UNCONDITIONAL, and holds even against a *malicious* sender. The wire
35
+ * message (pk_0, pk_1) is two i.i.d. honest ML-KEM public keys; the choice c only decides which
36
+ * secret the receiver keeps *locally*. The two orderings are therefore identically distributed, so
37
+ * the sender's view is independent of c with probability 1 (no assumption, no abort needed).
38
+ * • Sender privacy (receiver learns nothing about m_{1−c}) — SEMI-HONEST only, under ML-KEM IND-CCA
39
+ * security + H modelled as a random oracle. An honest-but-curious receiver discards sk_{1−c}; given
40
+ * only pk_{1−c} and ct_{1−c} it cannot distinguish ss_{1−c} from random (IND-CCA), so the pad hides
41
+ * m_{1−c}. A MALICIOUS receiver that *keeps both* secrets learns BOTH messages — this base OT does
42
+ * not stop that by itself.
43
+ * • POST-QUANTUM: both guarantees are PQ. Choice privacy is information-theoretic; sender privacy
44
+ * rests on Module-LWE (ML-KEM-768, NIST security level ~3) and a hash in the ROM — no classical
45
+ * discrete-log anywhere, which is the entire point of P3.
46
+ *
47
+ * ─── LIFTING TOWARD MALICIOUS (how this plugs into the stack) ──────────────────────────────────────
48
+ * These base OTs feed the existing KOS OT-extension (`ot.ts`). KOS runs the base OTs with ROLES
49
+ * INVERTED (the extension *sender* is the base *receiver*, holding the secret selector s) and then adds
50
+ * the KOS correlation check over sacrifice rows, which catches an extension receiver that deviates. The
51
+ * malicious surface a semi-honest base OT leaves open — a base receiver keeping both secrets — maps to
52
+ * the extension *sender* learning the extension *receiver*'s seed pairs; KOS's consistency machinery and
53
+ * the correlation-robust hash are what constrain that, exactly as documented for the IKNP/KOS layer.
54
+ * HONEST CAVEAT: a semi-honest base OT does not give a fully malicious base OT on its own; a
55
+ * deployment wanting malicious base security would add a base-OT consistency/commitment round (as the
56
+ * EC `baseOT` wrapper does with its per-message RO commitment). The KOS layer above raises the overall
57
+ * extension to malicious-with-abort against a cheating *extension receiver*, which is the attack IKNP is
58
+ * vulnerable to; see `ot.ts` for that argument.
59
+ *
60
+ * No hand-rolled lattice crypto: all PQ operations go through `ml_kem768` (keygen/encapsulate/
61
+ * decapsulate) as a black box. Randomness follows the module policy — a CSPRNG (`secureOtRandom`) by
62
+ * default, a seeded deterministic stream ONLY when a test passes one.
63
+ */
64
+ Object.defineProperty(exports, "__esModule", { value: true });
65
+ exports.kemOtPad = kemOtPad;
66
+ exports.kemBaseOtReceive = kemBaseOtReceive;
67
+ exports.kemBaseOtSend = kemBaseOtSend;
68
+ exports.kemBaseOtOpen = kemBaseOtOpen;
69
+ exports.kemBaseOT = kemBaseOT;
70
+ exports.withKemBaseOt = withKemBaseOt;
71
+ const node_crypto_1 = require("node:crypto");
72
+ const ml_kem_js_1 = require("@noble/post-quantum/ml-kem.js");
73
+ const csprng_1 = require("./csprng");
74
+ /** Bytes of fresh randomness fed to ML-KEM keygen (d ‖ z, FIPS 203). */
75
+ const KEYGEN_SEED_BYTES = 64;
76
+ /** Bytes of fresh randomness fed to ML-KEM encapsulation (the message m, FIPS 203). */
77
+ const ENCAPS_MSG_BYTES = ml_kem_js_1.ml_kem768.msgLen; // 32
78
+ const TAG_KEMOT = new TextEncoder().encode('atlas-pca-mpc/kem-ot-pad');
79
+ function sha256(...chunks) {
80
+ const h = (0, node_crypto_1.createHash)('sha256');
81
+ for (const c of chunks)
82
+ h.update(c);
83
+ return new Uint8Array(h.digest());
84
+ }
85
+ function u32(x) {
86
+ const b = new Uint8Array(4);
87
+ b[0] = (x >>> 24) & 0xff;
88
+ b[1] = (x >>> 16) & 0xff;
89
+ b[2] = (x >>> 8) & 0xff;
90
+ b[3] = x & 0xff;
91
+ return b;
92
+ }
93
+ /** XOR two equal-length byte arrays into a fresh array. */
94
+ function xorBytes(a, b) {
95
+ if (a.length !== b.length)
96
+ throw new Error('kem-ot: XOR length mismatch');
97
+ const out = new Uint8Array(a.length);
98
+ for (let i = 0; i < a.length; i++)
99
+ out[i] = a[i] ^ b[i];
100
+ return out;
101
+ }
102
+ /**
103
+ * The random-oracle pad for one slot: `H(TAG ‖ i ‖ b ‖ sharedSecret)` expanded (SHA-256 counter mode)
104
+ * to exactly `len` bytes. `i` binds the transfer index and `b` the slot, so the two pads of one transfer
105
+ * — and the pads across transfers — are domain-separated.
106
+ */
107
+ function kemOtPad(index, slot, sharedSecret, len) {
108
+ const out = new Uint8Array(len);
109
+ let produced = 0;
110
+ let ctr = 0;
111
+ while (produced < len) {
112
+ const block = sha256(TAG_KEMOT, u32(index), Uint8Array.of(slot & 1), u32(ctr), sharedSecret);
113
+ for (let k = 0; k < block.length && produced < len; k++)
114
+ out[produced++] = block[k];
115
+ ctr++;
116
+ }
117
+ return out;
118
+ }
119
+ /**
120
+ * RECEIVER round 1: for each choice bit, generate two real ML-KEM keypairs, send both public keys
121
+ * (chosen one at wire index `c`), and retain only the chosen secret key. Returns the wire message AND
122
+ * the retained receiver state.
123
+ */
124
+ function kemBaseOtReceive(choices, rng = (0, csprng_1.secureOtRandom)()) {
125
+ const publicKeys = [];
126
+ const secretKeys = [];
127
+ const retainedChoices = [];
128
+ for (let i = 0; i < choices.length; i++) {
129
+ const c = choices[i] & 1;
130
+ const chosen = ml_kem_js_1.ml_kem768.keygen(rng.bytes(KEYGEN_SEED_BYTES));
131
+ const other = ml_kem_js_1.ml_kem768.keygen(rng.bytes(KEYGEN_SEED_BYTES));
132
+ // Place the chosen public key at wire index c; the other at 1−c. The order is the only thing the
133
+ // sender sees, and both keys are i.i.d. honest keygen outputs, so it reveals nothing about c.
134
+ const pk0 = c === 0 ? chosen.publicKey : other.publicKey;
135
+ const pk1 = c === 0 ? other.publicKey : chosen.publicKey;
136
+ publicKeys.push([pk0, pk1]);
137
+ // Keep only the chosen secret; `other.secretKey` is deliberately dropped (never stored).
138
+ secretKeys.push(chosen.secretKey);
139
+ retainedChoices.push(c);
140
+ }
141
+ return { message: { publicKeys }, state: { choices: retainedChoices, secretKeys } };
142
+ }
143
+ /**
144
+ * SENDER round: encapsulate under each received public key and one-time-pad each message under
145
+ * `H(slot ‖ sharedSecret)`. `messages[i] = [m_0, m_1]`; the two messages of one transfer must be equal
146
+ * length (padding is derived to that length). Returns the sender wire message.
147
+ */
148
+ function kemBaseOtSend(messages, receiverMessage, rng = (0, csprng_1.secureOtRandom)()) {
149
+ const { publicKeys } = receiverMessage;
150
+ if (messages.length !== publicKeys.length) {
151
+ throw new Error('kemBaseOtSend: messages/publicKeys length mismatch');
152
+ }
153
+ const ciphertexts = [];
154
+ const encrypted = [];
155
+ for (let i = 0; i < messages.length; i++) {
156
+ const [m0, m1] = messages[i];
157
+ if (m0.length !== m1.length) {
158
+ throw new Error(`kemBaseOtSend (index ${i}): the two messages must be equal length`);
159
+ }
160
+ const [pk0, pk1] = publicKeys[i];
161
+ // ml_kem768.encapsulate derandomised with fresh bytes so a seeded test stays reproducible.
162
+ const e0 = ml_kem_js_1.ml_kem768.encapsulate(pk0, rng.bytes(ENCAPS_MSG_BYTES));
163
+ const e1 = ml_kem_js_1.ml_kem768.encapsulate(pk1, rng.bytes(ENCAPS_MSG_BYTES));
164
+ ciphertexts.push([e0.cipherText, e1.cipherText]);
165
+ encrypted.push([
166
+ xorBytes(m0, kemOtPad(i, 0, e0.sharedSecret, m0.length)),
167
+ xorBytes(m1, kemOtPad(i, 1, e1.sharedSecret, m1.length)),
168
+ ]);
169
+ }
170
+ return { ciphertexts, encrypted };
171
+ }
172
+ /**
173
+ * RECEIVER recovery: decapsulate ONLY the chosen ciphertext with the retained secret key and unmask the
174
+ * chosen message. The receiver never touches the non-chosen slot (it has no secret for it). Returns
175
+ * `received[i] = m_{i, choices[i]}`.
176
+ */
177
+ function kemBaseOtOpen(state, senderMessage) {
178
+ const { choices, secretKeys } = state;
179
+ const { ciphertexts, encrypted } = senderMessage;
180
+ if (ciphertexts.length !== choices.length || encrypted.length !== choices.length) {
181
+ throw new Error('kemBaseOtOpen: transcript/state length mismatch');
182
+ }
183
+ const received = [];
184
+ for (let i = 0; i < choices.length; i++) {
185
+ const c = choices[i] & 1;
186
+ const ss = ml_kem_js_1.ml_kem768.decapsulate(ciphertexts[i][c], secretKeys[i]);
187
+ const e = encrypted[i][c];
188
+ received.push(xorBytes(e, kemOtPad(i, c, ss, e.length)));
189
+ }
190
+ return received;
191
+ }
192
+ /**
193
+ * In-process combined simulator (both parties in one call), matching the shape of `baseOT` in `ot.ts`:
194
+ * run `count = choices.length` KEM base OTs and return the receiver's recovered messages plus the full
195
+ * transcript. This is the drop-in the KOS extension consumes (see `OtChannel`'s `baseOtFn` option).
196
+ */
197
+ function kemBaseOT(messages, choices, rng = (0, csprng_1.secureOtRandom)()) {
198
+ if (messages.length !== choices.length) {
199
+ throw new Error('kemBaseOT: messages/choices length mismatch');
200
+ }
201
+ const { message: receiverMessage, state } = kemBaseOtReceive(choices, rng);
202
+ const senderMessage = kemBaseOtSend(messages, receiverMessage, rng);
203
+ const received = kemBaseOtOpen(state, senderMessage);
204
+ return {
205
+ received,
206
+ transcript: {
207
+ publicKeys: receiverMessage.publicKeys,
208
+ ciphertexts: senderMessage.ciphertexts,
209
+ encrypted: senderMessage.encrypted,
210
+ },
211
+ };
212
+ }
213
+ /**
214
+ * Wire the ML-KEM base OT into the existing KOS OT-extension: returns the constructor option bag for
215
+ * `OtChannel` that makes it run its κ setup base OTs over `kemBaseOT` (post-quantum) instead of the
216
+ * classical EC Chou–Orlandi `baseOT`. Everything above the base OT (the IKNP matrix, the KOS correlation
217
+ * check, the correlation-robust hash) is unchanged.
218
+ *
219
+ * const ch = new OtChannel(secureOtRandom(), withKemBaseOt());
220
+ */
221
+ function withKemBaseOt() {
222
+ return { baseOtFn: kemBaseOT };
223
+ }
224
+ //# sourceMappingURL=kem-ot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kem-ot.js","sourceRoot":"","sources":["../src/kem-ot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,189 @@
1
+ /**
2
+ * The NO-DEALER offline phase (MASCOT-style) — the last trust assumption removed.
3
+ *
4
+ * The SPDZ online layer (`spdz.ts`/`spdz-runner.ts`) is maliciously secure with abort, but until now
5
+ * its *preprocessing* — the global MAC key α, the authenticated Beaver triples, and the authenticated
6
+ * input sharings — came from a TRUSTED DEALER who knew α and generated everything. This module removes
7
+ * that dealer. All preprocessing is derived from real oblivious transfer (`ot.ts`), so:
8
+ *
9
+ * • **The MAC key α is distributed, never reconstructed.** Each party i samples its own share α_i
10
+ * locally; `α = Σ α_i` is a value no party ever holds and the protocol never forms. (The in-process
11
+ * simulator can sum the shares for test assertions — see `MacContext.alpha` — but no per-party view
12
+ * and no protocol step contains α.)
13
+ *
14
+ * • **Beaver triples are generated by the parties from OT, no dealer.** Each party samples its own
15
+ * shares a_i, b_i of two random values; the product share `c = a·b` is obtained by OT-multiplying
16
+ * the share vectors (Gilboa), and the SPDZ MACs on a, b, c are obtained by OT-multiplying α's shares
17
+ * against each value's shares. No party learns a, b or c; no party learns α.
18
+ *
19
+ * ## The one OT-based primitive: `otShareMultiply`
20
+ *
21
+ * Given additive sharings `⟨x⟩ = (x_0,…,x_{n−1})` and `⟨y⟩`, the parties want an additive sharing
22
+ * `⟨z⟩` with `Σ z = (Σ x)(Σ y)`, learning nothing else. Expand:
23
+ *
24
+ * (Σ_i x_i)(Σ_j y_j) = Σ_i x_i·y_i + Σ_{i≠j} x_i·y_j.
25
+ *
26
+ * The diagonal `x_i·y_i` each party computes locally. Each cross term `x_i·y_j` (i≠j) is turned into an
27
+ * additive sharing between party i and party j by **Gilboa's OT multiplication**: party i (holding x_i)
28
+ * is the OT RECEIVER using the bits of x_i as choices; party j (holding y_j) is the OT SENDER. For bit
29
+ * k of x_i, the sender offers `(ρ_k, ρ_k + 2^k·y_j)` for a fresh random ρ_k; the receiver's choice bit
30
+ * `(x_i)_k` selects `ρ_k + (x_i)_k·2^k·y_j`. Summed over k the receiver gets `x_i·y_j + Σρ_k` and the
31
+ * sender keeps `−Σρ_k`, an additive sharing of `x_i·y_j`. Because the OT hides both the choice and the
32
+ * unchosen message, neither party learns the other's share.
33
+ *
34
+ * ## Malicious security (docs §7.1)
35
+ *
36
+ * The OT is now **malicious-secure**: Chou–Orlandi base OT + IKNP extension **with the KOS correlation
37
+ * check** (`ot.ts`), so a cheating OT-extension receiver is caught and the extension aborts. On top of
38
+ * that, triple generation runs the **MASCOT sacrifice**: every delivered triple is generated as a PAIR
39
+ * `(a,b,c)` and `(â,b,ĉ)` sharing `b`, and one is *sacrificed* to check the other under the MACs — a
40
+ * party that fed wrong OT inputs (producing `c ≠ a·b`, even one that is internally MAC-consistent) is
41
+ * caught when the sacrifice residual `τ = e·c − ĉ − (e·a−â)·b` opens to a non-zero value, or when the
42
+ * commit-then-open MAC-check on the sacrifice openings fails. There is also an **input-consistency
43
+ * check** (`checkInputConsistency`): a random masked combination of authenticated inputs is opened and
44
+ * MAC-checked, catching any input whose MAC does not match its value. Both the sacrifice and the input
45
+ * check ABORT (throw `MacCheckAbort`) rather than emit a bad triple / bad input.
46
+ *
47
+ * So the whole stack is now **malicious-with-abort, dishonest-majority** end to end: malicious-secure
48
+ * no-dealer OFFLINE (KOS OT + MASCOT sacrifice + input check, `α` never reconstructed) feeding the
49
+ * malicious-with-abort SPDZ ONLINE phase (the MAC-check still catches any online deviation). The base
50
+ * OTs are themselves malicious-secure (committed Chou–Orlandi: Schnorr PoK + point validation, `ot.ts`
51
+ * / `ec.ts`), and all randomness here is the CSPRNG by DEFAULT — construct `NoDealerOffline` WITHOUT a
52
+ * seed for production (`SecureFieldRng` + `secureOtRandom`); passing a seed selects the deterministic
53
+ * SplitMix64 stream for reproducible tests only. Residual boundary (docs §7.1): just two honestly
54
+ * unavoidable items — constant-time field arithmetic (a native/WASM workstream) and a real async
55
+ * network/transport (including the interactive secure coin-flip for the public MAC-check challenge).
56
+ */
57
+ import { type SharedValue } from './sharing';
58
+ import { type AuthSV, type AuthTriple, type MacContext } from './spdz';
59
+ /**
60
+ * A TEST-ONLY injection modelling a MALICIOUS party in the OFFLINE phase: when the triple PRODUCT
61
+ * multiplication at index `productMul` runs (0-based, counting only the OT multiplications that form a
62
+ * triple's `c = a·b`), `delta` is XOR... added into one party's product share. The resulting triple has
63
+ * `c = a·b + delta` but is still internally MAC-consistent (the authentication runs on the corrupted
64
+ * share) — exactly the "bad OT input" a malicious party can mount, which MACs ALONE cannot catch and
65
+ * the MASCOT SACRIFICE is designed to catch. Models "corrupting an OT message/share in the offline".
66
+ */
67
+ export interface OfflineDeviation {
68
+ /** Which triple-product OT multiplication to corrupt (0-based across all products this instance makes). */
69
+ productMul: number;
70
+ /** Field delta added to party 0's product share, making c = a·b + delta (self-consistently MAC'd). */
71
+ delta: bigint;
72
+ }
73
+ /** Options for the no-dealer offline phase. */
74
+ export interface NoDealerOptions {
75
+ /** Run the MASCOT sacrifice on every delivered triple (default true = malicious-secure). */
76
+ sacrifice?: boolean;
77
+ /** A test-only offline deviation (a malicious party corrupting a product share). */
78
+ deviation?: OfflineDeviation;
79
+ }
80
+ /**
81
+ * The no-dealer offline phase for a fixed party count `n`. Holds the distributed MAC key (α_i only),
82
+ * and a reusable set of per-ordered-pair OT-extension channels so the κ base OTs are amortized across
83
+ * every triple and input it produces. Deterministic given its seed (production: a CSPRNG per party).
84
+ *
85
+ * MALICIOUS-secure: the OT extension carries the KOS correlation check (`ot.ts`), and every delivered
86
+ * triple survives the MASCOT sacrifice. `checkInputConsistency` provides the input check.
87
+ */
88
+ export declare class NoDealerOffline {
89
+ readonly n: number;
90
+ /** α_i: party i's locally-sampled share of the MAC key. Σ α_i = α (never formed by any party). */
91
+ readonly alphaShares: bigint[];
92
+ private readonly rng;
93
+ private readonly seed;
94
+ /** Whether randomness is a deterministic (test) seed; when false, all randomness is the CSPRNG. */
95
+ private readonly deterministic;
96
+ /** OT channels keyed "i->j": receiver = party i, sender = party j. Base OTs run on first use. */
97
+ private readonly channels;
98
+ /** Whether to run the MASCOT sacrifice per delivered triple (malicious-secure when true). */
99
+ private readonly sacrificeOn;
100
+ /** Test-only offline deviation (a malicious party corrupting a product share). */
101
+ private readonly deviation?;
102
+ /** Count of triple-product OT multiplications performed (for targeting `deviation.productMul`). */
103
+ private productMulCount;
104
+ constructor(n: number, seed?: bigint, opts?: NoDealerOptions);
105
+ /**
106
+ * The SPDZ MAC context the online runner consumes. It carries ONLY `alphaShares` for the protocol;
107
+ * `alpha` is the sum the in-process simulator computes for TEST ASSERTIONS and is never used by any
108
+ * protocol step or held by any party.
109
+ */
110
+ get macContext(): MacContext;
111
+ private channel;
112
+ /**
113
+ * Gilboa OT multiplication of two scalars held by different parties. Party `i` holds `xi` (OT
114
+ * receiver, choice bits = bits of xi); party `j` holds `yj` (OT sender). Returns `[shareI, shareJ]`
115
+ * with `shareI + shareJ = xi·yj (mod p)`, neither party learning the other's input or share.
116
+ */
117
+ private gilboa;
118
+ /**
119
+ * OT-based share multiplication: given additive sharings `x`, `y` (length n), returns an additive
120
+ * sharing `z` with `Σ z = (Σ x)(Σ y) mod p`. Diagonal terms are local; each cross term is a Gilboa OT
121
+ * multiplication. No party learns Σx, Σy, or Σz.
122
+ */
123
+ otShareMultiply(x: SharedValue, y: SharedValue): SharedValue;
124
+ /** Authenticate a value sharing: produce MAC shares γ with Σ γ = α·(Σ value), via OT (no dealer). */
125
+ private authenticate;
126
+ /**
127
+ * The product sharing `c` (Σc = a·b) for a triple, via OT. Counts as a triple PRODUCT so a test
128
+ * `deviation` can corrupt one — modelling a malicious party that feeds a wrong OT input and gets a
129
+ * wrong-but-self-MAC-consistent product share (only the sacrifice catches this).
130
+ */
131
+ private tripleProduct;
132
+ /** A RAW authenticated triple (a, b, c=a·b) from OT, before the sacrifice. No dealer. */
133
+ private rawTriple;
134
+ /** A RAW authenticated triple (â, b, ĉ=â·b) SHARING the given authenticated `b` — the sacrifice pair. */
135
+ private rawTripleWithB;
136
+ /**
137
+ * The MASCOT **sacrifice** check. Given two triples sharing `b`, `(a,b,c)` (the one to KEEP) and
138
+ * `(â,b,ĉ)` (the one to SACRIFICE), draw a public random challenge `e`, open `ρ = e·a − â`, form the
139
+ * authenticated residual `⟦τ⟧ = e·⟦c⟧ − ⟦ĉ⟧ − ρ·⟦b⟧`, open `τ`, run the commit-then-open MAC-check on
140
+ * both openings, and require `τ = 0`. If both triples are correct, `τ = e·(c−ab) − (ĉ−âb) = 0`; if the
141
+ * kept triple has `c = ab + δ`, then `τ = e·δ`, which is non-zero (e ≠ 0, δ ≠ 0) — ABORT. Lying in the
142
+ * `τ` opening to force a zero value is caught by the MAC-check (the forged MAC won't reconcile). The
143
+ * sacrificed triple is consumed by the check and discarded; the kept triple is certified `c = a·b`.
144
+ */
145
+ private sacrifice;
146
+ /**
147
+ * Generate ONE authenticated Beaver triple with NO dealer, MALICIOUS-secure. Each party samples its
148
+ * own a_i, b_i; the product sharing c (Σc = a·b) comes from OT; every component is MAC'd via OT
149
+ * against the α shares. When the sacrifice is on (default), a SECOND triple sharing `b` is generated
150
+ * and sacrificed to certify `c = a·b` under the MACs, so a malformed triple ABORTS rather than being
151
+ * emitted. No party learns a, b, c, or α.
152
+ */
153
+ genAuthTriple(): AuthTriple;
154
+ /** Generate `count` authenticated triples (channels/base-OTs amortized across all of them). */
155
+ genAuthTriples(count: number): AuthTriple[];
156
+ /**
157
+ * Authenticated input sharing of `value`, owned by party `owner`, with NO dealer. The owner splits
158
+ * its own value into additive shares (local); the MAC `α·value` is computed via OT against the α
159
+ * shares. A malicious owner can of course choose any value (inputs are unconstrained in SPDZ), but it
160
+ * cannot make the sharing inconsistent with its MAC — the online MAC-check binds it.
161
+ */
162
+ authInput(value: bigint, owner: number): AuthSV;
163
+ /**
164
+ * The MASCOT **input-consistency check**: verify that a batch of authenticated inputs each satisfy
165
+ * `Σγ = α·(Σvalue)` — i.e. their MACs match their values — WITHOUT revealing any value, and ABORT if
166
+ * not. A malicious party that produced an input whose MAC does not match its value (e.g. by feeding a
167
+ * bad OT input to the authentication) is caught here rather than only downstream.
168
+ *
169
+ * Method: draw public random coefficients `e_k`, generate ONE fresh OT-authenticated random mask
170
+ * `⟦r⟧`, form `⟦w⟧ = Σ_k e_k·⟦x_k⟧ + ⟦r⟧`, partial-open `w` (masked by the uniform `r`, so no input
171
+ * leaks), and run the commit-then-open MAC-check on that opening. If some input's MAC is off by `δ`,
172
+ * the opened `w` carries MAC residual `Σ e_k·δ_k ≠ 0` w.h.p. over the random `e_k`, and the MAC-check
173
+ * aborts. No-op for an empty batch.
174
+ */
175
+ checkInputConsistency(inputs: ReadonlyArray<AuthSV>): void;
176
+ }
177
+ /**
178
+ * The offline material a malicious-secure runner consumes. A `NoDealerOffline` IS one of these
179
+ * (duck-typed by `macContext`/`genAuthTriple`/`authInput`), so the runner can take either this or the
180
+ * trusted-dealer functions behind the same tiny interface.
181
+ */
182
+ export interface OfflineProvider {
183
+ readonly n: number;
184
+ readonly macContext: MacContext;
185
+ genAuthTriple(): AuthTriple;
186
+ authInput(value: bigint, owner: number): AuthSV;
187
+ }
188
+ /** Convenience: a fresh no-dealer offline phase for `n` parties (malicious-secure by default). */
189
+ export declare function makeNoDealerOffline(n: number, seed?: bigint, opts?: NoDealerOptions): NoDealerOffline;