@umbra-privacy/sdk 1.0.0 → 2.0.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.
- package/README.md +104 -25
- package/dist/{addresses-Brzgurv_.d.ts → addresses-B7HybtbJ.d.ts} +2 -1
- package/dist/{addresses-D_0YAS6B.d.cts → addresses-CTVY1oi7.d.cts} +2 -1
- package/dist/arcium-BXXlryfe.d.cts +20 -0
- package/dist/arcium-BXXlryfe.d.ts +20 -0
- package/dist/chunk-4RHXVBNI.js +203 -0
- package/dist/chunk-4RHXVBNI.js.map +1 -0
- package/dist/chunk-4TZVXB5G.js +324 -0
- package/dist/chunk-4TZVXB5G.js.map +1 -0
- package/dist/chunk-5GUSMQ74.cjs +549 -0
- package/dist/chunk-5GUSMQ74.cjs.map +1 -0
- package/dist/chunk-5KPQXPQM.js +36 -0
- package/dist/chunk-5KPQXPQM.js.map +1 -0
- package/dist/chunk-AXD7LXYY.cjs +405 -0
- package/dist/chunk-AXD7LXYY.cjs.map +1 -0
- package/dist/{chunk-HOEXDXRC.cjs → chunk-BL6WXLPV.cjs} +32 -360
- package/dist/chunk-BL6WXLPV.cjs.map +1 -0
- package/dist/chunk-CFFLOE7D.cjs +598 -0
- package/dist/chunk-CFFLOE7D.cjs.map +1 -0
- package/dist/{chunk-BM7N6N7E.js → chunk-CFTW5WNG.js} +3 -325
- package/dist/chunk-CFTW5WNG.js.map +1 -0
- package/dist/chunk-DD2WCK4C.js +327 -0
- package/dist/chunk-DD2WCK4C.js.map +1 -0
- package/dist/chunk-DMPMQ74B.cjs +246 -0
- package/dist/chunk-DMPMQ74B.cjs.map +1 -0
- package/dist/{chunk-2Q75CQQJ.js → chunk-EEKF4553.js} +2 -2
- package/dist/chunk-EEKF4553.js.map +1 -0
- package/dist/chunk-ENVYYEM4.cjs +113 -0
- package/dist/chunk-ENVYYEM4.cjs.map +1 -0
- package/dist/chunk-FQX6ZYGJ.js +500 -0
- package/dist/chunk-FQX6ZYGJ.js.map +1 -0
- package/dist/chunk-FSK2ICMB.cjs +39 -0
- package/dist/chunk-FSK2ICMB.cjs.map +1 -0
- package/dist/chunk-FZYWLQAF.cjs +355 -0
- package/dist/chunk-FZYWLQAF.cjs.map +1 -0
- package/dist/chunk-GP26R377.js +436 -0
- package/dist/chunk-GP26R377.js.map +1 -0
- package/dist/chunk-HA5FLM63.js +393 -0
- package/dist/chunk-HA5FLM63.js.map +1 -0
- package/dist/chunk-INJ73LXQ.js +1107 -0
- package/dist/chunk-INJ73LXQ.js.map +1 -0
- package/dist/chunk-JPDF7BIT.cjs +10892 -0
- package/dist/chunk-JPDF7BIT.cjs.map +1 -0
- package/dist/{chunk-MDFSBU5W.cjs → chunk-LTCKPTZC.cjs} +2 -351
- package/dist/chunk-LTCKPTZC.cjs.map +1 -0
- package/dist/chunk-MKNCBUFA.js +564 -0
- package/dist/chunk-MKNCBUFA.js.map +1 -0
- package/dist/chunk-NKVMSABR.cjs +207 -0
- package/dist/chunk-NKVMSABR.cjs.map +1 -0
- package/dist/chunk-OFDWNWCL.js +70 -0
- package/dist/chunk-OFDWNWCL.js.map +1 -0
- package/dist/chunk-QJAUUYZU.cjs +331 -0
- package/dist/chunk-QJAUUYZU.cjs.map +1 -0
- package/dist/chunk-RVUYPKKD.js +10750 -0
- package/dist/chunk-RVUYPKKD.js.map +1 -0
- package/dist/chunk-TLR7A64G.js +103 -0
- package/dist/chunk-TLR7A64G.js.map +1 -0
- package/dist/{chunk-MVKTV3FT.cjs → chunk-TQQZGNOI.cjs} +2 -2
- package/dist/chunk-TQQZGNOI.cjs.map +1 -0
- package/dist/chunk-UOFYS6M3.js +219 -0
- package/dist/chunk-UOFYS6M3.js.map +1 -0
- package/dist/chunk-UXMQI6B7.js +2406 -0
- package/dist/chunk-UXMQI6B7.js.map +1 -0
- package/dist/chunk-WN75ORDT.js +571 -0
- package/dist/chunk-WN75ORDT.js.map +1 -0
- package/dist/chunk-Y55PYKXH.cjs +595 -0
- package/dist/chunk-Y55PYKXH.cjs.map +1 -0
- package/dist/chunk-YEZBTYCP.cjs +77 -0
- package/dist/chunk-YEZBTYCP.cjs.map +1 -0
- package/dist/chunk-ZQOIYCGA.cjs +1126 -0
- package/dist/chunk-ZQOIYCGA.cjs.map +1 -0
- package/dist/chunk-ZY3TSHMJ.cjs +2665 -0
- package/dist/chunk-ZY3TSHMJ.cjs.map +1 -0
- package/dist/client-DkVBHMWb.d.cts +2613 -0
- package/dist/client-V4AF6Bz9.d.ts +2613 -0
- package/dist/common/pda/index.cjs +145 -0
- package/dist/common/pda/index.cjs.map +1 -0
- package/dist/common/pda/index.d.cts +1250 -0
- package/dist/common/pda/index.d.ts +1250 -0
- package/dist/common/pda/index.js +8 -0
- package/dist/common/pda/index.js.map +1 -0
- package/dist/constants/index.cjs +38 -164
- package/dist/constants/index.cjs.map +1 -1
- package/dist/constants/index.d.cts +8 -425
- package/dist/constants/index.d.ts +8 -425
- package/dist/constants/index.js +15 -124
- package/dist/constants/index.js.map +1 -1
- package/dist/crypto/index.cjs +583 -0
- package/dist/crypto/index.cjs.map +1 -0
- package/dist/crypto/index.d.cts +6731 -0
- package/dist/crypto/index.d.ts +6731 -0
- package/dist/crypto/index.js +14 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/{cryptography-BTGC72u-.d.ts → cryptography-BFSJcvi6.d.ts} +3 -2465
- package/dist/{cryptography-BTGC72u-.d.cts → cryptography-D6tPDh-Y.d.cts} +3 -2465
- package/dist/errors/index.cjs +64 -54
- package/dist/errors/index.d.cts +7 -797
- package/dist/errors/index.d.ts +7 -797
- package/dist/errors/index.js +3 -1
- package/dist/errors-B9EoPeWV.d.cts +593 -0
- package/dist/errors-B9EoPeWV.d.ts +593 -0
- package/dist/errors-DAIrstEL.d.cts +300 -0
- package/dist/errors-DPNMfyh0.d.ts +300 -0
- package/dist/index-BG0yjL7C.d.cts +6006 -0
- package/dist/index-ByynoyBO.d.ts +6006 -0
- package/dist/index.cjs +5126 -16118
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1031 -7685
- package/dist/index.d.ts +1031 -7685
- package/dist/index.js +3219 -14905
- package/dist/index.js.map +1 -1
- package/dist/interfaces/index.d.cts +14 -6
- package/dist/interfaces/index.d.ts +14 -6
- package/dist/interfaces-43cReBcS.d.cts +3346 -0
- package/dist/interfaces-B8xKNl_6.d.ts +997 -0
- package/dist/interfaces-D2NO6kDD.d.cts +997 -0
- package/dist/interfaces-z_xYJlgV.d.ts +3346 -0
- package/dist/math/index.cjs +115 -0
- package/dist/math/index.cjs.map +1 -0
- package/dist/math/index.d.cts +1327 -0
- package/dist/math/index.d.ts +1327 -0
- package/dist/math/index.js +10 -0
- package/dist/math/index.js.map +1 -0
- package/dist/networks-RMd3abPE.d.ts +44 -0
- package/dist/networks-yAoO8peQ.d.cts +44 -0
- package/dist/relayer-NRRMSMNB.js +4 -0
- package/dist/relayer-NRRMSMNB.js.map +1 -0
- package/dist/relayer-RJHEIXJG.cjs +21 -0
- package/dist/relayer-RJHEIXJG.cjs.map +1 -0
- package/dist/solana/index.cjs +56 -0
- package/dist/solana/index.cjs.map +1 -0
- package/dist/solana/index.d.cts +105 -0
- package/dist/solana/index.d.ts +105 -0
- package/dist/solana/index.js +7 -0
- package/dist/solana/index.js.map +1 -0
- package/dist/{index-CLj_zWSD.d.ts → temporal-BbRaEPoO.d.ts} +1 -1
- package/dist/{index-CX6_pIRS.d.cts → temporal-oUj7iCaq.d.cts} +1 -1
- package/dist/transaction-forwarder-5mAMTjw6.d.ts +1155 -0
- package/dist/transaction-forwarder-C6gMUG7a.d.cts +1155 -0
- package/dist/types/index.cjs +232 -231
- package/dist/types/index.d.cts +15 -1485
- package/dist/types/index.d.ts +15 -1485
- package/dist/types/index.js +2 -1
- package/dist/types-BohhvPth.d.cts +87 -0
- package/dist/types-CW0oTT0j.d.ts +87 -0
- package/dist/types-C_V_CaKK.d.cts +2468 -0
- package/dist/types-C_V_CaKK.d.ts +2468 -0
- package/dist/types-Ca7frykr.d.ts +793 -0
- package/dist/types-CuKeoI19.d.cts +1296 -0
- package/dist/types-CxfTIpN9.d.ts +1052 -0
- package/dist/{types-n-sHFcgr.d.ts → types-D1jDUjfN.d.ts} +2 -2
- package/dist/types-DKEDUlH9.d.ts +1296 -0
- package/dist/types-EKuIfxTz.d.cts +1052 -0
- package/dist/{types-BBuELtY8.d.cts → types-IMGYmlv-.d.cts} +2 -2
- package/dist/types-PwNLi_2k.d.cts +793 -0
- package/dist/utils/index.cjs +823 -525
- package/dist/utils/index.d.cts +1711 -4021
- package/dist/utils/index.d.ts +1711 -4021
- package/dist/utils/index.js +9 -3
- package/dist/{versions-D9PqsEvj.d.cts → versions-BRlR36EA.d.cts} +1 -0
- package/dist/{versions-D9PqsEvj.d.ts → versions-BRlR36EA.d.ts} +1 -0
- package/package.json +79 -18
- package/dist/chunk-2Q75CQQJ.js.map +0 -1
- package/dist/chunk-BM7N6N7E.js.map +0 -1
- package/dist/chunk-GXKSUB2U.cjs +0 -4416
- package/dist/chunk-GXKSUB2U.cjs.map +0 -1
- package/dist/chunk-HOEXDXRC.cjs.map +0 -1
- package/dist/chunk-MDFSBU5W.cjs.map +0 -1
- package/dist/chunk-MQY7HDIA.js +0 -600
- package/dist/chunk-MQY7HDIA.js.map +0 -1
- package/dist/chunk-MVKTV3FT.cjs.map +0 -1
- package/dist/chunk-PG2J6V6Y.js +0 -4094
- package/dist/chunk-PG2J6V6Y.js.map +0 -1
- package/dist/chunk-VEGLTTYQ.cjs +0 -621
- package/dist/chunk-VEGLTTYQ.cjs.map +0 -1
- package/dist/chunk-WVHQ46DD.js +0 -758
- package/dist/chunk-WVHQ46DD.js.map +0 -1
- package/dist/index-B9pDY73x.d.ts +0 -12933
- package/dist/index-D33yo0qB.d.cts +0 -12933
- package/dist/networks-C-orpSFW.d.ts +0 -65
- package/dist/networks-FxYERGD1.d.cts +0 -65
|
@@ -0,0 +1,3346 @@
|
|
|
1
|
+
import { C as Curve25519FieldElement, B as Bn254FieldElement, g as PoseidonHash, h as PoseidonCounter, P as PoseidonKey, i as PoseidonKeystream, j as PoseidonCiphertext, k as PoseidonPlaintext, R as RcEncryptionNonce, D as DailyViewingKey, M as MasterSeed, H as HourlyViewingKey, d as MasterViewingKey, f as MintViewingKey, l as MinuteViewingKey, e as MonthlyViewingKey, S as SecondViewingKey, Y as YearlyViewingKey, m as RcCiphertext, n as RcPlaintext, o as RcCounter, p as RcKey } from './cryptography-D6tPDh-Y.cjs';
|
|
2
|
+
import { Address } from '@solana/kit';
|
|
3
|
+
import { i as U512BeBytes, f as U256 } from './types-C_V_CaKK.cjs';
|
|
4
|
+
import { Y as Year, M as Month, D as Day, H as Hour, a as Minute, S as Second } from './types-IMGYmlv-.cjs';
|
|
5
|
+
import { C as Curve25519KeypairResult } from './types-CuKeoI19.cjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Fiat-Shamir Challenge Interfaces
|
|
9
|
+
*
|
|
10
|
+
* This module defines TypeScript function type interfaces and dependency injection
|
|
11
|
+
* interfaces for Fiat-Shamir challenge generation and related polynomial operations
|
|
12
|
+
* used in Umbra's zero-knowledge proof systems.
|
|
13
|
+
*
|
|
14
|
+
* ## Fiat-Shamir Heuristic — Background
|
|
15
|
+
*
|
|
16
|
+
* An interactive sigma-protocol requires a verifier who sends a uniformly random
|
|
17
|
+
* challenge after seeing the prover's first message. The Fiat-Shamir transform
|
|
18
|
+
* replaces this interactive step with a hash of the transcript, turning the proof
|
|
19
|
+
* into a non-interactive argument of knowledge (NIAOK) secure in the Random
|
|
20
|
+
* Oracle Model (ROM).
|
|
21
|
+
*
|
|
22
|
+
* For Umbra's Groth16-based claim instructions, the challenge is derived from a
|
|
23
|
+
* 10-element transcript that includes all public inputs (nullifiers, commitments,
|
|
24
|
+
* root, fee parameters, etc.) in a canonical order defined in
|
|
25
|
+
* `rules/fiat-shamir-and-aggregated-hash.md`. The ordering **must not change**
|
|
26
|
+
* because the Circom circuit and the on-chain verifier both encode the same
|
|
27
|
+
* ordering — a mismatch silently produces wrong proofs.
|
|
28
|
+
*
|
|
29
|
+
* ## Security Considerations
|
|
30
|
+
*
|
|
31
|
+
* - Challenge generation must be deterministic given the same transcript.
|
|
32
|
+
* - Outputs must be uniformly distributed over the Curve25519 prime field GF(p).
|
|
33
|
+
* - All arithmetic must use constant-time field operations to prevent timing side-channels.
|
|
34
|
+
* - The hash function used (Keccak-256) must be modelled as a random oracle; do
|
|
35
|
+
* not substitute weaker primitives.
|
|
36
|
+
*
|
|
37
|
+
* @packageDocumentation
|
|
38
|
+
* @module interfaces/cryptography/challenges
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Function type for generating Fiat-Shamir challenges from a proof transcript.
|
|
44
|
+
*
|
|
45
|
+
* Takes a serialised transcript (an arbitrary byte array representing all public
|
|
46
|
+
* inputs in canonical order) and produces a uniformly distributed element of the
|
|
47
|
+
* Curve25519 prime field GF(p = 2^255 - 19) using Keccak-256 with rejection
|
|
48
|
+
* sampling.
|
|
49
|
+
*
|
|
50
|
+
* @param input - The serialised transcript byte array. Must contain all public
|
|
51
|
+
* proof inputs concatenated in the order mandated by the Circom circuit and
|
|
52
|
+
* on-chain verifier. The transcript is treated as opaque bytes; no internal
|
|
53
|
+
* parsing is performed.
|
|
54
|
+
* @returns A {@link Curve25519FieldElement} in the range [0, 2^255 - 19) derived
|
|
55
|
+
* deterministically from `input`.
|
|
56
|
+
*
|
|
57
|
+
* @remarks
|
|
58
|
+
* ## Contract
|
|
59
|
+
*
|
|
60
|
+
* Implementations of this type **must** guarantee:
|
|
61
|
+
* 1. **Determinism** — the same `input` always produces the same output.
|
|
62
|
+
* 2. **Uniform distribution** — the output is statistically indistinguishable
|
|
63
|
+
* from a uniformly random element of GF(p). Bias from simple modular
|
|
64
|
+
* reduction is not acceptable; use rejection sampling.
|
|
65
|
+
* 3. **No additional state** — the function must be pure (no caches or counters
|
|
66
|
+
* that could make two calls with the same `input` return different values).
|
|
67
|
+
*
|
|
68
|
+
* ## Algorithm (reference implementation)
|
|
69
|
+
*
|
|
70
|
+
* 1. Compute `h = Keccak256(input)` — 256-bit output.
|
|
71
|
+
* 2. Clear bit 255 (`h[31] &= 0x7F` in little-endian) to constrain to 255 bits.
|
|
72
|
+
* 3. Interpret `h` as a little-endian 256-bit unsigned integer `v`.
|
|
73
|
+
* 4. If `v < p`, return `v` as the challenge.
|
|
74
|
+
* 5. Otherwise, re-hash `h` (hash chaining) and repeat from step 2.
|
|
75
|
+
*
|
|
76
|
+
* ## Why Transcript Ordering Matters for Soundness
|
|
77
|
+
*
|
|
78
|
+
* In the ROM, the challenge is bound to the *entire* transcript. If two different
|
|
79
|
+
* transcripts (e.g. the same inputs in a different order) collide to the same
|
|
80
|
+
* challenge, an adversary could craft a proof that passes for one statement but
|
|
81
|
+
* was computed for another. The canonical element ordering in
|
|
82
|
+
* `rules/fiat-shamir-and-aggregated-hash.md` is part of the protocol definition
|
|
83
|
+
* and must be respected by all callers.
|
|
84
|
+
*
|
|
85
|
+
* @public
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* const generateChallenge: FiatShamirChallengeGeneratorFunction =
|
|
90
|
+
* getFiatShamirChallengeGeneratorFunction();
|
|
91
|
+
*
|
|
92
|
+
* // Serialise public inputs in canonical order before hashing:
|
|
93
|
+
* const transcript = buildFiatShamirTranscript({
|
|
94
|
+
* merkleRoot,
|
|
95
|
+
* nullifiers,
|
|
96
|
+
* utxoCommitments,
|
|
97
|
+
* recipientPublicKey,
|
|
98
|
+
* fees,
|
|
99
|
+
* });
|
|
100
|
+
* const challenge: Curve25519FieldElement = generateChallenge(transcript);
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* @see {@link getFiatShamirChallengeGeneratorFunction} — factory that returns the default implementation
|
|
104
|
+
*/
|
|
105
|
+
type FiatShamirChallengeGeneratorFunction = (input: Uint8Array) => Curve25519FieldElement;
|
|
106
|
+
/**
|
|
107
|
+
* Function type for computing successive powers of a Fiat-Shamir challenge value.
|
|
108
|
+
*
|
|
109
|
+
* Returns an array `[c^0, c^1, c^2, ..., c^maxPower]` where `c` is the challenge
|
|
110
|
+
* and all arithmetic is performed in the Curve25519 prime field GF(p = 2^255 - 19).
|
|
111
|
+
*
|
|
112
|
+
* @param challenge - The base challenge value to exponentiate.
|
|
113
|
+
* Must be a valid {@link Curve25519FieldElement} (i.e. `0 <= challenge < p`).
|
|
114
|
+
* @param maxPower - The highest power to compute (inclusive). Must be a
|
|
115
|
+
* non-negative integer. `maxPower === 0` returns `[1n]`.
|
|
116
|
+
* @returns An array of length `maxPower + 1` containing
|
|
117
|
+
* `[1, challenge, challenge^2, ..., challenge^maxPower]` as
|
|
118
|
+
* {@link Curve25519FieldElement} values.
|
|
119
|
+
*
|
|
120
|
+
* @throws {Error} If `maxPower` is negative.
|
|
121
|
+
*
|
|
122
|
+
* @remarks
|
|
123
|
+
* ## Contract
|
|
124
|
+
*
|
|
125
|
+
* Implementations of this type **must** guarantee:
|
|
126
|
+
* - The returned array has exactly `maxPower + 1` elements.
|
|
127
|
+
* - `result[0] === 1n` always (challenge^0 = 1 by convention).
|
|
128
|
+
* - `result[1] === challenge` when `maxPower >= 1`.
|
|
129
|
+
* - All elements are valid {@link Curve25519FieldElement} values in [0, p).
|
|
130
|
+
* - The computation uses O(maxPower) field multiplications (no repeated squaring
|
|
131
|
+
* is necessary because all intermediate values are needed).
|
|
132
|
+
*
|
|
133
|
+
* ## Use in ZK Protocols
|
|
134
|
+
*
|
|
135
|
+
* Challenge powers appear in batched polynomial commitment schemes: if a verifier
|
|
136
|
+
* wants to check multiple polynomial evaluations simultaneously, it combines
|
|
137
|
+
* the checks using challenge powers as random coefficients. For example, in
|
|
138
|
+
* a 10-element Fiat-Shamir transcript the Groth16 circuit may need
|
|
139
|
+
* `[c^0, ..., c^9]` to batch-verify all input commitments in a single pairing.
|
|
140
|
+
*
|
|
141
|
+
* @public
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* const computePowers: ChallengePowersFunction = getChallengePowersFunction();
|
|
146
|
+
* const challenge = generateChallenge(transcript);
|
|
147
|
+
* const powers = computePowers(challenge, 9);
|
|
148
|
+
* // powers[0] === 1n
|
|
149
|
+
* // powers[1] === challenge
|
|
150
|
+
* // powers[9] === challenge^9 mod p
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* @see {@link getChallengePowersFunction} — factory that returns the default implementation
|
|
154
|
+
* @see {@link FiatShamirChallengeGeneratorFunction} — produces the challenge input
|
|
155
|
+
*/
|
|
156
|
+
type ChallengePowersFunction = (challenge: Curve25519FieldElement, maxPower: number) => Curve25519FieldElement[];
|
|
157
|
+
/**
|
|
158
|
+
* Function type for evaluating a univariate polynomial over the Curve25519 field.
|
|
159
|
+
*
|
|
160
|
+
* For a polynomial P(x) = c[0] + c[1]·x + c[2]·x² + ... + c[n]·x^n,
|
|
161
|
+
* computes `P(point)` using Horner's method. All arithmetic is performed in
|
|
162
|
+
* GF(p = 2^255 - 19).
|
|
163
|
+
*
|
|
164
|
+
* @param coefficients - The coefficient array `[c[0], c[1], ..., c[n]]`
|
|
165
|
+
* where `c[i]` is the coefficient of `x^i` (constant term first, highest
|
|
166
|
+
* degree last). All elements must be valid {@link Curve25519FieldElement} values.
|
|
167
|
+
* An empty array represents the zero polynomial and returns `0n`.
|
|
168
|
+
* @param point - The evaluation point, must be a valid {@link Curve25519FieldElement}.
|
|
169
|
+
* @returns `P(point) mod p` as a {@link Curve25519FieldElement}.
|
|
170
|
+
*
|
|
171
|
+
* @remarks
|
|
172
|
+
* ## Contract
|
|
173
|
+
*
|
|
174
|
+
* Implementations of this type **must** guarantee:
|
|
175
|
+
* - The result is a valid {@link Curve25519FieldElement} in [0, p).
|
|
176
|
+
* - Empty `coefficients` returns `0n`.
|
|
177
|
+
* - The evaluation uses Horner's method or equivalent (n multiplications and
|
|
178
|
+
* n additions for a degree-n polynomial), not the naïve O(n^2) approach.
|
|
179
|
+
*
|
|
180
|
+
* ## Horner's Method — Mathematical Derivation
|
|
181
|
+
*
|
|
182
|
+
* ```
|
|
183
|
+
* P(x) = c[0] + c[1]·x + c[2]·x² + ... + c[n]·x^n
|
|
184
|
+
* = c[0] + x·(c[1] + x·(c[2] + ... + x·c[n]))
|
|
185
|
+
* ```
|
|
186
|
+
*
|
|
187
|
+
* Starting from the innermost term `c[n]`, multiply by `x` and add `c[n-1]`,
|
|
188
|
+
* repeat until `c[0]` is incorporated. This requires exactly n multiplications
|
|
189
|
+
* and n additions regardless of the polynomial's degree — the minimum possible.
|
|
190
|
+
*
|
|
191
|
+
* ## Edge Cases
|
|
192
|
+
* - Empty coefficients array → returns `0n`.
|
|
193
|
+
* - Single coefficient `[c]` → returns `c` (constant polynomial, `point` is ignored).
|
|
194
|
+
*
|
|
195
|
+
* @public
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```typescript
|
|
199
|
+
* const evaluate: PolynomialEvaluatorFunction = getPolynomialEvaluatorFunction();
|
|
200
|
+
*
|
|
201
|
+
* // P(x) = 1 + 2x + 3x^2
|
|
202
|
+
* const coefficients = [1n, 2n, 3n] as Curve25519FieldElement[];
|
|
203
|
+
* const point = 2n as Curve25519FieldElement;
|
|
204
|
+
* const value = evaluate(coefficients, point);
|
|
205
|
+
* // value === 1 + 2*2 + 3*4 === 17n
|
|
206
|
+
* ```
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```typescript
|
|
210
|
+
* // Zero polynomial
|
|
211
|
+
* const zero = evaluate([], anyPoint); // === 0n
|
|
212
|
+
*
|
|
213
|
+
* // Constant polynomial
|
|
214
|
+
* const constant = evaluate([42n as Curve25519FieldElement], anyPoint); // === 42n
|
|
215
|
+
* ```
|
|
216
|
+
*
|
|
217
|
+
* @see {@link getPolynomialEvaluatorFunction} — factory that returns the default implementation
|
|
218
|
+
*/
|
|
219
|
+
type PolynomialEvaluatorFunction = (coefficients: Curve25519FieldElement[], point: Curve25519FieldElement) => Curve25519FieldElement;
|
|
220
|
+
/**
|
|
221
|
+
* Function type for modular exponentiation over the Curve25519 prime field.
|
|
222
|
+
*
|
|
223
|
+
* Computes `base^exp mod p` where `p = 2^255 - 19` is the Curve25519 field prime.
|
|
224
|
+
* The exponent may be any non-negative `bigint`.
|
|
225
|
+
*
|
|
226
|
+
* @param base - The base value. Must be a valid {@link Curve25519FieldElement} in [0, p).
|
|
227
|
+
* @param exp - The exponent. May be any non-negative `bigint`; large values (e.g. the
|
|
228
|
+
* Rescue-XLIX inverse alpha exponent) are supported.
|
|
229
|
+
* @returns `base^exp mod p` as a {@link Curve25519FieldElement}.
|
|
230
|
+
*
|
|
231
|
+
* @remarks
|
|
232
|
+
* ## Contract
|
|
233
|
+
*
|
|
234
|
+
* Implementations of this type **must** guarantee:
|
|
235
|
+
* - The result is a valid {@link Curve25519FieldElement} in [0, p).
|
|
236
|
+
* - `base^0 === 1n` for all bases (including `0^0 = 1` by convention).
|
|
237
|
+
* - The algorithm runs in O(log exp) field multiplications.
|
|
238
|
+
*
|
|
239
|
+
* ## Algorithm
|
|
240
|
+
*
|
|
241
|
+
* Uses the standard binary square-and-multiply (right-to-left) method. For
|
|
242
|
+
* the special case where `exp === 0n`, returns `1n` immediately without entering
|
|
243
|
+
* the multiplication loop.
|
|
244
|
+
*
|
|
245
|
+
* ## Use Cases
|
|
246
|
+
*
|
|
247
|
+
* This function type is primarily used:
|
|
248
|
+
* - In the Rescue-XLIX inverse S-box: `x^(1/alpha)` requires computing the
|
|
249
|
+
* multiplicative inverse exponent over GF(p).
|
|
250
|
+
* - In pairing-based protocols where field element inversions are expressed
|
|
251
|
+
* as exponentiations using Fermat's little theorem: `x^(p-2)`.
|
|
252
|
+
*
|
|
253
|
+
* @public
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* const modPow: ModuloPowCurve25519Function = getModuloPowCurve25519Function();
|
|
258
|
+
* const base = 2n as Curve25519FieldElement;
|
|
259
|
+
* const result = modPow(base, 10n);
|
|
260
|
+
* // result === 1024n
|
|
261
|
+
* ```
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```typescript
|
|
265
|
+
* // Fermat's little theorem: x^(p-2) === x^(-1) mod p
|
|
266
|
+
* const modPow = getModuloPowCurve25519Function();
|
|
267
|
+
* const inverse = modPow(someElement, CURVE25519_FIELD_PRIME - 2n);
|
|
268
|
+
* ```
|
|
269
|
+
*
|
|
270
|
+
* @see {@link getModuloPowCurve25519Function} — factory that returns the default implementation
|
|
271
|
+
*/
|
|
272
|
+
type ModuloPowCurve25519Function = (base: Curve25519FieldElement, exp: bigint) => Curve25519FieldElement;
|
|
273
|
+
/**
|
|
274
|
+
* Optional dependency injection bag for {@link getChallengePowersFunction}.
|
|
275
|
+
*
|
|
276
|
+
* Allows callers to substitute a custom modular multiplication implementation,
|
|
277
|
+
* primarily for unit testing (e.g. wrapping with a spy) or for environments
|
|
278
|
+
* that provide a native field arithmetic implementation. When fields are
|
|
279
|
+
* omitted, the default constant-time Curve25519 multiplication is used.
|
|
280
|
+
*
|
|
281
|
+
* @remarks
|
|
282
|
+
* ## Why Dependency Injection?
|
|
283
|
+
*
|
|
284
|
+
* The challenge powers function captures `modMul` in a closure. Injecting
|
|
285
|
+
* the function at construction time (rather than calling a global singleton
|
|
286
|
+
* on every multiplication) avoids global state and makes the function's
|
|
287
|
+
* behaviour fully deterministic for a given `modMul` implementation.
|
|
288
|
+
*
|
|
289
|
+
* @public
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```typescript
|
|
293
|
+
* import { getChallengePowersFunction } from './challenges';
|
|
294
|
+
*
|
|
295
|
+
* // Testing: use a spy to count multiplications
|
|
296
|
+
* let mulCount = 0;
|
|
297
|
+
* const spyModMul = (a: bigint, b: bigint) => { mulCount++; return (a * b) % CURVE25519_FIELD_PRIME; };
|
|
298
|
+
* const powers = getChallengePowersFunction({ modMul: spyModMul })(challenge, 5);
|
|
299
|
+
* console.log(mulCount); // === 4 (for maxPower === 5)
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
interface ChallengePowersDeps {
|
|
303
|
+
/**
|
|
304
|
+
* Custom modular multiplication function over GF(p = 2^255 - 19).
|
|
305
|
+
*
|
|
306
|
+
* If provided, this function is used in place of the default constant-time
|
|
307
|
+
* Curve25519 multiplication. The function **must** return a value in [0, p)
|
|
308
|
+
* for any pair of inputs `(a, b)` in [0, p).
|
|
309
|
+
*
|
|
310
|
+
* @defaultValue The default constant-time implementation from `math/curve25519/field-arithmetic`.
|
|
311
|
+
*/
|
|
312
|
+
readonly modMul?: (a: bigint, b: bigint) => bigint;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Optional dependency injection bag for {@link getPolynomialEvaluatorFunction}.
|
|
316
|
+
*
|
|
317
|
+
* Allows callers to substitute custom modular addition and/or multiplication
|
|
318
|
+
* implementations, primarily for unit testing or specialised environments.
|
|
319
|
+
* When fields are omitted, the default constant-time Curve25519 operations are used.
|
|
320
|
+
*
|
|
321
|
+
* @remarks
|
|
322
|
+
* ## Partial Overrides
|
|
323
|
+
*
|
|
324
|
+
* Either `modAdd` or `modMul` may be omitted independently. If only one is
|
|
325
|
+
* provided, the other falls back to its default implementation. This is useful
|
|
326
|
+
* when you want to test only one of the two arithmetic paths in isolation.
|
|
327
|
+
*
|
|
328
|
+
* ## Caching Caveat
|
|
329
|
+
*
|
|
330
|
+
* When any custom dependency is provided, the returned evaluator function is
|
|
331
|
+
* **not** cached (unlike the no-deps default singleton). Each call to
|
|
332
|
+
* `getPolynomialEvaluatorFunction` with non-empty deps creates a fresh closure.
|
|
333
|
+
*
|
|
334
|
+
* @public
|
|
335
|
+
*
|
|
336
|
+
* @example
|
|
337
|
+
* ```typescript
|
|
338
|
+
* import { getPolynomialEvaluatorFunction } from './challenges';
|
|
339
|
+
*
|
|
340
|
+
* // Testing with a modular addition spy
|
|
341
|
+
* let addCount = 0;
|
|
342
|
+
* const spyModAdd = (a: bigint, b: bigint) => { addCount++; return (a + b) % PRIME; };
|
|
343
|
+
* const evaluate = getPolynomialEvaluatorFunction({ modAdd: spyModAdd });
|
|
344
|
+
* evaluate([1n, 2n, 3n] as Curve25519FieldElement[], 4n as Curve25519FieldElement);
|
|
345
|
+
* console.log(addCount); // === 2 (for a degree-2 polynomial)
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
interface PolynomialEvaluatorDeps {
|
|
349
|
+
/**
|
|
350
|
+
* Custom modular addition function over GF(p = 2^255 - 19).
|
|
351
|
+
*
|
|
352
|
+
* If provided, this function is used in place of the default constant-time
|
|
353
|
+
* Curve25519 addition. The function **must** return a value in [0, p) for
|
|
354
|
+
* any pair of inputs `(a, b)` in [0, p).
|
|
355
|
+
*
|
|
356
|
+
* @defaultValue The default constant-time implementation from `math/curve25519/field-arithmetic`.
|
|
357
|
+
*/
|
|
358
|
+
readonly modAdd?: (a: bigint, b: bigint) => bigint;
|
|
359
|
+
/**
|
|
360
|
+
* Custom modular multiplication function over GF(p = 2^255 - 19).
|
|
361
|
+
*
|
|
362
|
+
* If provided, this function is used in place of the default constant-time
|
|
363
|
+
* Curve25519 multiplication. The function **must** return a value in [0, p)
|
|
364
|
+
* for any pair of inputs `(a, b)` in [0, p).
|
|
365
|
+
*
|
|
366
|
+
* @defaultValue The default constant-time implementation from `math/curve25519/field-arithmetic`.
|
|
367
|
+
*/
|
|
368
|
+
readonly modMul?: (a: bigint, b: bigint) => bigint;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Poseidon Cipher Interfaces
|
|
373
|
+
*
|
|
374
|
+
* This module defines the TypeScript interface types for Poseidon hash and cipher
|
|
375
|
+
* operations. Poseidon is a cryptographic hash function optimized for zero-knowledge
|
|
376
|
+
* proof systems, operating natively on field elements.
|
|
377
|
+
*
|
|
378
|
+
* ## Overview
|
|
379
|
+
*
|
|
380
|
+
* Poseidon is designed for efficiency in SNARK and STARK circuits. It uses
|
|
381
|
+
* the BN254 (alt_bn128) field, making it compatible with Ethereum precompiles
|
|
382
|
+
* and widely-used ZK proof systems like Groth16 and PLONK.
|
|
383
|
+
*
|
|
384
|
+
* ## Features
|
|
385
|
+
*
|
|
386
|
+
* - **Hash Function**: Collision-resistant hashing of field elements
|
|
387
|
+
* - **Encryption**: Symmetric encryption using Poseidon permutation
|
|
388
|
+
* - **Decryption**: Symmetric decryption recovering original plaintexts
|
|
389
|
+
* - **Keystream Generation**: Counter-mode key derivation for streaming encryption
|
|
390
|
+
*
|
|
391
|
+
* ## Interface Design: Dependency Injection
|
|
392
|
+
*
|
|
393
|
+
* Every function type in this module is defined as a `type` alias for a function
|
|
394
|
+
* signature rather than as a concrete implementation. This enables dependency
|
|
395
|
+
* injection throughout the SDK: callers declare what they need
|
|
396
|
+
* (e.g., `PoseidonHashFunction`) and receive an implementation via a getter
|
|
397
|
+
* (e.g., `getPoseidonHasher()`). Tests can substitute mock implementations, and
|
|
398
|
+
* the production code remains decoupled from the underlying `@noble/curves` library.
|
|
399
|
+
*
|
|
400
|
+
* ## Aggregation vs. Hashing
|
|
401
|
+
*
|
|
402
|
+
* Two distinct hash function types are provided:
|
|
403
|
+
* - {@link PoseidonHashFunction} — low-level, returns an unbranded `Bn254FieldElement`.
|
|
404
|
+
* Suitable for recursive hashing, Merkle tree construction, and any use where the
|
|
405
|
+
* caller manages the output type.
|
|
406
|
+
* - {@link PoseidonAggregatorHashFunction} — high-level aggregator, returns a branded
|
|
407
|
+
* {@link PoseidonHash}. Used specifically for the aggregated ZK public input (the
|
|
408
|
+
* 70-element Poseidon chain that collapses all UTXO fields into a single on-chain
|
|
409
|
+
* verifier input).
|
|
410
|
+
*
|
|
411
|
+
* @see {@link https://eprint.iacr.org/2019/458} Poseidon: A New Hash Function for Zero-Knowledge Proof Systems
|
|
412
|
+
* @see {@link PoseidonKey} for the user's Poseidon private key type
|
|
413
|
+
* @see {@link PoseidonHashFunction} for the low-level hash interface
|
|
414
|
+
* @see {@link PoseidonAggregatorHashFunction} for the high-level aggregator interface
|
|
415
|
+
*
|
|
416
|
+
* @packageDocumentation
|
|
417
|
+
* @module crypto/poseidon/interfaces
|
|
418
|
+
*/
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* An asynchronous function that computes a Poseidon hash over N BN254 field elements.
|
|
422
|
+
*
|
|
423
|
+
* This is the foundational Poseidon interface in the SDK. It maps an ordered array of
|
|
424
|
+
* {@link Bn254FieldElement} values to a single {@link Bn254FieldElement} output using
|
|
425
|
+
* the Poseidon permutation with parameters tuned for the BN254 scalar field. The
|
|
426
|
+
* implementation is compatible with Circom's `Poseidon(N)` template.
|
|
427
|
+
*
|
|
428
|
+
* @param dataPoints - An ordered, readonly array of {@link Bn254FieldElement} values to hash.
|
|
429
|
+
* The implementation supports 1 to 12 elements. Input order is significant:
|
|
430
|
+
* `hash([a, b]) ≠ hash([b, a])`.
|
|
431
|
+
* @returns A `Promise` resolving to a single {@link Bn254FieldElement} — the Poseidon
|
|
432
|
+
* hash output. The output is always a valid BN254 field element.
|
|
433
|
+
*
|
|
434
|
+
* @remarks
|
|
435
|
+
* ## Poseidon Construction
|
|
436
|
+
*
|
|
437
|
+
* The Poseidon hash function uses a sponge construction with a width-`t` state
|
|
438
|
+
* (where `t = N + 1`) over the BN254 scalar field F_r. The permutation consists of:
|
|
439
|
+
* - Full rounds (applying x^5 to the entire state) at the beginning and end.
|
|
440
|
+
* - Partial rounds (applying x^5 only to the first element) in the middle.
|
|
441
|
+
*
|
|
442
|
+
* Round counts and MDS matrix coefficients are fixed parameters determined by
|
|
443
|
+
* the security analysis in the Poseidon paper (eprint 2019/458). They cannot
|
|
444
|
+
* be changed without breaking compatibility with existing ZK circuits.
|
|
445
|
+
*
|
|
446
|
+
* ## ZK-Friendliness
|
|
447
|
+
*
|
|
448
|
+
* Poseidon requires approximately 8N R1CS constraints per hash of N field elements,
|
|
449
|
+
* compared to ~25,000 for SHA-256. This makes it practical to prove hash pre-images
|
|
450
|
+
* inside a Groth16 proof. In Umbra, this property is essential for:
|
|
451
|
+
* - Proving knowledge of a UTXO's opening (amount, nullifier, blinding factor).
|
|
452
|
+
* - Proving membership in the indexed Merkle tree (IMT) without revealing the leaf.
|
|
453
|
+
* - Generating Fiat-Shamir challenges from a transcript of public values.
|
|
454
|
+
*
|
|
455
|
+
* ## Async Interface
|
|
456
|
+
*
|
|
457
|
+
* The `Promise` wrapper is used for consistency with the rest of the SDK's
|
|
458
|
+
* cryptographic API. The underlying computation is synchronous, but callers should
|
|
459
|
+
* always `await` the result.
|
|
460
|
+
*
|
|
461
|
+
* @example
|
|
462
|
+
* ```typescript
|
|
463
|
+
* import { getPoseidonHasher } from "@umbra/sdk";
|
|
464
|
+
*
|
|
465
|
+
* const hasher: PoseidonHashFunction = getPoseidonHasher();
|
|
466
|
+
*
|
|
467
|
+
* // Hash two field elements (order matters)
|
|
468
|
+
* const h1 = await hasher([123n, 456n]);
|
|
469
|
+
* const h2 = await hasher([456n, 123n]);
|
|
470
|
+
* // h1 !== h2 in general
|
|
471
|
+
*
|
|
472
|
+
* // Hash a single element (used as a PRF output or commitment)
|
|
473
|
+
* const commitment = await hasher([secretValue]);
|
|
474
|
+
* ```
|
|
475
|
+
*
|
|
476
|
+
* @example
|
|
477
|
+
* ```typescript
|
|
478
|
+
* // Merkle tree parent node computation
|
|
479
|
+
* const parent = await hasher([leftChild, rightChild]);
|
|
480
|
+
* ```
|
|
481
|
+
*
|
|
482
|
+
* @see {@link PoseidonAggregatorHashFunction} for the branded-output aggregation variant
|
|
483
|
+
* @see {@link PoseidonPrfFunction} for the PRF interface built on top of this hash
|
|
484
|
+
* @public
|
|
485
|
+
*/
|
|
486
|
+
type PoseidonHashFunction = (dataPoints: readonly Bn254FieldElement[]) => Promise<Bn254FieldElement>;
|
|
487
|
+
/**
|
|
488
|
+
* An asynchronous function that encrypts an array of {@link PoseidonPlaintext} values
|
|
489
|
+
* using Poseidon counter-mode encryption.
|
|
490
|
+
*
|
|
491
|
+
* This encryptor is the write side of Umbra's confidential token account scheme.
|
|
492
|
+
* For each plaintext at position `i` (0-indexed), it derives an independent keystream
|
|
493
|
+
* via the Poseidon PRF and adds it to the plaintext modulo the BN254 field prime,
|
|
494
|
+
* producing the corresponding ciphertext.
|
|
495
|
+
*
|
|
496
|
+
* @param plaintexts - An ordered, readonly array of {@link PoseidonPlaintext} values to encrypt.
|
|
497
|
+
* Each element is encrypted independently at its array position. The array length
|
|
498
|
+
* determines how many counter values are consumed.
|
|
499
|
+
* @param key - The {@link PoseidonKey} (user's Poseidon private key, derived from the master
|
|
500
|
+
* seed) used as the PRF key for keystream generation. Must be kept secret.
|
|
501
|
+
* @returns A `Promise` resolving to an array of {@link PoseidonCiphertext} values, in the
|
|
502
|
+
* same order as the input plaintexts. `result[i]` is the encryption of `plaintexts[i]`.
|
|
503
|
+
*
|
|
504
|
+
* @remarks
|
|
505
|
+
* ## Encryption Scheme
|
|
506
|
+
*
|
|
507
|
+
* For each plaintext position `i`, the cipher operates as:
|
|
508
|
+
* ```
|
|
509
|
+
* keystream[i] = Poseidon([key, i, 2n])
|
|
510
|
+
* ciphertext[i] = (plaintext[i] + keystream[i]) mod BN254_FIELD_PRIME
|
|
511
|
+
* ```
|
|
512
|
+
*
|
|
513
|
+
* The domain separation constant `2n` in the third argument slot ensures that
|
|
514
|
+
* keystream generation is distinct from any other Poseidon usage in the protocol
|
|
515
|
+
* (e.g., commitment hashing, Fiat-Shamir challenges).
|
|
516
|
+
*
|
|
517
|
+
* ## What Is Encrypted
|
|
518
|
+
*
|
|
519
|
+
* In Umbra's confidential token account model, the encrypted fields are:
|
|
520
|
+
* - Token balance (as a field element representing the token amount).
|
|
521
|
+
* - Blinding factors associated with UTXO commitments.
|
|
522
|
+
*
|
|
523
|
+
* These ciphertexts are stored in the on-chain `EncryptedTokenAccount` state and
|
|
524
|
+
* can only be decrypted by the holder of the corresponding `PoseidonKey`.
|
|
525
|
+
*
|
|
526
|
+
* ## IND-CPA Security
|
|
527
|
+
*
|
|
528
|
+
* Under the PRF security assumption for Poseidon, each ciphertext is computationally
|
|
529
|
+
* indistinguishable from a uniformly random field element. The scheme provides
|
|
530
|
+
* IND-CPA security as long as the key is secret and counter values are not reused.
|
|
531
|
+
*
|
|
532
|
+
* @example
|
|
533
|
+
* ```typescript
|
|
534
|
+
* import { getPoseidonEncryptor } from "@umbra/sdk";
|
|
535
|
+
*
|
|
536
|
+
* const encryptor: PoseidonEncryptorFunction = getPoseidonEncryptor();
|
|
537
|
+
*
|
|
538
|
+
* // Encrypt a token balance and blinding factor together
|
|
539
|
+
* const balance: PoseidonPlaintext = ...; // amount as field element
|
|
540
|
+
* const blinding: PoseidonPlaintext = ...; // random blinding factor
|
|
541
|
+
*
|
|
542
|
+
* const ciphertexts = await encryptor([balance, blinding], myPoseidonKey);
|
|
543
|
+
* // ciphertexts[0] = encryption of balance (counter 0)
|
|
544
|
+
* // ciphertexts[1] = encryption of blinding (counter 1)
|
|
545
|
+
* ```
|
|
546
|
+
*
|
|
547
|
+
* @see {@link PoseidonDecryptorFunction} for the corresponding decryption function
|
|
548
|
+
* @see {@link PoseidonKeystreamGeneratorFunction} for direct keystream access
|
|
549
|
+
* @see {@link PoseidonKey} for the key type accepted by this function
|
|
550
|
+
* @public
|
|
551
|
+
*/
|
|
552
|
+
type PoseidonEncryptorFunction = (plaintexts: readonly PoseidonPlaintext[], key: PoseidonKey) => Promise<PoseidonCiphertext[]>;
|
|
553
|
+
/**
|
|
554
|
+
* An asynchronous function that decrypts an array of {@link PoseidonCiphertext} values
|
|
555
|
+
* using Poseidon counter-mode decryption.
|
|
556
|
+
*
|
|
557
|
+
* This decryptor is the read side of Umbra's confidential token account scheme. For
|
|
558
|
+
* each ciphertext at position `i`, it regenerates the same keystream that was used
|
|
559
|
+
* during encryption and subtracts it (modulo the BN254 field prime) to recover the
|
|
560
|
+
* original plaintext.
|
|
561
|
+
*
|
|
562
|
+
* @param ciphertexts - An ordered, readonly array of {@link PoseidonCiphertext} values to decrypt.
|
|
563
|
+
* The array length determines how many counter values are consumed. `result[i]` is
|
|
564
|
+
* the decryption of `ciphertexts[i]`.
|
|
565
|
+
* @param key - The {@link PoseidonKey} that was used during encryption. Using a different
|
|
566
|
+
* key will produce garbage output without any error — the cipher has no authentication
|
|
567
|
+
* and does not detect wrong-key decryption.
|
|
568
|
+
* @returns A `Promise` resolving to an array of {@link PoseidonPlaintext} values, in the
|
|
569
|
+
* same order as the input ciphertexts.
|
|
570
|
+
*
|
|
571
|
+
* @remarks
|
|
572
|
+
* ## Decryption Formula
|
|
573
|
+
*
|
|
574
|
+
* For each ciphertext at position `i`:
|
|
575
|
+
* ```
|
|
576
|
+
* keystream[i] = Poseidon([key, i, 2n])
|
|
577
|
+
* plaintext[i] = (ciphertext[i] - keystream[i] + BN254_FIELD_PRIME) mod BN254_FIELD_PRIME
|
|
578
|
+
* ```
|
|
579
|
+
*
|
|
580
|
+
* The addition of `BN254_FIELD_PRIME` before the modulo ensures the result is
|
|
581
|
+
* non-negative even when `ciphertext[i] < keystream[i]` (subtraction would underflow
|
|
582
|
+
* without it).
|
|
583
|
+
*
|
|
584
|
+
* ## No Authentication
|
|
585
|
+
*
|
|
586
|
+
* This cipher provides confidentiality (IND-CPA) but NOT integrity. A wrong key
|
|
587
|
+
* silently produces incorrect plaintexts rather than throwing. Callers that need
|
|
588
|
+
* authenticated decryption must layer a MAC or use a scheme that includes a
|
|
589
|
+
* commitment to the ciphertext (e.g., comparing against an on-chain hash).
|
|
590
|
+
*
|
|
591
|
+
* In Umbra, ciphertext integrity is enforced externally via ZK proof verification:
|
|
592
|
+
* the prover must demonstrate knowledge of the plaintext that satisfies the
|
|
593
|
+
* circuit's constraints, which transitively checks correctness.
|
|
594
|
+
*
|
|
595
|
+
* @example
|
|
596
|
+
* ```typescript
|
|
597
|
+
* import { getPoseidonDecryptor } from "@umbra/sdk";
|
|
598
|
+
*
|
|
599
|
+
* const decryptor: PoseidonDecryptorFunction = getPoseidonDecryptor();
|
|
600
|
+
*
|
|
601
|
+
* // Recover balance and blinding factor from on-chain ciphertexts
|
|
602
|
+
* const [balance, blinding] = await decryptor(
|
|
603
|
+
* [encryptedBalance, encryptedBlinding],
|
|
604
|
+
* myPoseidonKey
|
|
605
|
+
* );
|
|
606
|
+
* // balance: PoseidonPlaintext — original amount
|
|
607
|
+
* // blinding: PoseidonPlaintext — original blinding factor
|
|
608
|
+
* ```
|
|
609
|
+
*
|
|
610
|
+
* @see {@link PoseidonEncryptorFunction} for the corresponding encryption function
|
|
611
|
+
* @see {@link PoseidonKey} for the key type accepted by this function
|
|
612
|
+
* @public
|
|
613
|
+
*/
|
|
614
|
+
type PoseidonDecryptorFunction = (ciphertexts: readonly PoseidonCiphertext[], key: PoseidonKey) => Promise<PoseidonPlaintext[]>;
|
|
615
|
+
/**
|
|
616
|
+
* An asynchronous function that generates per-counter Poseidon keystream values.
|
|
617
|
+
*
|
|
618
|
+
* This function is the core PRF primitive underlying Umbra's stream cipher. Given a set of
|
|
619
|
+
* counter values and a master {@link PoseidonKey}, it returns a map from each counter to
|
|
620
|
+
* its corresponding {@link PoseidonKeystream} value. Callers use these keystream values
|
|
621
|
+
* directly to perform field-element encryption and decryption, or they can rely on the
|
|
622
|
+
* higher-level {@link PoseidonEncryptorFunction} and {@link PoseidonDecryptorFunction}.
|
|
623
|
+
*
|
|
624
|
+
* @param counters - A readonly array of {@link PoseidonCounter} values specifying which
|
|
625
|
+
* positions in the keystream to generate. Counters may be provided in any order; the
|
|
626
|
+
* returned `Map` is keyed by counter value for random-access lookup.
|
|
627
|
+
* @param key - The master {@link PoseidonKey} (user's Poseidon private key) from which all
|
|
628
|
+
* keystream elements are derived. This value must remain secret.
|
|
629
|
+
* @returns A `Promise` resolving to a `Map<PoseidonCounter, PoseidonKeystream>` where each
|
|
630
|
+
* entry maps a requested counter to the corresponding derived keystream.
|
|
631
|
+
*
|
|
632
|
+
* @remarks
|
|
633
|
+
* ## Keystream Generation Formula
|
|
634
|
+
*
|
|
635
|
+
* For each counter `c` in the input array, the keystream element is:
|
|
636
|
+
* ```
|
|
637
|
+
* keystream(c) = Poseidon([key, c, 2n])
|
|
638
|
+
* ```
|
|
639
|
+
*
|
|
640
|
+
* - `key`: the user's {@link PoseidonKey} — 1 field element.
|
|
641
|
+
* - `c`: the {@link PoseidonCounter} — 1 field element (typically a small non-negative integer).
|
|
642
|
+
* - `2n`: domain separation constant — distinguishes keystream PRF from other Poseidon
|
|
643
|
+
* usages within the Umbra protocol.
|
|
644
|
+
*
|
|
645
|
+
* Total Poseidon inputs: 3 elements → state width t = 4.
|
|
646
|
+
*
|
|
647
|
+
* ## Usage as a Stream Cipher
|
|
648
|
+
*
|
|
649
|
+
* ```
|
|
650
|
+
* // Encrypt position i:
|
|
651
|
+
* ciphertext[i] = (plaintext[i] + keystream(i)) mod BN254_FIELD_PRIME
|
|
652
|
+
*
|
|
653
|
+
* // Decrypt position i:
|
|
654
|
+
* plaintext[i] = (ciphertext[i] - keystream(i) + BN254_FIELD_PRIME) mod BN254_FIELD_PRIME
|
|
655
|
+
* ```
|
|
656
|
+
*
|
|
657
|
+
* ## Keystream Commitment
|
|
658
|
+
*
|
|
659
|
+
* In Umbra's linker encryption scheme, keystream values are also committed on-chain via
|
|
660
|
+
* {@link KeystreamCommitmentFunction}:
|
|
661
|
+
* ```
|
|
662
|
+
* commitment = Poseidon([keystream, blindingFactor])
|
|
663
|
+
* ```
|
|
664
|
+
* This allows a ZK proof to verify that the correct keystream was used in a specific
|
|
665
|
+
* encryption operation without revealing the keystream itself.
|
|
666
|
+
*
|
|
667
|
+
* ## Security
|
|
668
|
+
*
|
|
669
|
+
* - Keystream values are cryptographic secrets. They must never be stored persistently,
|
|
670
|
+
* logged, or transmitted outside the current computation context.
|
|
671
|
+
* - Counter reuse with the same key reveals the field difference between the two
|
|
672
|
+
* plaintexts encrypted at that position.
|
|
673
|
+
* - The output `Map` uses `PoseidonCounter` as its key type, so `Map.get(c)` requires
|
|
674
|
+
* the same branded `PoseidonCounter` value used as input.
|
|
675
|
+
*
|
|
676
|
+
* @example
|
|
677
|
+
* ```typescript
|
|
678
|
+
* import { getPoseidonKeystreamGenerator } from "@umbra/sdk";
|
|
679
|
+
*
|
|
680
|
+
* const keystreamGenerator: PoseidonKeystreamGeneratorFunction =
|
|
681
|
+
* getPoseidonKeystreamGenerator();
|
|
682
|
+
*
|
|
683
|
+
* // Generate keystreams for positions 0, 1, and 2
|
|
684
|
+
* const counters: PoseidonCounter[] = [0n, 1n, 2n].map((c) => {
|
|
685
|
+
* assertPoseidonCounter(c); return c;
|
|
686
|
+
* });
|
|
687
|
+
*
|
|
688
|
+
* const keystreamMap = await keystreamGenerator(counters, myPoseidonKey);
|
|
689
|
+
*
|
|
690
|
+
* // Access individual keystream values
|
|
691
|
+
* const ks0 = keystreamMap.get(counters[0])!;
|
|
692
|
+
* const ks1 = keystreamMap.get(counters[1])!;
|
|
693
|
+
*
|
|
694
|
+
* // Encrypt manually
|
|
695
|
+
* const ciphertext0 = (plaintext0 + ks0) % BN254_FIELD_PRIME;
|
|
696
|
+
* const ciphertext1 = (plaintext1 + ks1) % BN254_FIELD_PRIME;
|
|
697
|
+
* ```
|
|
698
|
+
*
|
|
699
|
+
* @see {@link PoseidonEncryptorFunction} for the higher-level encryption interface
|
|
700
|
+
* @see {@link PoseidonDecryptorFunction} for the higher-level decryption interface
|
|
701
|
+
* @see {@link KeystreamCommitmentFunction} for committing keystream values in ZK proofs
|
|
702
|
+
* @see {@link PoseidonCounter} for the counter (nonce) type
|
|
703
|
+
* @see {@link PoseidonKeystream} for the keystream output type
|
|
704
|
+
* @public
|
|
705
|
+
*/
|
|
706
|
+
type PoseidonKeystreamGeneratorFunction = (counters: readonly PoseidonCounter[], key: PoseidonKey) => Promise<Map<PoseidonCounter, PoseidonKeystream>>;
|
|
707
|
+
/**
|
|
708
|
+
* An asynchronous function that aggregates multiple BN254 field elements into a single
|
|
709
|
+
* branded {@link PoseidonHash} via a sequence of Poseidon invocations.
|
|
710
|
+
*
|
|
711
|
+
* The aggregator is the high-level interface used to produce the single on-chain public
|
|
712
|
+
* input for the Groth16 verifier. Rather than passing all UTXO fields individually
|
|
713
|
+
* (which would be impractical on-chain), Umbra aggregates them into one field element
|
|
714
|
+
* that the ZK circuit can verify matches a committed value.
|
|
715
|
+
*
|
|
716
|
+
* @param plaintexts - A readonly array of {@link Bn254FieldElement} values representing
|
|
717
|
+
* the full set of UTXO fields to aggregate. In the Umbra claim protocol, this array
|
|
718
|
+
* contains exactly 70 elements: the Fiat-Shamir transcript concatenated with all
|
|
719
|
+
* per-UTXO field values (amounts, nullifiers, addresses, blinding factors). The
|
|
720
|
+
* order of elements must match the Circom circuit's expected ordering exactly.
|
|
721
|
+
* @returns A `Promise` resolving to a branded {@link PoseidonHash} — the single
|
|
722
|
+
* aggregated public input. This value is compared on-chain against the hash
|
|
723
|
+
* embedded in the Groth16 proof's public signals.
|
|
724
|
+
*
|
|
725
|
+
* @remarks
|
|
726
|
+
* ## Aggregation Algorithm
|
|
727
|
+
*
|
|
728
|
+
* The aggregator processes the input array in a chain: each Poseidon invocation takes
|
|
729
|
+
* two inputs — the running accumulator and the next element — and produces an updated
|
|
730
|
+
* accumulator:
|
|
731
|
+
* ```
|
|
732
|
+
* acc = plaintexts[0]
|
|
733
|
+
* for i in 1..N-1:
|
|
734
|
+
* acc = Poseidon([acc, plaintexts[i]])
|
|
735
|
+
* result = acc (as PoseidonHash)
|
|
736
|
+
* ```
|
|
737
|
+
*
|
|
738
|
+
* This is a serial (non-tree) aggregation. The result depends on the order of all
|
|
739
|
+
* inputs; swapping any two elements produces a completely different hash.
|
|
740
|
+
*
|
|
741
|
+
* ## Role in Umbra's ZK Protocol
|
|
742
|
+
*
|
|
743
|
+
* The 70-element aggregated public input condenses the Fiat-Shamir transcript and
|
|
744
|
+
* all UTXO data fields into a single on-chain verifiable value. The Groth16 prover
|
|
745
|
+
* produces a proof that the circuit constraints are satisfied given this exact public
|
|
746
|
+
* input, and the Solana program verifies both:
|
|
747
|
+
* 1. The Groth16 proof is valid for the given public input.
|
|
748
|
+
* 2. The public input matches the locally computed aggregated hash.
|
|
749
|
+
*
|
|
750
|
+
* ## Difference from `PoseidonHashFunction`
|
|
751
|
+
*
|
|
752
|
+
* While {@link PoseidonHashFunction} accepts up to 12 elements in a single call and
|
|
753
|
+
* returns an unbranded `Bn254FieldElement`, this function handles arbitrarily many
|
|
754
|
+
* elements via chaining and returns a branded `PoseidonHash`. The branded return type
|
|
755
|
+
* ensures aggregated hashes are not accidentally mixed with raw field elements in
|
|
756
|
+
* downstream code.
|
|
757
|
+
*
|
|
758
|
+
* @example
|
|
759
|
+
* ```typescript
|
|
760
|
+
* import { getPoseidonAggregator } from "@umbra/sdk";
|
|
761
|
+
*
|
|
762
|
+
* const aggregator: PoseidonAggregatorHashFunction = getPoseidonAggregator();
|
|
763
|
+
*
|
|
764
|
+
* // Build the 70-element input array (Fiat-Shamir + UTXO fields)
|
|
765
|
+
* const publicInputs: Bn254FieldElement[] = buildAggregatedInputArray(utxos, transcript);
|
|
766
|
+
* // publicInputs.length === 70
|
|
767
|
+
*
|
|
768
|
+
* const aggregatedHash = await aggregator(publicInputs);
|
|
769
|
+
* // aggregatedHash: PoseidonHash — single value submitted to the on-chain verifier
|
|
770
|
+
* ```
|
|
771
|
+
*
|
|
772
|
+
* @example
|
|
773
|
+
* ```typescript
|
|
774
|
+
* // Smaller example: aggregate three commitment values
|
|
775
|
+
* const data: Bn254FieldElement[] = [commitment1, commitment2, commitment3];
|
|
776
|
+
* const fingerprint = await aggregator(data);
|
|
777
|
+
* // fingerprint uniquely identifies the ordered set of commitments
|
|
778
|
+
* ```
|
|
779
|
+
*
|
|
780
|
+
* @see {@link PoseidonHashFunction} for the low-level, unbranded, single-call hash interface
|
|
781
|
+
* @see {@link PoseidonHash} for the branded output type
|
|
782
|
+
* @public
|
|
783
|
+
*/
|
|
784
|
+
type PoseidonAggregatorHashFunction = (plaintexts: readonly Bn254FieldElement[]) => Promise<PoseidonHash>;
|
|
785
|
+
/**
|
|
786
|
+
* An asynchronous function that evaluates the Poseidon Pseudo-Random Function (PRF).
|
|
787
|
+
*
|
|
788
|
+
* A PRF is a deterministic keyed function whose outputs are computationally
|
|
789
|
+
* indistinguishable from random when the key (seeds) is unknown. This Poseidon-based
|
|
790
|
+
* PRF is the foundation for all key derivation, nullifier generation, and keystream
|
|
791
|
+
* computation in the Umbra SDK.
|
|
792
|
+
*
|
|
793
|
+
* @param seeds - A readonly array of 1 to 11 {@link Bn254FieldElement} values serving as
|
|
794
|
+
* the secret key material. In combination with the evaluation point, the total number
|
|
795
|
+
* of Poseidon inputs must not exceed 12 (`seeds.length + 1 ≤ 12`).
|
|
796
|
+
* @param evaluationPoint - A public {@link Bn254FieldElement} at which to evaluate the PRF.
|
|
797
|
+
* Different evaluation points yield completely different outputs from the same seeds,
|
|
798
|
+
* allowing a single set of seeds to derive multiple independent values (one per point).
|
|
799
|
+
* @returns A `Promise` resolving to a {@link Bn254FieldElement} — the PRF output.
|
|
800
|
+
* The output is deterministic for the same `(seeds, evaluationPoint)` pair.
|
|
801
|
+
*
|
|
802
|
+
* @remarks
|
|
803
|
+
* ## PRF Construction
|
|
804
|
+
*
|
|
805
|
+
* The PRF is instantiated as a Poseidon hash over all seeds followed by the evaluation
|
|
806
|
+
* point:
|
|
807
|
+
* ```
|
|
808
|
+
* PRF(seeds, x) = Poseidon([seeds[0], seeds[1], ..., seeds[n-1], x])
|
|
809
|
+
* ```
|
|
810
|
+
*
|
|
811
|
+
* The evaluation point `x` is always the last input, ensuring a clean separation
|
|
812
|
+
* between the key material and the index.
|
|
813
|
+
*
|
|
814
|
+
* ## Input Constraints
|
|
815
|
+
*
|
|
816
|
+
* - `seeds.length` must be at least 1 and at most 11.
|
|
817
|
+
* - The total `seeds.length + 1` must not exceed 12, which is the maximum arity
|
|
818
|
+
* supported by this Poseidon implementation (state width t = 13).
|
|
819
|
+
* - Violating these constraints will produce an error from the underlying hasher.
|
|
820
|
+
*
|
|
821
|
+
* ## Security Properties
|
|
822
|
+
*
|
|
823
|
+
* - **Deterministic**: Identical `(seeds, evaluationPoint)` always yields the same output.
|
|
824
|
+
* - **Pseudo-random**: If at least one seed is unknown to the adversary, the output
|
|
825
|
+
* is computationally indistinguishable from a uniformly random field element.
|
|
826
|
+
* - **Key-dependent**: Even a single bit difference in seeds produces an entirely
|
|
827
|
+
* different output (avalanche effect of Poseidon's non-linear permutation).
|
|
828
|
+
* - **Collision-resistant**: Inherited from Poseidon's second-preimage resistance.
|
|
829
|
+
*
|
|
830
|
+
* ## Use Cases in Umbra
|
|
831
|
+
*
|
|
832
|
+
* - **Keystream generation**: `PRF(poseidonKey, counter)` derives per-counter keystreams
|
|
833
|
+
* for counter-mode encryption of confidential token balances.
|
|
834
|
+
* - **Nullifier derivation**: `PRF(viewingKey, utxoIndex)` produces the nullifier
|
|
835
|
+
* that is published on-chain when a UTXO is spent, preventing double-spending.
|
|
836
|
+
* - **User commitment**: `PRF([masterViewingKey, poseidonKey], domainTag)` produces the
|
|
837
|
+
* user's on-chain commitment that binds their cryptographic identity to a single
|
|
838
|
+
* field element verifiable in a ZK circuit.
|
|
839
|
+
*
|
|
840
|
+
* @example
|
|
841
|
+
* ```typescript
|
|
842
|
+
* import { getPoseidonPrf } from "@umbra/sdk";
|
|
843
|
+
*
|
|
844
|
+
* const prf: PoseidonPrfFunction = getPoseidonPrf();
|
|
845
|
+
*
|
|
846
|
+
* // Derive three independent values from the same secret seed
|
|
847
|
+
* const secretSeed: Bn254FieldElement = myDerivedKey;
|
|
848
|
+
* const keystream0 = await prf([secretSeed], 0n); // at point 0
|
|
849
|
+
* const keystream1 = await prf([secretSeed], 1n); // at point 1
|
|
850
|
+
* const keystream2 = await prf([secretSeed], 2n); // at point 2
|
|
851
|
+
* // All three are distinct and computationally random
|
|
852
|
+
* ```
|
|
853
|
+
*
|
|
854
|
+
* @example
|
|
855
|
+
* ```typescript
|
|
856
|
+
* // Nullifier generation in a ZK UTXO protocol
|
|
857
|
+
* const prf: PoseidonPrfFunction = getPoseidonPrf();
|
|
858
|
+
*
|
|
859
|
+
* const viewingKey: Bn254FieldElement = userViewingKey;
|
|
860
|
+
* const utxoIndex: Bn254FieldElement = BigInt(noteIndex);
|
|
861
|
+
*
|
|
862
|
+
* // Nullifier: PRF(viewingKey, utxoIndex)
|
|
863
|
+
* const nullifier = await prf([viewingKey], utxoIndex);
|
|
864
|
+
* // nullifier uniquely identifies the spent note without revealing the viewing key
|
|
865
|
+
* ```
|
|
866
|
+
*
|
|
867
|
+
* @example
|
|
868
|
+
* ```typescript
|
|
869
|
+
* // Multi-seed PRF for user commitment (binds two keys)
|
|
870
|
+
* const userCommitment = await prf([masterViewingKey, poseidonKey], domainSeparator);
|
|
871
|
+
* ```
|
|
872
|
+
*
|
|
873
|
+
* @see {@link PoseidonHashFunction} for the underlying hash primitive
|
|
874
|
+
* @see {@link PoseidonKeystreamGeneratorFunction} for the keystream-specific wrapper
|
|
875
|
+
* @public
|
|
876
|
+
*/
|
|
877
|
+
type PoseidonPrfFunction = (seeds: readonly Bn254FieldElement[], evaluationPoint: Bn254FieldElement) => Promise<Bn254FieldElement>;
|
|
878
|
+
/**
|
|
879
|
+
* Parameters for generating the H2 UTXO leaf as a flat Poseidon(6) hash.
|
|
880
|
+
*
|
|
881
|
+
* ```
|
|
882
|
+
* H2 = Poseidon([amount, nullifier, userCommitment, addrLow, addrHigh, h2BlindingFactor])
|
|
883
|
+
* ```
|
|
884
|
+
*
|
|
885
|
+
* The BN254 field constraint means the Solana address (32 bytes = 256 bits) must be
|
|
886
|
+
* split into two 128-bit halves (`finalDestinationAddressLow` and
|
|
887
|
+
* `finalDestinationAddressHigh`) for compatibility with Poseidon's field element inputs.
|
|
888
|
+
*
|
|
889
|
+
* Input ordering must exactly match the Circom circuit's witness assignment.
|
|
890
|
+
*
|
|
891
|
+
* @see {@link H2CircuitProvableHashFunction}
|
|
892
|
+
* @see {@link H2HashFunction}
|
|
893
|
+
* @public
|
|
894
|
+
*/
|
|
895
|
+
interface H2CircuitProvableParams {
|
|
896
|
+
/**
|
|
897
|
+
* The transaction amount in the smallest token unit (after protocol fees have been deducted).
|
|
898
|
+
*
|
|
899
|
+
* @remarks
|
|
900
|
+
* This is the net amount that will be claimable by the UTXO recipient after protocol
|
|
901
|
+
* and relayer fees. Must be a valid BN254 field element. For token amounts that fit
|
|
902
|
+
* within 64 bits (all SPL token amounts), this is always safely within field range.
|
|
903
|
+
*
|
|
904
|
+
* The circuit constrains the amount to be non-negative and consistent with the
|
|
905
|
+
* encrypted balance stored in the sender's confidential token account.
|
|
906
|
+
*/
|
|
907
|
+
amount: Bn254FieldElement;
|
|
908
|
+
/**
|
|
909
|
+
* The nullifier that uniquely identifies this UTXO and prevents double-spending.
|
|
910
|
+
*
|
|
911
|
+
* @remarks
|
|
912
|
+
* The nullifier is derived deterministically from the user's viewing key and the
|
|
913
|
+
* UTXO's position or index:
|
|
914
|
+
* ```
|
|
915
|
+
* nullifier = Poseidon([viewingKey, utxoIndex])
|
|
916
|
+
* ```
|
|
917
|
+
*
|
|
918
|
+
* When a UTXO is claimed, its nullifier is published on-chain and inserted into the
|
|
919
|
+
* treap nullifier set. Subsequent claim attempts for the same UTXO are rejected by
|
|
920
|
+
* checking for the nullifier's presence in the treap.
|
|
921
|
+
*
|
|
922
|
+
* The ZK circuit proves knowledge of the pre-image (viewing key + index) that
|
|
923
|
+
* produces this nullifier, without revealing the viewing key.
|
|
924
|
+
*/
|
|
925
|
+
nullifier: Bn254FieldElement;
|
|
926
|
+
/**
|
|
927
|
+
* The on-chain commitment that identifies the user authorized to claim this UTXO.
|
|
928
|
+
*
|
|
929
|
+
* @remarks
|
|
930
|
+
* The `userCommitment` is computed as:
|
|
931
|
+
* ```
|
|
932
|
+
* userCommitment = Poseidon([masterViewingKey, poseidonKey])
|
|
933
|
+
* ```
|
|
934
|
+
*
|
|
935
|
+
* Two locking modes exist in the Umbra protocol:
|
|
936
|
+
* - **Ephemeral unlocking**: the `userCommitment` is derived from a per-UTXO ephemeral
|
|
937
|
+
* key, allowing claim by anyone who knows that ephemeral key.
|
|
938
|
+
* - **Receiver unlocking**: the `userCommitment` is the receiver's registered
|
|
939
|
+
* commitment, requiring the receiver's `masterViewingKey` and `poseidonKey`.
|
|
940
|
+
*
|
|
941
|
+
* The circuit proves that the claimer knows the opening of this commitment.
|
|
942
|
+
*/
|
|
943
|
+
userCommitment: Bn254FieldElement;
|
|
944
|
+
/**
|
|
945
|
+
* The lower 128 bits of the final destination Solana address (bytes 0–15).
|
|
946
|
+
*
|
|
947
|
+
* @remarks
|
|
948
|
+
* Solana public keys are 32 bytes (256 bits). Because BN254 field elements are
|
|
949
|
+
* only ~254 bits, a full Solana address cannot be represented as a single field
|
|
950
|
+
* element without potential reduction. The address is therefore split into two
|
|
951
|
+
* 128-bit halves for safe inclusion as Poseidon inputs.
|
|
952
|
+
*
|
|
953
|
+
* The split is:
|
|
954
|
+
* ```
|
|
955
|
+
* finalDestinationAddressLow = address & 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFFn
|
|
956
|
+
* finalDestinationAddressHigh = address >> 128n
|
|
957
|
+
* ```
|
|
958
|
+
*/
|
|
959
|
+
finalDestinationAddressLow: Bn254FieldElement;
|
|
960
|
+
/**
|
|
961
|
+
* The upper 128 bits of the final destination Solana address (bytes 16–31).
|
|
962
|
+
*
|
|
963
|
+
* @remarks
|
|
964
|
+
* Pair of {@link H2CircuitProvableParams.finalDestinationAddressLow}. Together, the
|
|
965
|
+
* two halves reconstruct the full 32-byte Solana destination address:
|
|
966
|
+
* ```
|
|
967
|
+
* address = finalDestinationAddressLow | (finalDestinationAddressHigh << 128n)
|
|
968
|
+
* ```
|
|
969
|
+
*/
|
|
970
|
+
finalDestinationAddressHigh: Bn254FieldElement;
|
|
971
|
+
/**
|
|
972
|
+
* A uniformly random BN254 field element that provides hiding for the H2 commitment.
|
|
973
|
+
*
|
|
974
|
+
* @remarks
|
|
975
|
+
* Without a blinding factor, the UTXO commitment `H2` would be deterministic given
|
|
976
|
+
* the other public fields (amount, destination address). An observer could enumerate
|
|
977
|
+
* candidate amounts and precompute commitments to deanonymize transactions. The
|
|
978
|
+
* blinding factor makes each UTXO commitment computationally unique even for
|
|
979
|
+
* identical amounts and destinations.
|
|
980
|
+
*
|
|
981
|
+
* Must be generated using a CSPRNG and must remain secret until the UTXO is claimed.
|
|
982
|
+
*/
|
|
983
|
+
h2BlindingFactor: Bn254FieldElement;
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Backward-compatible alias for {@link H2CircuitProvableParams}.
|
|
987
|
+
*
|
|
988
|
+
* H2 is now computed as a flat Poseidon(6) hash — identical to the circuit-provable hash.
|
|
989
|
+
* This alias exists so that code referencing `H2FullParams` continues to compile.
|
|
990
|
+
*
|
|
991
|
+
* @public
|
|
992
|
+
*/
|
|
993
|
+
type H2FullParams = H2CircuitProvableParams;
|
|
994
|
+
/**
|
|
995
|
+
* Computes the circuit-provable Poseidon(6) hash over the six core UTXO fields.
|
|
996
|
+
*
|
|
997
|
+
* ```
|
|
998
|
+
* H2 = Poseidon([amount, nullifier, userCommitment, addrLow, addrHigh, h2BlindingFactor])
|
|
999
|
+
* ```
|
|
1000
|
+
*
|
|
1001
|
+
* Input ordering must exactly match the Circom circuit's witness assignment.
|
|
1002
|
+
*
|
|
1003
|
+
* @see {@link H2CircuitProvableParams}
|
|
1004
|
+
* @public
|
|
1005
|
+
*/
|
|
1006
|
+
type H2CircuitProvableHashFunction = (params: H2CircuitProvableParams) => Promise<Bn254FieldElement>;
|
|
1007
|
+
/**
|
|
1008
|
+
* Computes the complete H2 UTXO leaf commitment as a flat Poseidon(6) hash.
|
|
1009
|
+
*
|
|
1010
|
+
* ```
|
|
1011
|
+
* H2 = Poseidon([amount, nullifier, userCommitment, addrLow, addrHigh, h2BlindingFactor])
|
|
1012
|
+
* ```
|
|
1013
|
+
*
|
|
1014
|
+
* The result is the leaf inserted into the mixer's indexed Merkle tree (IMT).
|
|
1015
|
+
*
|
|
1016
|
+
* @see {@link H2CircuitProvableParams}
|
|
1017
|
+
* @public
|
|
1018
|
+
*/
|
|
1019
|
+
type H2HashFunction = (params: H2CircuitProvableParams) => Promise<Bn254FieldElement>;
|
|
1020
|
+
/**
|
|
1021
|
+
* Bundle of H2-related hash generation functions for dependency injection.
|
|
1022
|
+
*
|
|
1023
|
+
* Obtain via `getUtxoCommitmentHashGenerator()`. Both functions share the same Poseidon instance.
|
|
1024
|
+
*
|
|
1025
|
+
* @see {@link H2CircuitProvableHashFunction}
|
|
1026
|
+
* @see {@link H2HashFunction}
|
|
1027
|
+
* @public
|
|
1028
|
+
*/
|
|
1029
|
+
interface H2GeneratorFns {
|
|
1030
|
+
/** Computes the Poseidon(6) hash over the six core UTXO fields. */
|
|
1031
|
+
generateCircuitProvableHash: H2CircuitProvableHashFunction;
|
|
1032
|
+
/**
|
|
1033
|
+
* Computes the complete H2 UTXO leaf — flat Poseidon(6), identical to
|
|
1034
|
+
* `generateCircuitProvableHash`.
|
|
1035
|
+
*/
|
|
1036
|
+
generateH2: H2HashFunction;
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* An asynchronous function that generates a Poseidon commitment to a keystream value.
|
|
1040
|
+
*
|
|
1041
|
+
* Keystream commitments allow a ZK proof to verify that a specific keystream value was
|
|
1042
|
+
* used in a Poseidon encryption operation without revealing the keystream itself. They
|
|
1043
|
+
* are a core component of Umbra's linker encryption scheme, which provides
|
|
1044
|
+
* zero-knowledge proofs of correct encryption.
|
|
1045
|
+
*
|
|
1046
|
+
* @param keystream - The {@link Bn254FieldElement} keystream value to commit to (produced by
|
|
1047
|
+
* {@link PoseidonKeystreamGeneratorFunction}). This value is kept secret; only its
|
|
1048
|
+
* commitment is published.
|
|
1049
|
+
* @param blindingFactor - A uniformly random {@link Bn254FieldElement} that hides the
|
|
1050
|
+
* keystream in the commitment. Must be generated using a CSPRNG.
|
|
1051
|
+
* @returns A `Promise` resolving to a {@link Bn254FieldElement} — the keystream commitment.
|
|
1052
|
+
* This value is safe to publish on-chain or include in a ZK proof's public inputs.
|
|
1053
|
+
*
|
|
1054
|
+
* @remarks
|
|
1055
|
+
* ## Commitment Formula
|
|
1056
|
+
*
|
|
1057
|
+
* ```
|
|
1058
|
+
* commitment = Poseidon([keystream, blindingFactor])
|
|
1059
|
+
* ```
|
|
1060
|
+
*
|
|
1061
|
+
* This is a standard Poseidon-based Pedersen-style commitment: the `blindingFactor`
|
|
1062
|
+
* provides hiding (the commitment reveals no information about `keystream` to an
|
|
1063
|
+
* observer who does not know `blindingFactor`), and the collision resistance of
|
|
1064
|
+
* Poseidon provides binding (the committer cannot find two different `keystream`
|
|
1065
|
+
* values that open to the same commitment).
|
|
1066
|
+
*
|
|
1067
|
+
* ## Role in the Linker Encryption Scheme
|
|
1068
|
+
*
|
|
1069
|
+
* When a sender encrypts a value for a recipient, the keystream used in the
|
|
1070
|
+
* encryption is committed on-chain:
|
|
1071
|
+
* 1. The sender generates `keystream = Poseidon([transactionViewingKey, counter, 2n])`.
|
|
1072
|
+
* 2. The sender computes and publishes `commitment = Poseidon([keystream, blindingFactor])`.
|
|
1073
|
+
* 3. A ZK proof verifies that:
|
|
1074
|
+
* - The ciphertext was correctly formed using this keystream.
|
|
1075
|
+
* - The keystream corresponds to the published commitment.
|
|
1076
|
+
*
|
|
1077
|
+
* This binds the encryption to the commitment without revealing the keystream or
|
|
1078
|
+
* the transaction viewing key.
|
|
1079
|
+
*
|
|
1080
|
+
* @example
|
|
1081
|
+
* ```typescript
|
|
1082
|
+
* import { getKeystreamCommitmentGenerator } from "@umbra/sdk";
|
|
1083
|
+
*
|
|
1084
|
+
* const commitmentGenerator: KeystreamCommitmentFunction =
|
|
1085
|
+
* getKeystreamCommitmentGenerator();
|
|
1086
|
+
*
|
|
1087
|
+
* // Generate a keystream for counter 0
|
|
1088
|
+
* const keystreamMap = await keystreamGenerator([counter0], transactionViewingKey);
|
|
1089
|
+
* const keystream = keystreamMap.get(counter0)!;
|
|
1090
|
+
*
|
|
1091
|
+
* // Generate a random blinding factor (must use CSPRNG in production)
|
|
1092
|
+
* const blindingFactor: Bn254FieldElement = secureRandomFieldElement();
|
|
1093
|
+
*
|
|
1094
|
+
* // Commit to the keystream
|
|
1095
|
+
* const commitment = await commitmentGenerator(keystream, blindingFactor);
|
|
1096
|
+
* // commitment: Bn254FieldElement — safe to publish on-chain or in a ZK public input
|
|
1097
|
+
* ```
|
|
1098
|
+
*
|
|
1099
|
+
* @see {@link PoseidonKeystreamGeneratorFunction} for the function that produces keystreams
|
|
1100
|
+
* @see {@link PoseidonEncryptorFunction} for the encryption function whose keystream is committed
|
|
1101
|
+
* @public
|
|
1102
|
+
*/
|
|
1103
|
+
type KeystreamCommitmentFunction = (keystream: Bn254FieldElement, blindingFactor: Bn254FieldElement) => Promise<Bn254FieldElement>;
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* User Commitment Interfaces
|
|
1107
|
+
*
|
|
1108
|
+
* This module defines TypeScript function type interfaces for user commitment
|
|
1109
|
+
* generation used in the Umbra privacy protocol.
|
|
1110
|
+
*
|
|
1111
|
+
* ## Overview
|
|
1112
|
+
*
|
|
1113
|
+
* User commitments are cryptographic hashes that bind a user's identity to their
|
|
1114
|
+
* on-chain account without revealing the underlying keys. The commitment is
|
|
1115
|
+
* computed as a depth-2 Merkle tree root using Poseidon hashing, a hash function
|
|
1116
|
+
* specifically designed to be efficient inside zero-knowledge circuits.
|
|
1117
|
+
*
|
|
1118
|
+
* ## Tree Structure
|
|
1119
|
+
*
|
|
1120
|
+
* The commitment is computed as:
|
|
1121
|
+
* ```
|
|
1122
|
+
* left = Poseidon(masterViewingKey, mvkBlindingFactor)
|
|
1123
|
+
* right = Poseidon(poseidonPrivateKey, poseidonPrivateKeyBlindingFactor)
|
|
1124
|
+
* root = Poseidon(left, right)
|
|
1125
|
+
* ```
|
|
1126
|
+
*
|
|
1127
|
+
* ## Security Properties
|
|
1128
|
+
*
|
|
1129
|
+
* - The commitment is hiding (reveals nothing about the underlying inputs)
|
|
1130
|
+
* - The commitment is binding (cannot be opened to different input values)
|
|
1131
|
+
* - Poseidon is ZK-friendly, enabling efficient on-chain and off-circuit verification
|
|
1132
|
+
*
|
|
1133
|
+
* @packageDocumentation
|
|
1134
|
+
* @module interfaces/cryptography/commitment
|
|
1135
|
+
*/
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Function type for generating user commitments.
|
|
1139
|
+
*
|
|
1140
|
+
* Computes a depth-2 Merkle tree root from four BN254 field elements using
|
|
1141
|
+
* Poseidon hashing. The four inputs are grouped into two leaf nodes:
|
|
1142
|
+
*
|
|
1143
|
+
* - The left leaf commits to the master viewing key and its blinding factor.
|
|
1144
|
+
* - The right leaf commits to the Poseidon private key and its blinding factor.
|
|
1145
|
+
* - The root is the Poseidon hash of the two leaves.
|
|
1146
|
+
*
|
|
1147
|
+
* This function type is returned by {@link getUserCommitmentGeneratorFunction}
|
|
1148
|
+
* and is the primary interface consumers depend on when computing commitments.
|
|
1149
|
+
*
|
|
1150
|
+
* @param masterViewingKey - The user's master viewing key, a BN254 field element
|
|
1151
|
+
* derived from the user's wallet signature. This key governs decryption of all
|
|
1152
|
+
* confidential balances belonging to the user.
|
|
1153
|
+
* @param mvkBlindingFactor - A random BN254 field element used to blind the
|
|
1154
|
+
* master viewing key inside the left leaf, ensuring the leaf hides the key
|
|
1155
|
+
* from observers who only see the commitment.
|
|
1156
|
+
* @param poseidonPrivateKey - The user's Poseidon private key, a BN254 field
|
|
1157
|
+
* element used within ZK proof computations. This key is distinct from the
|
|
1158
|
+
* master viewing key and governs UTXO claim operations.
|
|
1159
|
+
* @param poseidonPrivateKeyBlindingFactor - A random BN254 field element used
|
|
1160
|
+
* to blind the Poseidon private key inside the right leaf.
|
|
1161
|
+
* @returns A `Promise` resolving to a {@link Bn254FieldElement} representing
|
|
1162
|
+
* the Merkle root commitment. This value is stored on-chain inside the
|
|
1163
|
+
* `EncryptedUserAccount`.
|
|
1164
|
+
*
|
|
1165
|
+
* @remarks
|
|
1166
|
+
* ## On-Chain Usage
|
|
1167
|
+
*
|
|
1168
|
+
* The user commitment is stored in the `EncryptedUserAccount` and is
|
|
1169
|
+
* used to:
|
|
1170
|
+
* - Bind the user's identity to their on-chain account without revealing keys
|
|
1171
|
+
* - Enable on-chain verification of key ownership during claim operations
|
|
1172
|
+
* - Support privacy-preserving balance proofs where the circuit checks the
|
|
1173
|
+
* commitment opening as a precondition for spending
|
|
1174
|
+
*
|
|
1175
|
+
* ## Blinding Factor Freshness
|
|
1176
|
+
*
|
|
1177
|
+
* The blinding factors MUST be sampled freshly from a cryptographically secure
|
|
1178
|
+
* random source for each new commitment. Reusing blinding factors across
|
|
1179
|
+
* commitments may allow an adversary to correlate commitments and deduce key
|
|
1180
|
+
* material.
|
|
1181
|
+
*
|
|
1182
|
+
* ## Security Properties
|
|
1183
|
+
*
|
|
1184
|
+
* - Hiding: the commitment reveals nothing about its four inputs
|
|
1185
|
+
* - Binding: it is computationally infeasible to open the commitment to
|
|
1186
|
+
* different input values under the Poseidon collision-resistance assumption
|
|
1187
|
+
* - ZK-friendliness: Poseidon has low multiplicative complexity, making
|
|
1188
|
+
* commitments cheap to verify inside Groth16 circuits
|
|
1189
|
+
*
|
|
1190
|
+
* @example
|
|
1191
|
+
* ```typescript
|
|
1192
|
+
* import { getUserCommitmentGeneratorFunction } from "./index";
|
|
1193
|
+
*
|
|
1194
|
+
* const generateCommitment = getUserCommitmentGeneratorFunction();
|
|
1195
|
+
*
|
|
1196
|
+
* const commitment = await generateCommitment(
|
|
1197
|
+
* masterViewingKey,
|
|
1198
|
+
* mvkBlindingFactor,
|
|
1199
|
+
* poseidonPrivateKey,
|
|
1200
|
+
* poseidonPrivateKeyBlindingFactor,
|
|
1201
|
+
* );
|
|
1202
|
+
*
|
|
1203
|
+
* // Store `commitment` in the EncryptedUserAccount on-chain
|
|
1204
|
+
* ```
|
|
1205
|
+
*
|
|
1206
|
+
* @see {@link getUserCommitmentGeneratorFunction} for the factory that produces
|
|
1207
|
+
* instances of this function type
|
|
1208
|
+
* @see {@link UserCommitmentGeneratorDeps} for optional dependency injection
|
|
1209
|
+
* @public
|
|
1210
|
+
*/
|
|
1211
|
+
type UserCommitmentGeneratorFunction = (masterViewingKey: Bn254FieldElement, mvkBlindingFactor: Bn254FieldElement, poseidonPrivateKey: Bn254FieldElement, poseidonPrivateKeyBlindingFactor: Bn254FieldElement) => Promise<Bn254FieldElement>;
|
|
1212
|
+
/**
|
|
1213
|
+
* Optional dependency injection bag for the user commitment generator.
|
|
1214
|
+
*
|
|
1215
|
+
* Allows callers to substitute the default Poseidon hasher with a custom
|
|
1216
|
+
* implementation. This is useful in two scenarios:
|
|
1217
|
+
*
|
|
1218
|
+
* - **Testing**: inject a deterministic or mock hasher to make tests
|
|
1219
|
+
* reproducible without relying on the default WASM-backed implementation.
|
|
1220
|
+
* - **Performance**: inject a pre-warmed hasher instance to avoid the
|
|
1221
|
+
* per-call initialisation cost of {@link getPoseidonHasher}.
|
|
1222
|
+
*
|
|
1223
|
+
* If the `hasher` field is omitted or `undefined`, the factory falls back to
|
|
1224
|
+
* the default singleton returned by `getPoseidonHasher()`.
|
|
1225
|
+
*
|
|
1226
|
+
* @example
|
|
1227
|
+
* ```typescript
|
|
1228
|
+
* import { getUserCommitmentGeneratorFunction } from "./index";
|
|
1229
|
+
* import type { UserCommitmentGeneratorDeps } from "./interfaces";
|
|
1230
|
+
*
|
|
1231
|
+
* // Inject a custom hasher for testing
|
|
1232
|
+
* const deps: UserCommitmentGeneratorDeps = { hasher: mockPoseidonHasher };
|
|
1233
|
+
* const generateCommitment = getUserCommitmentGeneratorFunction(deps);
|
|
1234
|
+
* ```
|
|
1235
|
+
*
|
|
1236
|
+
* @see {@link getUserCommitmentGeneratorFunction} for the factory that consumes
|
|
1237
|
+
* this interface
|
|
1238
|
+
* @see {@link PoseidonHashFunction} for the hasher contract
|
|
1239
|
+
* @public
|
|
1240
|
+
*/
|
|
1241
|
+
interface UserCommitmentGeneratorDeps {
|
|
1242
|
+
/**
|
|
1243
|
+
* Custom Poseidon hash function to use when generating commitments.
|
|
1244
|
+
*
|
|
1245
|
+
* If not provided, the factory uses the default implementation returned by
|
|
1246
|
+
* `getPoseidonHasher()`. Supplying a custom hasher also causes the factory
|
|
1247
|
+
* to maintain a separate cache entry keyed on the hasher reference, so
|
|
1248
|
+
* different hasher instances produce isolated generator functions.
|
|
1249
|
+
*
|
|
1250
|
+
* @defaultValue `getPoseidonHasher()` — the default WASM-backed Poseidon
|
|
1251
|
+
* hasher over the BN254 scalar field
|
|
1252
|
+
*/
|
|
1253
|
+
readonly hasher?: PoseidonHashFunction;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
/**
|
|
1257
|
+
* External Cryptography Dependency Interfaces
|
|
1258
|
+
*
|
|
1259
|
+
* This module defines interface types for external cryptographic functions
|
|
1260
|
+
* that are used by the SDK. These interfaces allow dependency injection
|
|
1261
|
+
* for testing and customization.
|
|
1262
|
+
*
|
|
1263
|
+
* @module interfaces/cryptography/external
|
|
1264
|
+
*/
|
|
1265
|
+
|
|
1266
|
+
/**
|
|
1267
|
+
* KMAC256 (Keccak Message Authentication Code) function interface.
|
|
1268
|
+
*
|
|
1269
|
+
* KMAC256 is a keyed hash function based on Keccak that provides
|
|
1270
|
+
* both authentication and domain separation capabilities.
|
|
1271
|
+
*
|
|
1272
|
+
* @param key - The key (customization string) as bytes
|
|
1273
|
+
* @param message - The message to authenticate
|
|
1274
|
+
* @param opts - Options object with output length
|
|
1275
|
+
* @returns The KMAC256 output as a Uint8Array
|
|
1276
|
+
*
|
|
1277
|
+
* @remarks
|
|
1278
|
+
* The default implementation uses `kmac256` from `@noble/hashes/sha3-addons`.
|
|
1279
|
+
*
|
|
1280
|
+
* @example
|
|
1281
|
+
* ```typescript
|
|
1282
|
+
* import { kmac256 } from "@noble/hashes/sha3-addons";
|
|
1283
|
+
*
|
|
1284
|
+
* const myKmac: Kmac256Function = (key, message, opts) =>
|
|
1285
|
+
* kmac256(key, message, opts);
|
|
1286
|
+
* ```
|
|
1287
|
+
*/
|
|
1288
|
+
type Kmac256Function = (key: Uint8Array, message: Uint8Array, options: {
|
|
1289
|
+
dkLen: number;
|
|
1290
|
+
personalization?: Uint8Array;
|
|
1291
|
+
}) => Uint8Array;
|
|
1292
|
+
/**
|
|
1293
|
+
* X25519 shared secret derivation function interface.
|
|
1294
|
+
*
|
|
1295
|
+
* This function computes the X25519 Diffie-Hellman shared secret from
|
|
1296
|
+
* a private key and a public key.
|
|
1297
|
+
*
|
|
1298
|
+
* @param privateKey - The X25519 private key (32 bytes)
|
|
1299
|
+
* @param publicKey - The X25519 public key of the counterparty (32 bytes)
|
|
1300
|
+
* @returns The shared secret as a Uint8Array (32 bytes)
|
|
1301
|
+
*
|
|
1302
|
+
* @remarks
|
|
1303
|
+
* The default implementation uses `x25519.getSharedSecret` from `@noble/curves/ed25519`.
|
|
1304
|
+
*
|
|
1305
|
+
* @example
|
|
1306
|
+
* ```typescript
|
|
1307
|
+
* import { x25519 } from "@noble/curves/ed25519";
|
|
1308
|
+
*
|
|
1309
|
+
* const myGetSharedSecret: X25519GetSharedSecretFunction =
|
|
1310
|
+
* (privateKey, publicKey) => x25519.getSharedSecret(privateKey, publicKey);
|
|
1311
|
+
* ```
|
|
1312
|
+
*/
|
|
1313
|
+
type X25519GetSharedSecretFunction = (privateKey: Uint8Array, publicKey: Uint8Array) => Uint8Array;
|
|
1314
|
+
/**
|
|
1315
|
+
* Random nonce generator function interface.
|
|
1316
|
+
*
|
|
1317
|
+
* This function generates a cryptographically secure random nonce
|
|
1318
|
+
* for use in encryption operations.
|
|
1319
|
+
*
|
|
1320
|
+
* @returns A random RcEncryptionNonce (128-bit value)
|
|
1321
|
+
*
|
|
1322
|
+
* @remarks
|
|
1323
|
+
* The default implementation uses `generateRandomNonce` from the SDK utilities.
|
|
1324
|
+
*
|
|
1325
|
+
* @example
|
|
1326
|
+
* ```typescript
|
|
1327
|
+
* import { generateRandomNonce } from "@umbra/sdk";
|
|
1328
|
+
*
|
|
1329
|
+
* const myGenerator: RandomNonceGeneratorFunction = () => generateRandomNonce();
|
|
1330
|
+
* ```
|
|
1331
|
+
*/
|
|
1332
|
+
type RandomNonceGeneratorFunction = () => RcEncryptionNonce;
|
|
1333
|
+
|
|
1334
|
+
/**
|
|
1335
|
+
* Key Generator Function Types
|
|
1336
|
+
*
|
|
1337
|
+
* This module defines function types (generator contracts) for all cryptographic
|
|
1338
|
+
* key generation operations in the Umbra key derivation system. Each type
|
|
1339
|
+
* specifies the signature that a conforming generator implementation must expose.
|
|
1340
|
+
*
|
|
1341
|
+
* @remarks
|
|
1342
|
+
* ## Design Philosophy
|
|
1343
|
+
*
|
|
1344
|
+
* All key generators follow a dependency-injection (DI) pattern:
|
|
1345
|
+
* - **Factory functions** (in `index.ts`) accept a client and optional deps and
|
|
1346
|
+
* return a generator function of one of the types defined here.
|
|
1347
|
+
* - **Generator function types** (this file) define the contract: parameter names,
|
|
1348
|
+
* types, and the Promise-based return value.
|
|
1349
|
+
*
|
|
1350
|
+
* This separation allows components to depend on the abstract generator type
|
|
1351
|
+
* without coupling to a concrete implementation, enabling easy testing via
|
|
1352
|
+
* mock generators and flexibility to swap cryptographic backends.
|
|
1353
|
+
*
|
|
1354
|
+
* ## Key Hierarchy and Generator Relationships
|
|
1355
|
+
*
|
|
1356
|
+
* ```
|
|
1357
|
+
* MasterSeed (64 bytes — root secret)
|
|
1358
|
+
* │
|
|
1359
|
+
* ├── [MasterSeedGeneratorFunction] — generates from entropy
|
|
1360
|
+
* ├── [EphemeralMasterSeedDeriverFunction] — generates per-offset ephemeral seed
|
|
1361
|
+
* │
|
|
1362
|
+
* ├── [MasterViewingKeyDeriverFunction] — KMAC256 → 252-bit BN254 element
|
|
1363
|
+
* ├── [MasterViewingKeyBlindingFactorDeriverFunction] — KMAC256 → BN254 element
|
|
1364
|
+
* │
|
|
1365
|
+
* ├── [PoseidonPrivateKeyDeriverFunction] — KMAC256 → BN254 element
|
|
1366
|
+
* ├── [PoseidonBlindingFactorDeriverFunction] — KMAC256 → BN254 element
|
|
1367
|
+
* │
|
|
1368
|
+
* ├── [Curve25519KeypairGeneratorFunction] — UserAccountX25519 keypair
|
|
1369
|
+
* ├── [MintX25519KeypairDeriverFunction] — per-(mint, offset) keypair
|
|
1370
|
+
* │
|
|
1371
|
+
* └── MVK → Poseidon hash chain → time-scoped viewing keys
|
|
1372
|
+
* [MintViewingKeyDeriverFunction]
|
|
1373
|
+
* [YearlyViewingKeyGeneratorFunction]
|
|
1374
|
+
* [MonthlyViewingKeyGeneratorFunction]
|
|
1375
|
+
* [DailyViewingKeyGeneratorFunction]
|
|
1376
|
+
* [HourlyViewingKeyGeneratorFunction]
|
|
1377
|
+
* [MinuteViewingKeyGeneratorFunction]
|
|
1378
|
+
* [SecondViewingKeyGeneratorFunction]
|
|
1379
|
+
* ```
|
|
1380
|
+
*
|
|
1381
|
+
* @packageDocumentation
|
|
1382
|
+
* @public
|
|
1383
|
+
*
|
|
1384
|
+
* @module interfaces/cryptography/key-generator
|
|
1385
|
+
*/
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* Options accepted by viewing key generator functions that use Poseidon hashing.
|
|
1389
|
+
*
|
|
1390
|
+
* @remarks
|
|
1391
|
+
* Viewing key derivation below the MVK level (mint, yearly, monthly, etc.) uses
|
|
1392
|
+
* the Poseidon hash function defined over the BN254 scalar field. This options
|
|
1393
|
+
* bag allows callers to inject a custom Poseidon implementation — useful for
|
|
1394
|
+
* testing with a mock hasher or for using a hardware-accelerated backend.
|
|
1395
|
+
*
|
|
1396
|
+
* If the `hasher` field is omitted, implementations MUST fall back to the
|
|
1397
|
+
* default `getPoseidonHasher()` implementation from the Poseidon module.
|
|
1398
|
+
*
|
|
1399
|
+
* @example
|
|
1400
|
+
* ```typescript
|
|
1401
|
+
* const generator = getMintViewingKeyDeriver({ client });
|
|
1402
|
+
* // Inject a test-double hasher for deterministic unit tests
|
|
1403
|
+
* const mintKey = await generator(usdcMint, { hasher: mockPoseidonHasher });
|
|
1404
|
+
* ```
|
|
1405
|
+
*
|
|
1406
|
+
* @see {@link MintViewingKeyDeriverFunction}
|
|
1407
|
+
* @see {@link YearlyViewingKeyGeneratorFunction}
|
|
1408
|
+
* @public
|
|
1409
|
+
*/
|
|
1410
|
+
interface ViewingKeyGeneratorOptions {
|
|
1411
|
+
/**
|
|
1412
|
+
* Optional custom Poseidon hasher function.
|
|
1413
|
+
*
|
|
1414
|
+
* @remarks
|
|
1415
|
+
* If omitted, implementations use the default `getPoseidonHasher()`.
|
|
1416
|
+
* The hasher must accept an array of BN254 field elements and return
|
|
1417
|
+
* a single BN254 field element that is the Poseidon hash output.
|
|
1418
|
+
*
|
|
1419
|
+
* @readonly
|
|
1420
|
+
*/
|
|
1421
|
+
readonly hasher?: PoseidonHashFunction;
|
|
1422
|
+
}
|
|
1423
|
+
/**
|
|
1424
|
+
* Generates a new master seed from high-entropy input.
|
|
1425
|
+
*
|
|
1426
|
+
* @remarks
|
|
1427
|
+
* The master seed is the root of the Umbra key derivation hierarchy. Implementations
|
|
1428
|
+
* MUST source entropy from a cryptographically secure random number generator
|
|
1429
|
+
* (CSPRNG) and hash it with Keccak-512 to produce the 64-byte master seed.
|
|
1430
|
+
*
|
|
1431
|
+
* Pre-conditions:
|
|
1432
|
+
* - The runtime environment MUST provide a CSPRNG (e.g., `crypto.getRandomValues`).
|
|
1433
|
+
*
|
|
1434
|
+
* Post-conditions:
|
|
1435
|
+
* - The returned `MasterSeed` is exactly 64 bytes.
|
|
1436
|
+
* - The seed has at least 256 bits of effective entropy.
|
|
1437
|
+
*
|
|
1438
|
+
* @returns A Promise resolving to a 64-byte MasterSeed
|
|
1439
|
+
*
|
|
1440
|
+
* @see {@link MasterSeed}
|
|
1441
|
+
* @public
|
|
1442
|
+
*/
|
|
1443
|
+
type MasterSeedGeneratorFunction = () => Promise<MasterSeed>;
|
|
1444
|
+
/**
|
|
1445
|
+
* Generates a deterministic ephemeral master seed from a U256 offset index.
|
|
1446
|
+
*
|
|
1447
|
+
* @remarks
|
|
1448
|
+
* Ephemeral seeds are used to derive single-use key sub-hierarchies for forward
|
|
1449
|
+
* secrecy. Each offset produces an independent seed via KMAC256 keyed by the
|
|
1450
|
+
* long-lived master seed. This ensures:
|
|
1451
|
+
* - Two different offsets always produce different ephemeral seeds.
|
|
1452
|
+
* - The same offset always produces the same ephemeral seed (deterministic).
|
|
1453
|
+
* - Knowledge of an ephemeral seed does not reveal the master seed.
|
|
1454
|
+
*
|
|
1455
|
+
* ## Algorithm
|
|
1456
|
+
*
|
|
1457
|
+
* Uses the internal pseudorandom U512 generator with domain separator:
|
|
1458
|
+
* `"Ephemeral Seed - {offset}"` where `offset` is formatted as a decimal string.
|
|
1459
|
+
*
|
|
1460
|
+
* Pre-conditions:
|
|
1461
|
+
* - The client MUST have a valid master seed loaded.
|
|
1462
|
+
*
|
|
1463
|
+
* Post-conditions:
|
|
1464
|
+
* - The returned value is exactly 64 bytes.
|
|
1465
|
+
* - Distinct offsets produce computationally independent outputs.
|
|
1466
|
+
*
|
|
1467
|
+
* @param offset - A U256 value used as the derivation offset (typically a generation index)
|
|
1468
|
+
* @returns A Promise resolving to a 64-byte MasterSeed usable for ephemeral key derivation
|
|
1469
|
+
*
|
|
1470
|
+
* @example
|
|
1471
|
+
* ```typescript
|
|
1472
|
+
* import { EphemeralMasterSeedDeriverFunction } from "@umbra/sdk";
|
|
1473
|
+
*
|
|
1474
|
+
* const generator: EphemeralMasterSeedDeriverFunction = getGenerator(client);
|
|
1475
|
+
*
|
|
1476
|
+
* // Generate ephemeral seeds with different offsets
|
|
1477
|
+
* const seed0 = await generator(0n as U256);
|
|
1478
|
+
* const seed1 = await generator(1n as U256);
|
|
1479
|
+
* const seed42 = await generator(42n as U256);
|
|
1480
|
+
*
|
|
1481
|
+
* // Use ephemeral seed for single-use key derivation; discard after use
|
|
1482
|
+
* ```
|
|
1483
|
+
*
|
|
1484
|
+
* @see {@link MasterSeed}
|
|
1485
|
+
* @public
|
|
1486
|
+
*/
|
|
1487
|
+
type EphemeralMasterSeedDeriverFunction = (offset: U256) => Promise<MasterSeed>;
|
|
1488
|
+
/**
|
|
1489
|
+
* Generates the master viewing key (MVK) from the client's master seed.
|
|
1490
|
+
*
|
|
1491
|
+
* @remarks
|
|
1492
|
+
* The master viewing key is the root of the read-only key tree. Sharing the MVK
|
|
1493
|
+
* allows a recipient to view ALL of the user's transactions across all tokens
|
|
1494
|
+
* and all time periods, without gaining any spending capability.
|
|
1495
|
+
*
|
|
1496
|
+
* ## Algorithm
|
|
1497
|
+
*
|
|
1498
|
+
* 1. Compute `KMAC256(key="Umbra Privacy - MasterViewingKey - {offset}", msg=masterSeed, dkLen=32)`
|
|
1499
|
+
* using the version-aware personalization string from the client.
|
|
1500
|
+
* 2. Interpret the 32-byte output as a big-endian unsigned integer.
|
|
1501
|
+
* 3. Mask to 252 bits: `value = value & (2^252 - 1)`.
|
|
1502
|
+
* 4. Assert `value < BN254_FIELD_PRIME`.
|
|
1503
|
+
*
|
|
1504
|
+
* The 252-bit constraint ensures compatibility with ZK circuits that perform
|
|
1505
|
+
* range checks on BN254 field element inputs.
|
|
1506
|
+
*
|
|
1507
|
+
* Pre-conditions:
|
|
1508
|
+
* - The client MUST have a valid master seed loaded.
|
|
1509
|
+
* - The client's offset for the master viewing key MUST be set.
|
|
1510
|
+
*
|
|
1511
|
+
* Post-conditions:
|
|
1512
|
+
* - The returned value is a BN254 field element strictly less than 2^252.
|
|
1513
|
+
*
|
|
1514
|
+
* @returns A Promise resolving to a MasterViewingKey (BN254 field element < 2^252)
|
|
1515
|
+
*
|
|
1516
|
+
* @example
|
|
1517
|
+
* ```typescript
|
|
1518
|
+
* import { getMasterViewingKeyDeriver } from "@umbra/sdk";
|
|
1519
|
+
*
|
|
1520
|
+
* const generator = getMasterViewingKeyDeriver({ masterSeed });
|
|
1521
|
+
* const mvk = await generator();
|
|
1522
|
+
*
|
|
1523
|
+
* // The MVK is constrained to 252 bits for ZK circuit compatibility
|
|
1524
|
+
* console.assert(mvk < 2n ** 252n);
|
|
1525
|
+
* ```
|
|
1526
|
+
*
|
|
1527
|
+
* @see {@link MasterViewingKey}
|
|
1528
|
+
* @see {@link MintViewingKeyDeriverFunction}
|
|
1529
|
+
* @public
|
|
1530
|
+
*/
|
|
1531
|
+
type MasterViewingKeyDeriverFunction = () => Promise<MasterViewingKey>;
|
|
1532
|
+
/**
|
|
1533
|
+
* Generates a blinding factor for the master viewing key commitment.
|
|
1534
|
+
*
|
|
1535
|
+
* @remarks
|
|
1536
|
+
* The blinding factor is used to conceal the master viewing key in a Pedersen
|
|
1537
|
+
* commitment: `commitment = g^mvk * h^blindingFactor`. This allows the user to
|
|
1538
|
+
* prove knowledge of the MVK in a ZK proof without revealing it directly.
|
|
1539
|
+
*
|
|
1540
|
+
* ## Algorithm
|
|
1541
|
+
*
|
|
1542
|
+
* Derives a BN254 field element via KMAC256 with the domain separator
|
|
1543
|
+
* `"UmbraPrivacy - Master Viewing Key Blinding Factor"` keyed by the master seed.
|
|
1544
|
+
*
|
|
1545
|
+
* Pre-conditions:
|
|
1546
|
+
* - The client MUST have a valid master seed loaded.
|
|
1547
|
+
*
|
|
1548
|
+
* Post-conditions:
|
|
1549
|
+
* - Returns a uniformly distributed BN254 field element in [0, BN254_FIELD_PRIME).
|
|
1550
|
+
* - Deterministic: same master seed always produces the same blinding factor.
|
|
1551
|
+
*
|
|
1552
|
+
* @returns A Promise resolving to a BN254 field element for use as the blinding factor
|
|
1553
|
+
*
|
|
1554
|
+
* @example
|
|
1555
|
+
* ```typescript
|
|
1556
|
+
* const generator = getMasterViewingKeyBlindingFactorDeriver({ client });
|
|
1557
|
+
* const blindingFactor = await generator();
|
|
1558
|
+
*
|
|
1559
|
+
* // Use in Pedersen commitment: commitment = g^mvk * h^blindingFactor
|
|
1560
|
+
* ```
|
|
1561
|
+
*
|
|
1562
|
+
* @see {@link MasterViewingKey}
|
|
1563
|
+
* @public
|
|
1564
|
+
*/
|
|
1565
|
+
type MasterViewingKeyBlindingFactorDeriverFunction = () => Promise<Bn254FieldElement>;
|
|
1566
|
+
/**
|
|
1567
|
+
* Generates the Poseidon private key for the user's master seed.
|
|
1568
|
+
*
|
|
1569
|
+
* @remarks
|
|
1570
|
+
* The Poseidon private key is used as the secret key input to the Poseidon
|
|
1571
|
+
* cipher (`PoseidonEnc`) for encrypting balances and UTXO fields. Each master
|
|
1572
|
+
* seed is associated with exactly one Poseidon private key.
|
|
1573
|
+
*
|
|
1574
|
+
* ## Algorithm
|
|
1575
|
+
*
|
|
1576
|
+
* Derives a BN254 field element via KMAC256 with the domain separator
|
|
1577
|
+
* `"PoseidonPrivateKey - {offset}"` where `offset` comes from the client's
|
|
1578
|
+
* `offsets.poseidonPrivateKey` field.
|
|
1579
|
+
*
|
|
1580
|
+
* ## Security Properties
|
|
1581
|
+
*
|
|
1582
|
+
* - Compromise of this key allows decryption of Poseidon-encrypted data.
|
|
1583
|
+
* - Different offsets produce independent Poseidon private keys.
|
|
1584
|
+
* - Knowledge of this key does NOT reveal the master seed or other keys.
|
|
1585
|
+
*
|
|
1586
|
+
* Pre-conditions:
|
|
1587
|
+
* - The client MUST have a valid master seed loaded.
|
|
1588
|
+
*
|
|
1589
|
+
* Post-conditions:
|
|
1590
|
+
* - Returns a uniformly distributed BN254 field element in [0, BN254_FIELD_PRIME).
|
|
1591
|
+
*
|
|
1592
|
+
* @returns A Promise resolving to a BN254 field element for use as the Poseidon private key
|
|
1593
|
+
*
|
|
1594
|
+
* @example
|
|
1595
|
+
* ```typescript
|
|
1596
|
+
* const generator = getPoseidonPrivateKeyDeriver({ client });
|
|
1597
|
+
* const privateKey = await generator();
|
|
1598
|
+
*
|
|
1599
|
+
* // Use as key for Poseidon encryption
|
|
1600
|
+
* const ciphertext = await poseidonEncrypt(plaintext, privateKey);
|
|
1601
|
+
* ```
|
|
1602
|
+
*
|
|
1603
|
+
* @see {@link PoseidonBlindingFactorDeriverFunction}
|
|
1604
|
+
* @public
|
|
1605
|
+
*/
|
|
1606
|
+
type PoseidonPrivateKeyDeriverFunction = () => Promise<Bn254FieldElement>;
|
|
1607
|
+
/**
|
|
1608
|
+
* Generates the Poseidon blinding factor for the user's master seed.
|
|
1609
|
+
*
|
|
1610
|
+
* @remarks
|
|
1611
|
+
* Blinding factors add randomness to Poseidon-based commitments while preserving
|
|
1612
|
+
* the ability to prove knowledge of the committed value in a ZK circuit.
|
|
1613
|
+
* Each master seed is associated with exactly one Poseidon blinding factor.
|
|
1614
|
+
*
|
|
1615
|
+
* ## Algorithm
|
|
1616
|
+
*
|
|
1617
|
+
* Derives a BN254 field element via KMAC256 with the domain separator
|
|
1618
|
+
* `"PoseidonBlindingFactor"` keyed by the master seed.
|
|
1619
|
+
*
|
|
1620
|
+
* Pre-conditions:
|
|
1621
|
+
* - The client MUST have a valid master seed loaded.
|
|
1622
|
+
*
|
|
1623
|
+
* Post-conditions:
|
|
1624
|
+
* - Returns a uniformly distributed BN254 field element in [0, BN254_FIELD_PRIME).
|
|
1625
|
+
*
|
|
1626
|
+
* @returns A Promise resolving to a BN254 field element for use as the blinding factor
|
|
1627
|
+
*
|
|
1628
|
+
* @example
|
|
1629
|
+
* ```typescript
|
|
1630
|
+
* const generator = getPoseidonBlindingFactorDeriver({ client });
|
|
1631
|
+
* const blindingFactor = await generator();
|
|
1632
|
+
*
|
|
1633
|
+
* // Use in Poseidon commitment: commitment = Poseidon([value, blindingFactor])
|
|
1634
|
+
* const commitment = await poseidonHash([value, blindingFactor]);
|
|
1635
|
+
* ```
|
|
1636
|
+
*
|
|
1637
|
+
* @see {@link PoseidonPrivateKeyDeriverFunction}
|
|
1638
|
+
* @public
|
|
1639
|
+
*/
|
|
1640
|
+
type PoseidonBlindingFactorDeriverFunction = () => Promise<Bn254FieldElement>;
|
|
1641
|
+
/**
|
|
1642
|
+
* Generates a mint-specific viewing key from the master viewing key and a mint address.
|
|
1643
|
+
*
|
|
1644
|
+
* @remarks
|
|
1645
|
+
* A mint viewing key grants read-only visibility into all transactions involving
|
|
1646
|
+
* a specific SPL token (identified by its mint address) without revealing
|
|
1647
|
+
* transactions of other tokens. It is the first Poseidon-hash step below the MVK.
|
|
1648
|
+
*
|
|
1649
|
+
* ## Algorithm
|
|
1650
|
+
*
|
|
1651
|
+
* ```
|
|
1652
|
+
* h0 = Poseidon(MVK, mintAddressLow, mintAddressHigh)
|
|
1653
|
+
* ```
|
|
1654
|
+
*
|
|
1655
|
+
* Where:
|
|
1656
|
+
* - `MVK` is the master viewing key (derived internally from the client's master seed)
|
|
1657
|
+
* - `mintAddressLow` = bytes 0–15 of the base58-decoded mint address as a little-endian U128
|
|
1658
|
+
* - `mintAddressHigh` = bytes 16–31 of the base58-decoded mint address as a little-endian U128
|
|
1659
|
+
*
|
|
1660
|
+
* Pre-conditions:
|
|
1661
|
+
* - The client MUST have a valid master seed loaded.
|
|
1662
|
+
* - `mint` MUST be a valid Solana address (32-byte base58-encoded string).
|
|
1663
|
+
*
|
|
1664
|
+
* Post-conditions:
|
|
1665
|
+
* - Returns a BN254 field element in [0, BN254_FIELD_PRIME).
|
|
1666
|
+
* - Deterministic: same MVK and same mint always produce the same mint viewing key.
|
|
1667
|
+
*
|
|
1668
|
+
* @param mint - The Solana SPL token mint address
|
|
1669
|
+
* @param options - Optional configuration, including a custom Poseidon hasher
|
|
1670
|
+
* @returns A Promise resolving to a MintViewingKey (BN254 field element)
|
|
1671
|
+
*
|
|
1672
|
+
* @example
|
|
1673
|
+
* ```typescript
|
|
1674
|
+
* import { getMintViewingKeyDeriver } from "@umbra/sdk";
|
|
1675
|
+
* import { address } from "@solana/kit";
|
|
1676
|
+
*
|
|
1677
|
+
* const generator = getMintViewingKeyDeriver({ masterViewingKey });
|
|
1678
|
+
* const usdcMint = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
1679
|
+
*
|
|
1680
|
+
* const mintKey = await generator(usdcMint);
|
|
1681
|
+
*
|
|
1682
|
+
* // Share with auditor for USDC-specific review only
|
|
1683
|
+
* shareViewingKey(auditor, mintKey);
|
|
1684
|
+
* ```
|
|
1685
|
+
*
|
|
1686
|
+
* @see {@link MintViewingKey}
|
|
1687
|
+
* @see {@link MasterViewingKey}
|
|
1688
|
+
* @public
|
|
1689
|
+
*/
|
|
1690
|
+
type MintViewingKeyDeriverFunction = (mint: Address, options?: ViewingKeyGeneratorOptions) => Promise<MintViewingKey>;
|
|
1691
|
+
/**
|
|
1692
|
+
* Generates a yearly viewing key for a specific calendar year and token.
|
|
1693
|
+
*
|
|
1694
|
+
* @remarks
|
|
1695
|
+
* A yearly viewing key scopes visibility to a single calendar year for a specific
|
|
1696
|
+
* token mint. It is derived by extending the mint viewing key with the year value.
|
|
1697
|
+
*
|
|
1698
|
+
* ## Algorithm
|
|
1699
|
+
*
|
|
1700
|
+
* ```
|
|
1701
|
+
* h0 = Poseidon(MVK, mintAddressLow, mintAddressHigh)
|
|
1702
|
+
* h1 = Poseidon(h0, year) // YearlyViewingKey
|
|
1703
|
+
* ```
|
|
1704
|
+
*
|
|
1705
|
+
* Pre-conditions:
|
|
1706
|
+
* - The client MUST have a valid master seed loaded.
|
|
1707
|
+
* - `year` MUST be a valid bigint in the `Year` branded type range.
|
|
1708
|
+
*
|
|
1709
|
+
* Post-conditions:
|
|
1710
|
+
* - Returns a BN254 field element in [0, BN254_FIELD_PRIME).
|
|
1711
|
+
*
|
|
1712
|
+
* @param mint - The Solana mint address of the token
|
|
1713
|
+
* @param year - The calendar year (e.g., `2024n as Year`)
|
|
1714
|
+
* @param options - Optional configuration including custom Poseidon hasher
|
|
1715
|
+
* @returns A Promise resolving to a YearlyViewingKey (BN254 field element)
|
|
1716
|
+
*
|
|
1717
|
+
* @example
|
|
1718
|
+
* ```typescript
|
|
1719
|
+
* import { assertYear } from "@umbra/sdk";
|
|
1720
|
+
*
|
|
1721
|
+
* const generator = getYearlyViewingKeyDeriver({ masterViewingKey });
|
|
1722
|
+
*
|
|
1723
|
+
* const year = 2024n;
|
|
1724
|
+
* assertYear(year);
|
|
1725
|
+
* const yearlyKey = await generator(usdcMint, year);
|
|
1726
|
+
*
|
|
1727
|
+
* // Share with tax authority for 2024 USDC audit
|
|
1728
|
+
* shareViewingKey(taxAuthority, yearlyKey);
|
|
1729
|
+
* ```
|
|
1730
|
+
*
|
|
1731
|
+
* @see {@link YearlyViewingKey}
|
|
1732
|
+
* @see {@link MintViewingKeyDeriverFunction}
|
|
1733
|
+
* @public
|
|
1734
|
+
*/
|
|
1735
|
+
type YearlyViewingKeyGeneratorFunction = (mint: Address, year: Year, options?: ViewingKeyGeneratorOptions) => Promise<YearlyViewingKey>;
|
|
1736
|
+
/**
|
|
1737
|
+
* Generates a monthly viewing key for a specific calendar month and token.
|
|
1738
|
+
*
|
|
1739
|
+
* @remarks
|
|
1740
|
+
* A monthly viewing key scopes visibility to a single calendar month (within a
|
|
1741
|
+
* specific year) for a specific token mint. It extends the yearly viewing key
|
|
1742
|
+
* with the month value.
|
|
1743
|
+
*
|
|
1744
|
+
* ## Algorithm
|
|
1745
|
+
*
|
|
1746
|
+
* ```
|
|
1747
|
+
* h0 = Poseidon(MVK, mintAddressLow, mintAddressHigh)
|
|
1748
|
+
* h1 = Poseidon(h0, year)
|
|
1749
|
+
* h2 = Poseidon(h1, month) // MonthlyViewingKey
|
|
1750
|
+
* ```
|
|
1751
|
+
*
|
|
1752
|
+
* Pre-conditions:
|
|
1753
|
+
* - The client MUST have a valid master seed loaded.
|
|
1754
|
+
* - `year` and `month` MUST be in their respective branded type ranges.
|
|
1755
|
+
* - `month` MUST be in [1, 12].
|
|
1756
|
+
*
|
|
1757
|
+
* Post-conditions:
|
|
1758
|
+
* - Returns a BN254 field element in [0, BN254_FIELD_PRIME).
|
|
1759
|
+
*
|
|
1760
|
+
* @param mint - The Solana mint address of the token
|
|
1761
|
+
* @param year - The calendar year (e.g., `2024n as Year`)
|
|
1762
|
+
* @param month - The calendar month, 1–12 (e.g., `6n as Month` for June)
|
|
1763
|
+
* @param options - Optional configuration including custom Poseidon hasher
|
|
1764
|
+
* @returns A Promise resolving to a MonthlyViewingKey (BN254 field element)
|
|
1765
|
+
*
|
|
1766
|
+
* @example
|
|
1767
|
+
* ```typescript
|
|
1768
|
+
* import { assertYear, assertMonth } from "@umbra/sdk";
|
|
1769
|
+
*
|
|
1770
|
+
* const generator = getMonthlyViewingKeyDeriver({ masterViewingKey });
|
|
1771
|
+
*
|
|
1772
|
+
* const year = 2024n;
|
|
1773
|
+
* const month = 6n;
|
|
1774
|
+
* assertYear(year);
|
|
1775
|
+
* assertMonth(month);
|
|
1776
|
+
* const monthlyKey = await generator(usdcMint, year, month); // June 2024
|
|
1777
|
+
*
|
|
1778
|
+
* // Share with accountant for monthly review
|
|
1779
|
+
* shareViewingKey(accountant, monthlyKey);
|
|
1780
|
+
* ```
|
|
1781
|
+
*
|
|
1782
|
+
* @see {@link MonthlyViewingKey}
|
|
1783
|
+
* @see {@link YearlyViewingKeyGeneratorFunction}
|
|
1784
|
+
* @public
|
|
1785
|
+
*/
|
|
1786
|
+
type MonthlyViewingKeyGeneratorFunction = (mint: Address, year: Year, month: Month, options?: ViewingKeyGeneratorOptions) => Promise<MonthlyViewingKey>;
|
|
1787
|
+
/**
|
|
1788
|
+
* Generates a daily viewing key for a specific calendar day and token.
|
|
1789
|
+
*
|
|
1790
|
+
* @remarks
|
|
1791
|
+
* A daily viewing key scopes visibility to a single calendar day (within a
|
|
1792
|
+
* specific year and month) for a specific token mint. It extends the monthly
|
|
1793
|
+
* viewing key with the day value.
|
|
1794
|
+
*
|
|
1795
|
+
* ## Algorithm
|
|
1796
|
+
*
|
|
1797
|
+
* ```
|
|
1798
|
+
* h0 = Poseidon(MVK, mintAddressLow, mintAddressHigh)
|
|
1799
|
+
* h1 = Poseidon(h0, year)
|
|
1800
|
+
* h2 = Poseidon(h1, month)
|
|
1801
|
+
* h3 = Poseidon(h2, day) // DailyViewingKey
|
|
1802
|
+
* ```
|
|
1803
|
+
*
|
|
1804
|
+
* Pre-conditions:
|
|
1805
|
+
* - The client MUST have a valid master seed loaded.
|
|
1806
|
+
* - `year`, `month`, and `day` MUST be in their respective branded type ranges.
|
|
1807
|
+
* - `day` MUST be in [1, 31].
|
|
1808
|
+
*
|
|
1809
|
+
* Post-conditions:
|
|
1810
|
+
* - Returns a BN254 field element in [0, BN254_FIELD_PRIME).
|
|
1811
|
+
*
|
|
1812
|
+
* @param mint - The Solana mint address of the token
|
|
1813
|
+
* @param year - The calendar year (e.g., `2024n as Year`)
|
|
1814
|
+
* @param month - The calendar month, 1–12 (e.g., `6n as Month`)
|
|
1815
|
+
* @param day - The calendar day, 1–31 (e.g., `15n as Day`)
|
|
1816
|
+
* @param options - Optional configuration including custom Poseidon hasher
|
|
1817
|
+
* @returns A Promise resolving to a DailyViewingKey (BN254 field element)
|
|
1818
|
+
*
|
|
1819
|
+
* @example
|
|
1820
|
+
* ```typescript
|
|
1821
|
+
* import { assertYear, assertMonth, assertDay } from "@umbra/sdk";
|
|
1822
|
+
*
|
|
1823
|
+
* const generator = getDailyViewingKeyDeriver({ masterViewingKey });
|
|
1824
|
+
*
|
|
1825
|
+
* const year = 2024n;
|
|
1826
|
+
* const month = 6n;
|
|
1827
|
+
* const day = 15n;
|
|
1828
|
+
* assertYear(year);
|
|
1829
|
+
* assertMonth(month);
|
|
1830
|
+
* assertDay(day);
|
|
1831
|
+
* const dailyKey = await generator(usdcMint, year, month, day); // June 15, 2024
|
|
1832
|
+
*
|
|
1833
|
+
* // Share for investigating a specific date
|
|
1834
|
+
* shareViewingKey(investigator, dailyKey);
|
|
1835
|
+
* ```
|
|
1836
|
+
*
|
|
1837
|
+
* @see {@link DailyViewingKey}
|
|
1838
|
+
* @see {@link MonthlyViewingKeyGeneratorFunction}
|
|
1839
|
+
* @public
|
|
1840
|
+
*/
|
|
1841
|
+
type DailyViewingKeyGeneratorFunction = (mint: Address, year: Year, month: Month, day: Day, options?: ViewingKeyGeneratorOptions) => Promise<DailyViewingKey>;
|
|
1842
|
+
/**
|
|
1843
|
+
* Generates an hourly viewing key for a specific hour and token.
|
|
1844
|
+
*
|
|
1845
|
+
* @remarks
|
|
1846
|
+
* An hourly viewing key scopes visibility to a single hour (within a specific
|
|
1847
|
+
* year, month, and day) for a specific token mint. It extends the daily viewing
|
|
1848
|
+
* key with the hour value (0–23).
|
|
1849
|
+
*
|
|
1850
|
+
* ## Algorithm
|
|
1851
|
+
*
|
|
1852
|
+
* ```
|
|
1853
|
+
* h0 = Poseidon(MVK, mintAddressLow, mintAddressHigh)
|
|
1854
|
+
* h1 = Poseidon(h0, year)
|
|
1855
|
+
* h2 = Poseidon(h1, month)
|
|
1856
|
+
* h3 = Poseidon(h2, day)
|
|
1857
|
+
* h4 = Poseidon(h3, hour) // HourlyViewingKey
|
|
1858
|
+
* ```
|
|
1859
|
+
*
|
|
1860
|
+
* Pre-conditions:
|
|
1861
|
+
* - The client MUST have a valid master seed loaded.
|
|
1862
|
+
* - All time parameters MUST be in their respective branded type ranges.
|
|
1863
|
+
* - `hour` MUST be in [0, 23].
|
|
1864
|
+
*
|
|
1865
|
+
* Post-conditions:
|
|
1866
|
+
* - Returns a BN254 field element in [0, BN254_FIELD_PRIME).
|
|
1867
|
+
*
|
|
1868
|
+
* @param mint - The Solana mint address of the token
|
|
1869
|
+
* @param year - The calendar year (e.g., `2024n as Year`)
|
|
1870
|
+
* @param month - The calendar month, 1–12
|
|
1871
|
+
* @param day - The calendar day, 1–31
|
|
1872
|
+
* @param hour - The hour of the day, 0–23 (e.g., `14n as Hour` for 2 PM UTC)
|
|
1873
|
+
* @param options - Optional configuration including custom Poseidon hasher
|
|
1874
|
+
* @returns A Promise resolving to a HourlyViewingKey (BN254 field element)
|
|
1875
|
+
*
|
|
1876
|
+
* @example
|
|
1877
|
+
* ```typescript
|
|
1878
|
+
* import { assertYear, assertMonth, assertDay, assertHour } from "@umbra/sdk";
|
|
1879
|
+
*
|
|
1880
|
+
* const generator = getHourlyViewingKeyDeriver({ masterViewingKey });
|
|
1881
|
+
*
|
|
1882
|
+
* const year = 2024n;
|
|
1883
|
+
* const month = 6n;
|
|
1884
|
+
* const day = 15n;
|
|
1885
|
+
* const hour = 14n;
|
|
1886
|
+
* assertYear(year);
|
|
1887
|
+
* assertMonth(month);
|
|
1888
|
+
* assertDay(day);
|
|
1889
|
+
* assertHour(hour);
|
|
1890
|
+
* const hourlyKey = await generator(usdcMint, year, month, day, hour); // June 15, 2024 2PM
|
|
1891
|
+
*
|
|
1892
|
+
* // Share for high-frequency trading audit within this hour
|
|
1893
|
+
* shareViewingKey(auditor, hourlyKey);
|
|
1894
|
+
* ```
|
|
1895
|
+
*
|
|
1896
|
+
* @see {@link HourlyViewingKey}
|
|
1897
|
+
* @see {@link DailyViewingKeyGeneratorFunction}
|
|
1898
|
+
* @public
|
|
1899
|
+
*/
|
|
1900
|
+
type HourlyViewingKeyGeneratorFunction = (mint: Address, year: Year, month: Month, day: Day, hour: Hour, options?: ViewingKeyGeneratorOptions) => Promise<HourlyViewingKey>;
|
|
1901
|
+
/**
|
|
1902
|
+
* Generates a minute viewing key for a specific minute and token.
|
|
1903
|
+
*
|
|
1904
|
+
* @remarks
|
|
1905
|
+
* A minute viewing key scopes visibility to a single minute within a specific
|
|
1906
|
+
* hour, day, month, and year for a specific token mint. It extends the hourly
|
|
1907
|
+
* viewing key with the minute value (0–59).
|
|
1908
|
+
*
|
|
1909
|
+
* ## Algorithm
|
|
1910
|
+
*
|
|
1911
|
+
* ```
|
|
1912
|
+
* h0 = Poseidon(MVK, mintAddressLow, mintAddressHigh)
|
|
1913
|
+
* h1 = Poseidon(h0, year)
|
|
1914
|
+
* h2 = Poseidon(h1, month)
|
|
1915
|
+
* h3 = Poseidon(h2, day)
|
|
1916
|
+
* h4 = Poseidon(h3, hour)
|
|
1917
|
+
* h5 = Poseidon(h4, minute) // MinuteViewingKey
|
|
1918
|
+
* ```
|
|
1919
|
+
*
|
|
1920
|
+
* Pre-conditions:
|
|
1921
|
+
* - The client MUST have a valid master seed loaded.
|
|
1922
|
+
* - All time parameters MUST be in their respective branded type ranges.
|
|
1923
|
+
* - `minute` MUST be in [0, 59].
|
|
1924
|
+
*
|
|
1925
|
+
* Post-conditions:
|
|
1926
|
+
* - Returns a BN254 field element in [0, BN254_FIELD_PRIME).
|
|
1927
|
+
*
|
|
1928
|
+
* @param mint - The Solana mint address of the token
|
|
1929
|
+
* @param year - The calendar year (e.g., `2024n as Year`)
|
|
1930
|
+
* @param month - The calendar month, 1–12
|
|
1931
|
+
* @param day - The calendar day, 1–31
|
|
1932
|
+
* @param hour - The hour of the day, 0–23
|
|
1933
|
+
* @param minute - The minute within the hour, 0–59 (e.g., `30n as Minute`)
|
|
1934
|
+
* @param options - Optional configuration including custom Poseidon hasher
|
|
1935
|
+
* @returns A Promise resolving to a MinuteViewingKey (BN254 field element)
|
|
1936
|
+
*
|
|
1937
|
+
* @example
|
|
1938
|
+
* ```typescript
|
|
1939
|
+
* import { assertYear, assertMonth, assertDay, assertHour, assertMinute } from "@umbra/sdk";
|
|
1940
|
+
*
|
|
1941
|
+
* const generator = getMinuteViewingKeyDeriver({ masterViewingKey });
|
|
1942
|
+
*
|
|
1943
|
+
* const year = 2024n;
|
|
1944
|
+
* const month = 6n;
|
|
1945
|
+
* const day = 15n;
|
|
1946
|
+
* const hour = 14n;
|
|
1947
|
+
* const minute = 30n;
|
|
1948
|
+
* assertYear(year);
|
|
1949
|
+
* assertMonth(month);
|
|
1950
|
+
* assertDay(day);
|
|
1951
|
+
* assertHour(hour);
|
|
1952
|
+
* assertMinute(minute);
|
|
1953
|
+
* const minuteKey = await generator(usdcMint, year, month, day, hour, minute);
|
|
1954
|
+
*
|
|
1955
|
+
* // Share for precise forensic analysis of a specific minute
|
|
1956
|
+
* shareViewingKey(forensicAnalyst, minuteKey);
|
|
1957
|
+
* ```
|
|
1958
|
+
*
|
|
1959
|
+
* @see {@link MinuteViewingKey}
|
|
1960
|
+
* @see {@link HourlyViewingKeyGeneratorFunction}
|
|
1961
|
+
* @public
|
|
1962
|
+
*/
|
|
1963
|
+
type MinuteViewingKeyGeneratorFunction = (mint: Address, year: Year, month: Month, day: Day, hour: Hour, minute: Minute, options?: ViewingKeyGeneratorOptions) => Promise<MinuteViewingKey>;
|
|
1964
|
+
/**
|
|
1965
|
+
* Generates a second viewing key for a specific second and token.
|
|
1966
|
+
*
|
|
1967
|
+
* @remarks
|
|
1968
|
+
* The second viewing key is the finest-granularity key in the Umbra viewing
|
|
1969
|
+
* key hierarchy, scoping visibility to a single second within a specific minute.
|
|
1970
|
+
* It is the leaf node of the Poseidon hash chain.
|
|
1971
|
+
*
|
|
1972
|
+
* ## Algorithm
|
|
1973
|
+
*
|
|
1974
|
+
* ```
|
|
1975
|
+
* h0 = Poseidon(MVK, mintAddressLow, mintAddressHigh)
|
|
1976
|
+
* h1 = Poseidon(h0, year)
|
|
1977
|
+
* h2 = Poseidon(h1, month)
|
|
1978
|
+
* h3 = Poseidon(h2, day)
|
|
1979
|
+
* h4 = Poseidon(h3, hour)
|
|
1980
|
+
* h5 = Poseidon(h4, minute)
|
|
1981
|
+
* TVK = Poseidon(h5, second) // SecondViewingKey (Terminal Viewing Key)
|
|
1982
|
+
* ```
|
|
1983
|
+
*
|
|
1984
|
+
* Pre-conditions:
|
|
1985
|
+
* - The client MUST have a valid master seed loaded.
|
|
1986
|
+
* - All time parameters MUST be in their respective branded type ranges.
|
|
1987
|
+
* - `second` MUST be in [0, 59].
|
|
1988
|
+
*
|
|
1989
|
+
* Post-conditions:
|
|
1990
|
+
* - Returns a BN254 field element in [0, BN254_FIELD_PRIME).
|
|
1991
|
+
* - No further time-scoped keys can be derived from a SecondViewingKey.
|
|
1992
|
+
*
|
|
1993
|
+
* @param mint - The Solana mint address of the token
|
|
1994
|
+
* @param year - The calendar year (e.g., `2024n as Year`)
|
|
1995
|
+
* @param month - The calendar month, 1–12
|
|
1996
|
+
* @param day - The calendar day, 1–31
|
|
1997
|
+
* @param hour - The hour of the day, 0–23
|
|
1998
|
+
* @param minute - The minute within the hour, 0–59
|
|
1999
|
+
* @param second - The second within the minute, 0–59 (e.g., `45n as Second`)
|
|
2000
|
+
* @param options - Optional configuration including custom Poseidon hasher
|
|
2001
|
+
* @returns A Promise resolving to a SecondViewingKey (BN254 field element)
|
|
2002
|
+
*
|
|
2003
|
+
* @example
|
|
2004
|
+
* ```typescript
|
|
2005
|
+
* import { assertYear, assertMonth, assertDay, assertHour, assertMinute, assertSecond } from "@umbra/sdk";
|
|
2006
|
+
*
|
|
2007
|
+
* const generator = getSecondViewingKeyDeriver({ masterViewingKey });
|
|
2008
|
+
*
|
|
2009
|
+
* const year = 2024n;
|
|
2010
|
+
* const month = 6n;
|
|
2011
|
+
* const day = 15n;
|
|
2012
|
+
* const hour = 14n;
|
|
2013
|
+
* const minute = 30n;
|
|
2014
|
+
* const second = 45n;
|
|
2015
|
+
* assertYear(year);
|
|
2016
|
+
* assertMonth(month);
|
|
2017
|
+
* assertDay(day);
|
|
2018
|
+
* assertHour(hour);
|
|
2019
|
+
* assertMinute(minute);
|
|
2020
|
+
* assertSecond(second);
|
|
2021
|
+
* const secondKey = await generator(usdcMint, year, month, day, hour, minute, second);
|
|
2022
|
+
*
|
|
2023
|
+
* // Share for investigating a specific transaction
|
|
2024
|
+
* shareViewingKey(investigator, secondKey);
|
|
2025
|
+
* ```
|
|
2026
|
+
*
|
|
2027
|
+
* @see {@link SecondViewingKey}
|
|
2028
|
+
* @see {@link MinuteViewingKeyGeneratorFunction}
|
|
2029
|
+
* @public
|
|
2030
|
+
*/
|
|
2031
|
+
type SecondViewingKeyGeneratorFunction = (mint: Address, year: Year, month: Month, day: Day, hour: Hour, minute: Minute, second: Second, options?: ViewingKeyGeneratorOptions) => Promise<SecondViewingKey>;
|
|
2032
|
+
/**
|
|
2033
|
+
* Generates a complete Curve25519 keypair (Ed25519 + X25519) from the master seed.
|
|
2034
|
+
*
|
|
2035
|
+
* @remarks
|
|
2036
|
+
* This function derives both an Ed25519 (Edwards curve) signing keypair and
|
|
2037
|
+
* an X25519 (Montgomery curve) ECDH keypair from the master seed using a single
|
|
2038
|
+
* deterministic derivation pipeline. The two keypairs share the same Curve25519
|
|
2039
|
+
* foundation and are used together in token account registration.
|
|
2040
|
+
*
|
|
2041
|
+
* ## Derivation Pipeline
|
|
2042
|
+
*
|
|
2043
|
+
* 1. Derive 64 bytes via `KMAC256(key="Umbra Privacy - {domain} - {offset}", msg=masterSeed, dkLen=64)`
|
|
2044
|
+
* 2. **Ed25519 Keypair**:
|
|
2045
|
+
* - Use first 32 bytes as the Ed25519 seed
|
|
2046
|
+
* - Derive Ed25519 public key: `ed25519.getPublicKey(seed)`
|
|
2047
|
+
* 3. **X25519 Keypair**:
|
|
2048
|
+
* - Hash seed with SHA-512 → 64 bytes
|
|
2049
|
+
* - Clamp first 32 bytes per RFC 8032 §5.1.5
|
|
2050
|
+
* - Derive X25519 public key via birational map: `ed25519.utils.toMontgomery(ed25519Pub)`
|
|
2051
|
+
*
|
|
2052
|
+
* ## Security Properties
|
|
2053
|
+
*
|
|
2054
|
+
* - **Deterministic**: Same master seed always produces the same keypairs
|
|
2055
|
+
* - **Domain Separated**: Independent from all other key derivations
|
|
2056
|
+
* - **Forward Secure**: Different offsets produce independent keypairs
|
|
2057
|
+
*
|
|
2058
|
+
* Pre-conditions:
|
|
2059
|
+
* - The client MUST have a valid master seed loaded.
|
|
2060
|
+
*
|
|
2061
|
+
* Post-conditions:
|
|
2062
|
+
* - Returns a valid `Curve25519KeypairResult` with both Ed25519 and X25519 keypairs.
|
|
2063
|
+
*
|
|
2064
|
+
* @returns A Promise resolving to a Curve25519KeypairResult with ed25519Keypair and x25519Keypair
|
|
2065
|
+
*
|
|
2066
|
+
* @example
|
|
2067
|
+
* ```typescript
|
|
2068
|
+
* import { getUserAccountX25519KeypairDeriver } from "@umbra/sdk";
|
|
2069
|
+
*
|
|
2070
|
+
* const generator = getUserAccountX25519KeypairDeriver({ client });
|
|
2071
|
+
* const result = await generator();
|
|
2072
|
+
*
|
|
2073
|
+
* // Use Ed25519 for signing
|
|
2074
|
+
* const signature = ed25519.sign(message, result.ed25519Keypair.seed);
|
|
2075
|
+
*
|
|
2076
|
+
* // Use X25519 for key exchange
|
|
2077
|
+
* const sharedSecret = x25519.getSharedSecret(
|
|
2078
|
+
* result.x25519Keypair.privateKey,
|
|
2079
|
+
* peerPublicKey
|
|
2080
|
+
* );
|
|
2081
|
+
* ```
|
|
2082
|
+
*
|
|
2083
|
+
* @see {@link Curve25519KeypairResult}
|
|
2084
|
+
* @see {@link MintX25519KeypairDeriverFunction}
|
|
2085
|
+
* @public
|
|
2086
|
+
*/
|
|
2087
|
+
type Curve25519KeypairGeneratorFunction = () => Promise<Curve25519KeypairResult>;
|
|
2088
|
+
/**
|
|
2089
|
+
* Generates a per-mint Curve25519 keypair (Ed25519 + X25519) for a given mint.
|
|
2090
|
+
*
|
|
2091
|
+
* @remarks
|
|
2092
|
+
* Used for `reencrypt_shared` operations — produces a unique keypair for each
|
|
2093
|
+
* mint address. The offset is taken from `client.offsets.mintX25519PrivateKey`
|
|
2094
|
+
* at construction time, consistent with all other key types.
|
|
2095
|
+
*
|
|
2096
|
+
* ## Algorithm
|
|
2097
|
+
*
|
|
2098
|
+
* Domain separator: `"MintX25519Keypair - {mint} - {client.offsets.mintX25519PrivateKey}"`
|
|
2099
|
+
*
|
|
2100
|
+
* Pre-conditions:
|
|
2101
|
+
* - The client MUST have a valid master seed loaded.
|
|
2102
|
+
* - `mint` MUST be a valid Solana address.
|
|
2103
|
+
*
|
|
2104
|
+
* Post-conditions:
|
|
2105
|
+
* - Returns a valid `Curve25519KeypairResult`.
|
|
2106
|
+
* - Different mints produce independent keypairs.
|
|
2107
|
+
* - Different `client.offsets.mintX25519PrivateKey` values produce independent keypairs.
|
|
2108
|
+
*
|
|
2109
|
+
* @param mint - The Solana SPL token mint address
|
|
2110
|
+
* @returns A Promise resolving to a Curve25519KeypairResult
|
|
2111
|
+
*
|
|
2112
|
+
* @example
|
|
2113
|
+
* ```typescript
|
|
2114
|
+
* const generator = getMintX25519KeypairDeriver({ client });
|
|
2115
|
+
* const usdcKeypair = await generator(usdcMint);
|
|
2116
|
+
* const solKeypair = await generator(solMint);
|
|
2117
|
+
* // usdcKeypair and solKeypair are independent due to different mints
|
|
2118
|
+
* ```
|
|
2119
|
+
*
|
|
2120
|
+
* @see {@link Curve25519KeypairGeneratorFunction}
|
|
2121
|
+
* @see {@link Curve25519KeypairResult}
|
|
2122
|
+
* @public
|
|
2123
|
+
*/
|
|
2124
|
+
type MintX25519KeypairDeriverFunction = (mint: Address) => Promise<Curve25519KeypairResult>;
|
|
2125
|
+
/**
|
|
2126
|
+
* Generates a 512-bit pseudorandom value from the master seed and a domain separator.
|
|
2127
|
+
*
|
|
2128
|
+
* @remarks
|
|
2129
|
+
* This is the base primitive for all KMAC256-based key derivation in Umbra.
|
|
2130
|
+
* It produces 512 bits of pseudorandom output from the master seed using
|
|
2131
|
+
* a single KMAC256 call with a domain-separated key. The 512-bit output size
|
|
2132
|
+
* ensures negligible bias when reducing to BN254 or Curve25519 field elements.
|
|
2133
|
+
*
|
|
2134
|
+
* ## Algorithm
|
|
2135
|
+
*
|
|
2136
|
+
* 1. Construct the KMAC key string: `"Umbra Privacy - {domainSeparator}"`
|
|
2137
|
+
* 2. Compute `KMAC256(key, masterSeed, dkLen=64, personalization=versionString)`
|
|
2138
|
+
* 3. Return the 64-byte big-endian output as `U512BeBytes`
|
|
2139
|
+
*
|
|
2140
|
+
* ## Security Properties
|
|
2141
|
+
*
|
|
2142
|
+
* - **Domain Separation**: Different domain separators produce computationally
|
|
2143
|
+
* independent outputs, even with the same master seed.
|
|
2144
|
+
* - **Deterministic**: Same domain separator and master seed always produce
|
|
2145
|
+
* the same output.
|
|
2146
|
+
* - **Pseudorandom**: Output is computationally indistinguishable from random
|
|
2147
|
+
* under standard assumptions about KMAC256.
|
|
2148
|
+
*
|
|
2149
|
+
* Pre-conditions:
|
|
2150
|
+
* - The client MUST have a valid master seed loaded.
|
|
2151
|
+
*
|
|
2152
|
+
* Post-conditions:
|
|
2153
|
+
* - Returns exactly 64 bytes encoded as big-endian `U512BeBytes`.
|
|
2154
|
+
*
|
|
2155
|
+
* @param domainSeparator - A string that uniquely identifies the derivation context
|
|
2156
|
+
* @returns A Promise resolving to a 512-bit big-endian byte array (U512BeBytes)
|
|
2157
|
+
*
|
|
2158
|
+
* @example
|
|
2159
|
+
* ```typescript
|
|
2160
|
+
* import { PseudorandomU512DeriverFunction } from "@umbra/sdk";
|
|
2161
|
+
*
|
|
2162
|
+
* const generator: PseudorandomU512DeriverFunction = getGenerator(client);
|
|
2163
|
+
*
|
|
2164
|
+
* // Generate different 512-bit values using domain separation
|
|
2165
|
+
* const value1 = await generator("MasterViewingKey");
|
|
2166
|
+
* const value2 = await generator("EphemeralSeed-12345");
|
|
2167
|
+
*
|
|
2168
|
+
* // Each domain separator produces a completely independent output
|
|
2169
|
+
* ```
|
|
2170
|
+
*
|
|
2171
|
+
* @see {@link MasterSeedBasedFieldElementDeriverFunction}
|
|
2172
|
+
* @public
|
|
2173
|
+
*/
|
|
2174
|
+
type PseudorandomU512DeriverFunction = (domainSeparator: string) => Promise<U512BeBytes>;
|
|
2175
|
+
/**
|
|
2176
|
+
* Derives a BN254 field element from the master seed using domain separation.
|
|
2177
|
+
*
|
|
2178
|
+
* @remarks
|
|
2179
|
+
* This function builds on top of `PseudorandomU512DeriverFunction` to reduce
|
|
2180
|
+
* the 512-bit pseudorandom output to a uniformly distributed BN254 field element.
|
|
2181
|
+
* The reduction uses constant-time modular arithmetic to prevent timing side-channels.
|
|
2182
|
+
*
|
|
2183
|
+
* ## Algorithm
|
|
2184
|
+
*
|
|
2185
|
+
* 1. Generate 512-bit pseudorandom value via KMAC256 with `domainSeparator`
|
|
2186
|
+
* 2. Reduce to BN254 field element using constant-time modular reduction:
|
|
2187
|
+
* `fieldElement = u512Value mod BN254_FIELD_PRIME`
|
|
2188
|
+
*
|
|
2189
|
+
* ## Security Properties
|
|
2190
|
+
*
|
|
2191
|
+
* - **Domain Separation**: Different domain separators produce independent outputs
|
|
2192
|
+
* - **Deterministic**: Same inputs always produce the same field element
|
|
2193
|
+
* - **Uniform Distribution**: Output is uniformly distributed in [0, BN254_FIELD_PRIME)
|
|
2194
|
+
* with bias 2^(-512 + log2(BN254_FIELD_PRIME)) ≈ 2^{-256}, which is negligible
|
|
2195
|
+
* - **Constant-Time**: Reduction is performed without data-dependent branches
|
|
2196
|
+
*
|
|
2197
|
+
* Pre-conditions:
|
|
2198
|
+
* - The client MUST have a valid master seed loaded.
|
|
2199
|
+
*
|
|
2200
|
+
* Post-conditions:
|
|
2201
|
+
* - Returns a bigint in [0, BN254_FIELD_PRIME).
|
|
2202
|
+
*
|
|
2203
|
+
* @param domainSeparator - A string that uniquely identifies the derivation context
|
|
2204
|
+
* @returns A Promise resolving to a BN254 field element in range [0, BN254_FIELD_PRIME)
|
|
2205
|
+
*
|
|
2206
|
+
* @example
|
|
2207
|
+
* ```typescript
|
|
2208
|
+
* import { getMasterSeedBasedFieldElementDeriver } from "@umbra/sdk";
|
|
2209
|
+
*
|
|
2210
|
+
* const deriver = getMasterSeedBasedFieldElementDeriver({ client });
|
|
2211
|
+
*
|
|
2212
|
+
* // Derive different keys using domain separation
|
|
2213
|
+
* const viewingKey = await deriver("MasterViewingKey");
|
|
2214
|
+
* const encryptionKey = await deriver("EncryptionKey");
|
|
2215
|
+
* const nullifierKey = await deriver("NullifierKey");
|
|
2216
|
+
*
|
|
2217
|
+
* // Each domain separator produces a completely independent output
|
|
2218
|
+
* ```
|
|
2219
|
+
*
|
|
2220
|
+
* @example
|
|
2221
|
+
* ```typescript
|
|
2222
|
+
* // Deriving time-scoped keys
|
|
2223
|
+
* const yearlyKey = await deriver("YearlyViewingKey-2024");
|
|
2224
|
+
* const monthlyKey = await deriver("MonthlyViewingKey-2024-01");
|
|
2225
|
+
* const dailyKey = await deriver("DailyViewingKey-2024-01-15");
|
|
2226
|
+
* ```
|
|
2227
|
+
*
|
|
2228
|
+
* @see {@link PseudorandomU512DeriverFunction}
|
|
2229
|
+
* @public
|
|
2230
|
+
*/
|
|
2231
|
+
type MasterSeedBasedFieldElementDeriverFunction = (domainSeparator: string) => Promise<Bn254FieldElement>;
|
|
2232
|
+
|
|
2233
|
+
/**
|
|
2234
|
+
* Generates a BN254 field element blinding factor for Rescue encryption commitments.
|
|
2235
|
+
*
|
|
2236
|
+
* @remarks
|
|
2237
|
+
* Rescue encryption commitment blinding factors are used to commit to ciphertexts
|
|
2238
|
+
* and plaintexts in UTXO creation ZK proofs. The derivation binds an offset
|
|
2239
|
+
* (typically derived from the generation index) to ensure unique blinding factors
|
|
2240
|
+
* for each UTXO creation event.
|
|
2241
|
+
*
|
|
2242
|
+
* ## Algorithm
|
|
2243
|
+
*
|
|
2244
|
+
* 1. Build domain separator: `"RescueEncryptionCommitmentBlindingFactor - {totalOffset}"`
|
|
2245
|
+
* where `totalOffset = client.offsets.rescueCommitmentBlindingFactor + offset`
|
|
2246
|
+
* 2. Generate 512-bit pseudorandom value using KMAC256
|
|
2247
|
+
* 3. Sample BN254 field element using constant-time reduction
|
|
2248
|
+
*
|
|
2249
|
+
* Pre-conditions:
|
|
2250
|
+
* - The client MUST have a valid master seed loaded.
|
|
2251
|
+
*
|
|
2252
|
+
* Post-conditions:
|
|
2253
|
+
* - Returns a BN254 field element uniformly distributed in [0, BN254_FIELD_PRIME).
|
|
2254
|
+
* - Different offsets produce independent blinding factors.
|
|
2255
|
+
*
|
|
2256
|
+
* @param offset - A U256 offset for unique derivation context (e.g., generation index)
|
|
2257
|
+
* @returns A Promise resolving to a BN254 field element blinding factor
|
|
2258
|
+
*
|
|
2259
|
+
* @example
|
|
2260
|
+
* ```typescript
|
|
2261
|
+
* const generator = getRescueCommitmentBlindingFactorDeriver({ client });
|
|
2262
|
+
* const blindingFactor = await generator(0n as U256);
|
|
2263
|
+
* ```
|
|
2264
|
+
*
|
|
2265
|
+
* @see {@link EphemeralRescueCommitmentBlindingFactorDeriverFunction}
|
|
2266
|
+
* @public
|
|
2267
|
+
*/
|
|
2268
|
+
type RescueCommitmentBlindingFactorDeriverFunction = (offset: U256) => Promise<Bn254FieldElement>;
|
|
2269
|
+
/**
|
|
2270
|
+
* Generates a Curve25519 field element random factor for polynomial commitment evaluation.
|
|
2271
|
+
*
|
|
2272
|
+
* @remarks
|
|
2273
|
+
* Random factors for polynomial commitments are Curve25519 field elements used
|
|
2274
|
+
* in Kate-style commitment evaluation to hide the committed polynomial from
|
|
2275
|
+
* a verifier who knows only the commitment, not the coefficients.
|
|
2276
|
+
*
|
|
2277
|
+
* ## Algorithm
|
|
2278
|
+
*
|
|
2279
|
+
* 1. Build domain separator: `"RandomFactorForPolynomialCommitment - {totalOffset}"`
|
|
2280
|
+
* where `totalOffset = client.offsets.randomCommitmentFactor + offset`
|
|
2281
|
+
* 2. Generate 512-bit pseudorandom value using KMAC256
|
|
2282
|
+
* 3. Sample Curve25519 field element using constant-time reduction
|
|
2283
|
+
*
|
|
2284
|
+
* Pre-conditions:
|
|
2285
|
+
* - The client MUST have a valid master seed loaded.
|
|
2286
|
+
*
|
|
2287
|
+
* Post-conditions:
|
|
2288
|
+
* - Returns a Curve25519 field element uniformly distributed in [0, curve25519_order).
|
|
2289
|
+
*
|
|
2290
|
+
* @param offset - A U256 offset for unique derivation context
|
|
2291
|
+
* @returns A Promise resolving to a Curve25519 field element random factor
|
|
2292
|
+
*
|
|
2293
|
+
* @example
|
|
2294
|
+
* ```typescript
|
|
2295
|
+
* const generator = getPolynomialCommitmentFactorDeriver({ client });
|
|
2296
|
+
* const randomFactor = await generator(0n as U256);
|
|
2297
|
+
* ```
|
|
2298
|
+
*
|
|
2299
|
+
* @see {@link RescueCommitmentBlindingFactorDeriverFunction}
|
|
2300
|
+
* @public
|
|
2301
|
+
*/
|
|
2302
|
+
type PolynomialCommitmentFactorDeriverFunction = (offset: U256) => Promise<Curve25519FieldElement>;
|
|
2303
|
+
/**
|
|
2304
|
+
* Generates an ephemeral master viewing key for sender-claimable UTXOs.
|
|
2305
|
+
*
|
|
2306
|
+
* @remarks
|
|
2307
|
+
* Ephemeral UTXO master viewing keys are BN254 field elements used as the
|
|
2308
|
+
* "MVK" component of UTXOs that the sender can reclaim. Each generation index
|
|
2309
|
+
* produces an independent ephemeral MVK, ensuring that different UTXOs cannot
|
|
2310
|
+
* be linked through a shared key.
|
|
2311
|
+
*
|
|
2312
|
+
* ## Algorithm
|
|
2313
|
+
*
|
|
2314
|
+
* 1. Build domain separator: `"Umbra Privacy - Ephemeral UTXO MasterViewingKey - {offset}"`
|
|
2315
|
+
* 2. Compute `KMAC256(domainSeparator, masterSeed, dkLen=64)` with version personalization
|
|
2316
|
+
* 3. Sample BN254 field element and mask to 252 bits (consistent with main MVK)
|
|
2317
|
+
*
|
|
2318
|
+
* ## Security Properties
|
|
2319
|
+
*
|
|
2320
|
+
* - **Keyed Derivation**: Only the holder of the master seed can derive this value
|
|
2321
|
+
* - **Domain Separated**: Different offsets produce independent ephemeral MVKs
|
|
2322
|
+
* - **Deterministic**: Same client and offset always produce the same key
|
|
2323
|
+
*
|
|
2324
|
+
* @param offset - A U256 offset for unique derivation (typically the generation index)
|
|
2325
|
+
* @returns A Promise resolving to a BN254 field element for the ephemeral MVK
|
|
2326
|
+
*
|
|
2327
|
+
* @example
|
|
2328
|
+
* ```typescript
|
|
2329
|
+
* const generator = getEphemeralUtxoMasterViewingKeyDeriver({ client });
|
|
2330
|
+
* const ephemeralMvk = await generator(generationIndex);
|
|
2331
|
+
* ```
|
|
2332
|
+
*
|
|
2333
|
+
* @see {@link EphemeralUtxoMasterViewingKeyBlindingFactorDeriverFunction}
|
|
2334
|
+
* @public
|
|
2335
|
+
*/
|
|
2336
|
+
type EphemeralUtxoMasterViewingKeyDeriverFunction = (offset: U256) => Promise<Bn254FieldElement>;
|
|
2337
|
+
/**
|
|
2338
|
+
* Generates a blinding factor for the ephemeral MVK commitment.
|
|
2339
|
+
*
|
|
2340
|
+
* @remarks
|
|
2341
|
+
* Pairs with `EphemeralUtxoMasterViewingKeyDeriverFunction` to produce the
|
|
2342
|
+
* blinding factor for the Pedersen commitment to the ephemeral MVK.
|
|
2343
|
+
*
|
|
2344
|
+
* ## Algorithm
|
|
2345
|
+
*
|
|
2346
|
+
* 1. Build domain separator: `"Umbra Privacy - Ephemeral UTXO MasterViewingKeyBlindingFactor - {offset}"`
|
|
2347
|
+
* 2. Compute `KMAC256(domainSeparator, masterSeed, dkLen=64)`
|
|
2348
|
+
* 3. Sample BN254 field element using constant-time reduction
|
|
2349
|
+
*
|
|
2350
|
+
* @param offset - A U256 offset for unique derivation context (typically the generation index)
|
|
2351
|
+
* @returns A Promise resolving to a BN254 field element blinding factor
|
|
2352
|
+
*
|
|
2353
|
+
* @example
|
|
2354
|
+
* ```typescript
|
|
2355
|
+
* const generator = getEphemeralUtxoMasterViewingKeyBlindingFactorDeriver({ client });
|
|
2356
|
+
* const ephemeralMvkBf = await generator(generationIndex);
|
|
2357
|
+
* ```
|
|
2358
|
+
*
|
|
2359
|
+
* @see {@link EphemeralUtxoMasterViewingKeyDeriverFunction}
|
|
2360
|
+
* @public
|
|
2361
|
+
*/
|
|
2362
|
+
type EphemeralUtxoMasterViewingKeyBlindingFactorDeriverFunction = (offset: U256) => Promise<Bn254FieldElement>;
|
|
2363
|
+
/**
|
|
2364
|
+
* Generates an ephemeral Poseidon private key for sender-claimable UTXOs.
|
|
2365
|
+
*
|
|
2366
|
+
* @remarks
|
|
2367
|
+
* Ephemeral UTXO Poseidon private keys serve as the secret key for the
|
|
2368
|
+
* Poseidon cipher used to encrypt UTXO fields in sender-claimable UTXOs.
|
|
2369
|
+
*
|
|
2370
|
+
* ## Algorithm
|
|
2371
|
+
*
|
|
2372
|
+
* 1. Build domain separator: `"Umbra Privacy - Ephemeral UTXO PoseidonPrivateKey - {offset}"`
|
|
2373
|
+
* 2. Compute `KMAC256(domainSeparator, masterSeed, dkLen=64)`
|
|
2374
|
+
* 3. Sample BN254 field element using constant-time reduction
|
|
2375
|
+
*
|
|
2376
|
+
* @param offset - A U256 offset for unique derivation context (typically the generation index)
|
|
2377
|
+
* @returns A Promise resolving to a BN254 field element for the ephemeral Poseidon private key
|
|
2378
|
+
*
|
|
2379
|
+
* @example
|
|
2380
|
+
* ```typescript
|
|
2381
|
+
* const generator = getEphemeralUtxoPoseidonPrivateKeyDeriver({ client });
|
|
2382
|
+
* const ephemeralPk = await generator(generationIndex);
|
|
2383
|
+
* ```
|
|
2384
|
+
*
|
|
2385
|
+
* @see {@link EphemeralUtxoPoseidonPrivateKeyBlindingFactorDeriverFunction}
|
|
2386
|
+
* @public
|
|
2387
|
+
*/
|
|
2388
|
+
type EphemeralUtxoPoseidonPrivateKeyDeriverFunction = (offset: U256) => Promise<Bn254FieldElement>;
|
|
2389
|
+
/**
|
|
2390
|
+
* Generates a blinding factor for the ephemeral Poseidon private key commitment.
|
|
2391
|
+
*
|
|
2392
|
+
* @remarks
|
|
2393
|
+
* Pairs with `EphemeralUtxoPoseidonPrivateKeyDeriverFunction` to produce the
|
|
2394
|
+
* blinding factor for the Pedersen commitment to the ephemeral Poseidon private key.
|
|
2395
|
+
*
|
|
2396
|
+
* ## Algorithm
|
|
2397
|
+
*
|
|
2398
|
+
* 1. Build domain separator: `"Umbra Privacy - Ephemeral UTXO PoseidonPrivateKeyBlindingFactor - {offset}"`
|
|
2399
|
+
* 2. Compute `KMAC256(domainSeparator, masterSeed, dkLen=64)`
|
|
2400
|
+
* 3. Sample BN254 field element using constant-time reduction
|
|
2401
|
+
*
|
|
2402
|
+
* @param offset - A U256 offset for unique derivation context (typically the generation index)
|
|
2403
|
+
* @returns A Promise resolving to a BN254 field element blinding factor
|
|
2404
|
+
*
|
|
2405
|
+
* @example
|
|
2406
|
+
* ```typescript
|
|
2407
|
+
* const generator = getEphemeralUtxoPoseidonPrivateKeyBlindingFactorDeriver({ client });
|
|
2408
|
+
* const ephemeralPkBf = await generator(generationIndex);
|
|
2409
|
+
* ```
|
|
2410
|
+
*
|
|
2411
|
+
* @see {@link EphemeralUtxoPoseidonPrivateKeyDeriverFunction}
|
|
2412
|
+
* @public
|
|
2413
|
+
*/
|
|
2414
|
+
type EphemeralUtxoPoseidonPrivateKeyBlindingFactorDeriverFunction = (offset: U256) => Promise<Bn254FieldElement>;
|
|
2415
|
+
/**
|
|
2416
|
+
* Generates a nullifier for an ephemeral UTXO to prevent double-spending.
|
|
2417
|
+
*
|
|
2418
|
+
* @remarks
|
|
2419
|
+
* Nullifiers are BN254 field elements that uniquely identify a UTXO spend event.
|
|
2420
|
+
* When a UTXO is claimed, its nullifier is burned in an on-chain Indexed Merkle
|
|
2421
|
+
* Tree (treap); any subsequent claim with the same nullifier is rejected.
|
|
2422
|
+
*
|
|
2423
|
+
* ## Algorithm
|
|
2424
|
+
*
|
|
2425
|
+
* 1. Build domain separator: `"Umbra Privacy - Ephemeral UTXO Nullifier - {offset}"`
|
|
2426
|
+
* 2. Compute `KMAC256(domainSeparator, masterSeed, dkLen=64)`
|
|
2427
|
+
* 3. Sample BN254 field element using constant-time reduction
|
|
2428
|
+
*
|
|
2429
|
+
* ## Security Properties
|
|
2430
|
+
*
|
|
2431
|
+
* - **Unique per UTXO**: Each generation index produces a distinct nullifier
|
|
2432
|
+
* - **Keyed Derivation**: Only the master seed holder can compute the nullifier
|
|
2433
|
+
* - **Deterministic**: Same client and offset always produce the same nullifier
|
|
2434
|
+
*
|
|
2435
|
+
* @param offset - A U256 offset for unique derivation context (typically the generation index)
|
|
2436
|
+
* @returns A Promise resolving to a BN254 field element nullifier
|
|
2437
|
+
*
|
|
2438
|
+
* @example
|
|
2439
|
+
* ```typescript
|
|
2440
|
+
* const generator = getEphemeralUtxoNullifierDeriver({ client });
|
|
2441
|
+
* const nullifier = await generator(generationIndex);
|
|
2442
|
+
* // nullifier is submitted on-chain when claiming the UTXO
|
|
2443
|
+
* ```
|
|
2444
|
+
*
|
|
2445
|
+
* @public
|
|
2446
|
+
*/
|
|
2447
|
+
type EphemeralUtxoNullifierDeriverFunction = (offset: U256) => Promise<Bn254FieldElement>;
|
|
2448
|
+
/**
|
|
2449
|
+
* Generates an H2 random secret (blinding factor) for ephemeral UTXO creation.
|
|
2450
|
+
*
|
|
2451
|
+
* @remarks
|
|
2452
|
+
* The H2 random secret is a privacy-preserving blinding factor included in the
|
|
2453
|
+
* H2 hash of a UTXO. It prevents the verifier from learning the other UTXO
|
|
2454
|
+
* fields (amount, nullifier, commitment, destination) through the H2 commitment.
|
|
2455
|
+
*
|
|
2456
|
+
* ## Algorithm
|
|
2457
|
+
*
|
|
2458
|
+
* 1. Build domain separator: `"Umbra Privacy - Ephemeral UTXO H2RandomSecret - {offset}"`
|
|
2459
|
+
* 2. Compute `KMAC256(domainSeparator, masterSeed, dkLen=64)`
|
|
2460
|
+
* 3. Sample BN254 field element using constant-time reduction
|
|
2461
|
+
*
|
|
2462
|
+
* ## Usage in H2 Hash
|
|
2463
|
+
*
|
|
2464
|
+
* The H2 hash is computed as:
|
|
2465
|
+
* ```
|
|
2466
|
+
* H2 = Poseidon([amount, nullifier, userCommitment, destAddrLow, destAddrHigh, h2RandomSecret])
|
|
2467
|
+
* ```
|
|
2468
|
+
*
|
|
2469
|
+
* @param offset - A U256 offset for unique derivation context (typically the generation index)
|
|
2470
|
+
* @returns A Promise resolving to a BN254 field element random secret
|
|
2471
|
+
*
|
|
2472
|
+
* @example
|
|
2473
|
+
* ```typescript
|
|
2474
|
+
* const generator = getEphemeralUtxoH2RandomSecretDeriver({ client });
|
|
2475
|
+
* const h2BlindingFactor = await generator(generationIndex);
|
|
2476
|
+
* ```
|
|
2477
|
+
*
|
|
2478
|
+
* @see {@link EphemeralUtxoNullifierDeriverFunction}
|
|
2479
|
+
* @public
|
|
2480
|
+
*/
|
|
2481
|
+
type EphemeralUtxoH2RandomSecretDeriverFunction = (offset: U256) => Promise<Bn254FieldElement>;
|
|
2482
|
+
/**
|
|
2483
|
+
* Generates a blinding factor for Poseidon keystream commitments.
|
|
2484
|
+
*
|
|
2485
|
+
* @remarks
|
|
2486
|
+
* Poseidon keystream blinding factors are used to commit to individual keystream
|
|
2487
|
+
* values in Poseidon cipher operations. Binding the blinding factor to both the
|
|
2488
|
+
* keystream value and an offset ensures that each keystream position has a unique,
|
|
2489
|
+
* independently derived blinding factor.
|
|
2490
|
+
*
|
|
2491
|
+
* ## Algorithm
|
|
2492
|
+
*
|
|
2493
|
+
* 1. Build domain separator: `"Umbra Privacy - PoseidonKeystreamBlindingFactor - {offset}"`
|
|
2494
|
+
* 2. Concatenate `masterSeed` (64 bytes) || `keystream` (32 bytes, LE) || `offset` (32 bytes, LE)
|
|
2495
|
+
* 3. Compute `KMAC256(key=domainSeparator, data=concatenated, dkLen=64)`
|
|
2496
|
+
* 4. Sample BN254 field element using constant-time reduction
|
|
2497
|
+
*
|
|
2498
|
+
* ## Usage in Keystream Commitments
|
|
2499
|
+
*
|
|
2500
|
+
* ```
|
|
2501
|
+
* commitment = Poseidon([keystream, blindingFactor])
|
|
2502
|
+
* ```
|
|
2503
|
+
*
|
|
2504
|
+
* Pre-conditions:
|
|
2505
|
+
* - The client MUST have a valid master seed loaded.
|
|
2506
|
+
* - `keystream` MUST be a valid BN254 field element.
|
|
2507
|
+
*
|
|
2508
|
+
* Post-conditions:
|
|
2509
|
+
* - Different `(keystream, offset)` pairs produce independent blinding factors.
|
|
2510
|
+
*
|
|
2511
|
+
* @param keystream - The Poseidon keystream value (BN254 field element) to bind to
|
|
2512
|
+
* @param offset - A U256 offset for additional domain separation (e.g., keystream counter index)
|
|
2513
|
+
* @returns A Promise resolving to a BN254 field element blinding factor
|
|
2514
|
+
*
|
|
2515
|
+
* @example
|
|
2516
|
+
* ```typescript
|
|
2517
|
+
* const generator = getPoseidonKeystreamBlindingFactorDeriver({ client });
|
|
2518
|
+
*
|
|
2519
|
+
* // Generate blinding factors for each keystream position
|
|
2520
|
+
* const bf0 = await generator(keystream0, 0n as U256);
|
|
2521
|
+
* const bf1 = await generator(keystream1, 1n as U256);
|
|
2522
|
+
* const bf2 = await generator(keystream2, 2n as U256);
|
|
2523
|
+
* ```
|
|
2524
|
+
*
|
|
2525
|
+
* @public
|
|
2526
|
+
*/
|
|
2527
|
+
type PoseidonKeystreamBlindingFactorDeriverFunction = (keystream: Bn254FieldElement, offset: U256) => Promise<Bn254FieldElement>;
|
|
2528
|
+
/**
|
|
2529
|
+
* Generates a blinding factor for ephemeral Rescue encryption commitments in UTXO creation.
|
|
2530
|
+
*
|
|
2531
|
+
* @remarks
|
|
2532
|
+
* Ephemeral Rescue encryption commitment blinding factors bind ciphertexts,
|
|
2533
|
+
* plaintexts, keys, nonce, and public key components into a zero-knowledge
|
|
2534
|
+
* verifiable commitment. This variant uses keyed derivation (master seed as
|
|
2535
|
+
* the KMAC key) rather than the offset-combined approach of the non-ephemeral variant.
|
|
2536
|
+
*
|
|
2537
|
+
* ## Algorithm
|
|
2538
|
+
*
|
|
2539
|
+
* 1. Build domain separator:
|
|
2540
|
+
* `"Umbra Privacy - Ephemeral RescueEncryptionCommitmentBlindingFactor - {offset}"`
|
|
2541
|
+
* 2. Compute `KMAC256(domainSeparator, masterSeed, dkLen=64)` with version personalization
|
|
2542
|
+
* 3. Sample BN254 field element using constant-time reduction
|
|
2543
|
+
*
|
|
2544
|
+
* ## Security Properties
|
|
2545
|
+
*
|
|
2546
|
+
* - **Keyed Derivation**: Uses the client's master seed as the KMAC message
|
|
2547
|
+
* - **Domain Separated**: Different offsets produce independent values
|
|
2548
|
+
* - **ZK-Friendly**: Output is a valid BN254 field element for circuit use
|
|
2549
|
+
*
|
|
2550
|
+
* @param offset - A U256 offset for unique derivation context (typically `expandedModifiedGenerationIndexU256`)
|
|
2551
|
+
* @returns A Promise resolving to a BN254 field element blinding factor
|
|
2552
|
+
*
|
|
2553
|
+
* @example
|
|
2554
|
+
* ```typescript
|
|
2555
|
+
* const generator = getEphemeralRescueCommitmentBlindingFactorDeriver({ client });
|
|
2556
|
+
* const blindingFactor = await generator(expandedModifiedGenerationIndexU256);
|
|
2557
|
+
* ```
|
|
2558
|
+
*
|
|
2559
|
+
* @see {@link RescueCommitmentBlindingFactorDeriverFunction}
|
|
2560
|
+
* @public
|
|
2561
|
+
*/
|
|
2562
|
+
type EphemeralRescueCommitmentBlindingFactorDeriverFunction = (offset: U256) => Promise<Bn254FieldElement>;
|
|
2563
|
+
|
|
2564
|
+
/**
|
|
2565
|
+
* Rescue Cipher Interface Types
|
|
2566
|
+
*
|
|
2567
|
+
* This module defines the function type signatures for Rescue cipher operations
|
|
2568
|
+
* at the high-level key-exchange layer. These types abstract over the private-key
|
|
2569
|
+
* binding so that callers can work with pre-configured encryptors, decryptors,
|
|
2570
|
+
* and key generators without repeatedly supplying raw key material.
|
|
2571
|
+
*
|
|
2572
|
+
* ## Design Philosophy
|
|
2573
|
+
*
|
|
2574
|
+
* Each factory function in `./index.ts` (e.g. `getRescueEncryptorFromPrivateKey`)
|
|
2575
|
+
* accepts a private key once and returns a function matching one of the types
|
|
2576
|
+
* defined here. Callers then use the returned function repeatedly without
|
|
2577
|
+
* re-exposing the private key. This minimises the surface area over which raw
|
|
2578
|
+
* key bytes are visible in calling code.
|
|
2579
|
+
*
|
|
2580
|
+
* ## Contract for All Implementors
|
|
2581
|
+
*
|
|
2582
|
+
* Implementations of these function types **must**:
|
|
2583
|
+
* 1. Validate all typed inputs at the boundary (assert branded types).
|
|
2584
|
+
* 2. Not cache cipher instances or shared secrets internally.
|
|
2585
|
+
* 3. Create a fresh cipher instance on each invocation to limit how long
|
|
2586
|
+
* sensitive material remains in heap memory.
|
|
2587
|
+
* 4. Return values in the expected branded types with appropriate assertions.
|
|
2588
|
+
*
|
|
2589
|
+
* ## Nonce Security
|
|
2590
|
+
*
|
|
2591
|
+
* The Rescue cipher uses counter mode (CTR): the keystream is generated by
|
|
2592
|
+
* encrypting successive counter values with the derived key. The nonce
|
|
2593
|
+
* determines the initial counter state. **Nonce reuse with the same key is
|
|
2594
|
+
* catastrophic** — it allows an attacker to cancel the keystream and recover
|
|
2595
|
+
* the XOR (or field-sum) of the two plaintexts. Always use
|
|
2596
|
+
* {@link RcEncryptorFunction} (which generates random nonces) unless you have
|
|
2597
|
+
* a specific reason to manage nonces externally.
|
|
2598
|
+
*
|
|
2599
|
+
* @packageDocumentation
|
|
2600
|
+
* @module interfaces/cryptography/rescue-cipher
|
|
2601
|
+
* @public
|
|
2602
|
+
*/
|
|
2603
|
+
|
|
2604
|
+
/**
|
|
2605
|
+
* Encryption function type with automatic nonce generation.
|
|
2606
|
+
*
|
|
2607
|
+
* A pre-configured encryptor that generates a fresh random nonce for each
|
|
2608
|
+
* encryption call. The nonce is returned alongside the ciphertexts because it
|
|
2609
|
+
* is required for decryption and must be stored by the caller.
|
|
2610
|
+
*
|
|
2611
|
+
* @param plaintext - Array of {@link RcPlaintext} field elements to encrypt.
|
|
2612
|
+
* Each element must be a valid Curve25519 field element (< 2^255 - 19).
|
|
2613
|
+
* The array may contain any number of elements; each is encrypted at a
|
|
2614
|
+
* successive counter position in the Rescue CTR stream.
|
|
2615
|
+
* @returns A promise resolving to an object containing:
|
|
2616
|
+
* - `ciphertexts` — one {@link RcCiphertext} per plaintext element.
|
|
2617
|
+
* - `nonce` — the 128-bit {@link RcEncryptionNonce} used for this encryption.
|
|
2618
|
+
* Store this alongside the ciphertexts; it is public and does not need
|
|
2619
|
+
* to be kept secret, but it must be unique per (key, nonce) pair.
|
|
2620
|
+
*
|
|
2621
|
+
* @remarks
|
|
2622
|
+
* ## Implementation Contract
|
|
2623
|
+
*
|
|
2624
|
+
* - A **fresh cryptographically random nonce** is generated for each call.
|
|
2625
|
+
* - The returned `ciphertexts` array has the same length as `plaintext`.
|
|
2626
|
+
* - All returned values are valid branded types (asserted before return).
|
|
2627
|
+
* - No cipher instance, shared secret, or nonce is retained after the
|
|
2628
|
+
* call completes.
|
|
2629
|
+
*
|
|
2630
|
+
* ## Nonce Properties
|
|
2631
|
+
*
|
|
2632
|
+
* Nonces generated by the default implementation are 128-bit values from
|
|
2633
|
+
* `generateRandomNonce()` in `../../common/arcium`. With 128-bit nonces,
|
|
2634
|
+
* the birthday-bound collision probability over N calls is approximately
|
|
2635
|
+
* N²/2^128, which is negligible for any practical N.
|
|
2636
|
+
*
|
|
2637
|
+
* @public
|
|
2638
|
+
*
|
|
2639
|
+
* @example
|
|
2640
|
+
* ```typescript
|
|
2641
|
+
* const encryptor: RcEncryptorFunction = getRescueEncryptorFromPrivateKey(
|
|
2642
|
+
* myPrivateKey,
|
|
2643
|
+
* { umbraX25519PublicKey: networkConfig.mxePubkey },
|
|
2644
|
+
* );
|
|
2645
|
+
*
|
|
2646
|
+
* const plaintexts: RcPlaintext[] = [balance, auxiliaryData];
|
|
2647
|
+
* const { ciphertexts, nonce } = await encryptor(plaintexts);
|
|
2648
|
+
*
|
|
2649
|
+
* // Both ciphertexts and nonce must be stored for later decryption:
|
|
2650
|
+
* await persistEncryptedBalance({ ciphertexts, nonce });
|
|
2651
|
+
* ```
|
|
2652
|
+
*
|
|
2653
|
+
* @see {@link RcEncryptorWithNonceFunction} — variant requiring explicit nonce
|
|
2654
|
+
* @see {@link getRescueEncryptorFromPrivateKey} — factory that produces this function type
|
|
2655
|
+
*/
|
|
2656
|
+
type RcEncryptorFunction = (plaintext: readonly RcPlaintext[]) => Promise<{
|
|
2657
|
+
ciphertexts: RcCiphertext[];
|
|
2658
|
+
nonce: RcEncryptionNonce;
|
|
2659
|
+
}>;
|
|
2660
|
+
/**
|
|
2661
|
+
* Encryption function type with explicit nonce parameter.
|
|
2662
|
+
*
|
|
2663
|
+
* A pre-configured encryptor where the caller supplies the nonce on each call.
|
|
2664
|
+
* This variant is appropriate when nonces are managed externally (e.g. as a
|
|
2665
|
+
* counter maintained by the caller) or when deterministic encryption is required
|
|
2666
|
+
* (e.g. for generating test vectors with a fixed nonce).
|
|
2667
|
+
*
|
|
2668
|
+
* @param plaintext - Array of {@link RcPlaintext} field elements to encrypt.
|
|
2669
|
+
* Each element must be a valid Curve25519 field element (< 2^255 - 19).
|
|
2670
|
+
* @param nonce - The 128-bit nonce to use for this encryption. Must be a valid
|
|
2671
|
+
* {@link RcEncryptionNonce} (bigint in [0, 2^128 - 1]).
|
|
2672
|
+
* **CRITICAL**: This nonce MUST be unique for each encryption call with the
|
|
2673
|
+
* same underlying key. See the nonce security note in the module documentation.
|
|
2674
|
+
* @returns A promise resolving to an array of {@link RcCiphertext} values,
|
|
2675
|
+
* one per input plaintext element.
|
|
2676
|
+
*
|
|
2677
|
+
* @remarks
|
|
2678
|
+
* ## Implementation Contract
|
|
2679
|
+
*
|
|
2680
|
+
* - `nonce` is validated via {@link assertRcEncryptionNonce} before use.
|
|
2681
|
+
* - The returned array has the same length as `plaintext`.
|
|
2682
|
+
* - All returned values are valid {@link RcCiphertext} branded types.
|
|
2683
|
+
* - No state is retained after the call completes.
|
|
2684
|
+
*
|
|
2685
|
+
* ## When to Prefer This Over `RcEncryptorFunction`
|
|
2686
|
+
*
|
|
2687
|
+
* - When the same nonce must be used across multiple related encryption calls
|
|
2688
|
+
* (e.g. encrypting a batch of UTXOs that share a nonce for on-chain space
|
|
2689
|
+
* efficiency).
|
|
2690
|
+
* - When reproducing exact ciphertexts from a known (plaintext, nonce, key)
|
|
2691
|
+
* triple for testing or recovery purposes.
|
|
2692
|
+
* - When the nonce is derived from a protocol-level counter rather than random
|
|
2693
|
+
* sampling (e.g. the transaction sequence number).
|
|
2694
|
+
*
|
|
2695
|
+
* @public
|
|
2696
|
+
*
|
|
2697
|
+
* @example
|
|
2698
|
+
* ```typescript
|
|
2699
|
+
* const encryptor: RcEncryptorWithNonceFunction = getRescueEncryptorWithNonceFromPrivateKey(
|
|
2700
|
+
* myPrivateKey,
|
|
2701
|
+
* { umbraX25519PublicKey: networkConfig.mxePubkey },
|
|
2702
|
+
* );
|
|
2703
|
+
*
|
|
2704
|
+
* // Caller-managed nonce (must be unique per key):
|
|
2705
|
+
* const nonce = generateRandomNonce();
|
|
2706
|
+
* const plaintexts: RcPlaintext[] = [balance];
|
|
2707
|
+
* const ciphertexts = await encryptor(plaintexts, nonce);
|
|
2708
|
+
* ```
|
|
2709
|
+
*
|
|
2710
|
+
* @example
|
|
2711
|
+
* ```typescript
|
|
2712
|
+
* // Deterministic test encryption (NEVER reuse in production with different plaintexts):
|
|
2713
|
+
* const fixedNonce = 1n as RcEncryptionNonce;
|
|
2714
|
+
* const ciphertexts = await encryptor(plaintexts, fixedNonce);
|
|
2715
|
+
* ```
|
|
2716
|
+
*
|
|
2717
|
+
* @see {@link RcEncryptorFunction} — preferred variant with automatic nonce generation
|
|
2718
|
+
* @see {@link getRescueEncryptorWithNonceFromPrivateKey} — factory that produces this function type
|
|
2719
|
+
*/
|
|
2720
|
+
type RcEncryptorWithNonceFunction = (plaintext: readonly RcPlaintext[], nonce: RcEncryptionNonce) => Promise<RcCiphertext[]>;
|
|
2721
|
+
/**
|
|
2722
|
+
* Decryption function type using a nonce.
|
|
2723
|
+
*
|
|
2724
|
+
* A pre-configured decryptor that recovers the original plaintext elements from
|
|
2725
|
+
* Rescue ciphertexts using the same nonce that was used during encryption.
|
|
2726
|
+
*
|
|
2727
|
+
* @param ciphertext - Array of {@link RcCiphertext} values to decrypt.
|
|
2728
|
+
* Must be the ciphertexts produced by a matching encryptor with the same
|
|
2729
|
+
* private key and public key. Each element must be a valid Curve25519 field
|
|
2730
|
+
* element.
|
|
2731
|
+
* @param nonce - The {@link RcEncryptionNonce} used during encryption.
|
|
2732
|
+
* Must exactly match the nonce stored alongside the ciphertexts; using a
|
|
2733
|
+
* different nonce will silently produce incorrect (garbage) plaintexts.
|
|
2734
|
+
* @returns A promise resolving to an array of {@link RcPlaintext} values,
|
|
2735
|
+
* one per input ciphertext element.
|
|
2736
|
+
*
|
|
2737
|
+
* @remarks
|
|
2738
|
+
* ## Implementation Contract
|
|
2739
|
+
*
|
|
2740
|
+
* - All `ciphertext` elements are validated via {@link assertRcCiphertext} before use.
|
|
2741
|
+
* - `nonce` is validated via {@link assertRcEncryptionNonce} before use.
|
|
2742
|
+
* - The returned array has the same length as `ciphertext`.
|
|
2743
|
+
* - All returned values are valid {@link RcPlaintext} branded types (asserted).
|
|
2744
|
+
* - No state is retained after the call completes.
|
|
2745
|
+
*
|
|
2746
|
+
* ## No Authentication
|
|
2747
|
+
*
|
|
2748
|
+
* Unlike AES-256-GCM, the Rescue cipher in CTR mode does not include an
|
|
2749
|
+
* authentication tag. This function will **not** detect:
|
|
2750
|
+
* - A wrong nonce (will silently return garbage).
|
|
2751
|
+
* - Tampered ciphertexts (will silently return incorrect plaintexts).
|
|
2752
|
+
*
|
|
2753
|
+
* Authentication is provided at the protocol level by the Arcium MPC computation
|
|
2754
|
+
* signature. For client-side usage, callers should cross-validate the decrypted
|
|
2755
|
+
* values against the on-chain commitments (e.g. Poseidon hash of the balance).
|
|
2756
|
+
*
|
|
2757
|
+
* @public
|
|
2758
|
+
*
|
|
2759
|
+
* @example
|
|
2760
|
+
* ```typescript
|
|
2761
|
+
* const decryptor: RcDecryptorFunction = getRescueDecryptorFromPrivateKey(
|
|
2762
|
+
* myPrivateKey,
|
|
2763
|
+
* { umbraX25519PublicKey: networkConfig.mxePubkey },
|
|
2764
|
+
* );
|
|
2765
|
+
*
|
|
2766
|
+
* // Load stored ciphertexts and the nonce used during encryption:
|
|
2767
|
+
* const { ciphertexts, nonce } = await loadEncryptedBalance(accountId);
|
|
2768
|
+
* const plaintexts = await decryptor(ciphertexts, nonce);
|
|
2769
|
+
* const [balance] = plaintexts;
|
|
2770
|
+
* ```
|
|
2771
|
+
*
|
|
2772
|
+
* @see {@link RcEncryptorFunction} — paired encryption function type
|
|
2773
|
+
* @see {@link getRescueDecryptorFromPrivateKey} — factory that produces this function type
|
|
2774
|
+
*/
|
|
2775
|
+
type RcDecryptorFunction = (ciphertext: readonly RcCiphertext[], nonce: RcEncryptionNonce) => Promise<RcPlaintext[]>;
|
|
2776
|
+
/**
|
|
2777
|
+
* Key generation function type for counter-based encryption schemes.
|
|
2778
|
+
*
|
|
2779
|
+
* Generates symmetric encryption keys for specified counter positions by
|
|
2780
|
+
* encrypting zero-value plaintexts at those positions. The resulting keys can
|
|
2781
|
+
* be used as independent per-counter symmetric keys in protocols that require
|
|
2782
|
+
* key separation between different token account positions.
|
|
2783
|
+
*
|
|
2784
|
+
* @param counters - Array of counter values identifying the key positions to
|
|
2785
|
+
* generate. Each must be a valid {@link RcCounter} (non-negative Curve25519
|
|
2786
|
+
* field element). Duplicates are allowed; the map will contain a single entry
|
|
2787
|
+
* per unique counter value.
|
|
2788
|
+
* @param nonce - The 128-bit {@link RcEncryptionNonce} to use for the internal
|
|
2789
|
+
* encryption that generates the keys. Must be unique per (key, nonce) pair.
|
|
2790
|
+
* @returns A promise resolving to a `ReadonlyMap<RcCounter, RcKey>` mapping
|
|
2791
|
+
* each requested counter to its derived {@link RcKey}. Counters not present
|
|
2792
|
+
* in the `counters` input are not included in the map.
|
|
2793
|
+
*
|
|
2794
|
+
* @remarks
|
|
2795
|
+
* ## Key Generation Algorithm
|
|
2796
|
+
*
|
|
2797
|
+
* The key for counter `c` is defined as:
|
|
2798
|
+
* ```
|
|
2799
|
+
* key[c] = Rescue-CTR-Encrypt(key=sharedSecret, nonce=nonce, plaintext=0n)[c]
|
|
2800
|
+
* ```
|
|
2801
|
+
*
|
|
2802
|
+
* That is, a zero-value plaintext is placed at each position from 0 to
|
|
2803
|
+
* `max(counters)` and all positions are encrypted in a single CTR invocation.
|
|
2804
|
+
* Only the positions in `counters` are returned.
|
|
2805
|
+
*
|
|
2806
|
+
* ## Security Properties
|
|
2807
|
+
*
|
|
2808
|
+
* - Keys generated with the same (private key, public key, nonce) are deterministic.
|
|
2809
|
+
* - Keys generated at different counter positions are computationally independent
|
|
2810
|
+
* (due to the CTR mode keystream).
|
|
2811
|
+
* - The nonce must be unique per key-generation session; reusing the nonce with
|
|
2812
|
+
* the same key pair produces the same keys (which may be intentional for
|
|
2813
|
+
* deterministic recovery, but is a security risk if used with different plaintexts).
|
|
2814
|
+
*
|
|
2815
|
+
* ## Implementation Notes
|
|
2816
|
+
*
|
|
2817
|
+
* - Counter values are compared as `bigint` to avoid precision loss.
|
|
2818
|
+
* - The function internally allocates `max(counters) + 1` zero plaintexts, so
|
|
2819
|
+
* very large sparse counter sets (e.g. `[0n, 2^128]`) would be impractical.
|
|
2820
|
+
* Callers should use small, dense counter ranges.
|
|
2821
|
+
*
|
|
2822
|
+
* ## Use Cases
|
|
2823
|
+
*
|
|
2824
|
+
* - Pre-computing AES-equivalent stream-cipher keys for batch operations.
|
|
2825
|
+
* - Deriving per-UTXO keys for counter-mode encryption schemes.
|
|
2826
|
+
* - Key derivation for specific token account generation indices.
|
|
2827
|
+
*
|
|
2828
|
+
* @public
|
|
2829
|
+
*
|
|
2830
|
+
* @example
|
|
2831
|
+
* ```typescript
|
|
2832
|
+
* const keyGenerator: RcKeyGeneratorFunction = getRescueKeyGeneratorFromPrivateKey(
|
|
2833
|
+
* myPrivateKey,
|
|
2834
|
+
* { umbraX25519PublicKey: networkConfig.mxePubkey },
|
|
2835
|
+
* );
|
|
2836
|
+
*
|
|
2837
|
+
* const counters: RcCounter[] = [0n, 1n, 5n];
|
|
2838
|
+
* const nonce = generateRandomNonce();
|
|
2839
|
+
* const keysMap = await keyGenerator(counters, nonce);
|
|
2840
|
+
*
|
|
2841
|
+
* const key0 = keysMap.get(0n); // RcKey — key for counter 0
|
|
2842
|
+
* const key5 = keysMap.get(5n); // RcKey — key for counter 5
|
|
2843
|
+
* // keysMap.get(2n) === undefined — counter 2 was not requested
|
|
2844
|
+
* ```
|
|
2845
|
+
*
|
|
2846
|
+
* @see {@link RcEncryptorWithNonceFunction} — used internally to generate the keys
|
|
2847
|
+
* @see {@link getRescueKeyGeneratorFromPrivateKey} — factory that produces this function type
|
|
2848
|
+
*/
|
|
2849
|
+
type RcKeyGeneratorFunction = (counters: readonly RcCounter[], nonce: RcEncryptionNonce) => Promise<ReadonlyMap<RcCounter, RcKey>>;
|
|
2850
|
+
/**
|
|
2851
|
+
* Rescue Cipher Internal Interfaces
|
|
2852
|
+
*
|
|
2853
|
+
* Type definitions and dependency injection interfaces for the Rescue-XLIX
|
|
2854
|
+
* cipher and Rescue-Prime hash function. All types in this module are internal
|
|
2855
|
+
* to the `rescue-cipher` package; consumers interact only with the public API
|
|
2856
|
+
* exported from `index.ts`.
|
|
2857
|
+
*
|
|
2858
|
+
* ## Design Rationale
|
|
2859
|
+
*
|
|
2860
|
+
* The interfaces in this file serve two purposes:
|
|
2861
|
+
*
|
|
2862
|
+
* - **Abstraction**: `FieldArithmetic` decouples the cipher and hash logic from
|
|
2863
|
+
* any specific field implementation. The default production path uses the
|
|
2864
|
+
* SDK's `math/curve25519/field-arithmetic` module, but tests can substitute
|
|
2865
|
+
* alternative implementations (e.g., slow-but-correct reference versions).
|
|
2866
|
+
*
|
|
2867
|
+
* - **Compatibility**: `RescueCipherInstance` mirrors the shape of
|
|
2868
|
+
* `@arcium-hq/client`'s `RescueCipher` class, allowing the SDK to drop in
|
|
2869
|
+
* its own implementation without breaking callers that depend on that shape.
|
|
2870
|
+
*
|
|
2871
|
+
* ## Dependency Injection
|
|
2872
|
+
*
|
|
2873
|
+
* `RescueCipherDeps` and `RescuePrimeHashDeps` follow the SDK-wide convention
|
|
2874
|
+
* of optional `*Deps` parameters on factory functions. Omitting them selects
|
|
2875
|
+
* the production defaults. Providing them bypasses module-level caches, so
|
|
2876
|
+
* tests can exercise the pure functional logic in isolation.
|
|
2877
|
+
*
|
|
2878
|
+
* @packageDocumentation
|
|
2879
|
+
* @module crypto/rescue-cipher/interfaces
|
|
2880
|
+
* @internal
|
|
2881
|
+
*/
|
|
2882
|
+
/**
|
|
2883
|
+
* A stateful Rescue-XLIX cipher instance capable of encrypting and decrypting
|
|
2884
|
+
* vectors of field elements in CTR (counter) mode.
|
|
2885
|
+
*
|
|
2886
|
+
* @remarks
|
|
2887
|
+
* This interface is intentionally designed to be structurally compatible with
|
|
2888
|
+
* the `RescueCipher` class from `@arcium-hq/client`, enabling the SDK to serve
|
|
2889
|
+
* as a drop-in replacement without any upstream changes to callers.
|
|
2890
|
+
*
|
|
2891
|
+
* ## CTR Mode Overview
|
|
2892
|
+
*
|
|
2893
|
+
* Counter mode turns the Rescue-XLIX block cipher into a stream cipher:
|
|
2894
|
+
*
|
|
2895
|
+
* 1. A counter block `[nonce, blockIndex, 0, 0, 0]` is assembled for each
|
|
2896
|
+
* block of `BLOCK_SIZE = 5` plaintext elements.
|
|
2897
|
+
* 2. The Rescue permutation is applied to the counter block to produce a
|
|
2898
|
+
* keystream block.
|
|
2899
|
+
* 3. Each plaintext element is added (modulo p) to the corresponding keystream
|
|
2900
|
+
* element to produce ciphertext.
|
|
2901
|
+
*
|
|
2902
|
+
* Decryption is identical to encryption because field addition is its own
|
|
2903
|
+
* inverse: `c - k ≡ p + k - k ≡ p` when the same keystream is generated.
|
|
2904
|
+
*
|
|
2905
|
+
* ## Key Material
|
|
2906
|
+
*
|
|
2907
|
+
* The cipher instance embeds a key schedule derived from the shared secret at
|
|
2908
|
+
* construction time (see `createRescueCipherInstance` in `index.ts`). The key
|
|
2909
|
+
* schedule consists of the intermediate states produced by running the Rescue
|
|
2910
|
+
* permutation on the derived key, used as round keys.
|
|
2911
|
+
*
|
|
2912
|
+
* ## Security Notes
|
|
2913
|
+
*
|
|
2914
|
+
* - Nonces MUST be unique for every (key, nonce) pair. Nonce reuse under the
|
|
2915
|
+
* same key results in complete loss of confidentiality (two-time-pad attack).
|
|
2916
|
+
* - Cipher instances are not cached; each call to `getRescueCipherInstance`
|
|
2917
|
+
* creates a fresh instance to limit key material lifetime in memory.
|
|
2918
|
+
*
|
|
2919
|
+
* @example
|
|
2920
|
+
* ```typescript
|
|
2921
|
+
* import { getRescueCipherInstance } from "./index";
|
|
2922
|
+
*
|
|
2923
|
+
* const sharedSecret = new Uint8Array(32).fill(0xab); // from X25519 ECDH
|
|
2924
|
+
* const nonce = crypto.getRandomValues(new Uint8Array(16));
|
|
2925
|
+
*
|
|
2926
|
+
* const cipher = getRescueCipherInstance(sharedSecret);
|
|
2927
|
+
*
|
|
2928
|
+
* // Encrypt a vector of field elements
|
|
2929
|
+
* const plaintext: bigint[] = [1n, 2n, 3n, 4n, 5n];
|
|
2930
|
+
* const ciphertext = cipher.encrypt(plaintext, nonce);
|
|
2931
|
+
* // ciphertext: number[][], each inner array is 32 bytes (LE field element)
|
|
2932
|
+
*
|
|
2933
|
+
* // Decrypt back
|
|
2934
|
+
* const recovered = cipher.decrypt(ciphertext, nonce);
|
|
2935
|
+
* // recovered: bigint[] === plaintext
|
|
2936
|
+
* ```
|
|
2937
|
+
*
|
|
2938
|
+
* @see {@link RescueCipherDeps} — dependency injection for custom field arithmetic and KDF
|
|
2939
|
+
* @see `getRescueCipherInstance` in `index.ts` — factory function that returns this instance
|
|
2940
|
+
*
|
|
2941
|
+
* @internal
|
|
2942
|
+
*/
|
|
2943
|
+
interface RescueCipherInstance {
|
|
2944
|
+
/**
|
|
2945
|
+
* Encrypts an array of field elements using Rescue-XLIX in CTR mode.
|
|
2946
|
+
*
|
|
2947
|
+
* @remarks
|
|
2948
|
+
* Plaintext elements must be valid field elements: `0 <= x < p`.
|
|
2949
|
+
* The nonce must be exactly `NONCE_BYTES = 16` bytes.
|
|
2950
|
+
*
|
|
2951
|
+
* The returned ciphertext is a parallel array of 32-byte little-endian
|
|
2952
|
+
* serialisations of the encrypted field elements. The length of the returned
|
|
2953
|
+
* array always equals `plaintext.length`.
|
|
2954
|
+
*
|
|
2955
|
+
* @param plaintext - Array of bigint field elements to encrypt; each must
|
|
2956
|
+
* satisfy `0 <= x < FIELD_PRIME`
|
|
2957
|
+
* @param nonce - 16-byte unique nonce; must not be reused under the same key
|
|
2958
|
+
* @returns Array of `number[]` where each inner array is a 32-byte
|
|
2959
|
+
* little-endian encoding of one ciphertext field element
|
|
2960
|
+
*
|
|
2961
|
+
* @throws `Error` if `nonce.length !== 16`
|
|
2962
|
+
*
|
|
2963
|
+
* @example
|
|
2964
|
+
* ```typescript
|
|
2965
|
+
* const nonce = crypto.getRandomValues(new Uint8Array(16));
|
|
2966
|
+
* const plain = [0n, 1n, 2n];
|
|
2967
|
+
* const cipher = getRescueCipherInstance(sharedSecret);
|
|
2968
|
+
* const enc = cipher.encrypt(plain, nonce);
|
|
2969
|
+
* // enc.length === 3, enc[0].length === 32
|
|
2970
|
+
* ```
|
|
2971
|
+
*/
|
|
2972
|
+
encrypt: (plaintext: bigint[], nonce: Uint8Array) => number[][];
|
|
2973
|
+
/**
|
|
2974
|
+
* Decrypts an array of 32-byte ciphertext elements using Rescue-XLIX in CTR mode.
|
|
2975
|
+
*
|
|
2976
|
+
* @remarks
|
|
2977
|
+
* The nonce must be the same 16-byte value that was used during encryption.
|
|
2978
|
+
* Each element of `ciphertext` must be exactly 32 bytes (one serialised
|
|
2979
|
+
* field element in little-endian byte order).
|
|
2980
|
+
*
|
|
2981
|
+
* Decryption is structurally identical to encryption (CTR mode symmetry):
|
|
2982
|
+
* the same keystream is generated and subtracted (mod p) from each
|
|
2983
|
+
* ciphertext element.
|
|
2984
|
+
*
|
|
2985
|
+
* @param ciphertext - Array of `number[]`; each inner array must be exactly
|
|
2986
|
+
* 32 bytes representing one little-endian field element
|
|
2987
|
+
* @param nonce - The same 16-byte nonce used during encryption
|
|
2988
|
+
* @returns Array of bigint plaintext field elements
|
|
2989
|
+
*
|
|
2990
|
+
* @throws `Error` if `nonce.length !== 16`
|
|
2991
|
+
* @throws `Error` if any element of `ciphertext` does not have length 32
|
|
2992
|
+
*
|
|
2993
|
+
* @example
|
|
2994
|
+
* ```typescript
|
|
2995
|
+
* const plain = [10n, 20n, 30n];
|
|
2996
|
+
* const nonce = crypto.getRandomValues(new Uint8Array(16));
|
|
2997
|
+
* const enc = cipher.encrypt(plain, nonce);
|
|
2998
|
+
* const dec = cipher.decrypt(enc, nonce);
|
|
2999
|
+
* // dec deepEquals plain
|
|
3000
|
+
* ```
|
|
3001
|
+
*/
|
|
3002
|
+
decrypt: (ciphertext: number[][], nonce: Uint8Array) => bigint[];
|
|
3003
|
+
}
|
|
3004
|
+
/**
|
|
3005
|
+
* Unified interface for finite field arithmetic over GF(p).
|
|
3006
|
+
*
|
|
3007
|
+
* @remarks
|
|
3008
|
+
* This interface abstracts all modular arithmetic needed by the Rescue-XLIX
|
|
3009
|
+
* permutation so that the cipher and hash implementations are independent of
|
|
3010
|
+
* the specific underlying library. The production default binds this to the
|
|
3011
|
+
* SDK's `math/curve25519/field-arithmetic` module, which targets the prime
|
|
3012
|
+
* `p = 2^255 - 19`.
|
|
3013
|
+
*
|
|
3014
|
+
* Implementations must enforce that all returned values are in the canonical
|
|
3015
|
+
* reduced range `[0, ORDER)`. In particular:
|
|
3016
|
+
*
|
|
3017
|
+
* - `create` must reduce any `bigint` into the field.
|
|
3018
|
+
* - `add`, `sub`, `mul`, `pow` must all return values in `[0, ORDER)`.
|
|
3019
|
+
* - `inv(0)` is undefined and implementations may throw or return 0.
|
|
3020
|
+
*
|
|
3021
|
+
* ## Compatibility
|
|
3022
|
+
*
|
|
3023
|
+
* The interface is intentionally aligned with the field accessor objects
|
|
3024
|
+
* exposed by `@noble/curves` (which uses `ORDER`, `BYTES`, `ZERO`, `ONE`,
|
|
3025
|
+
* `create`, `add`, `sub`, `mul`, `inv`, `pow`, `is0`), so that a `@noble`
|
|
3026
|
+
* field can be adapted to this interface with minimal glue code.
|
|
3027
|
+
*
|
|
3028
|
+
* @example
|
|
3029
|
+
* ```typescript
|
|
3030
|
+
* import { getFieldArithmetic } from "./index";
|
|
3031
|
+
*
|
|
3032
|
+
* const F = getFieldArithmetic();
|
|
3033
|
+
* const a = F.create(12345678901234567890n);
|
|
3034
|
+
* const b = F.create(98765432109876543210n);
|
|
3035
|
+
*
|
|
3036
|
+
* const sum = F.add(a, b); // (a + b) mod p
|
|
3037
|
+
* const prod = F.mul(a, b); // (a * b) mod p
|
|
3038
|
+
* const inv = F.inv(a); // a^(-1) mod p
|
|
3039
|
+
* const pow5 = F.pow(a, 5n); // a^5 mod p
|
|
3040
|
+
* console.log(F.is0(F.sub(a, a))); // true
|
|
3041
|
+
* ```
|
|
3042
|
+
*
|
|
3043
|
+
* @see {@link RescueCipherDeps} — inject a custom `FieldArithmetic` into the cipher
|
|
3044
|
+
* @see {@link RescuePrimeHashDeps} — inject a custom `FieldArithmetic` into the hash
|
|
3045
|
+
* @see `getFieldArithmetic` in `index.ts` — the default production implementation
|
|
3046
|
+
*
|
|
3047
|
+
* @internal
|
|
3048
|
+
*/
|
|
3049
|
+
interface FieldArithmetic {
|
|
3050
|
+
/**
|
|
3051
|
+
* The field modulus (prime characteristic p).
|
|
3052
|
+
*
|
|
3053
|
+
* @remarks
|
|
3054
|
+
* For the production implementation this equals `FIELD_PRIME = 2^255 - 19`.
|
|
3055
|
+
* All arithmetic results are in the range `[0, ORDER)`.
|
|
3056
|
+
*
|
|
3057
|
+
* @readonly
|
|
3058
|
+
*/
|
|
3059
|
+
readonly ORDER: bigint;
|
|
3060
|
+
/**
|
|
3061
|
+
* Number of bytes required to represent one field element.
|
|
3062
|
+
*
|
|
3063
|
+
* @remarks
|
|
3064
|
+
* For `p = 2^255 - 19`, this is 32 bytes (255 bits rounded up to the
|
|
3065
|
+
* nearest byte boundary).
|
|
3066
|
+
*
|
|
3067
|
+
* @readonly
|
|
3068
|
+
*/
|
|
3069
|
+
readonly BYTES: number;
|
|
3070
|
+
/**
|
|
3071
|
+
* Additive identity: 0 in GF(p).
|
|
3072
|
+
*
|
|
3073
|
+
* @readonly
|
|
3074
|
+
*/
|
|
3075
|
+
readonly ZERO: bigint;
|
|
3076
|
+
/**
|
|
3077
|
+
* Multiplicative identity: 1 in GF(p).
|
|
3078
|
+
*
|
|
3079
|
+
* @readonly
|
|
3080
|
+
*/
|
|
3081
|
+
readonly ONE: bigint;
|
|
3082
|
+
/**
|
|
3083
|
+
* Reduces an arbitrary bigint into the canonical field range `[0, ORDER)`.
|
|
3084
|
+
*
|
|
3085
|
+
* @param value - Any bigint, possibly negative or >= ORDER
|
|
3086
|
+
* @returns `((value % ORDER) + ORDER) % ORDER`
|
|
3087
|
+
*/
|
|
3088
|
+
create: (value: bigint) => bigint;
|
|
3089
|
+
/**
|
|
3090
|
+
* Field addition: `(a + b) mod p`.
|
|
3091
|
+
*
|
|
3092
|
+
* @param a - First operand; must be in `[0, ORDER)`
|
|
3093
|
+
* @param b - Second operand; must be in `[0, ORDER)`
|
|
3094
|
+
* @returns `(a + b) mod ORDER`, in `[0, ORDER)`
|
|
3095
|
+
*/
|
|
3096
|
+
add: (a: bigint, b: bigint) => bigint;
|
|
3097
|
+
/**
|
|
3098
|
+
* Field subtraction: `(a - b) mod p`.
|
|
3099
|
+
*
|
|
3100
|
+
* @param a - Minuend; must be in `[0, ORDER)`
|
|
3101
|
+
* @param b - Subtrahend; must be in `[0, ORDER)`
|
|
3102
|
+
* @returns `(a - b + ORDER) mod ORDER`, in `[0, ORDER)`
|
|
3103
|
+
*/
|
|
3104
|
+
sub: (a: bigint, b: bigint) => bigint;
|
|
3105
|
+
/**
|
|
3106
|
+
* Field multiplication: `(a * b) mod p`.
|
|
3107
|
+
*
|
|
3108
|
+
* @param a - First factor; must be in `[0, ORDER)`
|
|
3109
|
+
* @param b - Second factor; must be in `[0, ORDER)`
|
|
3110
|
+
* @returns `(a * b) mod ORDER`, in `[0, ORDER)`
|
|
3111
|
+
*/
|
|
3112
|
+
mul: (a: bigint, b: bigint) => bigint;
|
|
3113
|
+
/**
|
|
3114
|
+
* Modular inverse: `a^(-1) mod p`.
|
|
3115
|
+
*
|
|
3116
|
+
* @remarks
|
|
3117
|
+
* Typically computed via Fermat's little theorem as `a^(p-2) mod p`,
|
|
3118
|
+
* which requires a full modular exponentiation. The result satisfies
|
|
3119
|
+
* `mul(a, inv(a)) === ONE`.
|
|
3120
|
+
*
|
|
3121
|
+
* Behaviour for `a === 0` is undefined. The production implementation may
|
|
3122
|
+
* return `0n` or throw.
|
|
3123
|
+
*
|
|
3124
|
+
* @param a - Operand; must be in `(0, ORDER)`
|
|
3125
|
+
* @returns `a^(-1) mod ORDER`, in `[0, ORDER)`
|
|
3126
|
+
*/
|
|
3127
|
+
inv: (a: bigint) => bigint;
|
|
3128
|
+
/**
|
|
3129
|
+
* Modular exponentiation: `a^exponent mod p`.
|
|
3130
|
+
*
|
|
3131
|
+
* @remarks
|
|
3132
|
+
* Used for both the forward S-box (`x^ALPHA`) and the inverse S-box
|
|
3133
|
+
* (`x^ALPHA_INVERSE`). The exponent may be up to 255 bits long.
|
|
3134
|
+
*
|
|
3135
|
+
* @param a - Base; must be in `[0, ORDER)`
|
|
3136
|
+
* @param exponent - Non-negative exponent
|
|
3137
|
+
* @returns `a^exponent mod ORDER`, in `[0, ORDER)`
|
|
3138
|
+
*/
|
|
3139
|
+
pow: (a: bigint, exponent: bigint) => bigint;
|
|
3140
|
+
/**
|
|
3141
|
+
* Tests whether a field element is the additive identity (zero).
|
|
3142
|
+
*
|
|
3143
|
+
* @param a - Field element to test; must be in `[0, ORDER)`
|
|
3144
|
+
* @returns `true` if and only if `a === 0n`
|
|
3145
|
+
*/
|
|
3146
|
+
is0: (a: bigint) => boolean;
|
|
3147
|
+
}
|
|
3148
|
+
/**
|
|
3149
|
+
* A function that applies the Rescue-XLIX permutation to a state vector in place.
|
|
3150
|
+
*
|
|
3151
|
+
* @remarks
|
|
3152
|
+
* The Rescue-XLIX permutation maps a vector of `m` field elements to another
|
|
3153
|
+
* vector of `m` field elements. It is a bijection (invertible map) used as the
|
|
3154
|
+
* underlying primitive for both the cipher (in a key-dependent manner) and the
|
|
3155
|
+
* hash sponge (in a key-independent manner).
|
|
3156
|
+
*
|
|
3157
|
+
* The state is passed as a flat `bigint[]` and the permuted state is returned
|
|
3158
|
+
* as a new flat `bigint[]` of the same length. The original array is not mutated.
|
|
3159
|
+
*
|
|
3160
|
+
* In cipher mode: `m = BLOCK_SIZE = 5`, round keys are embedded in the closure.
|
|
3161
|
+
* In hash mode: `m = HASH_STATE_SIZE = 12`, round constants are public and fixed.
|
|
3162
|
+
*
|
|
3163
|
+
* @example
|
|
3164
|
+
* ```typescript
|
|
3165
|
+
* // Hash-mode permutation (internal usage)
|
|
3166
|
+
* const permute: RescuePermuteFunction = createHashPermutationFunction(fieldArithmetic);
|
|
3167
|
+
* const inputState = Array(12).fill(0n);
|
|
3168
|
+
* const outputState = permute(inputState);
|
|
3169
|
+
* // outputState.length === 12
|
|
3170
|
+
* ```
|
|
3171
|
+
*
|
|
3172
|
+
* @see `createHashPermutationFunction` in `index.ts` — hash-mode factory
|
|
3173
|
+
* @see `createRescueCipherInstance` in `index.ts` — cipher-mode factory (key-dependent)
|
|
3174
|
+
*
|
|
3175
|
+
* @internal
|
|
3176
|
+
*/
|
|
3177
|
+
type RescuePermuteFunction = (state: bigint[]) => bigint[];
|
|
3178
|
+
/**
|
|
3179
|
+
* A function that computes the Rescue-Prime hash digest of a message.
|
|
3180
|
+
*
|
|
3181
|
+
* @remarks
|
|
3182
|
+
* Rescue-Prime is a sponge hash built on the Rescue-XLIX permutation. The
|
|
3183
|
+
* function accepts a message of arbitrary length (as field elements), applies
|
|
3184
|
+
* multi-rate padding, absorbs blocks of `HASH_RATE = 7` elements per
|
|
3185
|
+
* permutation call, then squeezes `HASH_DIGEST_LENGTH = 5` elements.
|
|
3186
|
+
*
|
|
3187
|
+
* ## Padding
|
|
3188
|
+
*
|
|
3189
|
+
* The message is padded with the "10*" scheme (a mandatory 1 bit followed by
|
|
3190
|
+
* zeros to the next multiple of the rate):
|
|
3191
|
+
* ```
|
|
3192
|
+
* paddedMessage = [...message, 1n, 0n, ..., 0n]
|
|
3193
|
+
* ```
|
|
3194
|
+
* where padding brings `paddedMessage.length` to a multiple of `HASH_RATE`.
|
|
3195
|
+
*
|
|
3196
|
+
* ## Output
|
|
3197
|
+
*
|
|
3198
|
+
* The digest is exactly `HASH_DIGEST_LENGTH = 5` field elements
|
|
3199
|
+
* (approximately 1275 bits total).
|
|
3200
|
+
*
|
|
3201
|
+
* @example
|
|
3202
|
+
* ```typescript
|
|
3203
|
+
* const hash = getRescuePrimeHashFunction();
|
|
3204
|
+
* const digest = hash([1n, 2n, 3n]);
|
|
3205
|
+
* // digest.length === 5, each element in [0, FIELD_PRIME)
|
|
3206
|
+
* ```
|
|
3207
|
+
*
|
|
3208
|
+
* @see `getRescuePrimeHashFunction` in `index.ts` — public factory
|
|
3209
|
+
* @see `createRescuePrimeHashFunction` in `index.ts` — implementation
|
|
3210
|
+
* @see {@link RescuePrimeHashDeps} — optional dependency injection
|
|
3211
|
+
*
|
|
3212
|
+
* @internal
|
|
3213
|
+
*/
|
|
3214
|
+
type RescuePrimeHashFunction = (message: bigint[]) => bigint[];
|
|
3215
|
+
/**
|
|
3216
|
+
* A function that derives a `BLOCK_SIZE`-element cipher key from a shared secret.
|
|
3217
|
+
*
|
|
3218
|
+
* @remarks
|
|
3219
|
+
* The key derivation function (KDF) maps a raw 32-byte Diffie-Hellman output
|
|
3220
|
+
* to a vector of `BLOCK_SIZE = 5` field elements suitable for use as a
|
|
3221
|
+
* Rescue-XLIX cipher key.
|
|
3222
|
+
*
|
|
3223
|
+
* The default implementation (`deriveRescueCipherKey` in `index.ts`) follows
|
|
3224
|
+
* NIST SP 800-56C Rev 2, Section 4, Option 1, using Rescue-Prime as the
|
|
3225
|
+
* underlying hash:
|
|
3226
|
+
* ```
|
|
3227
|
+
* key = RescuePrimeHash([counter=1, secret_as_field_element, L=5])
|
|
3228
|
+
* ```
|
|
3229
|
+
*
|
|
3230
|
+
* Custom implementations provided via `RescueCipherDeps.deriveKey` must
|
|
3231
|
+
* return exactly `BLOCK_SIZE = 5` bigints in `[0, FIELD_PRIME)`.
|
|
3232
|
+
*
|
|
3233
|
+
* @example
|
|
3234
|
+
* ```typescript
|
|
3235
|
+
* // Custom test-only KDF that uses a fixed key
|
|
3236
|
+
* const fixedKey: RescueKeyDerivationFunction = (_secret) =>
|
|
3237
|
+
* [1n, 2n, 3n, 4n, 5n];
|
|
3238
|
+
*
|
|
3239
|
+
* const cipher = getRescueCipherInstance(sharedSecret, { deriveKey: fixedKey });
|
|
3240
|
+
* ```
|
|
3241
|
+
*
|
|
3242
|
+
* @see {@link RescueCipherDeps} — the deps interface that carries this function
|
|
3243
|
+
* @see `deriveRescueCipherKey` in `index.ts` — the default production KDF
|
|
3244
|
+
*
|
|
3245
|
+
* @internal
|
|
3246
|
+
*/
|
|
3247
|
+
type RescueKeyDerivationFunction = (sharedSecret: Uint8Array) => bigint[];
|
|
3248
|
+
/**
|
|
3249
|
+
* Optional dependency injection bag for `getRescueCipherInstance`.
|
|
3250
|
+
*
|
|
3251
|
+
* @remarks
|
|
3252
|
+
* Both fields are optional. When omitted, the production defaults are used:
|
|
3253
|
+
*
|
|
3254
|
+
* - `getFieldArithmetic` defaults to the SDK's `math/curve25519/field-arithmetic`
|
|
3255
|
+
* adapter (the {@link FieldArithmetic} singleton returned by `getFieldArithmetic()`
|
|
3256
|
+
* in `index.ts`).
|
|
3257
|
+
* - `deriveKey` defaults to the Rescue-Prime-based KDF (`deriveRescueCipherKey`
|
|
3258
|
+
* in `index.ts`) which follows NIST SP 800-56C Rev 2 Section 4.
|
|
3259
|
+
*
|
|
3260
|
+
* Providing custom deps bypasses the module-level caches, so each
|
|
3261
|
+
* `getRescueCipherInstance` call with custom deps creates independent instances.
|
|
3262
|
+
* This is intentional to facilitate test isolation and benchmarking.
|
|
3263
|
+
*
|
|
3264
|
+
* @example
|
|
3265
|
+
* ```typescript
|
|
3266
|
+
* // Test with a trivial field (not cryptographically secure)
|
|
3267
|
+
* const testDeps: RescueCipherDeps = {
|
|
3268
|
+
* getFieldArithmetic: () => myTestFieldArithmetic,
|
|
3269
|
+
* deriveKey: (secret) => Array.from({ length: 5 }, (_, i) => BigInt(i)),
|
|
3270
|
+
* };
|
|
3271
|
+
*
|
|
3272
|
+
* const cipher = getRescueCipherInstance(sharedSecret, testDeps);
|
|
3273
|
+
* ```
|
|
3274
|
+
*
|
|
3275
|
+
* @see `getRescueCipherInstance` in `index.ts` — the factory that consumes this interface
|
|
3276
|
+
* @see {@link FieldArithmetic} — the field arithmetic interface to implement
|
|
3277
|
+
* @see {@link RescueKeyDerivationFunction} — the KDF function signature
|
|
3278
|
+
*
|
|
3279
|
+
* @internal
|
|
3280
|
+
*/
|
|
3281
|
+
interface RescueCipherDeps {
|
|
3282
|
+
/**
|
|
3283
|
+
* Factory that returns a custom {@link FieldArithmetic} implementation.
|
|
3284
|
+
*
|
|
3285
|
+
* @remarks
|
|
3286
|
+
* When provided, this overrides the default SDK field arithmetic adapter.
|
|
3287
|
+
* The returned implementation must operate over the same prime field
|
|
3288
|
+
* (`p = 2^255 - 19`) as the round constants and MDS matrices.
|
|
3289
|
+
*
|
|
3290
|
+
* @defaultValue `getFieldArithmetic` from `index.ts` (SDK Curve25519 adapter)
|
|
3291
|
+
*/
|
|
3292
|
+
readonly getFieldArithmetic?: () => FieldArithmetic;
|
|
3293
|
+
/**
|
|
3294
|
+
* Custom key derivation function that maps a 32-byte secret to a key vector.
|
|
3295
|
+
*
|
|
3296
|
+
* @remarks
|
|
3297
|
+
* When provided, this replaces the NIST SP 800-56C Rescue-Prime KDF entirely.
|
|
3298
|
+
* The function must return exactly `BLOCK_SIZE = 5` bigints in `[0, FIELD_PRIME)`.
|
|
3299
|
+
*
|
|
3300
|
+
* @defaultValue `deriveRescueCipherKey` from `index.ts`
|
|
3301
|
+
*/
|
|
3302
|
+
readonly deriveKey?: RescueKeyDerivationFunction;
|
|
3303
|
+
}
|
|
3304
|
+
/**
|
|
3305
|
+
* Optional dependency injection bag for `getRescuePrimeHashFunction`.
|
|
3306
|
+
*
|
|
3307
|
+
* @remarks
|
|
3308
|
+
* Provides a hook to substitute a custom {@link FieldArithmetic} implementation
|
|
3309
|
+
* when constructing the Rescue-Prime sponge hash. This is useful for:
|
|
3310
|
+
*
|
|
3311
|
+
* - Unit testing with a small or mock field to verify the sponge logic
|
|
3312
|
+
* independently of the field arithmetic library.
|
|
3313
|
+
* - Benchmarking alternative field implementations.
|
|
3314
|
+
*
|
|
3315
|
+
* When the `getFieldArithmetic` field is omitted, the production default
|
|
3316
|
+
* (the SDK's `math/curve25519` adapter) is used and the result is cached
|
|
3317
|
+
* as a module-level singleton.
|
|
3318
|
+
*
|
|
3319
|
+
* @example
|
|
3320
|
+
* ```typescript
|
|
3321
|
+
* // Inject a custom field for testing
|
|
3322
|
+
* const hashFn = getRescuePrimeHashFunction({
|
|
3323
|
+
* getFieldArithmetic: () => myReferenceFieldImpl,
|
|
3324
|
+
* });
|
|
3325
|
+
* const digest = hashFn([1n, 2n, 3n]);
|
|
3326
|
+
* ```
|
|
3327
|
+
*
|
|
3328
|
+
* @see `getRescuePrimeHashFunction` in `index.ts` — the factory that consumes this interface
|
|
3329
|
+
* @see {@link FieldArithmetic} — the interface to implement
|
|
3330
|
+
*
|
|
3331
|
+
* @internal
|
|
3332
|
+
*/
|
|
3333
|
+
interface RescuePrimeHashDeps {
|
|
3334
|
+
/**
|
|
3335
|
+
* Factory that returns a custom {@link FieldArithmetic} implementation.
|
|
3336
|
+
*
|
|
3337
|
+
* @remarks
|
|
3338
|
+
* When provided, this bypasses the module-level `cachedRescuePrimeHashFunction`
|
|
3339
|
+
* cache and creates a fresh hash function instance backed by the custom field.
|
|
3340
|
+
*
|
|
3341
|
+
* @defaultValue `getFieldArithmetic` from `index.ts` (SDK Curve25519 adapter)
|
|
3342
|
+
*/
|
|
3343
|
+
readonly getFieldArithmetic?: () => FieldArithmetic;
|
|
3344
|
+
}
|
|
3345
|
+
|
|
3346
|
+
export type { RescuePrimeHashDeps as $, MinuteViewingKeyGeneratorFunction as A, ModuloPowCurve25519Function as B, Curve25519KeypairGeneratorFunction as C, DailyViewingKeyGeneratorFunction as D, EphemeralMasterSeedDeriverFunction as E, FiatShamirChallengeGeneratorFunction as F, MonthlyViewingKeyGeneratorFunction as G, H2CircuitProvableHashFunction as H, PolynomialEvaluatorDeps as I, PoseidonDecryptorFunction as J, KeystreamCommitmentFunction as K, PoseidonEncryptorFunction as L, MintX25519KeypairDeriverFunction as M, PoseidonHashFunction as N, PoseidonKeystreamBlindingFactorDeriverFunction as O, PoseidonPrivateKeyDeriverFunction as P, PoseidonKeystreamGeneratorFunction as Q, RescueCommitmentBlindingFactorDeriverFunction as R, PoseidonPrfFunction as S, PseudorandomU512DeriverFunction as T, UserCommitmentGeneratorFunction as U, RcDecryptorFunction as V, RcEncryptorFunction as W, RescueCipherDeps as X, RescueCipherInstance as Y, RescueKeyDerivationFunction as Z, RescuePermuteFunction as _, MasterViewingKeyDeriverFunction as a, RescuePrimeHashFunction as a0, SecondViewingKeyGeneratorFunction as a1, UserCommitmentGeneratorDeps as a2, ViewingKeyGeneratorOptions as a3, YearlyViewingKeyGeneratorFunction as a4, Kmac256Function as a5, RandomNonceGeneratorFunction as a6, X25519GetSharedSecretFunction as a7, MasterViewingKeyBlindingFactorDeriverFunction as b, PoseidonBlindingFactorDeriverFunction as c, PolynomialCommitmentFactorDeriverFunction as d, RcKeyGeneratorFunction as e, RcEncryptorWithNonceFunction as f, ChallengePowersFunction as g, PolynomialEvaluatorFunction as h, PoseidonAggregatorHashFunction as i, ChallengePowersDeps as j, EphemeralRescueCommitmentBlindingFactorDeriverFunction as k, EphemeralUtxoH2RandomSecretDeriverFunction as l, EphemeralUtxoMasterViewingKeyBlindingFactorDeriverFunction as m, EphemeralUtxoMasterViewingKeyDeriverFunction as n, EphemeralUtxoNullifierDeriverFunction as o, EphemeralUtxoPoseidonPrivateKeyBlindingFactorDeriverFunction as p, EphemeralUtxoPoseidonPrivateKeyDeriverFunction as q, FieldArithmetic as r, H2CircuitProvableParams as s, H2FullParams as t, H2GeneratorFns as u, H2HashFunction as v, HourlyViewingKeyGeneratorFunction as w, MasterSeedBasedFieldElementDeriverFunction as x, MasterSeedGeneratorFunction as y, MintViewingKeyDeriverFunction as z };
|