threshold-elgamal 1.0.9 → 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.
Files changed (98) hide show
  1. package/README.md +33 -35
  2. package/dist/core/bigint.d.ts +2 -1
  3. package/dist/core/bigint.js +6 -1
  4. package/dist/core/bytes.js +4 -0
  5. package/dist/core/crypto.js +5 -0
  6. package/dist/core/errors.d.ts +50 -11
  7. package/dist/core/errors.js +50 -11
  8. package/dist/core/groups.d.ts +6 -1
  9. package/dist/core/groups.js +13 -2
  10. package/dist/core/index.d.ts +4 -0
  11. package/dist/core/index.js +4 -0
  12. package/dist/core/public.d.ts +13 -0
  13. package/dist/core/public.js +12 -0
  14. package/dist/core/random.js +4 -0
  15. package/dist/core/ristretto.d.ts +7 -0
  16. package/dist/core/ristretto.js +7 -0
  17. package/dist/core/types.d.ts +14 -6
  18. package/dist/core/validation.d.ts +45 -12
  19. package/dist/core/validation.js +52 -12
  20. package/dist/dkg/pedersen-share-codec.d.ts +12 -2
  21. package/dist/dkg/pedersen-share-codec.js +19 -2
  22. package/dist/dkg/public.d.ts +12 -0
  23. package/dist/dkg/public.js +11 -0
  24. package/dist/dkg/verification.d.ts +33 -14
  25. package/dist/dkg/verification.js +16 -12
  26. package/dist/elgamal/additive.d.ts +15 -3
  27. package/dist/elgamal/additive.js +27 -23
  28. package/dist/elgamal/bsgs.js +7 -0
  29. package/dist/elgamal/public.d.ts +11 -0
  30. package/dist/elgamal/public.js +10 -0
  31. package/dist/elgamal/types.d.ts +6 -0
  32. package/dist/index.d.ts +27 -25
  33. package/dist/index.js +20 -19
  34. package/dist/proofs/disjunctive.d.ts +11 -16
  35. package/dist/proofs/disjunctive.js +12 -17
  36. package/dist/proofs/dleq.d.ts +17 -13
  37. package/dist/proofs/dleq.js +11 -12
  38. package/dist/proofs/helpers.d.ts +1 -1
  39. package/dist/proofs/helpers.js +5 -4
  40. package/dist/proofs/nonces.js +6 -0
  41. package/dist/proofs/public.d.ts +12 -0
  42. package/dist/proofs/public.js +11 -0
  43. package/dist/proofs/schnorr.d.ts +10 -11
  44. package/dist/proofs/schnorr.js +10 -11
  45. package/dist/proofs/types.d.ts +30 -5
  46. package/dist/protocol/board-audit.d.ts +8 -3
  47. package/dist/protocol/board-audit.js +8 -2
  48. package/dist/protocol/builders.d.ts +77 -13
  49. package/dist/protocol/builders.js +83 -13
  50. package/dist/protocol/canonical-json.js +4 -0
  51. package/dist/protocol/manifest.d.ts +32 -15
  52. package/dist/protocol/manifest.js +65 -18
  53. package/dist/protocol/ordering.js +6 -0
  54. package/dist/protocol/payloads.d.ts +3 -3
  55. package/dist/protocol/payloads.js +7 -3
  56. package/dist/protocol/public.d.ts +17 -0
  57. package/dist/protocol/public.js +16 -0
  58. package/dist/protocol/score-range.d.ts +6 -0
  59. package/dist/protocol/score-range.js +24 -0
  60. package/dist/protocol/transcript.js +4 -0
  61. package/dist/protocol/types.d.ts +110 -17
  62. package/dist/protocol/verification.d.ts +14 -7
  63. package/dist/protocol/verification.js +7 -5
  64. package/dist/protocol/voting-ballot-aggregation.d.ts +10 -2
  65. package/dist/protocol/voting-ballot-aggregation.js +6 -0
  66. package/dist/protocol/voting-ballots.d.ts +3 -5
  67. package/dist/protocol/voting-ballots.js +6 -5
  68. package/dist/protocol/voting-codecs.d.ts +8 -3
  69. package/dist/protocol/voting-codecs.js +18 -2
  70. package/dist/protocol/voting-decryption.d.ts +3 -5
  71. package/dist/protocol/voting-decryption.js +7 -5
  72. package/dist/protocol/voting-shared.d.ts +2 -1
  73. package/dist/protocol/voting-shared.js +9 -2
  74. package/dist/protocol/voting-verification.d.ts +27 -30
  75. package/dist/protocol/voting-verification.js +13 -26
  76. package/dist/serialize/encoding.js +4 -0
  77. package/dist/threshold/decrypt.d.ts +13 -5
  78. package/dist/threshold/decrypt.js +20 -5
  79. package/dist/threshold/public.d.ts +11 -0
  80. package/dist/threshold/public.js +10 -0
  81. package/dist/threshold/types.d.ts +23 -4
  82. package/dist/transport/auth.d.ts +10 -16
  83. package/dist/transport/auth.js +16 -16
  84. package/dist/transport/complaints.d.ts +2 -4
  85. package/dist/transport/complaints.js +8 -4
  86. package/dist/transport/envelopes.d.ts +5 -7
  87. package/dist/transport/envelopes.js +9 -7
  88. package/dist/transport/key-agreement.d.ts +15 -23
  89. package/dist/transport/key-agreement.js +21 -23
  90. package/dist/transport/types.d.ts +16 -2
  91. package/dist/vss/feldman.d.ts +11 -1
  92. package/dist/vss/feldman.js +11 -1
  93. package/dist/vss/pedersen.d.ts +13 -0
  94. package/dist/vss/pedersen.js +13 -0
  95. package/dist/vss/public.d.ts +10 -0
  96. package/dist/vss/public.js +9 -0
  97. package/dist/vss/types.d.ts +4 -0
  98. package/package.json +43 -14
