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,3 +1,9 @@
1
+ /**
2
+ * Public payload-builder façade for the supported ceremony.
3
+ *
4
+ * Application-facing code normally uses this module instead of hand-assembling
5
+ * protocol payload objects and signatures.
6
+ */
1
7
  import { encodeScalar } from '../core/ristretto.js';
2
8
  import { signPayloadBytes } from '../transport/auth.js';
3
9
  import { attachProtocolVersion, signedProtocolPayloadBytes } from './payloads.js';
@@ -7,7 +13,12 @@ import { assertUniqueSortedIndices, BALLOT_CLOSE_PHASE, BALLOT_SUBMISSION_PHASE,
7
13
  const isEncodedDisjunctiveProof = (proof) => typeof proof.branches[0]?.challenge === 'string';
8
14
  const isEncodedCompactProof = (proof) => typeof proof.challenge === 'string';
9
15
  const isEncodedFeldmanProofEntry = (proof) => typeof proof.challenge === 'string';
10
- /** Signs one canonical protocol payload with a participant auth key. */
16
+ /**
17
+ * Signs one canonical protocol payload with a participant auth key.
18
+ *
19
+ * All specialized payload builders in this module eventually route through
20
+ * this helper after normalizing the payload shape.
21
+ */
11
22
  export const signProtocolPayload = async (privateKey, payload) => {
12
23
  const signedPayload = attachProtocolVersion(payload);
13
24
  return {
@@ -15,7 +26,12 @@ export const signProtocolPayload = async (privateKey, payload) => {
15
26
  signature: await signPayloadBytes(privateKey, signedProtocolPayloadBytes(signedPayload)),
16
27
  };
17
28
  };
18
- /** Creates a signed manifest-publication payload. */
29
+ /**
30
+ * Creates a signed `manifest-publication` payload.
31
+ *
32
+ * This is the first public payload in the supported ceremony and anchors the
33
+ * explicit manifest on the board.
34
+ */
19
35
  export const createManifestPublicationPayload = async (privateKey, input) => signProtocolPayload(privateKey, {
20
36
  protocolVersion: input.protocolVersion,
21
37
  sessionId: input.sessionId,
@@ -25,7 +41,12 @@ export const createManifestPublicationPayload = async (privateKey, input) => sig
25
41
  messageType: 'manifest-publication',
26
42
  manifest: input.manifest,
27
43
  });
28
- /** Creates a signed registration payload for one participant. */
44
+ /**
45
+ * Creates a signed `registration` payload for one participant.
46
+ *
47
+ * Registrations publish the participant's auth key and transport key and are
48
+ * the source of truth for the accepted roster.
49
+ */
29
50
  export const createRegistrationPayload = async (privateKey, input) => signProtocolPayload(privateKey, {
30
51
  protocolVersion: input.protocolVersion,
31
52
  sessionId: input.sessionId,
@@ -37,7 +58,12 @@ export const createRegistrationPayload = async (privateKey, input) => signProtoc
37
58
  authPublicKey: input.authPublicKey,
38
59
  transportPublicKey: input.transportPublicKey,
39
60
  });
40
- /** Creates a signed manifest-acceptance payload. */
61
+ /**
62
+ * Creates a signed `manifest-acceptance` payload.
63
+ *
64
+ * This records that a participant accepted the frozen manifest and the roster
65
+ * hash under which they were assigned an index.
66
+ */
41
67
  export const createManifestAcceptancePayload = async (privateKey, input) => signProtocolPayload(privateKey, {
42
68
  protocolVersion: input.protocolVersion,
43
69
  sessionId: input.sessionId,
@@ -53,7 +79,12 @@ export const createManifestAcceptancePayload = async (privateKey, input) => sign
53
79
  accountIdHash: input.accountIdHash,
54
80
  }),
55
81
  });
