threshold-elgamal 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -94,7 +94,7 @@ The cryptographic threshold is derived internally from the accepted registration
94
94
 
95
95
  There is no supported `n-of-n` mode and no supported public `k-of-n` configuration.
96
96
 
97
- Transcript verification requires key-derivation confirmations from every qualified participant.
97
+ Transcript verification requires `key-derivation-confirmation` payloads from every qualified participant. In the current design those unanimous confirmations are part of verifier soundness: the library does not implement a public post-Feldman complaint/reconstruction phase, so the DKG verifier is participant-confirmed rather than fully public-data-only. Lowering confirmation acceptance to threshold-many is out of scope unless that missing public consistency machinery is added.
98
98
 
99
99
  See [Honest-majority voting flow](https://tenemo.github.io/threshold-elgamal/guides/three-participant-voting-flow/) for the full phase-by-phase transcript.
100
100
 
@@ -130,7 +130,7 @@ const rosterHash = await hashRosterEntries([
130
130
  const manifest = createElectionManifest({
131
131
  rosterHash,
132
132
  optionList: ["Option A", "Option B"],
133
- scoreRange: { min: 1, max: 10 },
133
+ scoreRange: { min: 0, max: 5 },
134
134
  });
135
135
 
136
136
  const manifestHash = await hashElectionManifest(manifest);
@@ -145,6 +145,10 @@ console.log(majorityThreshold(3)); // 2
145
145
  console.log(sessionId.length); // 64
146
146
  ```
147
147
 
148
+ The example uses `0..5` only as one concrete score range. The supported rule is
149
+ one manifest-declared contiguous range with non-negative bounds and
150
+ `scoreRange.max <= 100`.
151
+
148
152
  If your application consumes a complete public board, start with [Verifying a public board](https://tenemo.github.io/threshold-elgamal/guides/verifying-a-public-board/) and then move directly to the verifier entry point:
149
153
 
150
154
  ```typescript
@@ -158,7 +162,7 @@ const bundle: VerifyElectionCeremonyInput = {
158
162
  sessionId,
159
163
  dkgTranscript,
160
164
  ballotPayloads,
161
- ballotClosePayload,
165
+ ballotClosePayloads: [ballotClosePayload],
162
166
  decryptionSharePayloads,
163
167
  tallyPublications,
164
168
  };
@@ -173,6 +177,8 @@ if (!result.ok) {
173
177
  }
174
178
  ```
175
179
 
180
+ Pass the full published `ballot-close` slot in `ballotClosePayloads`, even when the normal case is one organizer payload. The verifier audits that slot, collapses only exact retransmissions, and requires exactly one accepted close record.
181
+
176
182
  The root package exposes the builders and lower-level helpers required for the documented ceremony, including:
177
183
 
178
184
  - manifest publication
@@ -78,9 +78,16 @@ export declare const deriveJointPublicKey: (feldmanCommitments: readonly {
78
78
  /**
79
79
  * Verifies a DKG transcript, its signatures, Feldman extraction proofs, the
80
80
  * exact claimed threshold degree, accepted complaint outcomes, the DKG
81
- * transcript hash, and the announced joint public key.
81
+ * transcript hash, the announced joint public key, and unanimous qualified
82
+ * participant key confirmations.
82
83
  *
83
84
  * This is the DKG-specific verifier that the full ceremony verifier delegates
84
- * to before it touches ballots or tally material.
85
+ * to before it touches ballots or tally material. It consumes a public signed
86
+ * transcript plus `key-derivation-confirmation` payloads from every qualified
87
+ * participant. It does not implement a public post-Feldman
88
+ * complaint/reconstruction phase, so it is a participant-confirmed transcript
89
+ * verifier rather than a fully public-data-only GJKR verifier. Lowering this
90
+ * requirement to threshold-many confirmations is out of scope unless that
91
+ * missing public consistency machinery is added.
85
92
  */
86
93
  export declare const verifyDKGTranscript: (input: VerifyDKGTranscriptInput) => Promise<VerifiedDKGTranscript>;
@@ -659,10 +659,17 @@ const verifyCheckpointedDKGTranscript = async (input, setup) => {
659
659
  /**
660
660
  * Verifies a DKG transcript, its signatures, Feldman extraction proofs, the
661
661
  * exact claimed threshold degree, accepted complaint outcomes, the DKG
662
- * transcript hash, and the announced joint public key.
662
+ * transcript hash, the announced joint public key, and unanimous qualified
663
+ * participant key confirmations.
663
664
  *
664
665
  * This is the DKG-specific verifier that the full ceremony verifier delegates
665
- * to before it touches ballots or tally material.
666
+ * to before it touches ballots or tally material. It consumes a public signed
667
+ * transcript plus `key-derivation-confirmation` payloads from every qualified
668
+ * participant. It does not implement a public post-Feldman
669
+ * complaint/reconstruction phase, so it is a participant-confirmed transcript
670
+ * verifier rather than a fully public-data-only GJKR verifier. Lowering this
671
+ * requirement to threshold-many confirmations is out of scope unless that
672
+ * missing public consistency machinery is added.
666
673
  */
667
674
  export const verifyDKGTranscript = async (input) => {
668
675
  const auditedTranscript = await auditSignedPayloads(input.transcript);
@@ -136,7 +136,7 @@ export type FeldmanCommitmentPayload = BaseProtocolPayload & {
136
136
  }[];
137
137
  };
138
138
  /**
139
- * Final key-derivation confirmation payload for the derived joint key.
139
+ * Final participant confirmation payload for the derived joint key.
140
140
  */
141
141
  export type KeyDerivationConfirmation = BaseProtocolPayload & {
142
142
  readonly messageType: 'key-derivation-confirmation';
@@ -279,14 +279,15 @@ export type VerifyDecryptionSharePayloadsByOptionInput = {
279
279
  * Input bundle for full ceremony verification across all published options.
280
280
  *
281
281
  * This is the top-level verifier input that an auditor or bulletin-board
282
- * reader supplies when replaying a full ceremony.
282
+ * reader supplies when replaying a full ceremony from the published board,
283
+ * including the full ballot-close slot instead of a preselected close record.
283
284
  */
284
285
  export type VerifyElectionCeremonyInput = {
285
286
  readonly manifest: ElectionManifest;
286
287
  readonly sessionId: string;
287
288
  readonly dkgTranscript: readonly SignedPayload[];
288
289
  readonly ballotPayloads: readonly SignedPayload<BallotSubmissionPayload>[];
289
- readonly ballotClosePayload: SignedPayload<BallotClosePayload>;
290
+ readonly ballotClosePayloads: readonly SignedPayload<BallotClosePayload>[];
290
291
  readonly decryptionSharePayloads: readonly SignedPayload<DecryptionSharePayload>[];
291
292
  readonly tallyPublications?: readonly SignedPayload<TallyPublicationPayload>[];
292
293
  };
@@ -4,8 +4,9 @@ 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
- * 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.
7
+ * This is the public entry point for applications that already collected
8
+ * signed ballot payloads and want the per-option verified ciphertext
9
+ * aggregates that feed threshold decryption. The helper re-audits the signed
10
+ * payloads before it decodes and aggregates them.
10
11
  */
11
12
  export declare const verifyBallotSubmissionPayloadsByOption: (input: VerifyBallotSubmissionPayloadsByOptionInput) => Promise<readonly VerifiedOptionBallotAggregation[]>;
@@ -40,9 +40,10 @@ const verifyAuditedBallotSubmissionPayloadsByOption = async (input) => {
40
40
  * Verifies typed ballot-submission payloads and recomputes one aggregate tally
41
41
  * ciphertext per manifest option.
42
42
  *
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.
43
+ * This is the public entry point for applications that already collected
44
+ * signed ballot payloads and want the per-option verified ciphertext
45
+ * aggregates that feed threshold decryption. The helper re-audits the signed
46
+ * payloads before it decodes and aggregates them.
46
47
  */
47
48
  export const verifyBallotSubmissionPayloadsByOption = async (input) => {
48
49
  const context = await buildVotingManifestContext(input.manifest, input.sessionId);
@@ -5,6 +5,7 @@ import type { VerifiedOptionDecryptionShares, VerifyDecryptionSharePayloadsByOpt
5
5
  *
6
6
  * This is the public entry point for applications that have already accepted a
7
7
  * DKG transcript and verified ballot aggregates and now need to validate the
8
- * published threshold shares.
8
+ * published threshold shares. The helper re-audits the signed share payloads
9
+ * before it groups and verifies them.
9
10
  */
10
11
  export declare const verifyDecryptionSharePayloadsByOption: (input: VerifyDecryptionSharePayloadsByOptionInput) => Promise<readonly VerifiedOptionDecryptionShares[]>;
@@ -98,7 +98,8 @@ const verifyAuditedDecryptionSharePayloadsByOption = async (input) => {
98
98
  *
99
99
  * This is the public entry point for applications that have already accepted a
100
100
  * DKG transcript and verified ballot aggregates and now need to validate the
101
- * published threshold shares.
101
+ * published threshold shares. The helper re-audits the signed share payloads
102
+ * before it groups and verifies them.
102
103
  */
103
104
  export const verifyDecryptionSharePayloadsByOption = async (input) => {
104
105
  const context = await buildVotingManifestContext(input.manifest, input.sessionId);
@@ -66,8 +66,8 @@ export type ElectionVerificationResult = {
66
66
  };
67
67
  /**
68
68
  * Replays the published ceremony from manifest to tally, including board audit,
69
- * DKG verification, ballot verification, decryption-share verification, and
70
- * per-option tally checks.
69
+ * DKG verification, full ballot-close-slot audit, ballot verification,
70
+ * decryption-share verification, and per-option tally checks.
71
71
  *
72
72
  * This is the main verifier entry point for callers that want failures to
73
73
  * abort immediately.
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * This module is the shortest path for auditors and bulletin-board readers who
5
5
  * want one call that replays manifest validation, board audit, DKG, ballots,
6
- * decryption shares, and tally checks.
6
+ * the full ballot-close slot, decryption shares, and tally checks.
7
7
  */
8
8
  import { InvalidPayloadError } from '../core/index.js';
9
9
  import { decodeScalar } from '../core/ristretto.js';
@@ -127,8 +127,8 @@ const verifyBallotClosePayload = (input) => {
127
127
  };
128
128
  /**
129
129
  * Replays the published ceremony from manifest to tally, including board audit,
130
- * DKG verification, ballot verification, decryption-share verification, and
131
- * per-option tally checks.
130
+ * DKG verification, full ballot-close-slot audit, ballot verification,
131
+ * decryption-share verification, and per-option tally checks.
132
132
  *
133
133
  * This is the main verifier entry point for callers that want failures to
134
134
  * abort immediately.
@@ -150,9 +150,7 @@ export const verifyElectionCeremony = async (input) => {
150
150
  try {
151
151
  dkgAudit = await auditSignedPayloads(input.dkgTranscript);
152
152
  ballotAudit = await auditSignedPayloads(input.ballotPayloads);
153
- ballotCloseAudit = await auditSignedPayloads([
154
- input.ballotClosePayload,
155
- ]);
153
+ ballotCloseAudit = await auditSignedPayloads(input.ballotClosePayloads);
156
154
  decryptionAudit = await auditSignedPayloads(input.decryptionSharePayloads);
157
155
  tallyAudit =
158
156
  input.tallyPublications === undefined ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "threshold-elgamal",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "A browser-native TypeScript ElGamal library for Ristretto255-based research prototypes, shipping additive ElGamal, threshold decryption, protocol helpers, board auditing, transport primitives, and log-driven DKG state machines.",
5
5
  "author": "Piotr Piech <piotr@piech.dev>",
6
6
  "license": "MPL-2.0",