@@ -1,12 +1,26 @@
1
+ /**
2
+ * Public protocol payload and verification types.
3
+ *
4
+ * These types describe the signed board records that move through manifest
5
+ * publication, DKG, ballot casting, decryption-share publication, and final
6
+ * tally verification.
7
+ */
1
8
  import type { EncodedPoint } from '../core/types.js';
2
9
  import type { VerifiedDKGTranscript } from '../dkg/verification.js';
3
10
  import type { DecryptionShare } from '../threshold/types.js';
4
11
  import type { EncodedAuthPublicKey, EncodedTransportPrivateKey, EncodedTransportPublicKey } from '../transport/types.js';
5
- /** Canonical protocol payload type identifiers. */
12
+ /**
13
+ * Canonical protocol payload type identifiers for the supported ceremony.
14
+ */
6
15
  export type ProtocolMessageType = 'manifest-publication' | 'registration' | 'manifest-acceptance' | 'phase-checkpoint' | 'pedersen-commitment' | 'encrypted-dual-share' | 'complaint' | 'complaint-resolution' | 'feldman-commitment' | 'key-derivation-confirmation' | 'ballot-submission' | 'ballot-close' | 'decryption-share' | 'tally-publication';
7
16
  /** Complaint reasons recognized by the protocol layer. */
8
17
  export type ComplaintReason = 'aes-gcm-failure' | 'malformed-plaintext' | 'pedersen-failure';
