@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/index.js ADDED
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ /**
3
+ * @atlasauth/pca-mpc — PCA evolution B5 reference prototype.
4
+ *
5
+ * A multi-stakeholder Policy VM composed under secure multi-party computation, so user + org +
6
+ * regulator decide allow / threshold jointly WITHOUT any party revealing its own policy. Two layers:
7
+ * - SEMI-HONEST: additive secret sharing over a prime field + Beaver-triple multiplication
8
+ * (`sharing.ts`, `beaver.ts`, `runner.ts`).
9
+ * - MALICIOUS-WITH-ABORT (dishonest-majority): SPDZ-style authenticated sharing with
10
+ * information-theoretic MACs + a commit-then-open MAC-check that ABORTS on any deviation
11
+ * (`spdz.ts`, `spdz-runner.ts`). Result is correct, or an abort — never a silent wrong answer.
12
+ * See docs/specs/pca-mpc-policy-vm.md for the security model and the trusted-dealer / no-dealer boundary.
13
+ */
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
26
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ __exportStar(require("./field"), exports);
30
+ __exportStar(require("./sharing"), exports);
31
+ __exportStar(require("./beaver"), exports);
32
+ __exportStar(require("./thermometer"), exports);
33
+ __exportStar(require("./party"), exports);
34
+ __exportStar(require("./compose"), exports);
35
+ __exportStar(require("./runner"), exports);
36
+ __exportStar(require("./spdz"), exports);
37
+ __exportStar(require("./spdz-runner"), exports);
38
+ __exportStar(require("./ec"), exports);
39
+ __exportStar(require("./gf128"), exports);
40
+ __exportStar(require("./csprng"), exports);
41
+ __exportStar(require("./ot"), exports);
42
+ __exportStar(require("./kem-ot"), exports);
43
+ __exportStar(require("./hybrid-ot"), exports);
44
+ __exportStar(require("./mascot"), exports);
45
+ __exportStar(require("./endemic-ot"), exports);
46
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;;;;;;;;;;;;;;AAEH,0CAAwB;AACxB,4CAA0B;AAC1B,2CAAyB;AACzB,gDAA8B;AAC9B,0CAAwB;AACxB,4CAA0B;AAC1B,2CAAyB;AACzB,yCAAuB;AACvB,gDAA8B;AAC9B,uCAAqB;AACrB,0CAAwB;AACxB,2CAAyB;AACzB,uCAAqB;AACrB,2CAAyB;AACzB,8CAA4B;AAC5B,2CAAyB;AACzB,+CAA6B"}
@@ -0,0 +1,292 @@
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
+ * ─── MALICIOUS-RECEIVER HARDENING (`kemBaseOtMal`, below) ──────────────────────────────────────────
47
+ * The semi-honest base OT above leaves two malicious-receiver surfaces. The committed variant
48
+ * `kemBaseOtMal` closes the one that CAN be closed from an opaque KEM, and documents — precisely,
49
+ * rather than faking a defence — the one that cannot:
50
+ *
51
+ * CLOSED (by an RO-commitment + session binding, mirroring the EC `baseOT`'s commit round):
52
+ * • ADAPTIVE / MALLEABLE / REPLAYED key material. The receiver first sends only a RO-commitment
53
+ * `com = H(rn ‖ pk_0 ‖ pk_1)` to its ordered public keys (and a receiver nonce `rn`); the sender
54
+ * replies with a fresh nonce `sn`; the parties set `sid = H(rn ‖ sn)`; only THEN does the receiver
55
+ * open `(pk_0, pk_1)` and the sender encapsulate, binding every pad to `(sid, com)`. The sender
56
+ * recomputes `com` from the opened keys and ABORTS on any mismatch. So the receiver must fix its
57
+ * keys BEFORE it learns `sn` (no adaptive/precomputed/related-key choice, no cross-session replay),
58
+ * and cannot open keys other than the ones it committed to — exactly the non-malleability the EC
59
+ * base OT gets from `R` being a single committed group element.
60
+ *
61
+ * NOT CLOSABLE from an OPAQUE ML-KEM, and WHY (an impossibility, not a shortcut):
62
+ * • A receiver that simply runs keygen TWICE and keeps BOTH secret keys learns both messages. To
63
+ * stop it the protocol would have to force one slot to a public key whose secret NOBODY knows
64
+ * (the Masny–Rindal "endemic OT" idea: `pk_{1−c}` derived in the public-key space from a random
65
+ * oracle / a common reference). That needs either (i) sampling / adding in ML-KEM's structured
66
+ * public-key space — reaching into the Module-LWE internals this module forbids (no hand-rolled
67
+ * lattice crypto), or (ii) a trusted setup / CRS. Crucially, the SAME decisional-Module-LWE
68
+ * indistinguishability that makes choice privacy hold (a real `pk` ≈ a uniform one) is what makes
69
+ * a cheating "both-keys-real" receiver UNDETECTABLE to the sender: it cannot tell a secretless
70
+ * slot from a real one. So this residue is intrinsic to the opaque primitive.
71
+ *
72
+ * ─── LIFTING THE RESIDUE (how this plugs into the stack) ───────────────────────────────────────────
73
+ * The residue is closed exactly where the EC stack closes its analogue: the KOS OT-extension (`ot.ts`).
74
+ * KOS runs the base OTs with ROLES INVERTED (the extension *sender* is the base *receiver*, holding the
75
+ * secret selector s) and adds the correlation check over sacrifice rows. A base receiver keeping both
76
+ * secrets maps to the extension *sender* learning the extension *receiver*'s seed pairs; the KOS
77
+ * consistency machinery + correlation-robust hash constrain that, raising the overall extension to
78
+ * malicious-with-abort against a cheating extension receiver — the attack IKNP is vulnerable to. So
79
+ * `kemBaseOtMal` is the right base layer: it hardens everything the opaque KEM allows (commitment /
80
+ * binding), and defers only the intrinsic residue to the layer that already handles it.
81
+ *
82
+ * No hand-rolled lattice crypto: all PQ operations go through `ml_kem768` (keygen/encapsulate/
83
+ * decapsulate) as a black box. Randomness follows the module policy — a CSPRNG (`secureOtRandom`) by
84
+ * default, a seeded deterministic stream ONLY when a test passes one.
85
+ */
86
+ import { type OtChannel, type OtRandom } from './ot';
87
+ /**
88
+ * The random-oracle pad for one slot: `H(TAG ‖ i ‖ b ‖ sharedSecret)` expanded (SHA-256 counter mode)
89
+ * to exactly `len` bytes. `i` binds the transfer index and `b` the slot, so the two pads of one transfer
90
+ * — and the pads across transfers — are domain-separated.
91
+ */
92
+ export declare function kemOtPad(index: number, slot: number, sharedSecret: Uint8Array, len: number): Uint8Array;
93
+ /** Receiver round-1 wire message: the ordered public-key pairs the sender encapsulates under. */
94
+ export interface KemOtReceiverMessage {
95
+ /** For each transfer i, [pk_0, pk_1]; the chosen key sits at index `choices[i]`. */
96
+ publicKeys: Array<[Uint8Array, Uint8Array]>;
97
+ }
98
+ /**
99
+ * The receiver's RETAINED state after round 1. An honest receiver keeps ONLY the chosen secret key per
100
+ * transfer (`secretKeys[i]` is `sk_{choices[i]}`); the other secret has been discarded. This is exactly
101
+ * the state the semi-honest model assumes, and the one the tests assert over ("the non-chosen ciphertext
102
+ * does not decrypt under the receiver's state").
103
+ */
104
+ export interface KemOtReceiverState {
105
+ /** The choice bits, one per transfer. */
106
+ choices: number[];
107
+ /** The chosen secret key per transfer (sk for slot `choices[i]`); the non-chosen secret is discarded. */
108
+ secretKeys: Uint8Array[];
109
+ }
110
+ /** Sender round wire message: the two ciphertexts + two padded messages per transfer. */
111
+ export interface KemOtSenderMessage {
112
+ /** For each transfer i, the ML-KEM ciphertexts [ct_0, ct_1] under [pk_0, pk_1]. */
113
+ ciphertexts: Array<[Uint8Array, Uint8Array]>;
114
+ /** 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)]. */
115
+ encrypted: Array<[Uint8Array, Uint8Array]>;
116
+ }
117
+ /** A full KEM base-OT transcript (receiver message + sender message), exposed for inspection/tests. */
118
+ export interface KemBaseOtTranscript extends KemOtReceiverMessage, KemOtSenderMessage {
119
+ }
120
+ export interface KemBaseOtResult {
121
+ /** Receiver's recovered messages m_{i, choices[i]}. */
122
+ received: Uint8Array[];
123
+ transcript: KemBaseOtTranscript;
124
+ }
125
+ /**
126
+ * RECEIVER round 1: for each choice bit, generate two real ML-KEM keypairs, send both public keys
127
+ * (chosen one at wire index `c`), and retain only the chosen secret key. Returns the wire message AND
128
+ * the retained receiver state.
129
+ */
130
+ export declare function kemBaseOtReceive(choices: number[], rng?: OtRandom): {
131
+ message: KemOtReceiverMessage;
132
+ state: KemOtReceiverState;
133
+ };
134
+ /**
135
+ * SENDER round: encapsulate under each received public key and one-time-pad each message under
136
+ * `H(slot ‖ sharedSecret)`. `messages[i] = [m_0, m_1]`; the two messages of one transfer must be equal
137
+ * length (padding is derived to that length). Returns the sender wire message.
138
+ */
139
+ export declare function kemBaseOtSend(messages: Array<[Uint8Array, Uint8Array]>, receiverMessage: KemOtReceiverMessage, rng?: OtRandom): KemOtSenderMessage;
140
+ /**
141
+ * RECEIVER recovery: decapsulate ONLY the chosen ciphertext with the retained secret key and unmask the
142
+ * chosen message. The receiver never touches the non-chosen slot (it has no secret for it). Returns
143
+ * `received[i] = m_{i, choices[i]}`.
144
+ */
145
+ export declare function kemBaseOtOpen(state: KemOtReceiverState, senderMessage: KemOtSenderMessage): Uint8Array[];
146
+ /**
147
+ * In-process combined simulator (both parties in one call), matching the shape of `baseOT` in `ot.ts`:
148
+ * run `count = choices.length` KEM base OTs and return the receiver's recovered messages plus the full
149
+ * transcript. This is the drop-in the KOS extension consumes (see `OtChannel`'s `baseOtFn` option).
150
+ */
151
+ export declare function kemBaseOT(messages: Array<[Uint8Array, Uint8Array]>, choices: number[], rng?: OtRandom): KemBaseOtResult;
152
+ /**
153
+ * Wire the ML-KEM base OT into the existing KOS OT-extension: returns the constructor option bag for
154
+ * `OtChannel` that makes it run its κ setup base OTs over `kemBaseOT` (post-quantum) instead of the
155
+ * classical EC Chou–Orlandi `baseOT`. Everything above the base OT (the IKNP matrix, the KOS correlation
156
+ * check, the correlation-robust hash) is unchanged.
157
+ *
158
+ * const ch = new OtChannel(secureOtRandom(), withKemBaseOt());
159
+ */
160
+ export declare function withKemBaseOt(): ConstructorParameters<typeof OtChannel>[1];
161
+ /** Raised when the committed KEM base OT aborts: a cheating receiver (opened keys ≠ commitment) is caught. */
162
+ export declare class KemBaseOtAbort extends Error {
163
+ constructor(message: string);
164
+ }
165
+ /** Receiver round-1 message: a nonce + the per-transfer commitments (public keys NOT yet revealed). */
166
+ export interface KemMalCommitMessage {
167
+ /** Receiver nonce `rn`, bound into every commitment and into `sid`. */
168
+ rn: Uint8Array;
169
+ /** Per-transfer RO-commitment `com_i = H(rn ‖ i ‖ pk_0 ‖ pk_1)`. */
170
+ commitments: Uint8Array[];
171
+ }
172
+ /** Receiver round-3 message: the opening of the committed public keys. */
173
+ export interface KemMalOpenMessage {
174
+ /** For each transfer i, the ordered public keys `[pk_0, pk_1]` the sender encapsulates under. */
175
+ publicKeys: Array<[Uint8Array, Uint8Array]>;
176
+ }
177
+ /** Sender round-2 message: the sender nonce `sn` completing the session id. */
178
+ export interface KemMalSenderNonce {
179
+ sn: Uint8Array;
180
+ }
181
+ /** Sender round-4 message: ciphertexts + `(sid, com)`-bound padded messages. */
182
+ export interface KemMalSenderMessage {
183
+ ciphertexts: Array<[Uint8Array, Uint8Array]>;
184
+ encrypted: Array<[Uint8Array, Uint8Array]>;
185
+ }
186
+ /** The receiver's retained state across the committed flow. */
187
+ export interface KemMalReceiverState {
188
+ choices: number[];
189
+ /** The chosen secret key per transfer (`sk_{choices[i]}`); the non-chosen secret is discarded. */
190
+ secretKeys: Uint8Array[];
191
+ /** The ordered public keys (opened in round 3). */
192
+ publicKeys: Array<[Uint8Array, Uint8Array]>;
193
+ rn: Uint8Array;
194
+ }
195
+ /** A full committed KEM base-OT transcript, exposed for inspection/tests. */
196
+ export interface KemMalTranscript extends KemMalCommitMessage, KemMalOpenMessage, KemMalSenderNonce, KemMalSenderMessage {
197
+ }
198
+ export interface KemBaseOtMalResult {
199
+ received: Uint8Array[];
200
+ transcript: KemMalTranscript;
201
+ }
202
+ /**
203
+ * TEST-ONLY injection points modelling a malicious RECEIVER in the committed flow. Each hook mutates a
204
+ * value exactly where it crosses the wire, so the honest sender's checks run against the adversarial
205
+ * value.
206
+ */
207
+ export interface KemMalTamper {
208
+ /** Malicious receiver: open public keys DIFFERENT from the committed ones (→ sender abort). */
209
+ tamperOpenedKeys?: (publicKeys: Array<[Uint8Array, Uint8Array]>) => void;
210
+ /** Malicious receiver/MITM: tamper the sender nonce the receiver binds `sid` with (→ pad mismatch). */
211
+ tamperReceiverSid?: (sid: Uint8Array) => Uint8Array;
212
+ }
213
+ /**
214
+ * RECEIVER round 1: generate the keypairs (keeping only the chosen secret), draw a nonce `rn`, and send
215
+ * only the COMMITMENTS to the ordered public keys. Returns the round-1 wire message plus the retained
216
+ * state (which holds the not-yet-revealed public keys for the round-3 opening).
217
+ */
218
+ export declare function kemBaseOtMalCommit(choices: number[], rng?: OtRandom): {
219
+ message: KemMalCommitMessage;
220
+ state: KemMalReceiverState;
221
+ };
222
+ /** SENDER round 2: draw the sender nonce `sn` completing the session id. */
223
+ export declare function kemBaseOtMalSenderNonce(rng?: OtRandom): KemMalSenderNonce;
224
+ /** RECEIVER round 3: open the committed public keys (the sender will re-check them against `com`). */
225
+ export declare function kemBaseOtMalOpen(state: KemMalReceiverState): KemMalOpenMessage;
226
+ /**
227
+ * SENDER round 4: VERIFY the opened keys against the round-1 commitments (ABORT on mismatch), then
228
+ * encapsulate under each and one-time-pad each message with a `(sid, com)`-bound pad.
229
+ */
230
+ export declare function kemBaseOtMalSend(messages: Array<[Uint8Array, Uint8Array]>, commit: KemMalCommitMessage, senderNonce: KemMalSenderNonce, opening: KemMalOpenMessage, rng?: OtRandom): KemMalSenderMessage;
231
+ /**
232
+ * RECEIVER round 5: decapsulate ONLY the chosen ciphertext and unmask with the `(sid, com)`-bound pad.
233
+ */
234
+ export declare function kemBaseOtMalRecover(state: KemMalReceiverState, senderNonce: KemMalSenderNonce, senderMessage: KemMalSenderMessage, tamper?: KemMalTamper): Uint8Array[];
235
+ /**
236
+ * In-process combined simulator for the MALICIOUS-HARDENED committed KEM base OT, matching the shape of
237
+ * `kemBaseOT` so it is a drop-in for `OtChannel`'s `baseOtFn` (`withKemBaseOtMal`). Runs all five
238
+ * rounds; a cheating receiver (opened keys ≠ commitment) makes the sender throw `KemBaseOtAbort`. The
239
+ * `tamper` hook injects malicious-receiver behaviour for tests.
240
+ */
241
+ export declare function kemBaseOtMal(messages: Array<[Uint8Array, Uint8Array]>, choices: number[], rng?: OtRandom, tamper?: KemMalTamper): KemBaseOtMalResult;
242
+ /**
243
+ * Wire the MALICIOUS-HARDENED committed ML-KEM base OT into the KOS OT-extension — the recommended
244
+ * post-quantum base. Like `withKemBaseOt`, but the κ setup base OTs run the committed/session-bound
245
+ * protocol (and abort on a cheating receiver). Everything above the base OT is unchanged.
246
+ *
247
+ * const ch = new OtChannel(secureOtRandom(), withKemBaseOtMal());
248
+ */
249
+ export declare function withKemBaseOtMal(): ConstructorParameters<typeof OtChannel>[1];
250
+ /** Byte length of a derived committed-KEM random-OT shared secret. */
251
+ export declare const KEM_OT_SECRET_BYTES = 32;
252
+ /** Sender round-4 message of the committed KEM RANDOM OT: ciphertexts (wire) + both per-slot secrets (sender-local). */
253
+ export interface KemMalRandomSenderMessage {
254
+ /** For each transfer i, the ML-KEM ciphertexts `[ct_0, ct_1]` (the only part that crosses the wire). */
255
+ ciphertexts: Array<[Uint8Array, Uint8Array]>;
256
+ /** Sender-LOCAL: the two session-bound shared secrets `[ss_0, ss_1]` per transfer (never sent). */
257
+ senderSecrets: Array<[Uint8Array, Uint8Array]>;
258
+ }
259
+ /** A full committed KEM RANDOM-OT transcript + the two parties' shared-secret outputs. */
260
+ export interface KemBaseOtMalRandomResult {
261
+ /** Sender's per-slot shared secrets `[ss_0, ss_1]` per transfer (the sender learns BOTH). */
262
+ senderSecrets: Array<[Uint8Array, Uint8Array]>;
263
+ /** Receiver's chosen shared secret `ss_{choices[i]}` per transfer (the honest receiver learns ONE). */
264
+ recvSecrets: Uint8Array[];
265
+ /** The session id `sid = H(rn ‖ sn)` the secrets are bound to (public; both parties derive it). */
266
+ sid: Uint8Array;
267
+ transcript: {
268
+ rn: Uint8Array;
269
+ commitments: Uint8Array[];
270
+ sn: Uint8Array;
271
+ publicKeys: Array<[Uint8Array, Uint8Array]>;
272
+ ciphertexts: Array<[Uint8Array, Uint8Array]>;
273
+ };
274
+ }
275
+ /**
276
+ * SENDER round 4 (RANDOM variant): VERIFY the opened keys against the round-1 commitments (ABORT on
277
+ * mismatch), encapsulate under each, and derive BOTH per-slot session-bound shared secrets. No messages
278
+ * are padded — the two secrets per transfer ARE the random output.
279
+ */
280
+ export declare function kemBaseOtMalRandomSend(commit: KemMalCommitMessage, senderNonce: KemMalSenderNonce, opening: KemMalOpenMessage, rng?: OtRandom): KemMalRandomSenderMessage;
281
+ /**
282
+ * RECEIVER round 5 (RANDOM variant): decapsulate ONLY the chosen ciphertext and derive the chosen
283
+ * session-bound shared secret. The receiver never touches the non-chosen slot (it kept no secret for it).
284
+ */
285
+ export declare function kemBaseOtMalRandomRecover(state: KemMalReceiverState, senderNonce: KemMalSenderNonce, senderMessage: KemMalRandomSenderMessage, tamper?: KemMalTamper): Uint8Array[];
286
+ /**
287
+ * In-process combined simulator for the committed KEM RANDOM OT. Runs the same five rounds as
288
+ * `kemBaseOtMal` (commit → sender nonce → open → verify+encapsulate → recover) but outputs per-slot
289
+ * shared secrets instead of padded messages. A cheating receiver (opened keys ≠ commitment) makes the
290
+ * sender throw `KemBaseOtAbort`. The `tamper` hook injects malicious-receiver behaviour for tests.
291
+ */
292
+ export declare function kemBaseOtMalRandom(choices: number[], rng?: OtRandom, tamper?: KemMalTamper): KemBaseOtMalRandomResult;