56
- /** Creates a signed phase-checkpoint payload over one DKG phase snapshot. */
82
+ /**
83
+ * Creates a signed `phase-checkpoint` payload over one DKG phase snapshot.
84
+ *
85
+ * Checkpoints let observers replay the DKG in coarse-grained epochs while
86
+ * preserving deterministic transcript commitments.
87
+ */
57
88
  export const createPhaseCheckpointPayload = async (privateKey, input) => signProtocolPayload(privateKey, {
58
89
  protocolVersion: input.protocolVersion,
59
90
  sessionId: input.sessionId,
@@ -65,20 +96,32 @@ export const createPhaseCheckpointPayload = async (privateKey, input) => signPro
65
96
  checkpointTranscriptHash: await hashProtocolPhaseSnapshot(input.transcript.map((entry) => entry.payload), input.checkpointPhase),
66
97
  qualifiedParticipantIndices: [...input.qualifiedParticipantIndices],
67
98
  });
68
- /** Creates a signed Pedersen-commitment payload for DKG phase 1. */
99
+ /**
100
+ * Creates a signed `pedersen-commitment` payload for DKG phase 1.
101
+ */
69
102
  export const createPedersenCommitmentPayload = async (privateKey, input) => signProtocolPayload(privateKey, {
70
103
  ...input,
71
104
  phase: 1,
72
105
  messageType: 'pedersen-commitment',
73
106
  commitments: [...input.commitments],
74
107
  });
75
- /** Creates a signed encrypted dual-share payload for DKG phase 1. */
108
+ /**
109
+ * Creates a signed `encrypted-dual-share` payload for DKG phase 1.
110
+ *
111
+ * The envelope contents are produced separately by the transport layer and are
112
+ * merely wrapped and signed here.
113
+ */
76
114
  export const createEncryptedDualSharePayload = async (privateKey, input) => signProtocolPayload(privateKey, {
77
115
  ...input,
78
116
  phase: 1,
79
117
  messageType: 'encrypted-dual-share',
80
118
  });
