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,6 +1,17 @@
1
+ /**
2
+ * Shared invariant checks for points, scalars, thresholds, and participant
3
+ * indices.
4
+ *
5
+ * These helpers sit underneath every higher-level subsystem: proofs, VSS, DKG,
6
+ * ballot handling, and the full ceremony verifier all route through them.
7
+ */
1
8
  import { IndexOutOfRangeError, InvalidGroupElementError, InvalidScalarError, PlaintextDomainError, ThresholdViolationError, } from './errors.js';
2
9
  import { decodePoint, RISTRETTO_ORDER } from './ristretto.js';
3
- /** Returns `true` when the value is a non-identity valid Ristretto point. */
10
+ /**
11
+ * Returns `true` when the value is a canonical non-identity Ristretto point.
12
+ *
13
+ * This is the predicate used for public keys and most commitment elements.
14
+ */
4
15
  export const isInSubgroup = (value) => {
5
16
  try {
6
17
  return !decodePoint(value).is0();
@@ -9,7 +20,13 @@ export const isInSubgroup = (value) => {
9
20
  return false;
10
21
  }
11
22
  };
12
- /** Returns `true` when the value is a valid Ristretto point, including identity. */
23
+ /**
24
+ * Returns `true` when the value is a canonical Ristretto point, including the
25
+ * identity element.
26
+ *
27
+ * This variant is used for ciphertext components and transcript values that
28
+ * are allowed to land on the identity.
29
+ */
13
30
  export const isInSubgroupOrIdentity = (value) => {
14
31
  try {
15
32
  decodePoint(value);
@@ -70,11 +87,11 @@ export const assertThreshold = (threshold, participantCount) => {
70
87
  }
71
88
  };
72
89
  /**
73
- * Derives the shipped GJKR honest-majority threshold `ceil(n / 2)`.
90
+ * Derives the supported honest-majority threshold `ceil(n / 2)`.
74
91
  *
75
- * For odd `n` this matches the usual strict-majority value. For even `n` the
76
- * shipped protocol uses the maximal honest-majority instantiation proved for
77
- * GJKR, which yields `k = n / 2`.
92
+ * This is the threshold policy used by the package's DKG and full voting flow.
93
+ * Callers do not choose a custom `k`; the verifier derives it from the
94
+ * accepted registration roster.
78
95
  */
79
96
  export const majorityThreshold = (participantCount) => {
80
97
  if (!Number.isInteger(participantCount) || participantCount < 1) {
@@ -83,7 +100,7 @@ export const majorityThreshold = (participantCount) => {
83
100
  return Math.ceil(participantCount / 2);
84
101
  };
85
102
  /**
86
- * Validates that the supplied threshold matches the shipped GJKR
103
+ * Validates that the supplied threshold matches the library's GJKR
87
104
  * honest-majority policy.
88
105
  */
89
106
  export const assertMajorityThreshold = (threshold, participantCount) => {
@@ -97,7 +114,12 @@ export const assertMajorityThreshold = (threshold, participantCount) => {
97
114
  }
98
115
  return threshold;
99
116
  };
100
- /** Validates a 1-based participant index without assuming a fixed count. */
117
+ /**
118
+ * Validates a 1-based participant index without assuming a fixed participant
119
+ * count.
120
+ *
121
+ * The package consistently numbers trustees and voters from `1`.
122
+ */
101
123
  export const assertPositiveParticipantIndex = (index) => {
102
124
  if (!Number.isInteger(index)) {
103
125
  throw new IndexOutOfRangeError('Participant index must be an integer');
@@ -106,7 +128,12 @@ export const assertPositiveParticipantIndex = (index) => {
106
128
  throw new IndexOutOfRangeError('Participant index must be a positive integer');
107
129
  }
108
130
  };
109
- /** Validates a 1-based participant index for a fixed participant count. */
131
+ /**
132
+ * Validates a 1-based participant index against a fixed participant count.
133
+ *
134
+ * This is the usual check for published payloads that already sit inside a
135
+ * frozen roster.
136
+ */
110
137
  export const assertValidParticipantIndex = (index, participantCount) => {
111
138
  if (!Number.isInteger(index) || !Number.isInteger(participantCount)) {
112
139
  throw new IndexOutOfRangeError('Participant index and count must be integers');
@@ -118,19 +145,32 @@ export const assertValidParticipantIndex = (index, participantCount) => {
118
145
  throw new IndexOutOfRangeError(`Participant index ${index} must satisfy 1 <= j <= n (n = ${participantCount})`);
119
146
  }
120
147
  };
121
- /** Validates that a value is a non-identity valid Ristretto point. */
148
+ /**
149
+ * Validates that a value is a canonical non-identity Ristretto point.
150
+ *
151
+ * This is the assertion form of {@link isInSubgroup}.
152
+ */
122
153
  export const assertInSubgroup = (value) => {
123
154
  if (!isInSubgroup(value)) {
124
155
  throw new InvalidGroupElementError('Element is not a valid non-identity Ristretto point');
125
156
  }
126
157
  };
127
- /** Validates that a value is a valid Ristretto point, including identity. */
158
+ /**
159
+ * Validates that a value is a canonical Ristretto point, including identity.
160
+ *
161
+ * This is the assertion form of {@link isInSubgroupOrIdentity}.
162
+ */
128
163
  export const assertInSubgroupOrIdentity = (value) => {
129
164
  if (!isInSubgroupOrIdentity(value)) {
130
165
  throw new InvalidGroupElementError('Element is not a valid Ristretto point');
131
166
  }
132
167
  };
133
- /** Validates a public key as a non-identity Ristretto point. */
168
+ /**
169
+ * Validates a public key as a canonical non-identity Ristretto point.
170
+ *
171
+ * Public keys, verification keys, and commitment generators all route through
172
+ * this helper.
173
+ */
134
174
  export const assertValidPublicKey = (value) => {
135
175
  if (!isInSubgroup(value)) {
136
176
  throw new InvalidGroupElementError('Public key must be a valid non-identity Ristretto point');
@@ -1,5 +1,15 @@
1
1
  import type { PedersenShare } from '../vss/types.js';
2
- /** Encodes one Pedersen share pair for encrypted dealer-to-recipient transport. */
2
+ /**
3
+ * Encodes one Pedersen share pair for encrypted dealer-to-recipient transport.
4
+ *
5
+ * The output is canonical JSON so the decrypted plaintext can later be audited
6
+ * byte-for-byte.
7
+ */
3
8
  export declare const encodePedersenShareEnvelope: (share: PedersenShare, byteLength: number) => string;
4
- /** Decodes one encrypted Pedersen share pair after envelope decryption. */
9
+ /**
10
+ * Decodes one encrypted Pedersen share pair after envelope decryption.
11
+ *
12
+ * Recipients use this after envelope decryption, and transcript verification
13
+ * uses the same rules when resolving complaints.
14
+ */
5
15
  export declare const decodePedersenShareEnvelope: (plaintext: Uint8Array, expectedParticipantIndex: number, label: string) => PedersenShare;
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Deterministic encoding for Pedersen share pairs before transport encryption.
3
+ *
4
+ * Dealers use this module to turn one recipient's share pair into canonical
5
+ * JSON bytes that can be encrypted into an envelope and later revalidated by
6
+ * the recipient or the verifier.
7
+ */
1
8
  import { InvalidPayloadError, RISTRETTO_GROUP } from '../core/index.js';
2
9
  import { canonicalizeJson } from '../protocol/canonical-json.js';
3
10
  import { bigintToFixedHex, fixedHexToBigInt } from '../serialize/encoding.js';
@@ -48,7 +55,12 @@ const parsePedersenShareEnvelopeRecord = (parsed, expectedParticipantIndex, labe
48
55
  secretValue: bigintToFixedHex(parseCanonicalFixedHex(record.secretValue, RISTRETTO_GROUP.scalarByteLength, `${label} secret value`), RISTRETTO_GROUP.scalarByteLength),
49
56
  };
50
57
  };
51
- /** Encodes one Pedersen share pair for encrypted dealer-to-recipient transport. */
58
+ /**
59
+ * Encodes one Pedersen share pair for encrypted dealer-to-recipient transport.
60
+ *
61
+ * The output is canonical JSON so the decrypted plaintext can later be audited
62
+ * byte-for-byte.
63
+ */
52
64
  export const encodePedersenShareEnvelope = (share, byteLength) => canonicalizeJson({
53
65
  index: share.index,
54
66
  secretValue: bigintToFixedHex(share.secretValue, byteLength),
@@ -56,7 +68,12 @@ export const encodePedersenShareEnvelope = (share, byteLength) => canonicalizeJs
56
68
  }, {
57
69
  bigintByteLength: byteLength,
58
70
  });
59
- /** Decodes one encrypted Pedersen share pair after envelope decryption. */
71
+ /**
72
+ * Decodes one encrypted Pedersen share pair after envelope decryption.
73
+ *
74
+ * Recipients use this after envelope decryption, and transcript verification
75
+ * uses the same rules when resolving complaints.
76
+ */
60
77
  export const decodePedersenShareEnvelope = (plaintext, expectedParticipantIndex, label) => {
61
78
  let decodedPlaintext;
62
79
  try {
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Public DKG transcript helpers and share-envelope codecs.
3
+ *
4
+ * Use this module when you need direct access to transcript replay, derived
5
+ * trustee verification keys, or encrypted share-envelope encoding.
6
+ *
7
+ * @module threshold-elgamal/dkg
8
+ * @packageDocumentation
9
+ */
10
+ export { deriveJointPublicKey, deriveTranscriptVerificationKey, verifyDKGTranscript, } from './verification.js';
11
+ export { decodePedersenShareEnvelope, encodePedersenShareEnvelope, } from './pedersen-share-codec.js';
12
+ export type { VerifyDKGTranscriptInput, VerifiedDKGTranscript, } from './verification.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Public DKG transcript helpers and share-envelope codecs.
3
+ *
4
+ * Use this module when you need direct access to transcript replay, derived
5
+ * trustee verification keys, or encrypted share-envelope encoding.
6
+ *
7
+ * @module threshold-elgamal/dkg
8
+ * @packageDocumentation
9
+ */
10
+ export { deriveJointPublicKey, deriveTranscriptVerificationKey, verifyDKGTranscript, } from './verification.js';
11
+ export { decodePedersenShareEnvelope, encodePedersenShareEnvelope, } from './pedersen-share-codec.js';
@@ -1,13 +1,30 @@
1
+ /**
2
+ * Full GJKR DKG transcript verification and derived-key extraction.
3
+ *
4
+ * This module is responsible for turning a signed public DKG transcript into a
5
+ * qualified participant set, transcript-derived trustee verification keys, and
6
+ * the final joint public key used by the voting flow.
7
+ */
1
8
  import { type CryptoGroup } from '../core/index.js';
2
9
  import type { EncodedPoint } from '../core/types.js';
3
10
  import type { ComplaintPayload, ElectionManifest, PhaseCheckpointPayload, RegistrationPayload, SignedPayload } from '../protocol/types.js';
4
- /** Input bundle for verifying a DKG transcript. */
11
+ /**
12
+ * Input bundle for verifying a DKG transcript.
13
+ *
14
+ * This is the DKG-only verifier input used directly by advanced consumers and
15
+ * indirectly by the full ceremony verifier.
16
+ */
5
17
  export type VerifyDKGTranscriptInput = {
6
18
  readonly transcript: readonly SignedPayload[];
7
19
  readonly manifest: ElectionManifest;
8
20
  readonly sessionId: string;
9
21
  };
10
- /** Verified DKG transcript result with reusable derived ceremony material. */
22
+ /**
23
+ * Verified DKG transcript result with reusable derived ceremony material.
24
+ *
25
+ * Later ballot and decryption verification stages consume this output rather
26
+ * than replaying the DKG from scratch.
27
+ */
11
28
  export type VerifiedDKGTranscript = {
12
29
  readonly acceptedComplaints: readonly ComplaintPayload[];
13
30
  readonly jointPublicKey: EncodedPoint;
@@ -25,7 +42,12 @@ export type VerifiedDKGTranscript = {
25
42
  readonly rosterHash: string;
26
43
  readonly threshold: number;
27
44
  };
28
- /** Finalized threshold-supported checkpoint for one DKG phase. */
45
+ /**
46
+ * Finalized threshold-supported checkpoint for one DKG phase.
47
+ *
48
+ * This represents the accepted checkpoint record after signer-set and
49
+ * transcript-hash validation.
50
+ */
29
51
  export type FinalizedPhaseCheckpoint = {
30
52
  readonly payload: PhaseCheckpointPayload;
31
53
  readonly signatures: readonly SignedPayload<PhaseCheckpointPayload>[];
@@ -35,10 +57,8 @@ export type FinalizedPhaseCheckpoint = {
35
57
  * Derives the transcript verification key `Y_j` for one participant index from
36
58
  * published Feldman commitments.
37
59
  *
38
- * @param feldmanCommitments Qualified dealer commitment vectors.
39
- * @param participantIndex Participant index whose key will be derived.
40
- * @param group Selected group.
41
- * @returns Transcript-derived verification key `Y_j`.
60
+ * Decryption-share verification uses this key as the public statement side of
61
+ * each trustee's DLEQ proof.
42
62
  */
43
63
  export declare const deriveTranscriptVerificationKey: (feldmanCommitments: readonly {
44
64
  readonly dealerIndex: number;
@@ -48,20 +68,19 @@ export declare const deriveTranscriptVerificationKey: (feldmanCommitments: reado
48
68
  * Derives the qualified joint public key from the constant Feldman
49
69
  * commitments.
50
70
  *
51
- * @param feldmanCommitments Qualified dealer commitment vectors.
52
- * @param group Selected group.
53
- * @returns Derived joint public key.
71
+ * Ballot encryption and tally verification both depend on this derived public
72
+ * key.
54
73
  */
55
74
  export declare const deriveJointPublicKey: (feldmanCommitments: readonly {
56
75
  readonly dealerIndex: number;
57
76
  readonly commitments: readonly EncodedPoint[];
58
77
  }[], group: CryptoGroup) => EncodedPoint;
59
78
  /**
60
- * Verifies a DKG transcript, its signatures, Feldman extraction proofs,
61
- * the exact claimed threshold degree, accepted complaint outcomes, the DKG
79
+ * Verifies a DKG transcript, its signatures, Feldman extraction proofs, the
80
+ * exact claimed threshold degree, accepted complaint outcomes, the DKG
62
81
  * transcript hash, and the announced joint public key.
63
82
  *
64
- * @param input Transcript verification input.
65
- * @returns Verified transcript metadata and derived ceremony material.
83
+ * This is the DKG-specific verifier that the full ceremony verifier delegates
84
+ * to before it touches ballots or tally material.
66
85
  */
67
86
  export declare const verifyDKGTranscript: (input: VerifyDKGTranscriptInput) => Promise<VerifiedDKGTranscript>;
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Full GJKR DKG transcript verification and derived-key extraction.
3
+ *
4
+ * This module is responsible for turning a signed public DKG transcript into a
5
+ * qualified participant set, transcript-derived trustee verification keys, and
6
+ * the final joint public key used by the voting flow.
7
+ */
1
8
  import { InvalidPayloadError, RISTRETTO_GROUP, ThresholdViolationError, assertCanonicalRistrettoGroup, assertInSubgroupOrIdentity, assertPositiveParticipantIndex, majorityThreshold, modQ, } from '../core/index.js';
2
9
  import { RISTRETTO_ZERO, decodePoint, decodeScalar, encodePoint, pointAdd, pointMultiply, } from '../core/ristretto.js';
3
10
  import { verifySchnorrProof } from '../proofs/schnorr.js';
@@ -152,7 +159,7 @@ const collectCheckpointVariants = (transcript, checkpointPhase) => {
152
159
  });
153
160
  };
154
161
  /**
155
- * Rejects phase-checkpoint payloads outside the shipped GJKR checkpoint plan.
162
+ * Rejects phase-checkpoint payloads outside the GJKR checkpoint plan.
156
163
  */
157
164
  const assertSupportedCheckpointPayloads = (transcript) => {
158
165
  for (const signedPayload of transcript) {
@@ -374,19 +381,16 @@ const deriveTranscriptVerificationKeyInternal = (commitmentSets, participantInde
374
381
  * Derives the transcript verification key `Y_j` for one participant index from
375
382
  * published Feldman commitments.
376
383
  *
377
- * @param feldmanCommitments Qualified dealer commitment vectors.
378
- * @param participantIndex Participant index whose key will be derived.
379
- * @param group Selected group.
380
- * @returns Transcript-derived verification key `Y_j`.
384
+ * Decryption-share verification uses this key as the public statement side of
385
+ * each trustee's DLEQ proof.
381
386
  */
382
387
  export const deriveTranscriptVerificationKey = (feldmanCommitments, participantIndex, group) => deriveTranscriptVerificationKeyInternal(feldmanCommitments.map((entry) => entry.commitments), participantIndex, group);
383
388
  /**
384
389
  * Derives the qualified joint public key from the constant Feldman
385
390
  * commitments.
386
391
  *
387
- * @param feldmanCommitments Qualified dealer commitment vectors.
388
- * @param group Selected group.
389
- * @returns Derived joint public key.
392
+ * Ballot encryption and tally verification both depend on this derived public
393
+ * key.
390
394
  */
391
395
  export const deriveJointPublicKey = (feldmanCommitments, group) => {
392
396
  assertCanonicalRistrettoGroup(group, 'Joint public-key derivation group');
@@ -653,12 +657,12 @@ const verifyCheckpointedDKGTranscript = async (input, setup) => {
653
657
  return finalizeVerifiedTranscript(input, setup.verifiedSignatures, setup.manifestPublication.participantIndex, acceptedComplaints, setup.manifestAccepted, phaseCheckpoints, finalQualifiedParticipantIndices, RISTRETTO_GROUP, setup.threshold);
654
658
  };
655
659
  /**
656
- * Verifies a DKG transcript, its signatures, Feldman extraction proofs,
657
- * the exact claimed threshold degree, accepted complaint outcomes, the DKG
660
+ * Verifies a DKG transcript, its signatures, Feldman extraction proofs, the
661
+ * exact claimed threshold degree, accepted complaint outcomes, the DKG
658
662
  * transcript hash, and the announced joint public key.
659
663
  *
660
- * @param input Transcript verification input.
661
- * @returns Verified transcript metadata and derived ceremony material.
664
+ * This is the DKG-specific verifier that the full ceremony verifier delegates
665
+ * to before it touches ballots or tally material.
662
666
  */
663
667
  export const verifyDKGTranscript = async (input) => {
664
668
  const auditedTranscript = await auditSignedPayloads(input.transcript);
@@ -1,11 +1,23 @@
1
1
  import type { ElGamalCiphertext } from './types.js';
2
- /** Validates an additive ciphertext that may already be an aggregate. */
2
+ /**
3
+ * Validates an additive ciphertext that may already represent an aggregate.
4
+ *
5
+ * Both raw ballot ciphertexts and summed ciphertexts share the same structural
6
+ * requirements.
7
+ */
3
8
  export declare const assertValidAdditiveCiphertext: (ciphertext: ElGamalCiphertext) => void;
4
9
  /**
5
- * Adds two additive-mode ciphertexts component-wise.
10
+ * Adds two additive ciphertexts component-wise.
11
+ *
12
+ * This is the homomorphic step behind ballot aggregation and tally
13
+ * recomputation.
6
14
  */
7
15
  export declare const addEncryptedValues: (left: ElGamalCiphertext, right: ElGamalCiphertext) => ElGamalCiphertext;
8
16
  /**
9
- * Encrypts an additive plaintext with caller-supplied randomness.
17
+ * Encrypts one additive plaintext with caller-supplied randomness.
18
+ *
19
+ * Public ballot builders usually sit one layer above this helper, but advanced
20
+ * consumers and tests can use it directly when they need explicit control over
21
+ * the randomness input and plaintext bound.
10
22
  */
11
23
  export declare const encryptAdditiveWithRandomness: (message: bigint, publicKey: string, randomness: bigint, bound: bigint) => ElGamalCiphertext;
@@ -1,31 +1,27 @@
1
+ /**
2
+ * Additive ElGamal primitives over the built-in Ristretto255 suite.
3
+ *
4
+ * Ballot payload construction, ciphertext aggregation, and threshold decryption
5
+ * all depend on this layer.
6
+ */
1
7
  import { RISTRETTO_GROUP, assertAdditiveBound, assertInSubgroupOrIdentity, assertPlaintextAdditive, assertValidPublicKey, InvalidScalarError, } from '../core/index.js';
2
8
  import { decodePoint, encodePoint, multiplyBase, pointAdd, pointMultiply, } from '../core/ristretto.js';
3
- /** Validates an additive-mode public key against the shipped suite. */
4
- const assertValidAdditivePublicKey = (publicKey) => {
5
- assertValidPublicKey(publicKey);
6
- };
7
- /** Validates the caller-supplied additive plaintext bound. */
8
- const assertValidAdditiveBound = (bound) => assertAdditiveBound(bound, RISTRETTO_GROUP.q);
9
- /** Validates the plaintext domain and caller-supplied bound for additive mode. */
10
- const assertValidAdditivePlaintext = (value, bound) => assertPlaintextAdditive(value, bound, RISTRETTO_GROUP.q);
11
- /** Validates an additive ciphertext that may already be an aggregate. */
9
+ /**
10
+ * Validates an additive ciphertext that may already represent an aggregate.
11
+ *
12
+ * Both raw ballot ciphertexts and summed ciphertexts share the same structural
13
+ * requirements.
14
+ */
12
15
  export const assertValidAdditiveCiphertext = (ciphertext) => {
13
16
  assertInSubgroupOrIdentity(ciphertext.c1);
14
17
  assertInSubgroupOrIdentity(ciphertext.c2);
15
18
  };
16
- const resolveAdditiveBound = (bound, operation) => {
19
+ const requireAdditiveBound = (bound) => {
17
20
  if (typeof bound !== 'bigint') {
18
- throw new InvalidScalarError(`Additive ${operation} requires an explicit plaintext bound`);
21
+ throw new InvalidScalarError('Additive encryption requires an explicit plaintext bound');
19
22
  }
20
23
  return bound;
21
24
  };
22
- const resolveAdditiveContext = (bound, operation) => {
23
- const resolvedBound = resolveAdditiveBound(bound, operation);
24
- assertValidAdditiveBound(resolvedBound);
25
- return {
26
- bound: resolvedBound,
27
- };
28
- };
29
25
  const assertEncryptionRandomness = (randomness) => {
30
26
  if (randomness <= 0n || randomness >= RISTRETTO_GROUP.q) {
31
27
  throw new InvalidScalarError('Encryption randomness must be in the range 1..q-1');
@@ -43,7 +39,10 @@ const encryptAdditiveWithValidatedInputs = (message, publicKey, randomness) => {
43
39
  };
44
40
  };
45
41
  /**
46
- * Adds two additive-mode ciphertexts component-wise.
42
+ * Adds two additive ciphertexts component-wise.
43
+ *
44
+ * This is the homomorphic step behind ballot aggregation and tally
45
+ * recomputation.
47
46
  */
48
47
  export const addEncryptedValues = (left, right) => {
49
48
  assertValidAdditiveCiphertext(left);
@@ -54,12 +53,17 @@ export const addEncryptedValues = (left, right) => {
54
53
  };
55
54
  };
56
55
  /**
57
- * Encrypts an additive plaintext with caller-supplied randomness.
56
+ * Encrypts one additive plaintext with caller-supplied randomness.
57
+ *
58
+ * Public ballot builders usually sit one layer above this helper, but advanced
59
+ * consumers and tests can use it directly when they need explicit control over
60
+ * the randomness input and plaintext bound.
58
61
  */
59
62
  export const encryptAdditiveWithRandomness = (message, publicKey, randomness, bound) => {
60
- const context = resolveAdditiveContext(bound, 'encryption');
61
- assertValidAdditivePlaintext(message, context.bound);
62
- assertValidAdditivePublicKey(publicKey);
63
+ const resolvedBound = requireAdditiveBound(bound);
64
+ assertAdditiveBound(resolvedBound, RISTRETTO_GROUP.q);
65
+ assertPlaintextAdditive(message, resolvedBound, RISTRETTO_GROUP.q);
66
+ assertValidPublicKey(publicKey);
63
67
  assertEncryptionRandomness(randomness);
64
68
  return encryptAdditiveWithValidatedInputs(message, publicKey, randomness);
65
69
  };
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Bounded baby-step giant-step discrete-log solver for additive ElGamal
3
+ * plaintext recovery.
4
+ *
5
+ * The threshold reconstruction path uses this only after all cryptographic
6
+ * checks have passed and only within an explicit caller-supplied bound.
7
+ */
1
8
  import { InvalidScalarError } from '../core/index.js';
2
9
  import { decodePoint, encodePoint, pointAdd, pointSubtract, pointMultiply, } from '../core/ristretto.js';
3
10
  const integerSquareRootCeil = (value) => {
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Low-level additive ElGamal helpers.
3
+ *
4
+ * Use this module when you need direct ciphertext construction primitives
5
+ * instead of the higher-level protocol payload builders.
6
+ *
7
+ * @module threshold-elgamal/elgamal
8
+ * @packageDocumentation
9
+ */
10
+ export { encryptAdditiveWithRandomness } from './additive.js';
11
+ export type { ElGamalCiphertext } from './types.js';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Low-level additive ElGamal helpers.
3
+ *
4
+ * Use this module when you need direct ciphertext construction primitives
5
+ * instead of the higher-level protocol payload builders.
6
+ *
7
+ * @module threshold-elgamal/elgamal
8
+ * @packageDocumentation
9
+ */
10
+ export { encryptAdditiveWithRandomness } from './additive.js';
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Ciphertext types for additive ElGamal.
3
+ *
4
+ * These types are shared by low-level encryption helpers, ballot codecs,
5
+ * threshold decryption, and the generated API reference.
6
+ */
1
7
  import type { EncodedPoint } from '../core/types.js';
2
8
  /** Standard additive ElGamal ciphertext pair `(c1, c2)` encoded as points. */
3
9
  export type ElGamalCiphertext = {
package/dist/index.d.ts CHANGED
@@ -1,39 +1,41 @@
1
1
  /**
2
- * Safe root package exports for the current surface.
2
+ * Root package exports for the supported public API.
3
3
  *
4
- * Use this entry point for group definitions, additive ElGamal, validation
5
- * helpers, and serialization helpers that are safe for the shipped package.
4
+ * Use this entry point for the full supported voting workflow: manifest and
5
+ * roster setup, transport keys and envelopes, DKG commitments and share
6
+ * handling, ballot encryption and proofs, decryption-share publication, tally
7
+ * reconstruction, and full ceremony verification.
8
+ *
9
+ * Public subpath modules such as `threshold-elgamal/proofs`,
10
+ * `threshold-elgamal/threshold`, and `threshold-elgamal/dkg` remain available
11
+ * when you prefer grouped imports by subsystem, but the supported ceremony can
12
+ * be implemented from this root package alone.
6
13
  *
7
14
  * @module threshold-elgamal
8
15
  * @packageDocumentation
9
16
  */
10
- export { IndexOutOfRangeError, InvalidGroupElementError, InvalidPayloadError, InvalidProofError, InvalidScalarError, InvalidShareError, PhaseViolationError, PlaintextDomainError, ThresholdViolationError, TranscriptMismatchError, UnsupportedSuiteError, } from './core/errors.js';
11
- export { modQ } from './core/bigint.js';
12
- export { RISTRETTO_GROUP } from './core/groups.js';
13
- export type { EncodedPoint } from './core/types.js';
17
+ export { RISTRETTO_GROUP, modQ } from './core/public.js';
18
+ export type { EncodedPoint } from './core/public.js';
14
19
  export { majorityThreshold } from './core/validation.js';
15
- export { encryptAdditiveWithRandomness } from './elgamal/additive.js';
16
- export { createDisjunctiveProof, verifyDisjunctiveProof, } from './proofs/disjunctive.js';
17
- export { createDLEQProof, type DLEQStatement, verifyDLEQProof, } from './proofs/dleq.js';
18
- export { createSchnorrProof, verifySchnorrProof } from './proofs/schnorr.js';
19
- export type { DLEQProof, DisjunctiveProof, ProofContext, SchnorrProof, } from './proofs/types.js';
20
+ export { encryptAdditiveWithRandomness } from './elgamal/public.js';
21
+ export type { ElGamalCiphertext } from './elgamal/public.js';
22
+ export { deriveJointPublicKey, deriveTranscriptVerificationKey, encodePedersenShareEnvelope, decodePedersenShareEnvelope, verifyDKGTranscript, } from './dkg/public.js';
23
+ export type { VerifyDKGTranscriptInput, VerifiedDKGTranscript, } from './dkg/public.js';
24
+ export { createDisjunctiveProof, createDLEQProof, createSchnorrProof, verifyDisjunctiveProof, verifyDLEQProof, verifySchnorrProof, } from './proofs/public.js';
25
+ export type { DLEQProof, DisjunctiveProof, DLEQStatement, ProofContext, SchnorrProof, } from './proofs/public.js';
20
26
  export { decryptEnvelope, encryptEnvelope } from './transport/envelopes.js';
21
27
  export { exportAuthPublicKey, generateAuthKeyPair } from './transport/auth.js';
22
28
  export { exportTransportPublicKey, generateTransportKeyPair, } from './transport/key-agreement.js';
23
29
  export type { EncodedAuthPublicKey, EncodedTransportPublicKey, EncryptedEnvelope, EnvelopeContext, TransportKeyPair, } from './transport/types.js';
24
- export { combineDecryptionShares, createDecryptionShare, prepareAggregateForDecryption, } from './threshold/decrypt.js';
25
- export type { AggregateDecryptionPreparationInput, DecryptionShare, Share, VerifiedAggregateCiphertext, } from './threshold/types.js';
26
- export { deriveJointPublicKey, deriveTranscriptVerificationKey, verifyDKGTranscript, } from './dkg/verification.js';
27
- export { decodePedersenShareEnvelope, encodePedersenShareEnvelope, } from './dkg/pedersen-share-codec.js';
28
- export type { VerifyDKGTranscriptInput, VerifiedDKGTranscript, } from './dkg/verification.js';
29
- export { generateFeldmanCommitments, verifyFeldmanShare } from './vss/feldman.js';
30
- export { derivePedersenShares, generatePedersenCommitments, verifyPedersenShare, } from './vss/pedersen.js';
31
- export type { FeldmanCommitments, PedersenCommitments, PedersenShare, } from './vss/types.js';
32
- export { createBallotClosePayload, createBallotSubmissionPayload, createDecryptionSharePayload, createEncryptedDualSharePayload, createFeldmanCommitmentPayload, createKeyDerivationConfirmationPayload, createManifestAcceptancePayload, createManifestPublicationPayload, createPedersenCommitmentPayload, createPhaseCheckpointPayload, createRegistrationPayload, createTallyPublicationPayload, signProtocolPayload, } from './protocol/builders.js';
30
+ export { createBallotClosePayload, createBallotSubmissionPayload, createDecryptionSharePayload, createEncryptedDualSharePayload, createFeldmanCommitmentPayload, createKeyDerivationConfirmationPayload, createManifestAcceptancePayload, createManifestPublicationPayload, createPedersenCommitmentPayload, createPhaseCheckpointPayload, createRegistrationPayload, signProtocolPayload, createTallyPublicationPayload, } from './protocol/builders.js';
33
31
  export { canonicalizeElectionManifest, createElectionManifest, deriveSessionId, hashElectionManifest, SHIPPED_PROTOCOL_VERSION, validateElectionManifest, } from './protocol/manifest.js';
34
- export { hashRosterEntries } from './protocol/verification.js';
32
+ export { hashRosterEntries, verifySignedProtocolPayloads, type RosterEntry, type VerifiedProtocolSignatures, } from './protocol/verification.js';
35
33
  export { hashProtocolTranscript } from './protocol/transcript.js';
36
- export { verifyElectionCeremony, tryVerifyElectionCeremony, type ElectionVerificationErrorCode, type ElectionVerificationFailure, type ElectionVerificationResult, type ElectionVerificationStage, type VerifiedElectionCeremony, } from './protocol/voting-verification.js';
37
34
  export { verifyBallotSubmissionPayloadsByOption } from './protocol/voting-ballots.js';
38
- export { scoreVotingDomain } from './protocol/voting-codecs.js';
39
- export type { BallotClosePayload, BallotSubmissionPayload, DecryptionSharePayload, ElectionManifest, EncodedCiphertext, EncodedCompactProof, EncodedDisjunctiveProof, EncryptedDualSharePayload, FeldmanCommitmentPayload, KeyDerivationConfirmation, ManifestAcceptancePayload, ManifestPublicationPayload, PedersenCommitmentPayload, PhaseCheckpointPayload, ProtocolMessageType, ProtocolPayload, RegistrationPayload, SignedPayload, TallyPublicationPayload, VerifyElectionCeremonyInput, } from './protocol/types.js';
35
+ export { scoreRangeDomain } from './protocol/voting-codecs.js';
36
+ export { verifyElectionCeremony, tryVerifyElectionCeremony, type ElectionVerificationErrorCode, type ElectionVerificationFailure, type ElectionVerificationResult, type ElectionVerificationStage, type VerifiedElectionCeremony, } from './protocol/voting-verification.js';
37
+ export type { BallotClosePayload, BallotSubmissionPayload, DecryptionSharePayload, ElectionManifest, EncodedCiphertext, EncodedCompactProof, EncodedDisjunctiveProof, EncryptedDualSharePayload, FeldmanCommitmentPayload, KeyDerivationConfirmation, ManifestAcceptancePayload, ManifestPublicationPayload, PedersenCommitmentPayload, PhaseCheckpointPayload, ProtocolMessageType, ProtocolPayload, RegistrationPayload, ScoreRange, SignedPayload, TallyPublicationPayload, VerifyElectionCeremonyInput, } from './protocol/types.js';
38
+ export { combineDecryptionShares, createDecryptionShare, prepareAggregateForDecryption, } from './threshold/public.js';
39
+ export type { AggregateDecryptionPreparationInput, DecryptionShare, Share, VerifiedAggregateCiphertext, } from './threshold/public.js';
40
+ export { derivePedersenShares, generateFeldmanCommitments, generatePedersenCommitments, verifyFeldmanShare, verifyPedersenShare, } from './vss/public.js';
41
+ export type { FeldmanCommitments, PedersenCommitments, PedersenShare, } from './vss/public.js';
package/dist/index.js CHANGED
@@ -1,32 +1,33 @@
1
1
  /**
2
- * Safe root package exports for the current surface.
2
+ * Root package exports for the supported public API.
3
3
  *
4
- * Use this entry point for group definitions, additive ElGamal, validation
5
- * helpers, and serialization helpers that are safe for the shipped package.
4
+ * Use this entry point for the full supported voting workflow: manifest and
5
+ * roster setup, transport keys and envelopes, DKG commitments and share
6
+ * handling, ballot encryption and proofs, decryption-share publication, tally
7
+ * reconstruction, and full ceremony verification.
8
+ *
9
+ * Public subpath modules such as `threshold-elgamal/proofs`,
10
+ * `threshold-elgamal/threshold`, and `threshold-elgamal/dkg` remain available
11
+ * when you prefer grouped imports by subsystem, but the supported ceremony can
12
+ * be implemented from this root package alone.
6
13
  *
7
14
  * @module threshold-elgamal
8
15
  * @packageDocumentation
9
16
  */
10
- export { IndexOutOfRangeError, InvalidGroupElementError, InvalidPayloadError, InvalidProofError, InvalidScalarError, InvalidShareError, PhaseViolationError, PlaintextDomainError, ThresholdViolationError, TranscriptMismatchError, UnsupportedSuiteError, } from './core/errors.js';
11
- export { modQ } from './core/bigint.js';
12
- export { RISTRETTO_GROUP } from './core/groups.js';
17
+ export { RISTRETTO_GROUP, modQ } from './core/public.js';
13
18
  export { majorityThreshold } from './core/validation.js';
14
- export { encryptAdditiveWithRandomness } from './elgamal/additive.js';
15
- export { createDisjunctiveProof, verifyDisjunctiveProof, } from './proofs/disjunctive.js';
16
- export { createDLEQProof, verifyDLEQProof, } from './proofs/dleq.js';
17
- export { createSchnorrProof, verifySchnorrProof } from './proofs/schnorr.js';
19
+ export { encryptAdditiveWithRandomness } from './elgamal/public.js';
20
+ export { deriveJointPublicKey, deriveTranscriptVerificationKey, encodePedersenShareEnvelope, decodePedersenShareEnvelope, verifyDKGTranscript, } from './dkg/public.js';
21
+ export { createDisjunctiveProof, createDLEQProof, createSchnorrProof, verifyDisjunctiveProof, verifyDLEQProof, verifySchnorrProof, } from './proofs/public.js';
18
22
  export { decryptEnvelope, encryptEnvelope } from './transport/envelopes.js';
19
23
  export { exportAuthPublicKey, generateAuthKeyPair } from './transport/auth.js';
20
24
  export { exportTransportPublicKey, generateTransportKeyPair, } from './transport/key-agreement.js';
21
- export { combineDecryptionShares, createDecryptionShare, prepareAggregateForDecryption, } from './threshold/decrypt.js';
22
- export { deriveJointPublicKey, deriveTranscriptVerificationKey, verifyDKGTranscript, } from './dkg/verification.js';
23
- export { decodePedersenShareEnvelope, encodePedersenShareEnvelope, } from './dkg/pedersen-share-codec.js';
24
- export { generateFeldmanCommitments, verifyFeldmanShare } from './vss/feldman.js';
25
- export { derivePedersenShares, generatePedersenCommitments, verifyPedersenShare, } from './vss/pedersen.js';
26
- export { createBallotClosePayload, createBallotSubmissionPayload, createDecryptionSharePayload, createEncryptedDualSharePayload, createFeldmanCommitmentPayload, createKeyDerivationConfirmationPayload, createManifestAcceptancePayload, createManifestPublicationPayload, createPedersenCommitmentPayload, createPhaseCheckpointPayload, createRegistrationPayload, createTallyPublicationPayload, signProtocolPayload, } from './protocol/builders.js';
25
+ export { createBallotClosePayload, createBallotSubmissionPayload, createDecryptionSharePayload, createEncryptedDualSharePayload, createFeldmanCommitmentPayload, createKeyDerivationConfirmationPayload, createManifestAcceptancePayload, createManifestPublicationPayload, createPedersenCommitmentPayload, createPhaseCheckpointPayload, createRegistrationPayload, signProtocolPayload, createTallyPublicationPayload, } from './protocol/builders.js';
27
26
  export { canonicalizeElectionManifest, createElectionManifest, deriveSessionId, hashElectionManifest, SHIPPED_PROTOCOL_VERSION, validateElectionManifest, } from './protocol/manifest.js';
28
- export { hashRosterEntries } from './protocol/verification.js';
27
+ export { hashRosterEntries, verifySignedProtocolPayloads, } from './protocol/verification.js';
29
28
  export { hashProtocolTranscript } from './protocol/transcript.js';
30
- export { verifyElectionCeremony, tryVerifyElectionCeremony, } from './protocol/voting-verification.js';
31
29
  export { verifyBallotSubmissionPayloadsByOption } from './protocol/voting-ballots.js';
32
- export { scoreVotingDomain } from './protocol/voting-codecs.js';
30
+ export { scoreRangeDomain } from './protocol/voting-codecs.js';
31
+ export { verifyElectionCeremony, tryVerifyElectionCeremony, } from './protocol/voting-verification.js';
32
+ export { combineDecryptionShares, createDecryptionShare, prepareAggregateForDecryption, } from './threshold/public.js';
33
+ export { derivePedersenShares, generateFeldmanCommitments, generatePedersenCommitments, verifyFeldmanShare, verifyPedersenShare, } from './vss/public.js';