9
- /** Shared fields present on every unsigned protocol payload. */
18
+ /**
19
+ * Shared fields present on every unsigned protocol payload.
20
+ *
21
+ * These fields bind each payload to one manifest, session, participant, and
22
+ * protocol phase.
23
+ */
10
24
  export type BaseProtocolPayload = {
11
25
  readonly protocolVersion: string;
12
26
  readonly sessionId: string;
@@ -34,14 +48,23 @@ export type EncodedDisjunctiveBranch = {
34
48
  export type EncodedDisjunctiveProof = {
35
49
  readonly branches: readonly EncodedDisjunctiveBranch[];
36
50
  };
37
- /** Registration payload carrying ceremony auth and transport keys. */
51
+ /**
52
+ * Registration payload carrying ceremony auth and transport keys.
53
+ *
54
+ * This is the public source of truth for the accepted roster.
55
+ */
38
56
  export type RegistrationPayload = BaseProtocolPayload & {
39
57
  readonly messageType: 'registration';
40
58
  readonly rosterHash: string;
41
59
  readonly authPublicKey: EncodedAuthPublicKey;
42
60
  readonly transportPublicKey: EncodedTransportPublicKey;
43
61
  };
44
- /** Participant-signed manifest acceptance payload. */
62
+ /**
63
+ * Participant-signed manifest acceptance payload.
64
+ *
65
+ * This records acceptance of the frozen manifest and roster hash for one
66
+ * assigned participant index.
67
+ */
45
68
  export type ManifestAcceptancePayload = BaseProtocolPayload & {
46
69
  readonly messageType: 'manifest-acceptance';
47
70
  readonly rosterHash: string;
@@ -55,12 +78,21 @@ export type PhaseCheckpointPayload = BaseProtocolPayload & {
55
78
  readonly checkpointTranscriptHash: string;
56
79
  readonly qualifiedParticipantIndices: readonly number[];
57
80
  };
58
- /** Broadcast payload carrying Pedersen coefficient commitments. */
81
+ /**
82
+ * Broadcast payload carrying Pedersen coefficient commitments.
83
+ *
84
+ * This belongs to DKG phase 1.
85
+ */
59
86
  export type PedersenCommitmentPayload = BaseProtocolPayload & {
60
87
  readonly messageType: 'pedersen-commitment';
61
88
  readonly commitments: readonly string[];
62
89
  };
63
- /** Encrypted share-envelope payload for the share-distribution step. */
90
+ /**
91
+ * Encrypted share-envelope payload for the share-distribution step.
92
+ *
93
+ * This publishes the sender-ephemeral envelope metadata for one dealer to one
94
+ * recipient in DKG phase 1.
95
+ */
64
96
  export type EncryptedDualSharePayload = BaseProtocolPayload & {
65
97
  readonly messageType: 'encrypted-dual-share';
66
98
  readonly recipientIndex: number;
@@ -89,7 +121,11 @@ export type ComplaintResolutionPayload = BaseProtocolPayload & {
89
121
  readonly suite: 'X25519';
90
122
  readonly revealedEphemeralPrivateKey: EncodedTransportPrivateKey;
91
123
  };
92
- /** Broadcast payload carrying Feldman commitments and coefficient proofs. */
124
+ /**
125
+ * Broadcast payload carrying Feldman commitments and coefficient proofs.
126
+ *
127
+ * This belongs to DKG phase 3.
128
+ */
93
129
  export type FeldmanCommitmentPayload = BaseProtocolPayload & {
94
130
  readonly messageType: 'feldman-commitment';
95
131
  readonly commitments: readonly string[];
@@ -107,19 +143,32 @@ export type KeyDerivationConfirmation = BaseProtocolPayload & {
107
143
  readonly dkgTranscriptHash: string;
108
144
  readonly publicKey: EncodedPoint;
109
145
  };
110
- /** Signed manifest-publication payload anchoring the frozen manifest. */
146
+ /**
147
+ * Signed manifest-publication payload anchoring the frozen manifest.
148
+ *
149
+ * This is the first public payload in the supported ceremony.
150
+ */
111
151
  export type ManifestPublicationPayload = BaseProtocolPayload & {
112
152
  readonly messageType: 'manifest-publication';
113
153
  readonly manifest: ElectionManifest;
114
154
  };
115
- /** Signed additive ballot payload for one participant and one option slot. */
155
+ /**
156
+ * Signed additive ballot payload for one participant and one option slot.
157
+ *
158
+ * A complete voter ballot is represented as one such payload per option.
159
+ */
116
160
  export type BallotSubmissionPayload = BaseProtocolPayload & {
117
161
  readonly messageType: 'ballot-submission';
118
162
  readonly optionIndex: number;
119
163
  readonly ciphertext: EncodedCiphertext;
120
164
  readonly proof: EncodedDisjunctiveProof;
121
165
  };
122
- /** Signed organizer payload that freezes which participants are counted. */
166
+ /**
167
+ * Signed organizer payload that freezes which participants are counted.
168
+ *
169
+ * This is the cutoff record that determines the accepted ballot set used for
170
+ * tallying.
171
+ */
123
172
  export type BallotClosePayload = BaseProtocolPayload & {
124
173
  readonly messageType: 'ballot-close';
125
174
  readonly countedParticipantIndices: readonly number[];
@@ -136,7 +185,12 @@ export type DecryptionSharePayload = BaseProtocolPayload & {
136
185
  readonly decryptionShare: EncodedPoint;
137
186
  readonly proof: EncodedCompactProof;
138
187
  };
139
- /** Signed tally-publication payload for the recovered additive tally. */
188
+ /**
189
+ * Signed tally-publication payload for the recovered additive tally.
190
+ *
191
+ * This is the optional published record checked against the verifier's own
192
+ * recomputed tally.
193
+ */
140
194
  export type TallyPublicationPayload = BaseProtocolPayload & {
141
195
  readonly messageType: 'tally-publication';
142
196
  readonly optionIndex: number;
@@ -145,18 +199,42 @@ export type TallyPublicationPayload = BaseProtocolPayload & {
145
199
  readonly tally: string;
146
200
  readonly decryptionParticipantIndices: readonly number[];
147
201
  };
148
- /** Union of all unsigned protocol payload shapes. */
202
+ /**
203
+ * Union of all unsigned protocol payload shapes that may appear on the board.
204
+ */
149
205
  export type ProtocolPayload = ManifestPublicationPayload | RegistrationPayload | ManifestAcceptancePayload | PhaseCheckpointPayload | PedersenCommitmentPayload | EncryptedDualSharePayload | ComplaintPayload | ComplaintResolutionPayload | FeldmanCommitmentPayload | KeyDerivationConfirmation | BallotSubmissionPayload | BallotClosePayload | DecryptionSharePayload | TallyPublicationPayload;
150
- /** Unsigned protocol payload paired with an authentication signature. */
206
+ /**
207
+ * Unsigned protocol payload paired with an authentication signature.
208
+ *
209
+ * This is the canonical board record shape accepted by the audit and
210
+ * verification layers.
211
+ */
151
212
  export type SignedPayload<TPayload extends ProtocolPayload = ProtocolPayload> = {
152
213
  readonly payload: TPayload;
153
214
  /** Raw Ed25519 signature bytes encoded as lowercase hex. */
154
215
  readonly signature: string;
155
216
  };
156
- /** Canonical election-manifest shape bound into protocol transcripts. */
217
+ /**
218
+ * Canonical election-manifest shape bound into protocol transcripts.
219
+ *
220
+ * The manifest is intentionally compact: it fixes the frozen roster hash,
221
+ * option list, and one explicit global score range, while participant count
222
+ * and threshold are derived later from the accepted registration roster.
223
+ */
224
+ export type ScoreRange = {
225
+ readonly min: number;
226
+ readonly max: number;
227
+ };
228
+ /**
229
+ * Canonical election-manifest shape bound into protocol transcripts.
230
+ *
231
+ * The score range applies uniformly to every option slot in the supported
232
+ * workflow.
233
+ */
157
234
  export type ElectionManifest = {
158
235
  readonly rosterHash: string;
159
236
  readonly optionList: readonly string[];
237
+ readonly scoreRange: ScoreRange;
160
238
  };
161
239
  /**
162
240
  * Input bundle for verifying typed ballot payloads.
@@ -169,7 +247,12 @@ type VerifyBallotSubmissionPayloadsInput = {
169
247
  };
170
248
  /** Input bundle for verifying typed ballot payloads across all options. */
171
249
  export type VerifyBallotSubmissionPayloadsByOptionInput = VerifyBallotSubmissionPayloadsInput;
172
- /** Verified typed decryption-share payload. */
250
+ /**
251
+ * Verified typed decryption-share payload.
252
+ *
253
+ * This pairs the original signed payload with the decoded low-level share used
254
+ * in final tally recomputation.
255
+ */
173
256
  export type VerifiedDecryptionSharePayload = {
174
257
  readonly payload: SignedPayload<DecryptionSharePayload>;
175
258
  readonly share: DecryptionShare;
@@ -192,7 +275,12 @@ export type VerifyDecryptionSharePayloadsByOptionInput = {
192
275
  readonly manifest: ElectionManifest;
193
276
  readonly sessionId: string;
194
277
  };
195
- /** Input bundle for full ceremony verification across all published options. */
278
+ /**
279
+ * Input bundle for full ceremony verification across all published options.
280
+ *
281
+ * This is the top-level verifier input that an auditor or bulletin-board
282
+ * reader supplies when replaying a full ceremony.
283
+ */
196
284
  export type VerifyElectionCeremonyInput = {
197
285
  readonly manifest: ElectionManifest;
198
286
  readonly sessionId: string;
@@ -202,7 +290,12 @@ export type VerifyElectionCeremonyInput = {
202
290
  readonly decryptionSharePayloads: readonly SignedPayload<DecryptionSharePayload>[];
203
291
  readonly tallyPublications?: readonly SignedPayload<TallyPublicationPayload>[];
204
292
  };
205
- /** Verified published tally for one option slot. */
293
+ /**
294
+ * Verified published tally for one option slot.
295
+ *
296
+ * The full ceremony verifier returns one of these per manifest option after
297
+ * replaying ballots, decryption shares, and optional tally publications.
298
+ */
206
299
  export type VerifiedPublishedOptionVotingResult = {
207
300
  readonly optionIndex: number;
208
301
  readonly ballots: import('./voting-ballot-aggregation.js').VerifiedOptionBallotAggregation;
@@ -1,12 +1,21 @@
1
1
  import type { EncodedAuthPublicKey, EncodedTransportPublicKey } from '../transport/types.js';
2
2
  import type { RegistrationPayload, SignedPayload } from './types.js';
3
- /** Roster entry used for deterministic roster hashing. */
3
+ /**
4
+ * Roster entry used for deterministic roster hashing.
5
+ *
6
+ * This is the minimal frozen identity view derived from accepted registrations.
7
+ */
4
8
  export type RosterEntry = {
5
9
  readonly participantIndex: number;
6
10
  readonly authPublicKey: EncodedAuthPublicKey;
7
11
  readonly transportPublicKey: EncodedTransportPublicKey;
8
12
  };
9
- /** Verified protocol-signature result with the frozen registration roster. */
13
+ /**
14
+ * Verified protocol-signature result with the frozen registration roster.
15
+ *
16
+ * Downstream verifiers reuse this result instead of rebuilding the roster map
17
+ * for every phase.
18
+ */
10
19
  export type VerifiedProtocolSignatures = {
11
20
  readonly participantCount: number;
12
21
  readonly registrations: readonly SignedPayload<RegistrationPayload>[];
@@ -16,8 +25,7 @@ export type VerifiedProtocolSignatures = {
16
25
  /**
17
26
  * Hashes a deterministic roster view with SHA-256.
18
27
  *
19
- * @param rosterEntries Deterministic roster entries.
20
- * @returns Lowercase hexadecimal roster hash.
28
+ * Manifest creation and registration verification both depend on this digest.
21
29
  */
22
30
  export declare const hashRosterEntries: (rosterEntries: readonly RosterEntry[]) => Promise<string>;
23
31
  /**
@@ -28,8 +36,7 @@ export declare const hashRosterEntries: (rosterEntries: readonly RosterEntry[])
28
36
  * same registration payload. Every later payload is verified against the
29
37
  * registered auth key for its participant index.
30
38
  *
31
- * @param signedPayloads Signed protocol payloads.
32
- * @param participantCount Optional expected participant count.
33
- * @returns Verified registration roster and derived roster hash.
39
+ * This is the signature gate that stands between raw board data and every
40
+ * later semantic verifier.
34
41
  */
35
42
  export declare const verifySignedProtocolPayloads: (signedPayloads: readonly SignedPayload[], participantCount?: number) => Promise<VerifiedProtocolSignatures>;
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Registration-roster hashing and signature verification for published
3
+ * protocol payloads.
4
+ */
1
5
  import { bytesToHex } from '../core/bytes.js';
2
6
  import { InvalidPayloadError, assertValidParticipantIndex, sha256, utf8ToBytes, } from '../core/index.js';
3
7
  import { importAuthPublicKey, verifyPayloadSignature } from '../transport/auth.js';
@@ -51,8 +55,7 @@ const canonicalizeRosterEntries = (rosterEntries) => {
51
55
  /**
52
56
  * Hashes a deterministic roster view with SHA-256.
53
57
  *
54
- * @param rosterEntries Deterministic roster entries.
55
- * @returns Lowercase hexadecimal roster hash.
58
+ * Manifest creation and registration verification both depend on this digest.
56
59
  */
57
60
  export const hashRosterEntries = async (rosterEntries) => bytesToHex(await sha256(utf8ToBytes(canonicalizeRosterEntries(rosterEntries))));
58
61
  const assertUniqueParticipantIndices = (payloads, participantCount) => {
@@ -89,9 +92,8 @@ const registrationKey = (payload) => ({
89
92
  * same registration payload. Every later payload is verified against the
90
93
  * registered auth key for its participant index.
91
94
  *
92
- * @param signedPayloads Signed protocol payloads.
93
- * @param participantCount Optional expected participant count.
94
- * @returns Verified registration roster and derived roster hash.
95
+ * This is the signature gate that stands between raw board data and every
96
+ * later semantic verifier.
95
97
  */
96
98
  export const verifySignedProtocolPayloads = async (signedPayloads, participantCount) => {
97
99
  const registrations = signedPayloads.filter((payload) => payload.payload.messageType === 'registration');
@@ -9,13 +9,21 @@ export type BallotTranscriptEntry = {
9
9
  readonly ciphertext: ElGamalCiphertext;
10
10
  readonly proof: DisjunctiveProof;
11
11
  };
12
- /** Result of verifying and aggregating a ballot transcript. */
12
+ /**
13
+ * Result of verifying and aggregating a ballot transcript.
14
+ *
15
+ * This is the bridge between ballot verification and threshold decryption.
16
+ */
13
17
  export type VerifiedBallotAggregation = {
14
18
  readonly aggregate: VerifiedAggregateCiphertext;
15
19
  readonly ballots: readonly BallotTranscriptEntry[];
16
20
  readonly transcriptHash: string;
17
21
  };
18
- /** Verified additive ballot aggregation for one manifest option slot. */
22
+ /**
23
+ * Verified additive ballot aggregation for one manifest option slot.
24
+ *
25
+ * The full ceremony verifier works one option at a time at this stage.
26
+ */
19
27
  export type VerifiedOptionBallotAggregation = VerifiedBallotAggregation & {
20
28
  readonly optionIndex: number;
21
29
  };
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Ballot transcript verification and additive aggregation helpers.
3
+ *
4
+ * This module turns verified ballot ciphertexts into per-option aggregates that
5
+ * the decryption and tally stages can consume.
6
+ */
1
7
  import { bytesToHex } from '../core/bytes.js';
2
8
  import { InvalidPayloadError, RISTRETTO_GROUP, assertInSubgroup, sha256, utf8ToBytes, } from '../core/index.js';
3
9
  import { encodePoint, RISTRETTO_ZERO } from '../core/ristretto.js';
@@ -4,10 +4,8 @@ import { type VerifiedOptionBallotAggregation } from './voting-ballot-aggregatio
4
4
  * Verifies typed ballot-submission payloads and recomputes one aggregate tally
5
5
  * ciphertext per manifest option.
6
6
  *
7
- * Signatures are expected to have been checked already against the frozen
8
- * registration roster.
9
- *
10
- * @param input Typed ballot verification input.
11
- * @returns Ordered per-option additive ballot aggregations.
7
+ * This is the public entry point for applications that already have
8
+ * signature-checked ballot payloads and want the per-option verified
9
+ * ciphertext aggregates that feed threshold decryption.
12
10
  */
13
11
  export declare const verifyBallotSubmissionPayloadsByOption: (input: VerifyBallotSubmissionPayloadsByOptionInput) => Promise<readonly VerifiedOptionBallotAggregation[]>;
@@ -1,3 +1,6 @@
1
+ /**
2
+ * Public ballot-verification entry point for the supported score-voting flow.
3
+ */
1
4
  import { InvalidPayloadError } from '../core/index.js';
2
5
  import { auditSignedPayloads } from './board-audit.js';
3
6
  import { verifyAndAggregateBallotsByOption, } from './voting-ballot-aggregation.js';
@@ -37,11 +40,9 @@ const verifyAuditedBallotSubmissionPayloadsByOption = async (input) => {
37
40
  * Verifies typed ballot-submission payloads and recomputes one aggregate tally
38
41
  * ciphertext per manifest option.
39
42
  *
40
- * Signatures are expected to have been checked already against the frozen
41
- * registration roster.
42
- *
43
- * @param input Typed ballot verification input.
44
- * @returns Ordered per-option additive ballot aggregations.
43
+ * This is the public entry point for applications that already have
44
+ * signature-checked ballot payloads and want the per-option verified
45
+ * ciphertext aggregates that feed threshold decryption.
45
46
  */
46
47
  export const verifyBallotSubmissionPayloadsByOption = async (input) => {
47
48
  const context = await buildVotingManifestContext(input.manifest, input.sessionId);
@@ -1,6 +1,6 @@
1
1
  import type { ElGamalCiphertext } from '../elgamal/types.js';
2
2
  import type { DLEQProof, DisjunctiveProof } from '../proofs/types.js';
3
- import type { EncodedCiphertext, EncodedCompactProof, EncodedDisjunctiveProof } from './types.js';
3
+ import type { EncodedCiphertext, EncodedCompactProof, EncodedDisjunctiveProof, ScoreRange } from './types.js';
4
4
  /**
5
5
  * Encodes an additive ciphertext into fixed-width protocol hex.
6
6
  *
@@ -47,6 +47,11 @@ export declare const encodeDisjunctiveProof: (proof: DisjunctiveProof) => Encode
47
47
  */
48
48
  export declare const decodeDisjunctiveProof: (proof: EncodedDisjunctiveProof) => DisjunctiveProof;
49
49
  /**
50
- * Returns the fixed shipped score-voting domain `1..10`.
50
+ * Expands one inclusive contiguous score range into its allowed plaintext
51
+ * domain.
52
+ *
53
+ * Ballot builders and verifiers pass the resulting values into the
54
+ * disjunctive-proof layer so each encrypted score can be proven to belong to
55
+ * the manifest-declared domain.
51
56
  */
52
- export declare const scoreVotingDomain: () => readonly bigint[];
57
+ export declare const scoreRangeDomain: (scoreRange: ScoreRange) => readonly bigint[];
@@ -1,4 +1,9 @@
1
+ /**
2
+ * Encode and decode helpers that bridge low-level cryptographic objects and
3
+ * their published protocol payload representations.
4
+ */
1
5
  import { decodePoint, decodeScalar, encodeScalar } from '../core/ristretto.js';
6
+ import { validateSupportedScoreRange } from './score-range.js';
2
7
  /**
3
8
  * Encodes an additive ciphertext into fixed-width protocol hex.
4
9
  *
@@ -62,6 +67,17 @@ export const decodeDisjunctiveProof = (proof) => ({
62
67
  branches: proof.branches.map((branch) => decodeCompactProof(branch)),
63
68
  });
64
69
  /**
65
- * Returns the fixed shipped score-voting domain `1..10`.
70
+ * Expands one inclusive contiguous score range into its allowed plaintext
71
+ * domain.
72
+ *
73
+ * Ballot builders and verifiers pass the resulting values into the
74
+ * disjunctive-proof layer so each encrypted score can be proven to belong to
75
+ * the manifest-declared domain.
66
76
  */
67
- export const scoreVotingDomain = () => Object.freeze(Array.from({ length: 10 }, (_value, index) => BigInt(index + 1)));
77
+ export const scoreRangeDomain = (scoreRange) => {
78
+ validateSupportedScoreRange(scoreRange, {
79
+ min: 'Score range min',
80
+ max: 'Score range max',
81
+ });
82
+ return Object.freeze(Array.from({ length: scoreRange.max - scoreRange.min + 1 }, (_value, index) => BigInt(scoreRange.min + index)));
83
+ };
@@ -3,10 +3,8 @@ import type { VerifiedOptionDecryptionShares, VerifyDecryptionSharePayloadsByOpt
3
3
  * Verifies typed decryption-share payloads against the DKG transcript-derived
4
4
  * trustee keys and one locally recomputed aggregate ciphertext per option slot.
5
5
  *
6
- * Signatures are expected to have been checked already against the frozen
7
- * registration roster.
8
- *
9
- * @param input Typed decryption-share verification input.
10
- * @returns Verified decryption shares grouped by option.
6
+ * This is the public entry point for applications that have already accepted a
7
+ * DKG transcript and verified ballot aggregates and now need to validate the
8
+ * published threshold shares.
11
9
  */
12
10
  export declare const verifyDecryptionSharePayloadsByOption: (input: VerifyDecryptionSharePayloadsByOptionInput) => Promise<readonly VerifiedOptionDecryptionShares[]>;
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Public decryption-share verification entry point for the supported voting
3
+ * workflow.
4
+ */
1
5
  import { InvalidPayloadError, RISTRETTO_GROUP, assertInSubgroupOrIdentity, } from '../core/index.js';
2
6
  import { deriveTranscriptVerificationKey } from '../dkg/verification.js';
3
7
  import { verifyDLEQProof } from '../proofs/dleq.js';
@@ -92,11 +96,9 @@ const verifyAuditedDecryptionSharePayloadsByOption = async (input) => {
92
96
  * Verifies typed decryption-share payloads against the DKG transcript-derived
93
97
  * trustee keys and one locally recomputed aggregate ciphertext per option slot.
94
98
  *
95
- * Signatures are expected to have been checked already against the frozen
96
- * registration roster.
97
- *
98
- * @param input Typed decryption-share verification input.
99
- * @returns Verified decryption shares grouped by option.
99
+ * This is the public entry point for applications that have already accepted a
100
+ * DKG transcript and verified ballot aggregates and now need to validate the
101
+ * published threshold shares.
100
102
  */
101
103
  export const verifyDecryptionSharePayloadsByOption = async (input) => {
102
104
  const context = await buildVotingManifestContext(input.manifest, input.sessionId);
@@ -1,5 +1,5 @@
1
1
  import type { ProofContext } from '../proofs/types.js';
2
- import type { DecryptionSharePayload, ElectionManifest, ProtocolPayload, RegistrationPayload, SignedPayload, OptionAggregateInput } from './types.js';
2
+ import type { DecryptionSharePayload, ElectionManifest, RegistrationPayload, ProtocolPayload, SignedPayload, OptionAggregateInput } from './types.js';
3
3
  export declare const BALLOT_SUBMISSION_PHASE = 5;
4
4
  export declare const BALLOT_CLOSE_PHASE = 6;
5
5
  export declare const DECRYPTION_SHARE_PHASE = 7;
@@ -10,6 +10,7 @@ type VotingManifestContext = {
10
10
  readonly optionCount: number;
11
11
  readonly protocolVersion: string;
12
12
  readonly scoreDomainValues: readonly bigint[];
13
+ readonly scoreRangeMax: bigint;
13
14
  readonly sessionId: string;
14
15
  };
15
16
  export declare const assertPhase: (payload: ProtocolPayload, expectedPhase: number, label: string) => void;
@@ -1,8 +1,14 @@
1
+ /**
2
+ * Shared voting-workflow validation and context helpers.
3
+ *
4
+ * Ballot verification, decryption-share verification, tally verification, and
5
+ * the full ceremony verifier all reuse this layer.
6
+ */
1
7
  import { assertPositiveParticipantIndex, InvalidPayloadError, RISTRETTO_GROUP, } from '../core/index.js';
2
8
  import { importAuthPublicKey, verifyPayloadSignature } from '../transport/auth.js';
3
9
  import { hashElectionManifest, assertSupportedProtocolVersion, SHIPPED_PROTOCOL_VERSION, validateElectionManifest, } from './manifest.js';
4
10
  import { signedProtocolPayloadBytes } from './payloads.js';
5
- import { scoreVotingDomain } from './voting-codecs.js';
11
+ import { scoreRangeDomain } from './voting-codecs.js';
6
12
  export const BALLOT_SUBMISSION_PHASE = 5;
7
13
  export const BALLOT_CLOSE_PHASE = 6;
8
14
  export const DECRYPTION_SHARE_PHASE = 7;
@@ -47,7 +53,8 @@ export const buildVotingManifestContext = async (manifest, sessionId) => {
47
53
  manifestHash: await hashElectionManifest(validatedManifest),
48
54
  optionCount: validatedManifest.optionList.length,
49
55
  protocolVersion: SHIPPED_PROTOCOL_VERSION,
50
- scoreDomainValues: scoreVotingDomain(),
56
+ scoreDomainValues: scoreRangeDomain(validatedManifest.scoreRange),
57
+ scoreRangeMax: BigInt(validatedManifest.scoreRange.max),
51
58
  sessionId,
52
59
  };
53
60
  };
@@ -1,17 +1,31 @@
1
1
  import { type VerifiedDKGTranscript } from '../dkg/verification.js';
2
2
  import { type BoardAudit } from './board-audit.js';
3
3
  import type { BallotClosePayload, BallotSubmissionPayload, DecryptionSharePayload, ElectionManifest, VerifyElectionCeremonyInput, VerifiedPublishedOptionVotingResult, TallyPublicationPayload } from './types.js';
4
- /** Stable high-level failure codes for full ceremony verification. */
4
+ /**
5
+ * Stable high-level failure codes for full ceremony verification.
6
+ *
7
+ * Applications can key off these codes instead of parsing free-form error
8
+ * strings.
9
+ */
5
10
  export type ElectionVerificationErrorCode = 'MANIFEST_INVALID' | 'BOARD_INVALID' | 'DKG_INVALID' | 'SIGNATURE_INVALID' | 'BALLOT_INVALID' | 'DECRYPTION_INVALID' | 'TALLY_INVALID';
6
- /** Named verification stage used by the high-level ceremony verifier. */
11
+ /**
12
+ * Named verification stage used by the high-level ceremony verifier.
13
+ */
7
14
  export type ElectionVerificationStage = 'manifest' | 'board' | 'dkg' | 'signatures' | 'ballots' | 'decryption' | 'tally';
8
- /** Stable structured failure result returned by the non-throwing verifier. */
15
+ /**
16
+ * Stable structured failure result returned by the non-throwing verifier.
17
+ */
9
18
  export type ElectionVerificationFailure = {
10
19
  readonly code: ElectionVerificationErrorCode;
11
20
  readonly stage: ElectionVerificationStage;
12
21
  readonly reason: string;
13
22
  };
14
- /** Detailed successful output from full ceremony verification. */
23
+ /**
24
+ * Detailed successful output from full ceremony verification.
25
+ *
26
+ * This bundles the derived DKG material, accepted ballot set, per-option
27
+ * tallies, and deterministic board-audit output.
28
+ */
15
29
  export type VerifiedElectionCeremony = {
16
30
  readonly manifest: ElectionManifest;
17
31
  readonly manifestHash: string;
@@ -38,7 +52,11 @@ export type VerifiedElectionCeremony = {
38
52
  readonly dkg: VerifiedDKGTranscript;
39
53
  readonly options: readonly VerifiedPublishedOptionVotingResult[];
40
54
  };
41
- /** Non-throwing result shape for full ceremony verification. */
55
+ /**
56
+ * Non-throwing result shape for full ceremony verification.
57
+ *
58
+ * Most application integrations should prefer this over exceptions.
59
+ */
42
60
  export type ElectionVerificationResult = {
43
61
  readonly ok: true;
44
62
  readonly verified: VerifiedElectionCeremony;
@@ -51,36 +69,15 @@ export type ElectionVerificationResult = {
51
69
  * DKG verification, ballot verification, decryption-share verification, and
52
70
  * per-option tally checks.
53
71
  *
54
- * @param input Full public ceremony input bundle.
55
- * @returns Detailed verified ceremony output.
56
- *
57
- * @example
58
- * ```ts
59
- * const verified = await verifyElectionCeremony(bundle);
60
- *
61
- * console.log(verified.boardAudit.overall.fingerprint);
62
- * console.log(verified.countedParticipantIndices);
63
- * console.log(verified.perOptionTallies);
64
- * ```
72
+ * This is the main verifier entry point for callers that want failures to
73
+ * abort immediately.
65
74
  */
66
75
  export declare const verifyElectionCeremony: (input: VerifyElectionCeremonyInput) => Promise<VerifiedElectionCeremony>;
67
76
  /**
68
77
  * Runs the full ceremony verifier and returns a structured success-or-failure
69
78
  * result without throwing.
70
79
  *
71
- * @param input Full public ceremony input bundle.
72
- * @returns Verified ceremony output or a stable structured failure.
73
- *
74
- * @example
75
- * ```ts
76
- * const result = await tryVerifyElectionCeremony(bundle);
77
- *
78
- * if (!result.ok) {
79
- * console.error(result.error.stage, result.error.code, result.error.reason);
80
- * return;
81
- * }
82
- *
83
- * console.log(result.verified.qualifiedParticipantIndices);
84
- * ```
80
+ * This is the preferred integration point for applications that want stable
81
+ * failure stages and codes instead of exception handling.
85
82
  */
86
83
  export declare const tryVerifyElectionCeremony: (input: VerifyElectionCeremonyInput) => Promise<ElectionVerificationResult>;