threshold-elgamal 1.0.0-beta.7 → 1.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 +63 -12
- package/dist/core/bigint.d.ts +0 -13
- package/dist/core/bigint.js +1 -11
- package/dist/core/bytes.d.ts +1 -1
- package/dist/core/bytes.js +6 -3
- package/dist/core/errors.d.ts +12 -12
- package/dist/core/errors.js +12 -12
- package/dist/core/groups.d.ts +2 -7
- package/dist/core/groups.js +11 -10
- package/dist/core/ristretto.d.ts +2 -6
- package/dist/core/ristretto.js +1 -11
- package/dist/core/types.d.ts +0 -2
- package/dist/dkg/checkpoints.d.ts +8 -9
- package/dist/dkg/checkpoints.js +50 -12
- package/dist/dkg/pedersen-share-codec.js +65 -8
- package/dist/dkg/verification-complaints.d.ts +2 -2
- package/dist/dkg/verification-complaints.js +3 -3
- package/dist/dkg/verification.d.ts +74 -5
- package/dist/dkg/verification.js +371 -76
- package/dist/elgamal/additive.d.ts +15 -8
- package/dist/elgamal/additive.js +34 -30
- package/dist/elgamal/index.d.ts +0 -3
- package/dist/elgamal/index.js +0 -3
- package/dist/elgamal/types.d.ts +3 -7
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/proofs/disjunctive.d.ts +3 -3
- package/dist/proofs/dleq.d.ts +2 -2
- package/dist/proofs/helpers.d.ts +0 -1
- package/dist/proofs/helpers.js +1 -3
- package/dist/protocol/board-audit.d.ts +5 -12
- package/dist/protocol/board-audit.js +7 -34
- package/dist/protocol/builders.d.ts +3 -3
- package/dist/protocol/builders.js +22 -20
- package/dist/protocol/canonical-json.d.ts +4 -3
- package/dist/protocol/index.d.ts +3 -4
- package/dist/protocol/index.js +3 -4
- package/dist/protocol/manifest.d.ts +0 -6
- package/dist/protocol/manifest.js +0 -6
- package/dist/protocol/payloads.js +0 -4
- package/dist/protocol/transcript.d.ts +0 -19
- package/dist/protocol/transcript.js +3 -4
- package/dist/protocol/types.d.ts +62 -25
- package/dist/protocol/verification.js +1 -1
- package/dist/protocol/{ballots.d.ts → voting-ballot-aggregation.d.ts} +6 -17
- package/dist/protocol/{ballots.js → voting-ballot-aggregation.js} +3 -3
- package/dist/protocol/{ballot-close.d.ts → voting-ballot-close.d.ts} +2 -1
- package/dist/protocol/{ballot-close.js → voting-ballot-close.js} +6 -6
- package/dist/protocol/voting-ballots.d.ts +2 -2
- package/dist/protocol/voting-ballots.js +24 -17
- package/dist/protocol/voting-codecs.d.ts +5 -8
- package/dist/protocol/voting-codecs.js +10 -19
- package/dist/protocol/voting-decryption.d.ts +1 -10
- package/dist/protocol/voting-decryption.js +23 -40
- package/dist/protocol/voting-shared.d.ts +4 -7
- package/dist/protocol/voting-shared.js +4 -10
- package/dist/protocol/{election-verification.d.ts → voting-verification.d.ts} +27 -17
- package/dist/protocol/{election-verification.js → voting-verification.js} +28 -7
- package/dist/serialize/encoding.d.ts +1 -1
- package/dist/serialize/encoding.js +5 -18
- package/dist/threshold/decrypt.d.ts +13 -9
- package/dist/threshold/decrypt.js +21 -15
- package/dist/threshold/index.d.ts +1 -4
- package/dist/threshold/index.js +1 -4
- package/dist/threshold/types.d.ts +3 -3
- package/dist/transport/auth.d.ts +1 -1
- package/dist/transport/auth.js +1 -1
- package/dist/transport/complaints.d.ts +0 -22
- package/dist/transport/complaints.js +1 -48
- package/dist/transport/envelopes.d.ts +2 -0
- package/dist/transport/envelopes.js +5 -3
- package/dist/transport/index.d.ts +1 -1
- package/dist/transport/index.js +1 -1
- package/dist/transport/key-agreement.d.ts +0 -6
- package/dist/transport/key-agreement.js +0 -18
- package/dist/vss/feldman.d.ts +2 -0
- package/dist/vss/feldman.js +13 -5
- package/dist/vss/pedersen.d.ts +2 -1
- package/dist/vss/pedersen.js +13 -7
- package/package.json +15 -29
- package/dist/core/group-invariants.d.ts +0 -2
- package/dist/core/group-invariants.js +0 -14
- package/dist/dkg/complaints.d.ts +0 -51
- package/dist/dkg/complaints.js +0 -129
- package/dist/dkg/gjkr.d.ts +0 -25
- package/dist/dkg/gjkr.js +0 -30
- package/dist/dkg/index.d.ts +0 -8
- package/dist/dkg/index.js +0 -8
- package/dist/dkg/majority-reducer.d.ts +0 -4
- package/dist/dkg/majority-reducer.js +0 -144
- package/dist/dkg/phase-plan.d.ts +0 -2
- package/dist/dkg/phase-plan.js +0 -21
- package/dist/dkg/reconstruction.d.ts +0 -9
- package/dist/dkg/reconstruction.js +0 -28
- package/dist/dkg/reducer-auth.d.ts +0 -3
- package/dist/dkg/reducer-auth.js +0 -87
- package/dist/dkg/types.d.ts +0 -33
- package/dist/dkg/types.js +0 -1
- package/dist/dkg/verification-checkpoints.d.ts +0 -5
- package/dist/dkg/verification-checkpoints.js +0 -40
- package/dist/dkg/verification-derivation.d.ts +0 -66
- package/dist/dkg/verification-derivation.js +0 -85
- package/dist/dkg/verification-feldman.d.ts +0 -8
- package/dist/dkg/verification-feldman.js +0 -80
- package/dist/dkg/verification-roster.d.ts +0 -5
- package/dist/dkg/verification-roster.js +0 -61
- package/dist/dkg/verification-shared.d.ts +0 -19
- package/dist/dkg/verification-shared.js +0 -98
- package/dist/dkg/verification-types.d.ts +0 -51
- package/dist/dkg/verification-types.js +0 -1
- package/dist/elgamal/ciphertext.d.ts +0 -5
- package/dist/elgamal/ciphertext.js +0 -13
- package/dist/elgamal/keygen.d.ts +0 -9
- package/dist/elgamal/keygen.js +0 -18
- package/dist/elgamal/validation.d.ts +0 -15
- package/dist/elgamal/validation.js +0 -27
- package/dist/protocol/voting-types.d.ts +0 -64
- package/dist/protocol/voting-types.js +0 -1
- package/dist/threshold/lagrange.d.ts +0 -9
- package/dist/threshold/lagrange.js +0 -21
- package/dist/threshold/polynomial.d.ts +0 -24
- package/dist/threshold/polynomial.js +0 -41
- package/dist/threshold/shares.d.ts +0 -6
- package/dist/threshold/shares.js +0 -25
- package/dist/transport/envelope-crypto.d.ts +0 -3
- package/dist/transport/envelope-crypto.js +0 -6
- package/dist/vss/commitment-product.d.ts +0 -2
- package/dist/vss/commitment-product.js +0 -14
package/README.md
CHANGED
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/threshold-elgamal)
|
|
4
4
|
[](https://www.npmjs.com/package/threshold-elgamal)
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
5
8
|
[](https://github.com/Tenemo/threshold-elgamal/actions/workflows/ci.yml)
|
|
6
|
-
[](https://tenemo.github.io/threshold-elgamal/coverage-summary.json)
|
|
7
10
|
[](https://github.com/Tenemo/threshold-elgamal/actions/workflows/pages.yml)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
8
14
|
[](https://nodejs.org/)
|
|
9
15
|
[](LICENSE)
|
|
10
16
|
|
|
@@ -24,7 +30,7 @@ This is a hardened research prototype. It has not been audited.
|
|
|
24
30
|
## Installation
|
|
25
31
|
|
|
26
32
|
```bash
|
|
27
|
-
|
|
33
|
+
npm install threshold-elgamal
|
|
28
34
|
```
|
|
29
35
|
|
|
30
36
|
## Runtime requirements
|
|
@@ -35,6 +41,18 @@ pnpm add threshold-elgamal
|
|
|
35
41
|
- Authentication signatures require Web Crypto `Ed25519`.
|
|
36
42
|
- Transport share exchange requires Web Crypto `X25519`.
|
|
37
43
|
|
|
44
|
+
## Documentation
|
|
45
|
+
|
|
46
|
+
- Hosted documentation site: [tenemo.github.io/threshold-elgamal](https://tenemo.github.io/threshold-elgamal/)
|
|
47
|
+
- Get started: [tenemo.github.io/threshold-elgamal/guides/getting-started](https://tenemo.github.io/threshold-elgamal/guides/getting-started/)
|
|
48
|
+
- Verifying a public board: [tenemo.github.io/threshold-elgamal/guides/verifying-a-public-board](https://tenemo.github.io/threshold-elgamal/guides/verifying-a-public-board/)
|
|
49
|
+
- Browser and worker usage: [tenemo.github.io/threshold-elgamal/guides/browser-and-worker-usage](https://tenemo.github.io/threshold-elgamal/guides/browser-and-worker-usage/)
|
|
50
|
+
- Published payload examples: [tenemo.github.io/threshold-elgamal/guides/published-payload-examples](https://tenemo.github.io/threshold-elgamal/guides/published-payload-examples/)
|
|
51
|
+
- Honest-majority voting flow: [tenemo.github.io/threshold-elgamal/guides/three-participant-voting-flow](https://tenemo.github.io/threshold-elgamal/guides/three-participant-voting-flow/)
|
|
52
|
+
- Security boundary: [tenemo.github.io/threshold-elgamal/guides/security-and-non-goals](https://tenemo.github.io/threshold-elgamal/guides/security-and-non-goals/)
|
|
53
|
+
- Production voting safety review: [tenemo.github.io/threshold-elgamal/guides/production-voting-safety-review](https://tenemo.github.io/threshold-elgamal/guides/production-voting-safety-review/)
|
|
54
|
+
- API docs: [tenemo.github.io/threshold-elgamal/api](https://tenemo.github.io/threshold-elgamal/api/)
|
|
55
|
+
|
|
38
56
|
## Browser support
|
|
39
57
|
|
|
40
58
|
The shipped cryptographic browser path is fixed:
|
|
@@ -62,7 +80,7 @@ The supported boardroom flow is:
|
|
|
62
80
|
5. Post ballot payloads for complete `1..10` score ballots.
|
|
63
81
|
6. Post one organizer-signed `ballot-close` payload that freezes which complete ballots are counted.
|
|
64
82
|
7. Post threshold decryption shares and tally publications for the close-selected ballot set.
|
|
65
|
-
8. Verify the whole ceremony with `
|
|
83
|
+
8. Verify the whole ceremony with `verifyElectionCeremony(...)`.
|
|
66
84
|
|
|
67
85
|
The cryptographic threshold is derived internally from the accepted registration roster:
|
|
68
86
|
|
|
@@ -72,6 +90,14 @@ The cryptographic threshold is derived internally from the accepted registration
|
|
|
72
90
|
|
|
73
91
|
There is no supported `n-of-n` mode and no supported public `k-of-n` configuration.
|
|
74
92
|
|
|
93
|
+
Transcript verification requires key-derivation confirmations from every qualified participant.
|
|
94
|
+
|
|
95
|
+
## Choose your entry point
|
|
96
|
+
|
|
97
|
+
- Verifying a public board: start with the hosted guide for `tryVerifyElectionCeremony(...)` and `verifyElectionCeremony(...)`.
|
|
98
|
+
- Browser and worker usage: start with the browser guide for key generation, manifest setup, and encrypted transport envelopes.
|
|
99
|
+
- Payload shapes and storage: start with the payload examples guide if you need concrete JSON, posting, or persistence patterns.
|
|
100
|
+
|
|
75
101
|
## Getting started
|
|
76
102
|
|
|
77
103
|
```typescript
|
|
@@ -118,6 +144,34 @@ console.log(majorityThreshold(3)); // 2
|
|
|
118
144
|
console.log(sessionId.length); // 64
|
|
119
145
|
```
|
|
120
146
|
|
|
147
|
+
If your application consumes a complete public board, the shortest safe verifier entry point is:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import {
|
|
151
|
+
tryVerifyElectionCeremony,
|
|
152
|
+
type VerifyElectionCeremonyInput,
|
|
153
|
+
} from "threshold-elgamal";
|
|
154
|
+
|
|
155
|
+
const bundle: VerifyElectionCeremonyInput = {
|
|
156
|
+
manifest,
|
|
157
|
+
sessionId,
|
|
158
|
+
dkgTranscript,
|
|
159
|
+
ballotPayloads,
|
|
160
|
+
ballotClosePayload,
|
|
161
|
+
decryptionSharePayloads,
|
|
162
|
+
tallyPublications,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const result = await tryVerifyElectionCeremony(bundle);
|
|
166
|
+
|
|
167
|
+
if (!result.ok) {
|
|
168
|
+
console.error(result.error.stage, result.error.code, result.error.reason);
|
|
169
|
+
} else {
|
|
170
|
+
console.log(result.verified.perOptionTallies);
|
|
171
|
+
console.log(result.verified.boardAudit.overall.fingerprint);
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
121
175
|
The root package also exposes public builders for:
|
|
122
176
|
|
|
123
177
|
- manifest publication
|
|
@@ -132,7 +186,7 @@ The root package also exposes public builders for:
|
|
|
132
186
|
- decryption shares
|
|
133
187
|
- tally publication
|
|
134
188
|
|
|
135
|
-
For
|
|
189
|
+
For concrete integration examples, start with the hosted guides below. The repository integration harness exercises the same workflow, but it is not part of the supported public API.
|
|
136
190
|
|
|
137
191
|
## Security boundary
|
|
138
192
|
|
|
@@ -157,19 +211,16 @@ What it does not claim:
|
|
|
157
211
|
|
|
158
212
|
`ballot-close` is an auditable administrative cutoff, not a fairness proof about board arrival order. The library proves what was counted, not whether the organizer waited long enough before closing.
|
|
159
213
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
- Hosted documentation site: [tenemo.github.io/threshold-elgamal](https://tenemo.github.io/threshold-elgamal/)
|
|
163
|
-
- Get started: [tenemo.github.io/threshold-elgamal/guides/getting-started](https://tenemo.github.io/threshold-elgamal/guides/getting-started/)
|
|
164
|
-
- Honest-majority voting flow: [tenemo.github.io/threshold-elgamal/guides/three-participant-voting-flow](https://tenemo.github.io/threshold-elgamal/guides/three-participant-voting-flow/)
|
|
165
|
-
- Security boundary: [tenemo.github.io/threshold-elgamal/guides/security-and-non-goals](https://tenemo.github.io/threshold-elgamal/guides/security-and-non-goals/)
|
|
166
|
-
- API docs: [tenemo.github.io/threshold-elgamal/api](https://tenemo.github.io/threshold-elgamal/api/)
|
|
214
|
+
For a production-threat-model verdict that maps these boundaries to the shipped verifier and tests, read the production voting safety review in the hosted docs.
|
|
167
215
|
|
|
168
216
|
## Development
|
|
169
217
|
|
|
170
218
|
```bash
|
|
171
219
|
pnpm install
|
|
172
|
-
pnpm run
|
|
220
|
+
pnpm run lint
|
|
221
|
+
pnpm run tsc
|
|
222
|
+
pnpm run test
|
|
223
|
+
pnpm run build
|
|
173
224
|
```
|
|
174
225
|
|
|
175
226
|
## License
|
package/dist/core/bigint.d.ts
CHANGED
|
@@ -4,25 +4,12 @@
|
|
|
4
4
|
* @throws {@link InvalidScalarError} When `modulus` is not positive.
|
|
5
5
|
*/
|
|
6
6
|
export declare const mod: (value: bigint, modulus: bigint) => bigint;
|
|
7
|
-
/**
|
|
8
|
-
* Reduces a value into the range `0..p-1`.
|
|
9
|
-
*
|
|
10
|
-
* @throws {@link InvalidScalarError} When `p` is not positive.
|
|
11
|
-
*/
|
|
12
|
-
export declare const modP: (value: bigint, p: bigint) => bigint;
|
|
13
7
|
/**
|
|
14
8
|
* Reduces a value into the range `0..q-1`.
|
|
15
9
|
*
|
|
16
10
|
* @throws {@link InvalidScalarError} When `q` is not positive.
|
|
17
11
|
*/
|
|
18
12
|
export declare const modQ: (value: bigint, q: bigint) => bigint;
|
|
19
|
-
/**
|
|
20
|
-
* Computes the multiplicative inverse of a value modulo `p`.
|
|
21
|
-
*
|
|
22
|
-
* @throws {@link InvalidScalarError} When `p` is not positive or the inverse
|
|
23
|
-
* does not exist.
|
|
24
|
-
*/
|
|
25
|
-
export declare const modInvP: (value: bigint, p: bigint) => bigint;
|
|
26
13
|
/**
|
|
27
14
|
* Computes the multiplicative inverse of a value modulo `q`.
|
|
28
15
|
*
|
package/dist/core/bigint.js
CHANGED
|
@@ -62,23 +62,13 @@ export const mod = (value, modulus) => {
|
|
|
62
62
|
*
|
|
63
63
|
* @throws {@link InvalidScalarError} When `p` is not positive.
|
|
64
64
|
*/
|
|
65
|
-
|
|
65
|
+
const modP = (value, p) => mod(value, p);
|
|
66
66
|
/**
|
|
67
67
|
* Reduces a value into the range `0..q-1`.
|
|
68
68
|
*
|
|
69
69
|
* @throws {@link InvalidScalarError} When `q` is not positive.
|
|
70
70
|
*/
|
|
71
71
|
export const modQ = (value, q) => mod(value, q);
|
|
72
|
-
/**
|
|
73
|
-
* Computes the multiplicative inverse of a value modulo `p`.
|
|
74
|
-
*
|
|
75
|
-
* @throws {@link InvalidScalarError} When `p` is not positive or the inverse
|
|
76
|
-
* does not exist.
|
|
77
|
-
*/
|
|
78
|
-
export const modInvP = (value, p) => {
|
|
79
|
-
assertPositiveModulus(p);
|
|
80
|
-
return modInv(modP(value, p), p);
|
|
81
|
-
};
|
|
82
72
|
/**
|
|
83
73
|
* Computes the multiplicative inverse of a value modulo `q`.
|
|
84
74
|
*
|
package/dist/core/bytes.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const bytesToHex: (bytes: Uint8Array) => string;
|
|
2
|
-
export declare const hexToBytes: (hex: string) => Uint8Array;
|
|
2
|
+
export declare const hexToBytes: (hex: string, errorMessage?: string) => Uint8Array;
|
|
3
3
|
export declare const bytesToBigInt: (bytes: Uint8Array) => bigint;
|
|
4
4
|
export declare const bytesToBigIntLE: (bytes: Uint8Array) => bigint;
|
|
5
5
|
export declare const toBufferSource: (bytes: Uint8Array) => ArrayBuffer;
|
package/dist/core/bytes.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { InvalidPayloadError } from './errors.js';
|
|
2
|
+
const hexPattern = /^[0-9a-f]+$/i;
|
|
3
|
+
const defaultHexErrorMessage = 'Hex input must be a non-empty even-length hexadecimal string';
|
|
1
4
|
export const bytesToHex = (bytes) => {
|
|
2
5
|
let hex = '';
|
|
3
6
|
for (const byte of bytes) {
|
|
@@ -5,9 +8,9 @@ export const bytesToHex = (bytes) => {
|
|
|
5
8
|
}
|
|
6
9
|
return hex;
|
|
7
10
|
};
|
|
8
|
-
export const hexToBytes = (hex) => {
|
|
9
|
-
if (hex.length === 0 || hex.length % 2 !== 0 ||
|
|
10
|
-
throw new
|
|
11
|
+
export const hexToBytes = (hex, errorMessage = defaultHexErrorMessage) => {
|
|
12
|
+
if (hex.length === 0 || hex.length % 2 !== 0 || !hexPattern.test(hex)) {
|
|
13
|
+
throw new InvalidPayloadError(errorMessage);
|
|
11
14
|
}
|
|
12
15
|
const bytes = new Uint8Array(hex.length / 2);
|
|
13
16
|
for (let index = 0; index < hex.length; index += 2) {
|
package/dist/core/errors.d.ts
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
declare class
|
|
1
|
+
declare class ThresholdElGamalError extends Error {
|
|
2
2
|
constructor(message: string);
|
|
3
3
|
}
|
|
4
4
|
/** Raised when a scalar value falls outside the expected mathematical domain. */
|
|
5
|
-
export declare class InvalidScalarError extends
|
|
5
|
+
export declare class InvalidScalarError extends ThresholdElGamalError {
|
|
6
6
|
}
|
|
7
7
|
/** Raised when a group element is not valid for the selected suite. */
|
|
8
|
-
export declare class InvalidGroupElementError extends
|
|
8
|
+
export declare class InvalidGroupElementError extends ThresholdElGamalError {
|
|
9
9
|
}
|
|
10
10
|
/** Raised when a participant index falls outside the valid `1..n` range. */
|
|
11
|
-
export declare class IndexOutOfRangeError extends
|
|
11
|
+
export declare class IndexOutOfRangeError extends ThresholdElGamalError {
|
|
12
12
|
}
|
|
13
13
|
/** Raised when serialized payload bytes do not satisfy the required encoding. */
|
|
14
|
-
export declare class InvalidPayloadError extends
|
|
14
|
+
export declare class InvalidPayloadError extends ThresholdElGamalError {
|
|
15
15
|
}
|
|
16
16
|
/** Raised when a proof transcript or response fails verification. */
|
|
17
|
-
export declare class InvalidProofError extends
|
|
17
|
+
export declare class InvalidProofError extends ThresholdElGamalError {
|
|
18
18
|
}
|
|
19
19
|
/** Raised when the requested suite or runtime capability is unavailable. */
|
|
20
|
-
export declare class UnsupportedSuiteError extends
|
|
20
|
+
export declare class UnsupportedSuiteError extends ThresholdElGamalError {
|
|
21
21
|
}
|
|
22
22
|
/** Raised when a plaintext lies outside the allowed domain for the chosen mode. */
|
|
23
|
-
export declare class PlaintextDomainError extends
|
|
23
|
+
export declare class PlaintextDomainError extends ThresholdElGamalError {
|
|
24
24
|
}
|
|
25
25
|
/** Raised when a serialized or reconstructed share fails validation. */
|
|
26
|
-
export declare class InvalidShareError extends
|
|
26
|
+
export declare class InvalidShareError extends ThresholdElGamalError {
|
|
27
27
|
}
|
|
28
28
|
/** Raised when a protocol step transition violates the state machine rules. */
|
|
29
|
-
export declare class PhaseViolationError extends
|
|
29
|
+
export declare class PhaseViolationError extends ThresholdElGamalError {
|
|
30
30
|
}
|
|
31
31
|
/** Raised when threshold parameters do not satisfy `1 <= k <= n`. */
|
|
32
|
-
export declare class ThresholdViolationError extends
|
|
32
|
+
export declare class ThresholdViolationError extends ThresholdElGamalError {
|
|
33
33
|
}
|
|
34
34
|
/** Raised when transcript hashes or canonical bytes do not match expectations. */
|
|
35
|
-
export declare class TranscriptMismatchError extends
|
|
35
|
+
export declare class TranscriptMismatchError extends ThresholdElGamalError {
|
|
36
36
|
}
|
|
37
37
|
export {};
|
package/dist/core/errors.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
1
|
+
class ThresholdElGamalError extends Error {
|
|
2
2
|
constructor(message) {
|
|
3
3
|
super(message);
|
|
4
4
|
this.name = new.target.name;
|
|
@@ -6,35 +6,35 @@ class ThresholdElgamalError extends Error {
|
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
/** Raised when a scalar value falls outside the expected mathematical domain. */
|
|
9
|
-
export class InvalidScalarError extends
|
|
9
|
+
export class InvalidScalarError extends ThresholdElGamalError {
|
|
10
10
|
}
|
|
11
11
|
/** Raised when a group element is not valid for the selected suite. */
|
|
12
|
-
export class InvalidGroupElementError extends
|
|
12
|
+
export class InvalidGroupElementError extends ThresholdElGamalError {
|
|
13
13
|
}
|
|
14
14
|
/** Raised when a participant index falls outside the valid `1..n` range. */
|
|
15
|
-
export class IndexOutOfRangeError extends
|
|
15
|
+
export class IndexOutOfRangeError extends ThresholdElGamalError {
|
|
16
16
|
}
|
|
17
17
|
/** Raised when serialized payload bytes do not satisfy the required encoding. */
|
|
18
|
-
export class InvalidPayloadError extends
|
|
18
|
+
export class InvalidPayloadError extends ThresholdElGamalError {
|
|
19
19
|
}
|
|
20
20
|
/** Raised when a proof transcript or response fails verification. */
|
|
21
|
-
export class InvalidProofError extends
|
|
21
|
+
export class InvalidProofError extends ThresholdElGamalError {
|
|
22
22
|
}
|
|
23
23
|
/** Raised when the requested suite or runtime capability is unavailable. */
|
|
24
|
-
export class UnsupportedSuiteError extends
|
|
24
|
+
export class UnsupportedSuiteError extends ThresholdElGamalError {
|
|
25
25
|
}
|
|
26
26
|
/** Raised when a plaintext lies outside the allowed domain for the chosen mode. */
|
|
27
|
-
export class PlaintextDomainError extends
|
|
27
|
+
export class PlaintextDomainError extends ThresholdElGamalError {
|
|
28
28
|
}
|
|
29
29
|
/** Raised when a serialized or reconstructed share fails validation. */
|
|
30
|
-
export class InvalidShareError extends
|
|
30
|
+
export class InvalidShareError extends ThresholdElGamalError {
|
|
31
31
|
}
|
|
32
32
|
/** Raised when a protocol step transition violates the state machine rules. */
|
|
33
|
-
export class PhaseViolationError extends
|
|
33
|
+
export class PhaseViolationError extends ThresholdElGamalError {
|
|
34
34
|
}
|
|
35
35
|
/** Raised when threshold parameters do not satisfy `1 <= k <= n`. */
|
|
36
|
-
export class ThresholdViolationError extends
|
|
36
|
+
export class ThresholdViolationError extends ThresholdElGamalError {
|
|
37
37
|
}
|
|
38
38
|
/** Raised when transcript hashes or canonical bytes do not match expectations. */
|
|
39
|
-
export class TranscriptMismatchError extends
|
|
39
|
+
export class TranscriptMismatchError extends ThresholdElGamalError {
|
|
40
40
|
}
|
package/dist/core/groups.d.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import type { CryptoGroup
|
|
1
|
+
import type { CryptoGroup } from './types.js';
|
|
2
2
|
/** Immutable definition of the shipped Ristretto255 tally group. */
|
|
3
3
|
export declare const RISTRETTO_GROUP: CryptoGroup;
|
|
4
|
-
|
|
5
|
-
export declare const getGroup: (identifier: GroupIdentifier) => CryptoGroup;
|
|
6
|
-
/** @internal Lists the immutable built-in group definitions. */
|
|
7
|
-
export declare const listGroups: () => readonly CryptoGroup[];
|
|
8
|
-
/** Returns the canonical deterministic secondary generator encoding. */
|
|
9
|
-
export declare const deriveH: () => string;
|
|
4
|
+
export declare const assertCanonicalRistrettoGroup: (group: CryptoGroup, label?: string) => void;
|
package/dist/core/groups.js
CHANGED
|
@@ -10,15 +10,16 @@ export const RISTRETTO_GROUP = Object.freeze({
|
|
|
10
10
|
h: derivePedersenGenerator(),
|
|
11
11
|
securityEstimate: 128,
|
|
12
12
|
});
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const sameCanonicalRistrettoGroup = (group) => group.name === RISTRETTO_GROUP.name &&
|
|
14
|
+
group.byteLength === RISTRETTO_GROUP.byteLength &&
|
|
15
|
+
group.scalarByteLength === RISTRETTO_GROUP.scalarByteLength &&
|
|
16
|
+
group.q === RISTRETTO_GROUP.q &&
|
|
17
|
+
group.g === RISTRETTO_GROUP.g &&
|
|
18
|
+
group.h === RISTRETTO_GROUP.h &&
|
|
19
|
+
group.securityEstimate === RISTRETTO_GROUP.securityEstimate;
|
|
20
|
+
export const assertCanonicalRistrettoGroup = (group, label = 'Group') => {
|
|
21
|
+
const normalizedLabel = label.length > 0 ? `${label[0].toLowerCase()}${label.slice(1)}` : label;
|
|
22
|
+
if (!sameCanonicalRistrettoGroup(group)) {
|
|
23
|
+
throw new UnsupportedSuiteError(`${normalizedLabel} must match the shipped canonical ristretto255 group definition`);
|
|
18
24
|
}
|
|
19
|
-
return RISTRETTO_GROUP;
|
|
20
25
|
};
|
|
21
|
-
/** @internal Lists the immutable built-in group definitions. */
|
|
22
|
-
export const listGroups = () => GROUPS;
|
|
23
|
-
/** Returns the canonical deterministic secondary generator encoding. */
|
|
24
|
-
export const deriveH = () => RISTRETTO_GROUP.h;
|
package/dist/core/ristretto.d.ts
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
import { ristretto255 } from '@noble/curves/ed25519.js';
|
|
2
2
|
import type { EncodedPoint } from './types.js';
|
|
3
|
-
|
|
4
|
-
export declare const RISTRETTO_GROUP_NAME = "ristretto255";
|
|
3
|
+
type InternalPoint = InstanceType<typeof ristretto255.Point>;
|
|
5
4
|
export declare const RISTRETTO_ORDER: bigint;
|
|
6
5
|
export declare const RISTRETTO_BYTE_LENGTH = 32;
|
|
7
|
-
export declare const RISTRETTO_SECURITY_ESTIMATE = 128;
|
|
8
|
-
export declare const RISTRETTO_BASE: InternalPoint;
|
|
9
6
|
export declare const RISTRETTO_ZERO: InternalPoint;
|
|
10
7
|
export declare const encodePoint: (point: InternalPoint) => EncodedPoint;
|
|
11
8
|
export declare const decodePoint: (value: string, label?: string) => InternalPoint;
|
|
12
9
|
export declare const encodeScalar: (value: bigint) => string;
|
|
13
10
|
export declare const decodeScalar: (value: string, label?: string) => bigint;
|
|
14
|
-
export declare const assertValidPoint: (value: string, label?: string) => void;
|
|
15
|
-
export declare const assertNonIdentityPoint: (value: string, label?: string) => void;
|
|
16
11
|
export declare const pointAdd: (left: InternalPoint, right: InternalPoint) => InternalPoint;
|
|
17
12
|
export declare const pointSubtract: (left: InternalPoint, right: InternalPoint) => InternalPoint;
|
|
18
13
|
export declare const pointMultiply: (point: InternalPoint, scalar: bigint) => InternalPoint;
|
|
19
14
|
export declare const multiplyBase: (scalar: bigint) => InternalPoint;
|
|
20
15
|
export declare const derivePedersenGenerator: () => EncodedPoint;
|
|
21
16
|
export declare const hashChallengeToScalar: (payload: Uint8Array) => bigint;
|
|
17
|
+
export {};
|
package/dist/core/ristretto.js
CHANGED
|
@@ -3,12 +3,10 @@ import { sha512 } from '@noble/hashes/sha2.js';
|
|
|
3
3
|
import { bytesToBigInt, bytesToBigIntLE, bytesToHex, hexToBytes, } from './bytes.js';
|
|
4
4
|
import { utf8ToBytes } from './crypto.js';
|
|
5
5
|
import { InvalidGroupElementError, InvalidPayloadError, InvalidScalarError, } from './errors.js';
|
|
6
|
-
export const RISTRETTO_GROUP_NAME = 'ristretto255';
|
|
7
6
|
const RISTRETTO_POINT = ristretto255.Point;
|
|
8
7
|
export const RISTRETTO_ORDER = ristretto255.Point.Fn.ORDER;
|
|
9
8
|
export const RISTRETTO_BYTE_LENGTH = 32;
|
|
10
|
-
|
|
11
|
-
export const RISTRETTO_BASE = ristretto255.Point.BASE;
|
|
9
|
+
const RISTRETTO_BASE = ristretto255.Point.BASE;
|
|
12
10
|
export const RISTRETTO_ZERO = ristretto255.Point.ZERO;
|
|
13
11
|
const scalarByteLength = RISTRETTO_BYTE_LENGTH;
|
|
14
12
|
const pointHexLength = RISTRETTO_BYTE_LENGTH * 2;
|
|
@@ -48,14 +46,6 @@ export const decodeScalar = (value, label = 'Scalar encoding') => {
|
|
|
48
46
|
}
|
|
49
47
|
return scalar;
|
|
50
48
|
};
|
|
51
|
-
export const assertValidPoint = (value, label = 'Point') => {
|
|
52
|
-
decodePoint(value, label);
|
|
53
|
-
};
|
|
54
|
-
export const assertNonIdentityPoint = (value, label = 'Point') => {
|
|
55
|
-
if (decodePoint(value, label).is0()) {
|
|
56
|
-
throw new InvalidGroupElementError(`${label} must not be the identity`);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
49
|
export const pointAdd = (left, right) => left.add(right);
|
|
60
50
|
export const pointSubtract = (left, right) => left.subtract(right);
|
|
61
51
|
export const pointMultiply = (point, scalar) => {
|
package/dist/core/types.d.ts
CHANGED
|
@@ -11,8 +11,6 @@ export type GroupName = 'ristretto255';
|
|
|
11
11
|
export type GroupIdentifier = GroupName;
|
|
12
12
|
/** Canonical 32-byte Ristretto point encoding exposed at the public boundary. */
|
|
13
13
|
export type EncodedPoint = Brand<string, 'EncodedPoint'>;
|
|
14
|
-
/** Scalar value intended to live in the prime-order field `Z_q`. */
|
|
15
|
-
export type ScalarQ = Brand<bigint, 'ScalarQ'>;
|
|
16
14
|
/** @internal Immutable built-in group definition for the shipped suite. */
|
|
17
15
|
export type CryptoGroup = {
|
|
18
16
|
/** Canonical suite name. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PhaseCheckpointPayload, SignedPayload } from '../protocol/types.js';
|
|
2
|
+
import { type ResolvePhaseCheckpointInput } from './verification.js';
|
|
2
3
|
/** Finalized threshold-supported checkpoint for one DKG phase. */
|
|
3
4
|
export type FinalizedPhaseCheckpoint = {
|
|
4
5
|
readonly payload: PhaseCheckpointPayload;
|
|
@@ -7,14 +8,12 @@ export type FinalizedPhaseCheckpoint = {
|
|
|
7
8
|
};
|
|
8
9
|
/** Returns `true` when the signed payload is a phase checkpoint. */
|
|
9
10
|
export declare const isPhaseCheckpointPayload: (payload: SignedPayload) => payload is SignedPayload<PhaseCheckpointPayload>;
|
|
10
|
-
/** Returns the required checkpoint phases for the shipped GJKR reducer. */
|
|
11
|
-
export declare const requiredCheckpointPhases: () => readonly number[];
|
|
12
|
-
/** Returns the last required checkpoint phase for the shipped GJKR reducer. */
|
|
13
|
-
export declare const finalCheckpointPhase: () => number;
|
|
14
|
-
/** Groups all checkpoint variants observed for one closed DKG phase. */
|
|
15
|
-
export declare const collectCheckpointVariants: (transcript: readonly SignedPayload[], checkpointPhase: number) => readonly FinalizedPhaseCheckpoint[];
|
|
16
11
|
/**
|
|
17
|
-
*
|
|
18
|
-
* `null` when no unique threshold-supported checkpoint exists yet.
|
|
12
|
+
* Rejects phase-checkpoint payloads outside the shipped GJKR checkpoint plan.
|
|
19
13
|
*/
|
|
20
|
-
export declare const
|
|
14
|
+
export declare const assertSupportedCheckpointPayloads: (transcript: readonly SignedPayload[]) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Resolves and validates the unique threshold-supported checkpoint for one
|
|
17
|
+
* closed DKG phase.
|
|
18
|
+
*/
|
|
19
|
+
export declare const resolveVerifiedPhaseCheckpoint: (input: ResolvePhaseCheckpointInput) => Promise<FinalizedPhaseCheckpoint>;
|
package/dist/dkg/checkpoints.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { InvalidPayloadError } from '../core/index.js';
|
|
2
|
+
import { hashProtocolPhaseSnapshot } from '../protocol/transcript.js';
|
|
3
|
+
import { assertIndexSubset, assertUniqueSortedParticipantIndices, } from './verification.js';
|
|
1
4
|
const checkpointKey = (payload) => JSON.stringify({
|
|
2
5
|
sessionId: payload.sessionId,
|
|
3
6
|
manifestHash: payload.manifestHash,
|
|
@@ -5,17 +8,15 @@ const checkpointKey = (payload) => JSON.stringify({
|
|
|
5
8
|
messageType: payload.messageType,
|
|
6
9
|
checkpointPhase: payload.checkpointPhase,
|
|
7
10
|
checkpointTranscriptHash: payload.checkpointTranscriptHash,
|
|
8
|
-
|
|
11
|
+
qualifiedParticipantIndices: payload.qualifiedParticipantIndices,
|
|
9
12
|
});
|
|
10
13
|
const compareNumbers = (left, right) => left - right;
|
|
14
|
+
const sameParticipantIndexList = (left, right) => left.length === right.length &&
|
|
15
|
+
left.every((participantIndex, index) => participantIndex === right[index]);
|
|
11
16
|
/** Returns `true` when the signed payload is a phase checkpoint. */
|
|
12
17
|
export const isPhaseCheckpointPayload = (payload) => payload.payload.messageType === 'phase-checkpoint';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
/** Returns the last required checkpoint phase for the shipped GJKR reducer. */
|
|
16
|
-
export const finalCheckpointPhase = () => requiredCheckpointPhases()[requiredCheckpointPhases().length - 1] ?? 0;
|
|
17
|
-
/** Groups all checkpoint variants observed for one closed DKG phase. */
|
|
18
|
-
export const collectCheckpointVariants = (transcript, checkpointPhase) => {
|
|
18
|
+
const requiredCheckpointPhases = () => [0, 1, 2, 3];
|
|
19
|
+
const collectCheckpointVariants = (transcript, checkpointPhase) => {
|
|
19
20
|
const grouped = new Map();
|
|
20
21
|
for (const signedPayload of transcript) {
|
|
21
22
|
if (!isPhaseCheckpointPayload(signedPayload) ||
|
|
@@ -40,10 +41,47 @@ export const collectCheckpointVariants = (transcript, checkpointPhase) => {
|
|
|
40
41
|
});
|
|
41
42
|
};
|
|
42
43
|
/**
|
|
43
|
-
*
|
|
44
|
-
* `null` when no unique threshold-supported checkpoint exists yet.
|
|
44
|
+
* Rejects phase-checkpoint payloads outside the shipped GJKR checkpoint plan.
|
|
45
45
|
*/
|
|
46
|
-
export const
|
|
47
|
-
const
|
|
48
|
-
|
|
46
|
+
export const assertSupportedCheckpointPayloads = (transcript) => {
|
|
47
|
+
for (const signedPayload of transcript) {
|
|
48
|
+
if (isPhaseCheckpointPayload(signedPayload) &&
|
|
49
|
+
!requiredCheckpointPhases().includes(signedPayload.payload.checkpointPhase)) {
|
|
50
|
+
throw new InvalidPayloadError(`Checkpoint phase ${signedPayload.payload.checkpointPhase} is not part of the GJKR phase plan`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Resolves and validates the unique threshold-supported checkpoint for one
|
|
56
|
+
* closed DKG phase.
|
|
57
|
+
*/
|
|
58
|
+
export const resolveVerifiedPhaseCheckpoint = async (input) => {
|
|
59
|
+
const supported = collectCheckpointVariants(input.transcript, input.checkpointPhase).filter((entry) => entry.signatures.length >= input.threshold);
|
|
60
|
+
if (supported.length === 0) {
|
|
61
|
+
throw new InvalidPayloadError(`Missing threshold-supported phase checkpoint for phase ${input.checkpointPhase}`);
|
|
62
|
+
}
|
|
63
|
+
if (supported.length > 1) {
|
|
64
|
+
throw new InvalidPayloadError(`Observed multiple threshold-supported phase checkpoints for phase ${input.checkpointPhase}`);
|
|
65
|
+
}
|
|
66
|
+
const checkpoint = supported[0];
|
|
67
|
+
const qualifiedParticipantIndices = checkpoint.payload.qualifiedParticipantIndices;
|
|
68
|
+
assertUniqueSortedParticipantIndices(qualifiedParticipantIndices, input.participantCount, `Phase ${input.checkpointPhase} checkpoint qualified participant`);
|
|
69
|
+
if (!sameParticipantIndexList(qualifiedParticipantIndices, input.expectedQualifiedParticipantIndices)) {
|
|
70
|
+
throw new InvalidPayloadError(`Phase ${input.checkpointPhase} checkpoint qualified participant set does not match the verifier-computed active participant set`);
|
|
71
|
+
}
|
|
72
|
+
if (qualifiedParticipantIndices.length < input.threshold) {
|
|
73
|
+
throw new InvalidPayloadError(`Checkpoint qualified participant set for phase ${input.checkpointPhase} must contain at least ${input.threshold} participants`);
|
|
74
|
+
}
|
|
75
|
+
const expectedSnapshotHash = await hashProtocolPhaseSnapshot(input.transcript.map((entry) => entry.payload), input.checkpointPhase);
|
|
76
|
+
if (checkpoint.payload.checkpointTranscriptHash !== expectedSnapshotHash) {
|
|
77
|
+
throw new InvalidPayloadError(`Phase ${input.checkpointPhase} checkpoint transcript hash does not match the signed transcript snapshot`);
|
|
78
|
+
}
|
|
79
|
+
assertIndexSubset(checkpoint.signers, input.signerUniverse, `Phase ${input.checkpointPhase} checkpoint signer`);
|
|
80
|
+
const qualifiedParticipantSet = new Set(qualifiedParticipantIndices);
|
|
81
|
+
for (const signer of checkpoint.signers) {
|
|
82
|
+
if (!qualifiedParticipantSet.has(signer)) {
|
|
83
|
+
throw new InvalidPayloadError(`Phase ${input.checkpointPhase} checkpoint signer ${signer} is not part of the checkpoint qualified participant set`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return checkpoint;
|
|
49
87
|
};
|
|
@@ -1,6 +1,53 @@
|
|
|
1
|
-
import { InvalidPayloadError } from '../core/index.js';
|
|
1
|
+
import { InvalidPayloadError, RISTRETTO_GROUP } from '../core/index.js';
|
|
2
2
|
import { canonicalizeJson } from '../protocol/canonical-json.js';
|
|
3
|
-
import { bigintToFixedHex,
|
|
3
|
+
import { bigintToFixedHex, fixedHexToBigInt } from '../serialize/index.js';
|
|
4
|
+
const pedersenShareEnvelopeKeys = [
|
|
5
|
+
'blindingValue',
|
|
6
|
+
'index',
|
|
7
|
+
'secretValue',
|
|
8
|
+
];
|
|
9
|
+
const fixedLowercaseHexPattern = /^[0-9a-f]+$/;
|
|
10
|
+
const pedersenShareTextDecoder = new TextDecoder('utf-8', { fatal: true });
|
|
11
|
+
const assertCanonicalEnvelopeIndex = (value, expectedParticipantIndex, label) => {
|
|
12
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value < 1) {
|
|
13
|
+
throw new InvalidPayloadError(`${label} share index must be a positive integer`);
|
|
14
|
+
}
|
|
15
|
+
if (value !== expectedParticipantIndex) {
|
|
16
|
+
throw new InvalidPayloadError(`${label} share index mismatch: expected ${expectedParticipantIndex}, received ${value}`);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
};
|
|
20
|
+
const parseCanonicalFixedHex = (value, byteLength, label) => {
|
|
21
|
+
const expectedLength = byteLength * 2;
|
|
22
|
+
if (typeof value !== 'string' ||
|
|
23
|
+
value.length !== expectedLength ||
|
|
24
|
+
!fixedLowercaseHexPattern.test(value)) {
|
|
25
|
+
throw new InvalidPayloadError(`${label} must be a lowercase fixed-width hexadecimal string of length ${expectedLength}`);
|
|
26
|
+
}
|
|
27
|
+
const decoded = fixedHexToBigInt(value);
|
|
28
|
+
if (bigintToFixedHex(decoded, byteLength) !== value) {
|
|
29
|
+
throw new InvalidPayloadError(`${label} must use canonical fixed-width hexadecimal encoding`);
|
|
30
|
+
}
|
|
31
|
+
return decoded;
|
|
32
|
+
};
|
|
33
|
+
const parsePedersenShareEnvelopeRecord = (parsed, expectedParticipantIndex, label) => {
|
|
34
|
+
if (parsed === null ||
|
|
35
|
+
Array.isArray(parsed) ||
|
|
36
|
+
typeof parsed !== 'object') {
|
|
37
|
+
throw new InvalidPayloadError(`${label} plaintext must be a JSON object`);
|
|
38
|
+
}
|
|
39
|
+
const record = parsed;
|
|
40
|
+
const keys = Object.keys(record).sort();
|
|
41
|
+
if (keys.length !== pedersenShareEnvelopeKeys.length ||
|
|
42
|
+
!keys.every((key, index) => key === pedersenShareEnvelopeKeys[index])) {
|
|
43
|
+
throw new InvalidPayloadError(`${label} plaintext must contain only blindingValue, index, and secretValue`);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
blindingValue: bigintToFixedHex(parseCanonicalFixedHex(record.blindingValue, RISTRETTO_GROUP.scalarByteLength, `${label} blinding value`), RISTRETTO_GROUP.scalarByteLength),
|
|
47
|
+
index: assertCanonicalEnvelopeIndex(record.index, expectedParticipantIndex, label),
|
|
48
|
+
secretValue: bigintToFixedHex(parseCanonicalFixedHex(record.secretValue, RISTRETTO_GROUP.scalarByteLength, `${label} secret value`), RISTRETTO_GROUP.scalarByteLength),
|
|
49
|
+
};
|
|
50
|
+
};
|
|
4
51
|
/** Encodes one Pedersen share pair for encrypted dealer-to-recipient transport. */
|
|
5
52
|
export const encodePedersenShareEnvelope = (share, byteLength) => canonicalizeJson({
|
|
6
53
|
index: share.index,
|
|
@@ -11,19 +58,29 @@ export const encodePedersenShareEnvelope = (share, byteLength) => canonicalizeJs
|
|
|
11
58
|
});
|
|
12
59
|
/** Decodes one encrypted Pedersen share pair after envelope decryption. */
|
|
13
60
|
export const decodePedersenShareEnvelope = (plaintext, expectedParticipantIndex, label) => {
|
|
14
|
-
let
|
|
61
|
+
let decodedPlaintext;
|
|
15
62
|
try {
|
|
16
|
-
|
|
63
|
+
decodedPlaintext = pedersenShareTextDecoder.decode(plaintext);
|
|
17
64
|
}
|
|
18
65
|
catch {
|
|
19
66
|
throw new InvalidPayloadError(`${label} plaintext is not valid canonical JSON`);
|
|
20
67
|
}
|
|
21
|
-
|
|
22
|
-
|
|
68
|
+
let parsed;
|
|
69
|
+
try {
|
|
70
|
+
parsed = parsePedersenShareEnvelopeRecord(JSON.parse(decodedPlaintext), expectedParticipantIndex, label);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error instanceof InvalidPayloadError) {
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
throw new InvalidPayloadError(`${label} plaintext is not valid canonical JSON`);
|
|
77
|
+
}
|
|
78
|
+
if (canonicalizeJson(parsed) !== decodedPlaintext) {
|
|
79
|
+
throw new InvalidPayloadError(`${label} plaintext must use canonical JSON encoding`);
|
|
23
80
|
}
|
|
24
81
|
return {
|
|
25
82
|
index: parsed.index,
|
|
26
|
-
secretValue:
|
|
27
|
-
blindingValue:
|
|
83
|
+
secretValue: fixedHexToBigInt(parsed.secretValue),
|
|
84
|
+
blindingValue: fixedHexToBigInt(parsed.blindingValue),
|
|
28
85
|
};
|
|
29
86
|
};
|
|
@@ -2,9 +2,9 @@ import { type CryptoGroup } from '../core/index.js';
|
|
|
2
2
|
import type { EncodedPoint } from '../core/types.js';
|
|
3
3
|
import type { ComplaintPayload, SignedPayload } from '../protocol/types.js';
|
|
4
4
|
import type { VerifiedProtocolSignatures } from '../protocol/verification.js';
|
|
5
|
-
import type { EncryptedShareMatrix, VerifyDKGTranscriptInput } from './verification
|
|
5
|
+
import type { EncryptedShareMatrix, VerifyDKGTranscriptInput } from './verification.js';
|
|
6
6
|
export declare const buildEncryptedShareMatrix: (transcript: readonly SignedPayload[], participantCount: number) => EncryptedShareMatrix;
|
|
7
7
|
export declare const assertEncryptedShareCoverage: (encryptedShareMatrix: EncryptedShareMatrix, participantIndices: readonly number[]) => void;
|
|
8
|
-
export declare const parsePedersenCommitmentMap: (transcript: readonly SignedPayload[], threshold: number
|
|
8
|
+
export declare const parsePedersenCommitmentMap: (transcript: readonly SignedPayload[], threshold: number) => ReadonlyMap<number, readonly EncodedPoint[]>;
|
|
9
9
|
export declare const assertPedersenCommitmentCoverage: (pedersenCommitmentMap: ReadonlyMap<number, readonly EncodedPoint[]>, dealerIndices: readonly number[]) => void;
|
|
10
10
|
export declare const verifyComplaintOutcomes: (input: VerifyDKGTranscriptInput, verifiedSignatures: VerifiedProtocolSignatures, encryptedShareMatrix: EncryptedShareMatrix, pedersenCommitmentMap: ReadonlyMap<number, readonly EncodedPoint[]>, group: CryptoGroup, allowedParticipants: ReadonlySet<number>) => Promise<readonly ComplaintPayload[]>;
|