81
- /** Creates a signed Feldman-commitment payload for DKG phase 3. */
119
+ /**
120
+ * Creates a signed `feldman-commitment` payload for DKG phase 3.
121
+ *
122
+ * This payload exposes the public Feldman commitments and their Schnorr proofs
123
+ * after the complaint phase has been resolved.
124
+ */
82
125
  export const createFeldmanCommitmentPayload = async (privateKey, input) => {
83
126
  const payload = {
84
127
  ...input,
@@ -97,13 +140,25 @@ export const createFeldmanCommitmentPayload = async (privateKey, input) => {
97
140
  };
98
141
  return signProtocolPayload(privateKey, payload);
99
142
  };
100
- /** Creates a signed key-derivation-confirmation payload for DKG phase 4. */
143
+ /**
144
+ * Creates a signed `key-derivation-confirmation` payload for DKG phase 4.
145
+ *
146
+ * Trustees use this to confirm the final derived joint key and transcript
147
+ * digest they believe the DKG produced.
148
+ */
101
149
  export const createKeyDerivationConfirmationPayload = async (privateKey, input) => signProtocolPayload(privateKey, {
102
150
  ...input,
103
151
  phase: 4,
104
152
  messageType: 'key-derivation-confirmation',
105
153
  });
106
- /** Creates a signed ballot payload for one participant and one option slot. */
154
+ /**
155
+ * Creates a signed `ballot-submission` payload for one participant and one
156
+ * option slot.
157
+ *
158
+ * Higher-level application code is expected to create the ciphertext and
159
+ * disjunctive proof first, then hand them to this builder for encoding and
160
+ * signing.
161
+ */
107
162
  export const createBallotSubmissionPayload = async (privateKey, input) => {
108
163
  const payload = {
109
164
  ...input,
@@ -116,7 +171,12 @@ export const createBallotSubmissionPayload = async (privateKey, input) => {
116
171
  };
117
172
  return signProtocolPayload(privateKey, payload);
118
173
  };
119
- /** Creates the organizer-signed ballot cutoff payload. */
174
+ /**
175
+ * Creates the organizer-signed `ballot-close` payload.
176
+ *
177
+ * This freezes which complete ballots are counted for the tally and therefore
178
+ * determines the accepted aggregate seen by every later decryption step.
179
+ */
120
180
  export const createBallotClosePayload = async (privateKey, input) => {
121
181
  const countedParticipantIndices = [...input.countedParticipantIndices].sort((left, right) => left - right);
122
182
  assertUniqueSortedIndices(countedParticipantIndices, 'Ballot close participant');
@@ -127,7 +187,12 @@ export const createBallotClosePayload = async (privateKey, input) => {
127
187
  countedParticipantIndices,
128
188
  });
129
189
  };
130
- /** Creates a signed threshold decryption-share payload for one option slot. */
190
+ /**
191
+ * Creates a signed `decryption-share` payload for one option slot.
192
+ *
193
+ * The caller is responsible for computing the share and DLEQ proof first; this
194
+ * helper encodes and signs the published object.
195
+ */
131
196
  export const createDecryptionSharePayload = async (privateKey, input) => {
132
197
  const payload = {
133
198
  ...input,
@@ -139,7 +204,12 @@ export const createDecryptionSharePayload = async (privateKey, input) => {
139
204
  };
140
205
  return signProtocolPayload(privateKey, payload);
141
206
  };
142
- /** Creates a signed tally-publication payload for one option slot. */
207
+ /**
208
+ * Creates a signed `tally-publication` payload for one option slot.
209
+ *
210
+ * This is the final organizer-facing publication step after the accepted
211
+ * decryption shares have been combined into a bounded plaintext tally.
212
+ */
143
213
  export const createTallyPublicationPayload = async (privateKey, input) => {
144
214
  const decryptionParticipantIndices = [
145
215
  ...input.decryptionParticipantIndices,
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Deterministic JSON serialization for manifests, protocol payloads, and
3
+ * other transcript-bound objects.
4
+ */
1
5
  import { InvalidPayloadError } from '../core/index.js';
2
6
  import { bigintToFixedHex } from '../serialize/encoding.js';
3
7
  const canonicalize = (value, options) => {
@@ -1,18 +1,38 @@
1
1
  import type { ElectionManifest } from './types.js';
2
- /** Fixed transcript version string for the shipped beta protocol. */
2
+ /**
3
+ * Default protocol namespace used by the built-in helpers and verifier.
4
+ *
5
+ * The current verifier only accepts this namespace, so applications that want
6
+ * verifier-compatible payloads should treat this value as fixed.
7
+ */
3
8
  export declare const SHIPPED_PROTOCOL_VERSION = "v1";
4
- /** Validates that a protocol version string is present. */
9
+ /**
10
+ * Validates that a protocol version string is present and non-empty.
11
+ *
12
+ * Builders use this to normalize explicit overrides before attaching them to
13
+ * published payloads.
14
+ */
5
15
  export declare const assertValidProtocolVersion: (protocolVersion: string, label?: string) => string;
6
- /** Validates that a protocol version matches the shipped verifier line. */
16
+ /**
17
+ * Validates that a protocol version matches the verifier namespace.
18
+ *
19
+ * Verifier-facing paths call this when they need to reject protocol variants
20
+ * that are outside the package's supported public workflow.
21
+ */
7
22
  export declare const assertSupportedProtocolVersion: (protocolVersion: string, label?: string) => string;
8
23
  /**
9
- * Validates the supported election-manifest invariants for the shipped
10
- * score-voting workflow.
24
+ * Validates the supported election-manifest invariants for the score-voting
25
+ * workflow.
11
26
  *
12
- * @param manifest Election manifest to validate.
27
+ * The manifest is intentionally minimal: it fixes the frozen roster hash and
28
+ * option list together with one explicit global score range, while participant
29
+ * count and threshold are derived later from the accepted registration roster.
13
30
  */
14
31
  export declare const validateElectionManifest: (manifest: ElectionManifest) => ElectionManifest;
15
- /** Creates the minimal shipped election manifest. */
32
+ /**
33
+ * Creates the explicit election manifest after validating the supported
34
+ * invariants.
35
+ */
16
36
  export declare const createElectionManifest: (manifest: ElectionManifest) => ElectionManifest;
17
37
  /**
18
38
  * Canonically serializes an election manifest.
@@ -24,18 +44,15 @@ export declare const canonicalizeElectionManifest: (manifest: ElectionManifest)
24
44
  /**
25
45
  * Hashes a canonical election manifest with SHA-256.
26
46
  *
27
- * @param manifest Election manifest to hash.
28
- * @returns Lowercase hexadecimal SHA-256 digest.
47
+ * The resulting digest is the manifest anchor reused by every later payload,
48
+ * proof context, and verifier stage.
29
49
  */
30
50
  export declare const hashElectionManifest: (manifest: ElectionManifest) => Promise<string>;
31
51
  /**
32
52
  * Derives a globally unique session identifier from the frozen setup values.
33
53
  *
34
- * @param manifestHash Canonical manifest hash.
35
- * @param rosterHash Canonical roster hash.
36
- * @param randomNonce Public random nonce.
37
- * @param timestamp Timestamp string included in the derivation.
38
- * @param protocolVersion Protocol version namespace for the derived session.
39
- * @returns Lowercase hexadecimal SHA-256 digest.
54
+ * Applications normally compute this once after freezing the manifest and
55
+ * roster so every later payload can bind itself to one concrete ceremony
56
+ * instance.
40
57
  */
41
58
  export declare const deriveSessionId: (manifestHash: string, rosterHash: string, randomNonce: string, timestamp: string, protocolVersion?: string) => Promise<string>;
@@ -1,20 +1,59 @@
1
+ /**
2
+ * Manifest validation, hashing, and session-derivation helpers.
3
+ *
4
+ * This is the entry point for freezing the ceremony root that every later DKG,
5
+ * ballot, decryption-share, and tally payload binds to.
6
+ */
1
7
  import { bytesToHex } from '../core/bytes.js';
2
8
  import { InvalidPayloadError, sha256, utf8ToBytes } from '../core/index.js';
3
9
  import { encodeForChallenge } from '../serialize/encoding.js';
4
10
  import { canonicalizeJson } from './canonical-json.js';
5
- /** Fixed transcript version string for the shipped beta protocol. */
11
+ import { validateSupportedScoreRange } from './score-range.js';
12
+ /**
13
+ * Default protocol namespace used by the built-in helpers and verifier.
14
+ *
15
+ * The current verifier only accepts this namespace, so applications that want
16
+ * verifier-compatible payloads should treat this value as fixed.
17
+ */
6
18
  export const SHIPPED_PROTOCOL_VERSION = 'v1';
7
19
  const assertNonEmptyString = (value, label) => {
8
20
  if (value.trim() === '') {
9
21
  throw new InvalidPayloadError(`${label} must be a non-empty string`);
10
22
  }
11
23
  };
12
- /** Validates that a protocol version string is present. */
24
+ const validateScoreRange = (scoreRange) => {
25
+ if (typeof scoreRange !== 'object' ||
26
+ scoreRange === null ||
27
+ Array.isArray(scoreRange)) {
28
+ throw new InvalidPayloadError('Election manifest scoreRange must be an object with min and max bounds');
29
+ }
30
+ const scoreRangeRecord = scoreRange;
31
+ if (typeof scoreRangeRecord.min !== 'number' ||
32
+ typeof scoreRangeRecord.max !== 'number') {
33
+ throw new InvalidPayloadError('Election manifest scoreRange requires numeric min and max bounds');
34
+ }
35
+ return validateSupportedScoreRange(scoreRange, {
36
+ comparisonMax: 'scoreRange.max',
37
+ min: 'Election manifest scoreRange.min',
38
+ max: 'Election manifest scoreRange.max',
39
+ });
40
+ };
41
+ /**
42
+ * Validates that a protocol version string is present and non-empty.
43
+ *
44
+ * Builders use this to normalize explicit overrides before attaching them to
45
+ * published payloads.
46
+ */
13
47
  export const assertValidProtocolVersion = (protocolVersion, label = 'Protocol version') => {
14
48
  assertNonEmptyString(protocolVersion, label);
15
49
  return protocolVersion;
16
50
  };
17
- /** Validates that a protocol version matches the shipped verifier line. */
51
+ /**
52
+ * Validates that a protocol version matches the verifier namespace.
53
+ *
54
+ * Verifier-facing paths call this when they need to reject protocol variants
55
+ * that are outside the package's supported public workflow.
56
+ */
18
57
  export const assertSupportedProtocolVersion = (protocolVersion, label = 'Protocol version') => {
19
58
  assertValidProtocolVersion(protocolVersion, label);
20
59
  if (protocolVersion !== SHIPPED_PROTOCOL_VERSION) {
@@ -23,14 +62,19 @@ export const assertSupportedProtocolVersion = (protocolVersion, label = 'Protoco
23
62
  return protocolVersion;
24
63
  };
25
64
  /**
26
- * Validates the supported election-manifest invariants for the shipped
27
- * score-voting workflow.
65
+ * Validates the supported election-manifest invariants for the score-voting
66
+ * workflow.
28
67
  *
29
- * @param manifest Election manifest to validate.
68
+ * The manifest is intentionally minimal: it fixes the frozen roster hash and
69
+ * option list together with one explicit global score range, while participant
70
+ * count and threshold are derived later from the accepted registration roster.
30
71
  */
31
72
  export const validateElectionManifest = (manifest) => {
32
73
  const manifestRecord = manifest;
33
74
  assertNonEmptyString(manifest.rosterHash, 'Roster hash');
75
+ if (!('scoreRange' in manifestRecord)) {
76
+ throw new InvalidPayloadError('Election manifest requires an explicit scoreRange');
77
+ }
34
78
  for (const legacyField of [
35
79
  'participantCount',
36
80
  'reconstructionThreshold',
@@ -48,7 +92,7 @@ export const validateElectionManifest = (manifest) => {
48
92
  'requiresAllOptions',
49
93
  ]) {
50
94
  if (legacyField in manifestRecord) {
51
- throw new InvalidPayloadError(`Legacy manifest field "${legacyField}" is not supported on the Ristretto beta line`);
95
+ throw new InvalidPayloadError(`Legacy manifest field "${legacyField}" is not supported by the public manifest`);
52
96
  }
53
97
  }
54
98
  if (manifest.optionList.length === 0) {
@@ -62,9 +106,15 @@ export const validateElectionManifest = (manifest) => {
62
106
  }
63
107
  seenOptions.add(option);
64
108
  }
65
- return manifest;
109
+ return {
110
+ ...manifest,
111
+ scoreRange: validateScoreRange(manifest.scoreRange),
112
+ };
66
113
  };
67
- /** Creates the minimal shipped election manifest. */
114
+ /**
115
+ * Creates the explicit election manifest after validating the supported
116
+ * invariants.
117
+ */
68
118
  export const createElectionManifest = (manifest) => validateElectionManifest(manifest);
69
119
  /**
70
120
  * Canonically serializes an election manifest.
@@ -76,18 +126,15 @@ export const canonicalizeElectionManifest = (manifest) => canonicalizeJson(valid
76
126
  /**
77
127
  * Hashes a canonical election manifest with SHA-256.
78
128
  *
79
- * @param manifest Election manifest to hash.
80
- * @returns Lowercase hexadecimal SHA-256 digest.
129
+ * The resulting digest is the manifest anchor reused by every later payload,
130
+ * proof context, and verifier stage.
81
131
  */
82
132
  export const hashElectionManifest = async (manifest) => bytesToHex(await sha256(utf8ToBytes(canonicalizeElectionManifest(manifest))));
83
133
  /**
84
134
  * Derives a globally unique session identifier from the frozen setup values.
85
135
  *
86
- * @param manifestHash Canonical manifest hash.
87
- * @param rosterHash Canonical roster hash.
88
- * @param randomNonce Public random nonce.
89
- * @param timestamp Timestamp string included in the derivation.
90
- * @param protocolVersion Protocol version namespace for the derived session.
91
- * @returns Lowercase hexadecimal SHA-256 digest.
136
+ * Applications normally compute this once after freezing the manifest and
137
+ * roster so every later payload can bind itself to one concrete ceremony
138
+ * instance.
92
139
  */
93
- export const deriveSessionId = async (manifestHash, rosterHash, randomNonce, timestamp, protocolVersion = SHIPPED_PROTOCOL_VERSION) => bytesToHex(await sha256(encodeForChallenge(assertValidProtocolVersion(protocolVersion, 'Session protocol version'), manifestHash, rosterHash, randomNonce, timestamp)));
140
+ export const deriveSessionId = async (manifestHash, rosterHash, randomNonce, timestamp, protocolVersion) => bytesToHex(await sha256(encodeForChallenge(assertValidProtocolVersion(protocolVersion ?? SHIPPED_PROTOCOL_VERSION, 'Session protocol version'), manifestHash, rosterHash, randomNonce, timestamp)));
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Deterministic ordering helpers for protocol payloads.
3
+ *
4
+ * Transcript hashing and board audit both rely on this ordering so all
5
+ * observers hash the same payload set in the same way.
6
+ */
1
7
  import { bytesToHex } from '../core/bytes.js';
2
8
  import { canonicalUnsignedPayloadBytes, payloadSlotKey } from './payloads.js';
3
9
  const compareStrings = (left, right) => {
@@ -21,9 +21,9 @@ export declare const canonicalUnsignedPayloadBytes: (payload: ProtocolPayload, b
21
21
  /**
22
22
  * Serializes the payload bytes that are covered by the outer Ed25519 signature.
23
23
  *
24
- * The signature preimage is domain-separated and version-bound so that protocol
25
- * payload signatures are not re-used across transcript families or protocol
26
- * revisions.
24
+ * The signature preimage is domain-separated and protocol-bound so that
25
+ * protocol payload signatures are not re-used across transcript families or
26
+ * protocol namespaces.
27
27
  *
28
28
  * @param payload Unsigned protocol payload.
29
29
  * @param bigintByteLength Fixed byte width used for any bigint fields.
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Canonical protocol-payload attachment, serialization, and slot-classification
3
+ * helpers shared by payload builders, board audit, and verification.
4
+ */
1
5
  import { bytesToHex } from '../core/bytes.js';
2
6
  import { utf8ToBytes } from '../core/index.js';
3
7
  import { encodeForChallenge } from '../serialize/encoding.js';
@@ -48,9 +52,9 @@ export const canonicalUnsignedPayloadBytes = (payload, bigintByteLength) => utf8
48
52
  /**
49
53
  * Serializes the payload bytes that are covered by the outer Ed25519 signature.
50
54
  *
51
- * The signature preimage is domain-separated and version-bound so that protocol
52
- * payload signatures are not re-used across transcript families or protocol
53
- * revisions.
55
+ * The signature preimage is domain-separated and protocol-bound so that
56
+ * protocol payload signatures are not re-used across transcript families or
57
+ * protocol namespaces.
54
58
  *
55
59
  * @param payload Unsigned protocol payload.
56
60
  * @param bigintByteLength Fixed byte width used for any bigint fields.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Low-level protocol helpers for transcript hashing, generic signed payloads,
3
+ * registration-roster hashing, signature verification, ballot proof
4
+ * verification, and protocol payload types.
5
+ *
6
+ * Use this module when you want protocol helpers grouped by subsystem instead
7
+ * of importing them from the root package.
8
+ *
9
+ * @module threshold-elgamal/protocol
10
+ * @packageDocumentation
11
+ */
12
+ export { signProtocolPayload } from './builders.js';
13
+ export { hashProtocolTranscript } from './transcript.js';
14
+ export { hashRosterEntries, verifySignedProtocolPayloads, type RosterEntry, type VerifiedProtocolSignatures, } from './verification.js';
15
+ export { verifyBallotSubmissionPayloadsByOption } from './voting-ballots.js';
16
+ export { scoreRangeDomain } from './voting-codecs.js';
17
+ export type { EncodedCiphertext, EncodedCompactProof, EncodedDisjunctiveProof, ProtocolMessageType, ProtocolPayload, ScoreRange, } from './types.js';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Low-level protocol helpers for transcript hashing, generic signed payloads,
3
+ * registration-roster hashing, signature verification, ballot proof
4
+ * verification, and protocol payload types.
5
+ *
6
+ * Use this module when you want protocol helpers grouped by subsystem instead
7
+ * of importing them from the root package.
8
+ *
9
+ * @module threshold-elgamal/protocol
10
+ * @packageDocumentation
11
+ */
12
+ export { signProtocolPayload } from './builders.js';
13
+ export { hashProtocolTranscript } from './transcript.js';
14
+ export { hashRosterEntries, verifySignedProtocolPayloads, } from './verification.js';
15
+ export { verifyBallotSubmissionPayloadsByOption } from './voting-ballots.js';
16
+ export { scoreRangeDomain } from './voting-codecs.js';
@@ -0,0 +1,6 @@
1
+ import type { ScoreRange } from './types.js';
2
+ export declare const validateSupportedScoreRange: (scoreRange: ScoreRange, labels: {
3
+ readonly comparisonMax?: string;
4
+ readonly min: string;
5
+ readonly max: string;
6
+ }) => ScoreRange;
@@ -0,0 +1,24 @@
1
+ import { InvalidPayloadError } from '../core/index.js';
2
+ const MAX_SUPPORTED_SCORE_RANGE_MAX = 100;
3
+ const assertSafeInteger = (value, label) => {
4
+ if (!Number.isSafeInteger(value)) {
5
+ throw new InvalidPayloadError(`${label} must be a safe integer`);
6
+ }
7
+ };
8
+ export const validateSupportedScoreRange = (scoreRange, labels) => {
9
+ assertSafeInteger(scoreRange.min, labels.min);
10
+ assertSafeInteger(scoreRange.max, labels.max);
11
+ if (scoreRange.min < 0) {
12
+ throw new InvalidPayloadError(`${labels.min} must be non-negative`);
13
+ }
14
+ if (scoreRange.max < 0) {
15
+ throw new InvalidPayloadError(`${labels.max} must be non-negative`);
16
+ }
17
+ if (scoreRange.min > scoreRange.max) {
18
+ throw new InvalidPayloadError(`${labels.min} must not exceed ${labels.comparisonMax ?? labels.max}`);
19
+ }
20
+ if (scoreRange.max > MAX_SUPPORTED_SCORE_RANGE_MAX) {
21
+ throw new InvalidPayloadError(`${labels.max} must not exceed ${MAX_SUPPORTED_SCORE_RANGE_MAX}`);
22
+ }
23
+ return scoreRange;
24
+ };
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Transcript hashing and fingerprint helpers for full protocol logs and
3
+ * individual phase snapshots.
4
+ */
1
5
  import { bytesToHex } from '../core/bytes.js';
2
6
  import { sha256, utf8ToBytes } from '../core/index.js';
3
7
  import { canonicalizeJson } from './canonical-json.js';