threshold-elgamal 1.1.0 → 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.
- package/README.md +17 -13
- package/dist/core/bigint.d.ts +2 -1
- package/dist/core/bigint.js +2 -1
- package/dist/elgamal/additive.js +6 -20
- package/dist/index.d.ts +27 -11
- package/dist/index.js +20 -10
- package/dist/proofs/disjunctive.d.ts +4 -3
- package/dist/proofs/disjunctive.js +5 -4
- package/dist/proofs/helpers.d.ts +1 -1
- package/dist/proofs/helpers.js +1 -4
- package/dist/proofs/types.d.ts +2 -2
- package/dist/protocol/builders.d.ts +1 -1
- package/dist/protocol/builders.js +1 -1
- package/dist/protocol/manifest.d.ts +3 -3
- package/dist/protocol/manifest.js +29 -5
- package/dist/protocol/public.d.ts +7 -5
- package/dist/protocol/public.js +6 -4
- package/dist/protocol/score-range.d.ts +6 -0
- package/dist/protocol/score-range.js +24 -0
- package/dist/protocol/types.d.ts +14 -2
- package/dist/protocol/voting-codecs.d.ts +7 -4
- package/dist/protocol/voting-codecs.js +13 -3
- package/dist/protocol/voting-shared.d.ts +2 -1
- package/dist/protocol/voting-shared.js +3 -2
- package/dist/protocol/voting-verification.js +2 -1
- package/package.json +13 -12
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# threshold-elgamal
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/threshold-elgamal)
|
|
3
|
+
[](https://www.npmjs.com/package/threshold-elgamal) [](https://www.npmjs.com/package/threshold-elgamal)
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
|
|
17
17
|
- additive ElGamal on `ristretto255`
|
|
18
18
|
- honest-majority GJKR DKG
|
|
19
|
-
-
|
|
20
|
-
-
|
|
19
|
+
- one explicit global contiguous score range per ceremony
|
|
20
|
+
- manifest `scoreRange.max` capped at `100` to keep proofs and tally recovery tractable
|
|
21
|
+
- one public manifest shape: `rosterHash`, `optionList`, and `scoreRange`
|
|
21
22
|
- organizer-signed `ballot-close` before decryption
|
|
22
23
|
- full local recomputation and full ceremony verification from the public board
|
|
23
24
|
|
|
@@ -77,10 +78,10 @@ Older browsers, stale embedded webviews, and runtimes without Web Crypto `X25519
|
|
|
77
78
|
The supported boardroom flow is:
|
|
78
79
|
|
|
79
80
|
1. Freeze the roster in the application and hash it with `hashRosterEntries(...)`.
|
|
80
|
-
2. Build the
|
|
81
|
+
2. Build the manifest with `createElectionManifest({ rosterHash, optionList, scoreRange })`.
|
|
81
82
|
3. Publish the manifest, registrations, and manifest acceptances.
|
|
82
83
|
4. Run the honest-majority GJKR transcript.
|
|
83
|
-
5. Post ballot payloads for complete
|
|
84
|
+
5. Post ballot payloads for complete scores inside the manifest-declared range.
|
|
84
85
|
6. Post one organizer-signed `ballot-close` payload that freezes which complete ballots are counted.
|
|
85
86
|
7. Post threshold decryption shares and tally publications for the close-selected ballot set.
|
|
86
87
|
8. Verify the whole ceremony with `verifyElectionCeremony(...)`.
|
|
@@ -129,6 +130,7 @@ const rosterHash = await hashRosterEntries([
|
|
|
129
130
|
const manifest = createElectionManifest({
|
|
130
131
|
rosterHash,
|
|
131
132
|
optionList: ["Option A", "Option B"],
|
|
133
|
+
scoreRange: { min: 1, max: 10 },
|
|
132
134
|
});
|
|
133
135
|
|
|
134
136
|
const manifestHash = await hashElectionManifest(manifest);
|
|
@@ -171,7 +173,7 @@ if (!result.ok) {
|
|
|
171
173
|
}
|
|
172
174
|
```
|
|
173
175
|
|
|
174
|
-
The root package exposes the
|
|
176
|
+
The root package exposes the builders and lower-level helpers required for the documented ceremony, including:
|
|
175
177
|
|
|
176
178
|
- manifest publication
|
|
177
179
|
- registration
|
|
@@ -186,14 +188,16 @@ The root package exposes the workflow-facing builders for the signed protocol pa
|
|
|
186
188
|
- decryption shares
|
|
187
189
|
- tally publication
|
|
188
190
|
|
|
189
|
-
|
|
191
|
+
The reveal path also works from the root package:
|
|
190
192
|
|
|
191
|
-
- prepare the accepted aggregate with `prepareAggregateForDecryption(...)`
|
|
192
|
-
- compute each partial share with `createDecryptionShare(...)`
|
|
193
|
-
- prove it with `createDLEQProof(...)`
|
|
194
|
-
- publish it with `createDecryptionSharePayload(...)`
|
|
193
|
+
- prepare the accepted aggregate with `prepareAggregateForDecryption(...)`
|
|
194
|
+
- compute each partial share with `createDecryptionShare(...)`
|
|
195
|
+
- prove it with `createDLEQProof(...)`
|
|
196
|
+
- publish it with `createDecryptionSharePayload(...)`
|
|
195
197
|
|
|
196
|
-
After collecting a threshold subset, recover the tally with `combineDecryptionShares(...)`
|
|
198
|
+
After collecting a threshold subset, recover the tally with `combineDecryptionShares(...)` against the prepared aggregate ciphertext.
|
|
199
|
+
|
|
200
|
+
The grouped public submodules remain available when you prefer narrower imports by subsystem, but the supported full ceremony does not require them.
|
|
197
201
|
|
|
198
202
|
For concrete posted JSON shapes, use [Published payload examples](https://tenemo.github.io/threshold-elgamal/guides/published-payload-examples/).
|
|
199
203
|
|
|
@@ -204,7 +208,7 @@ The library is designed for an honest-origin, honest-client, static-adversary se
|
|
|
204
208
|
What it tries to enforce:
|
|
205
209
|
|
|
206
210
|
- additive-only tallying on `ristretto255`
|
|
207
|
-
-
|
|
211
|
+
- one explicit global contiguous manifest score range
|
|
208
212
|
- grouped per-option ballot verification
|
|
209
213
|
- mandatory local aggregate recomputation before decryption
|
|
210
214
|
- organizer-visible and auditable ballot cutoff through `ballot-close`
|
package/dist/core/bigint.d.ts
CHANGED
|
@@ -7,7 +7,8 @@ export declare const mod: (value: bigint, modulus: bigint) => bigint;
|
|
|
7
7
|
/**
|
|
8
8
|
* Reduces a value into the range `0..q-1`.
|
|
9
9
|
*
|
|
10
|
-
* @throws {@link InvalidScalarError
|
|
10
|
+
* @throws {@link threshold-elgamal/core!InvalidScalarError | InvalidScalarError}
|
|
11
|
+
* When `q` is not positive.
|
|
11
12
|
*/
|
|
12
13
|
export declare const modQ: (value: bigint, q: bigint) => bigint;
|
|
13
14
|
/**
|
package/dist/core/bigint.js
CHANGED
|
@@ -70,7 +70,8 @@ const modP = (value, p) => mod(value, p);
|
|
|
70
70
|
/**
|
|
71
71
|
* Reduces a value into the range `0..q-1`.
|
|
72
72
|
*
|
|
73
|
-
* @throws {@link InvalidScalarError
|
|
73
|
+
* @throws {@link threshold-elgamal/core!InvalidScalarError | InvalidScalarError}
|
|
74
|
+
* When `q` is not positive.
|
|
74
75
|
*/
|
|
75
76
|
export const modQ = (value, q) => mod(value, q);
|
|
76
77
|
/**
|
package/dist/elgamal/additive.js
CHANGED
|
@@ -6,14 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { RISTRETTO_GROUP, assertAdditiveBound, assertInSubgroupOrIdentity, assertPlaintextAdditive, assertValidPublicKey, InvalidScalarError, } from '../core/index.js';
|
|
8
8
|
import { decodePoint, encodePoint, multiplyBase, pointAdd, pointMultiply, } from '../core/ristretto.js';
|
|
9
|
-
/** Validates an additive-mode public key against the built-in suite. */
|
|
10
|
-
const assertValidAdditivePublicKey = (publicKey) => {
|
|
11
|
-
assertValidPublicKey(publicKey);
|
|
12
|
-
};
|
|
13
|
-
/** Validates the caller-supplied additive plaintext bound. */
|
|
14
|
-
const assertValidAdditiveBound = (bound) => assertAdditiveBound(bound, RISTRETTO_GROUP.q);
|
|
15
|
-
/** Validates the plaintext domain and caller-supplied bound for additive mode. */
|
|
16
|
-
const assertValidAdditivePlaintext = (value, bound) => assertPlaintextAdditive(value, bound, RISTRETTO_GROUP.q);
|
|
17
9
|
/**
|
|
18
10
|
* Validates an additive ciphertext that may already represent an aggregate.
|
|
19
11
|
*
|
|
@@ -24,19 +16,12 @@ export const assertValidAdditiveCiphertext = (ciphertext) => {
|
|
|
24
16
|
assertInSubgroupOrIdentity(ciphertext.c1);
|
|
25
17
|
assertInSubgroupOrIdentity(ciphertext.c2);
|
|
26
18
|
};
|
|
27
|
-
const
|
|
19
|
+
const requireAdditiveBound = (bound) => {
|
|
28
20
|
if (typeof bound !== 'bigint') {
|
|
29
|
-
throw new InvalidScalarError(
|
|
21
|
+
throw new InvalidScalarError('Additive encryption requires an explicit plaintext bound');
|
|
30
22
|
}
|
|
31
23
|
return bound;
|
|
32
24
|
};
|
|
33
|
-
const resolveAdditiveContext = (bound, operation) => {
|
|
34
|
-
const resolvedBound = resolveAdditiveBound(bound, operation);
|
|
35
|
-
assertValidAdditiveBound(resolvedBound);
|
|
36
|
-
return {
|
|
37
|
-
bound: resolvedBound,
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
25
|
const assertEncryptionRandomness = (randomness) => {
|
|
41
26
|
if (randomness <= 0n || randomness >= RISTRETTO_GROUP.q) {
|
|
42
27
|
throw new InvalidScalarError('Encryption randomness must be in the range 1..q-1');
|
|
@@ -75,9 +60,10 @@ export const addEncryptedValues = (left, right) => {
|
|
|
75
60
|
* the randomness input and plaintext bound.
|
|
76
61
|
*/
|
|
77
62
|
export const encryptAdditiveWithRandomness = (message, publicKey, randomness, bound) => {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
63
|
+
const resolvedBound = requireAdditiveBound(bound);
|
|
64
|
+
assertAdditiveBound(resolvedBound, RISTRETTO_GROUP.q);
|
|
65
|
+
assertPlaintextAdditive(message, resolvedBound, RISTRETTO_GROUP.q);
|
|
66
|
+
assertValidPublicKey(publicKey);
|
|
81
67
|
assertEncryptionRandomness(randomness);
|
|
82
68
|
return encryptAdditiveWithValidatedInputs(message, publicKey, randomness);
|
|
83
69
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,25 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Root package exports for the supported public API.
|
|
3
3
|
*
|
|
4
|
-
* Use this entry point for the supported voting workflow: manifest and
|
|
5
|
-
* setup, transport keys and envelopes,
|
|
6
|
-
*
|
|
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.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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.
|
|
12
13
|
*
|
|
13
14
|
* @module threshold-elgamal
|
|
14
15
|
* @packageDocumentation
|
|
15
16
|
*/
|
|
17
|
+
export { RISTRETTO_GROUP, modQ } from './core/public.js';
|
|
18
|
+
export type { EncodedPoint } from './core/public.js';
|
|
16
19
|
export { majorityThreshold } from './core/validation.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';
|
|
17
26
|
export { decryptEnvelope, encryptEnvelope } from './transport/envelopes.js';
|
|
18
27
|
export { exportAuthPublicKey, generateAuthKeyPair } from './transport/auth.js';
|
|
19
28
|
export { exportTransportPublicKey, generateTransportKeyPair, } from './transport/key-agreement.js';
|
|
20
29
|
export type { EncodedAuthPublicKey, EncodedTransportPublicKey, EncryptedEnvelope, EnvelopeContext, TransportKeyPair, } from './transport/types.js';
|
|
21
|
-
export { createBallotClosePayload, createBallotSubmissionPayload, createDecryptionSharePayload, createEncryptedDualSharePayload, createFeldmanCommitmentPayload, createKeyDerivationConfirmationPayload, createManifestAcceptancePayload, createManifestPublicationPayload, createPedersenCommitmentPayload, createPhaseCheckpointPayload, createRegistrationPayload, createTallyPublicationPayload, } from './protocol/builders.js';
|
|
30
|
+
export { createBallotClosePayload, createBallotSubmissionPayload, createDecryptionSharePayload, createEncryptedDualSharePayload, createFeldmanCommitmentPayload, createKeyDerivationConfirmationPayload, createManifestAcceptancePayload, createManifestPublicationPayload, createPedersenCommitmentPayload, createPhaseCheckpointPayload, createRegistrationPayload, signProtocolPayload, createTallyPublicationPayload, } from './protocol/builders.js';
|
|
22
31
|
export { canonicalizeElectionManifest, createElectionManifest, deriveSessionId, hashElectionManifest, SHIPPED_PROTOCOL_VERSION, validateElectionManifest, } from './protocol/manifest.js';
|
|
23
|
-
export { hashRosterEntries } from './protocol/verification.js';
|
|
32
|
+
export { hashRosterEntries, verifySignedProtocolPayloads, type RosterEntry, type VerifiedProtocolSignatures, } from './protocol/verification.js';
|
|
33
|
+
export { hashProtocolTranscript } from './protocol/transcript.js';
|
|
34
|
+
export { verifyBallotSubmissionPayloadsByOption } from './protocol/voting-ballots.js';
|
|
35
|
+
export { scoreRangeDomain } from './protocol/voting-codecs.js';
|
|
24
36
|
export { verifyElectionCeremony, tryVerifyElectionCeremony, type ElectionVerificationErrorCode, type ElectionVerificationFailure, type ElectionVerificationResult, type ElectionVerificationStage, type VerifiedElectionCeremony, } from './protocol/voting-verification.js';
|
|
25
|
-
export type { BallotClosePayload, BallotSubmissionPayload, DecryptionSharePayload, ElectionManifest, EncryptedDualSharePayload, FeldmanCommitmentPayload, KeyDerivationConfirmation, ManifestAcceptancePayload, ManifestPublicationPayload, PedersenCommitmentPayload, PhaseCheckpointPayload, RegistrationPayload, SignedPayload, TallyPublicationPayload, VerifyElectionCeremonyInput, } from './protocol/types.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,23 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Root package exports for the supported public API.
|
|
3
3
|
*
|
|
4
|
-
* Use this entry point for the supported voting workflow: manifest and
|
|
5
|
-
* setup, transport keys and envelopes,
|
|
6
|
-
*
|
|
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.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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.
|
|
12
13
|
*
|
|
13
14
|
* @module threshold-elgamal
|
|
14
15
|
* @packageDocumentation
|
|
15
16
|
*/
|
|
17
|
+
export { RISTRETTO_GROUP, modQ } from './core/public.js';
|
|
16
18
|
export { majorityThreshold } from './core/validation.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';
|
|
17
22
|
export { decryptEnvelope, encryptEnvelope } from './transport/envelopes.js';
|
|
18
23
|
export { exportAuthPublicKey, generateAuthKeyPair } from './transport/auth.js';
|
|
19
24
|
export { exportTransportPublicKey, generateTransportKeyPair, } from './transport/key-agreement.js';
|
|
20
|
-
export { createBallotClosePayload, createBallotSubmissionPayload, createDecryptionSharePayload, createEncryptedDualSharePayload, createFeldmanCommitmentPayload, createKeyDerivationConfirmationPayload, createManifestAcceptancePayload, createManifestPublicationPayload, createPedersenCommitmentPayload, createPhaseCheckpointPayload, createRegistrationPayload, createTallyPublicationPayload, } from './protocol/builders.js';
|
|
25
|
+
export { createBallotClosePayload, createBallotSubmissionPayload, createDecryptionSharePayload, createEncryptedDualSharePayload, createFeldmanCommitmentPayload, createKeyDerivationConfirmationPayload, createManifestAcceptancePayload, createManifestPublicationPayload, createPedersenCommitmentPayload, createPhaseCheckpointPayload, createRegistrationPayload, signProtocolPayload, createTallyPublicationPayload, } from './protocol/builders.js';
|
|
21
26
|
export { canonicalizeElectionManifest, createElectionManifest, deriveSessionId, hashElectionManifest, SHIPPED_PROTOCOL_VERSION, validateElectionManifest, } from './protocol/manifest.js';
|
|
22
|
-
export { hashRosterEntries } from './protocol/verification.js';
|
|
27
|
+
export { hashRosterEntries, verifySignedProtocolPayloads, } from './protocol/verification.js';
|
|
28
|
+
export { hashProtocolTranscript } from './protocol/transcript.js';
|
|
29
|
+
export { verifyBallotSubmissionPayloadsByOption } from './protocol/voting-ballots.js';
|
|
30
|
+
export { scoreRangeDomain } from './protocol/voting-codecs.js';
|
|
23
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';
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CDS94-style disjunctive proofs for additive
|
|
2
|
+
* CDS94-style disjunctive proofs for additive ElGamal plaintexts drawn from an
|
|
3
|
+
* explicit finite value set.
|
|
3
4
|
*
|
|
4
5
|
* Ballot payloads use this module to prove that a ciphertext encodes one value
|
|
5
|
-
* from the
|
|
6
|
+
* from the manifest-declared domain without revealing which value was chosen.
|
|
6
7
|
*/
|
|
7
8
|
import { type CryptoGroup, type RandomBytesSource } from '../core/index.js';
|
|
8
9
|
import type { ElGamalCiphertext } from '../elgamal/types.js';
|
|
@@ -18,6 +19,6 @@ export declare const createDisjunctiveProof: (plaintext: bigint, randomness: big
|
|
|
18
19
|
* Verifies a CDS94-style disjunctive proof for additive ElGamal plaintexts.
|
|
19
20
|
*
|
|
20
21
|
* Ballot verification uses this to reject ciphertexts that do not encode one
|
|
21
|
-
* of the allowed
|
|
22
|
+
* of the allowed manifest-domain values for the current option slot.
|
|
22
23
|
*/
|
|
23
24
|
export declare const verifyDisjunctiveProof: (proof: DisjunctiveProof, ciphertext: ElGamalCiphertext, publicKey: string, validValues: readonly bigint[], group: CryptoGroup, context: ProofContext) => Promise<boolean>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CDS94-style disjunctive proofs for additive
|
|
2
|
+
* CDS94-style disjunctive proofs for additive ElGamal plaintexts drawn from an
|
|
3
|
+
* explicit finite value set.
|
|
3
4
|
*
|
|
4
5
|
* Ballot payloads use this module to prove that a ciphertext encodes one value
|
|
5
|
-
* from the
|
|
6
|
+
* from the manifest-declared domain without revealing which value was chosen.
|
|
6
7
|
*/
|
|
7
8
|
import { assertInSubgroup, assertInSubgroupOrIdentity, assertScalarInZq, InvalidProofError, modQ, randomScalarBelow, } from '../core/index.js';
|
|
8
9
|
import { decodePoint, encodePoint, multiplyBase, pointMultiply, pointSubtract, } from '../core/ristretto.js';
|
|
@@ -11,7 +12,7 @@ import { assertProofContext, contextElements, fixedPoint, fixedScalar, hashChall
|
|
|
11
12
|
import { hedgedNonce } from './nonces.js';
|
|
12
13
|
const candidateEncoding = (ciphertext, candidateValue, group) => encodePoint(pointSubtract(decodePoint(ciphertext.c2, 'Ciphertext c2'), multiplyBase(modQ(candidateValue, group.q))));
|
|
13
14
|
const commitmentSequence = (commitments) => encodeSequenceForChallenge(commitments.map((commitment) => concatBytes(encodeForChallenge(fixedPoint(commitment.a1)), encodeForChallenge(fixedPoint(commitment.a2)))));
|
|
14
|
-
const challengePayload = (ciphertext, publicKey, validValues, commitments, group, context) => encodeForChallenge(...contextElements(context), fixedPoint(group.g), fixedPoint(publicKey), fixedPoint(ciphertext.c1), fixedPoint(ciphertext.c2), encodeSequenceForChallenge(validValues.map((value) => fixedScalar(modQ(value, group.q)
|
|
15
|
+
const challengePayload = (ciphertext, publicKey, validValues, commitments, group, context) => encodeForChallenge(...contextElements(context), fixedPoint(group.g), fixedPoint(publicKey), fixedPoint(ciphertext.c1), fixedPoint(ciphertext.c2), encodeSequenceForChallenge(validValues.map((value) => fixedScalar(modQ(value, group.q)))), commitmentSequence(commitments));
|
|
15
16
|
/**
|
|
16
17
|
* Creates a CDS94-style disjunctive proof for additive ElGamal plaintexts.
|
|
17
18
|
*
|
|
@@ -67,7 +68,7 @@ export const createDisjunctiveProof = async (plaintext, randomness, ciphertext,
|
|
|
67
68
|
* Verifies a CDS94-style disjunctive proof for additive ElGamal plaintexts.
|
|
68
69
|
*
|
|
69
70
|
* Ballot verification uses this to reject ciphertexts that do not encode one
|
|
70
|
-
* of the allowed
|
|
71
|
+
* of the allowed manifest-domain values for the current option slot.
|
|
71
72
|
*/
|
|
72
73
|
export const verifyDisjunctiveProof = async (proof, ciphertext, publicKey, validValues, group, context) => {
|
|
73
74
|
assertProofContext(context, group);
|
package/dist/proofs/helpers.d.ts
CHANGED
|
@@ -3,6 +3,6 @@ import type { ProofContext } from './types.js';
|
|
|
3
3
|
export declare const assertProofContext: (context: ProofContext, group: CryptoGroup) => void;
|
|
4
4
|
export declare const contextElements: (context: ProofContext) => (bigint | string | Uint8Array)[];
|
|
5
5
|
export declare const fixedPoint: (value: string) => Uint8Array;
|
|
6
|
-
export declare const fixedScalar: (value: bigint
|
|
6
|
+
export declare const fixedScalar: (value: bigint) => Uint8Array;
|
|
7
7
|
export declare const hashChallenge: (payload: Uint8Array, q: bigint) => Promise<bigint>;
|
|
8
8
|
export declare const sumChallenges: (values: readonly bigint[], q: bigint) => bigint;
|
package/dist/proofs/helpers.js
CHANGED
|
@@ -59,9 +59,6 @@ export const contextElements = (context) => {
|
|
|
59
59
|
return fields;
|
|
60
60
|
};
|
|
61
61
|
export const fixedPoint = (value) => hexToBytes(value);
|
|
62
|
-
export const fixedScalar = (value
|
|
63
|
-
void group;
|
|
64
|
-
return hexToBytes(encodeScalar(value));
|
|
65
|
-
};
|
|
62
|
+
export const fixedScalar = (value) => hexToBytes(encodeScalar(value));
|
|
66
63
|
export const hashChallenge = (payload, q) => Promise.resolve(modQ(hashChallengeToScalar(payload), q));
|
|
67
64
|
export const sumChallenges = (values, q) => values.reduce((sum, value) => modQ(sum + value, q), 0n);
|
package/dist/proofs/types.d.ts
CHANGED
|
@@ -58,8 +58,8 @@ export type DisjunctiveBranch = {
|
|
|
58
58
|
/**
|
|
59
59
|
* A disjunctive proof over an ordered set of valid plaintext values.
|
|
60
60
|
*
|
|
61
|
-
* Ballot payloads use this to prove that an encrypted
|
|
62
|
-
* allowed
|
|
61
|
+
* Ballot payloads use this to prove that an encrypted value came from the
|
|
62
|
+
* allowed manifest domain without revealing which value was chosen.
|
|
63
63
|
*/
|
|
64
64
|
export type DisjunctiveProof = {
|
|
65
65
|
readonly branches: readonly DisjunctiveBranch[];
|
|
@@ -19,7 +19,7 @@ export declare const signProtocolPayload: <TMessageType extends ProtocolMessageT
|
|
|
19
19
|
* Creates a signed `manifest-publication` payload.
|
|
20
20
|
*
|
|
21
21
|
* This is the first public payload in the supported ceremony and anchors the
|
|
22
|
-
*
|
|
22
|
+
* explicit manifest on the board.
|
|
23
23
|
*/
|
|
24
24
|
export declare const createManifestPublicationPayload: (privateKey: CryptoKey, input: {
|
|
25
25
|
readonly manifest: ElectionManifest;
|
|
@@ -30,7 +30,7 @@ export const signProtocolPayload = async (privateKey, payload) => {
|
|
|
30
30
|
* Creates a signed `manifest-publication` payload.
|
|
31
31
|
*
|
|
32
32
|
* This is the first public payload in the supported ceremony and anchors the
|
|
33
|
-
*
|
|
33
|
+
* explicit manifest on the board.
|
|
34
34
|
*/
|
|
35
35
|
export const createManifestPublicationPayload = async (privateKey, input) => signProtocolPayload(privateKey, {
|
|
36
36
|
protocolVersion: input.protocolVersion,
|
|
@@ -25,12 +25,12 @@ export declare const assertSupportedProtocolVersion: (protocolVersion: string, l
|
|
|
25
25
|
* workflow.
|
|
26
26
|
*
|
|
27
27
|
* The manifest is intentionally minimal: it fixes the frozen roster hash and
|
|
28
|
-
* option list
|
|
29
|
-
* accepted registration roster.
|
|
28
|
+
* option list together with one explicit global score range, while participant
|
|
29
|
+
* count and threshold are derived later from the accepted registration roster.
|
|
30
30
|
*/
|
|
31
31
|
export declare const validateElectionManifest: (manifest: ElectionManifest) => ElectionManifest;
|
|
32
32
|
/**
|
|
33
|
-
* Creates the
|
|
33
|
+
* Creates the explicit election manifest after validating the supported
|
|
34
34
|
* invariants.
|
|
35
35
|
*/
|
|
36
36
|
export declare const createElectionManifest: (manifest: ElectionManifest) => ElectionManifest;
|
|
@@ -8,6 +8,7 @@ import { bytesToHex } from '../core/bytes.js';
|
|
|
8
8
|
import { InvalidPayloadError, sha256, utf8ToBytes } from '../core/index.js';
|
|
9
9
|
import { encodeForChallenge } from '../serialize/encoding.js';
|
|
10
10
|
import { canonicalizeJson } from './canonical-json.js';
|
|
11
|
+
import { validateSupportedScoreRange } from './score-range.js';
|
|
11
12
|
/**
|
|
12
13
|
* Default protocol namespace used by the built-in helpers and verifier.
|
|
13
14
|
*
|
|
@@ -20,6 +21,23 @@ const assertNonEmptyString = (value, label) => {
|
|
|
20
21
|
throw new InvalidPayloadError(`${label} must be a non-empty string`);
|
|
21
22
|
}
|
|
22
23
|
};
|
|
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
|
+
};
|
|
23
41
|
/**
|
|
24
42
|
* Validates that a protocol version string is present and non-empty.
|
|
25
43
|
*
|
|
@@ -48,12 +66,15 @@ export const assertSupportedProtocolVersion = (protocolVersion, label = 'Protoco
|
|
|
48
66
|
* workflow.
|
|
49
67
|
*
|
|
50
68
|
* The manifest is intentionally minimal: it fixes the frozen roster hash and
|
|
51
|
-
* option list
|
|
52
|
-
* accepted registration roster.
|
|
69
|
+
* option list together with one explicit global score range, while participant
|
|
70
|
+
* count and threshold are derived later from the accepted registration roster.
|
|
53
71
|
*/
|
|
54
72
|
export const validateElectionManifest = (manifest) => {
|
|
55
73
|
const manifestRecord = manifest;
|
|
56
74
|
assertNonEmptyString(manifest.rosterHash, 'Roster hash');
|
|
75
|
+
if (!('scoreRange' in manifestRecord)) {
|
|
76
|
+
throw new InvalidPayloadError('Election manifest requires an explicit scoreRange');
|
|
77
|
+
}
|
|
57
78
|
for (const legacyField of [
|
|
58
79
|
'participantCount',
|
|
59
80
|
'reconstructionThreshold',
|
|
@@ -71,7 +92,7 @@ export const validateElectionManifest = (manifest) => {
|
|
|
71
92
|
'requiresAllOptions',
|
|
72
93
|
]) {
|
|
73
94
|
if (legacyField in manifestRecord) {
|
|
74
|
-
throw new InvalidPayloadError(`Legacy manifest field "${legacyField}" is not supported
|
|
95
|
+
throw new InvalidPayloadError(`Legacy manifest field "${legacyField}" is not supported by the public manifest`);
|
|
75
96
|
}
|
|
76
97
|
}
|
|
77
98
|
if (manifest.optionList.length === 0) {
|
|
@@ -85,10 +106,13 @@ export const validateElectionManifest = (manifest) => {
|
|
|
85
106
|
}
|
|
86
107
|
seenOptions.add(option);
|
|
87
108
|
}
|
|
88
|
-
return
|
|
109
|
+
return {
|
|
110
|
+
...manifest,
|
|
111
|
+
scoreRange: validateScoreRange(manifest.scoreRange),
|
|
112
|
+
};
|
|
89
113
|
};
|
|
90
114
|
/**
|
|
91
|
-
* Creates the
|
|
115
|
+
* Creates the explicit election manifest after validating the supported
|
|
92
116
|
* invariants.
|
|
93
117
|
*/
|
|
94
118
|
export const createElectionManifest = (manifest) => validateElectionManifest(manifest);
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Low-level protocol helpers for transcript hashing, generic signed payloads,
|
|
3
|
-
*
|
|
3
|
+
* registration-roster hashing, signature verification, ballot proof
|
|
4
|
+
* verification, and protocol payload types.
|
|
4
5
|
*
|
|
5
|
-
* Use this module when you
|
|
6
|
-
*
|
|
6
|
+
* Use this module when you want protocol helpers grouped by subsystem instead
|
|
7
|
+
* of importing them from the root package.
|
|
7
8
|
*
|
|
8
9
|
* @module threshold-elgamal/protocol
|
|
9
10
|
* @packageDocumentation
|
|
10
11
|
*/
|
|
11
12
|
export { signProtocolPayload } from './builders.js';
|
|
12
13
|
export { hashProtocolTranscript } from './transcript.js';
|
|
14
|
+
export { hashRosterEntries, verifySignedProtocolPayloads, type RosterEntry, type VerifiedProtocolSignatures, } from './verification.js';
|
|
13
15
|
export { verifyBallotSubmissionPayloadsByOption } from './voting-ballots.js';
|
|
14
|
-
export {
|
|
15
|
-
export type { EncodedCiphertext, EncodedCompactProof, EncodedDisjunctiveProof, ProtocolMessageType, ProtocolPayload, } from './types.js';
|
|
16
|
+
export { scoreRangeDomain } from './voting-codecs.js';
|
|
17
|
+
export type { EncodedCiphertext, EncodedCompactProof, EncodedDisjunctiveProof, ProtocolMessageType, ProtocolPayload, ScoreRange, } from './types.js';
|
package/dist/protocol/public.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Low-level protocol helpers for transcript hashing, generic signed payloads,
|
|
3
|
-
*
|
|
3
|
+
* registration-roster hashing, signature verification, ballot proof
|
|
4
|
+
* verification, and protocol payload types.
|
|
4
5
|
*
|
|
5
|
-
* Use this module when you
|
|
6
|
-
*
|
|
6
|
+
* Use this module when you want protocol helpers grouped by subsystem instead
|
|
7
|
+
* of importing them from the root package.
|
|
7
8
|
*
|
|
8
9
|
* @module threshold-elgamal/protocol
|
|
9
10
|
* @packageDocumentation
|
|
10
11
|
*/
|
|
11
12
|
export { signProtocolPayload } from './builders.js';
|
|
12
13
|
export { hashProtocolTranscript } from './transcript.js';
|
|
14
|
+
export { hashRosterEntries, verifySignedProtocolPayloads, } from './verification.js';
|
|
13
15
|
export { verifyBallotSubmissionPayloadsByOption } from './voting-ballots.js';
|
|
14
|
-
export {
|
|
16
|
+
export { scoreRangeDomain } from './voting-codecs.js';
|
|
@@ -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
|
+
};
|
package/dist/protocol/types.d.ts
CHANGED
|
@@ -217,12 +217,24 @@ export type SignedPayload<TPayload extends ProtocolPayload = ProtocolPayload> =
|
|
|
217
217
|
/**
|
|
218
218
|
* Canonical election-manifest shape bound into protocol transcripts.
|
|
219
219
|
*
|
|
220
|
-
* The manifest is intentionally
|
|
221
|
-
*
|
|
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.
|
|
222
233
|
*/
|
|
223
234
|
export type ElectionManifest = {
|
|
224
235
|
readonly rosterHash: string;
|
|
225
236
|
readonly optionList: readonly string[];
|
|
237
|
+
readonly scoreRange: ScoreRange;
|
|
226
238
|
};
|
|
227
239
|
/**
|
|
228
240
|
* Input bundle for verifying typed ballot payloads.
|
|
@@ -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,8 +47,11 @@ export declare const encodeDisjunctiveProof: (proof: DisjunctiveProof) => Encode
|
|
|
47
47
|
*/
|
|
48
48
|
export declare const decodeDisjunctiveProof: (proof: EncodedDisjunctiveProof) => DisjunctiveProof;
|
|
49
49
|
/**
|
|
50
|
-
*
|
|
50
|
+
* Expands one inclusive contiguous score range into its allowed plaintext
|
|
51
|
+
* domain.
|
|
51
52
|
*
|
|
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.
|
|
53
56
|
*/
|
|
54
|
-
export declare const
|
|
57
|
+
export declare const scoreRangeDomain: (scoreRange: ScoreRange) => readonly bigint[];
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* their published protocol payload representations.
|
|
4
4
|
*/
|
|
5
5
|
import { decodePoint, decodeScalar, encodeScalar } from '../core/ristretto.js';
|
|
6
|
+
import { validateSupportedScoreRange } from './score-range.js';
|
|
6
7
|
/**
|
|
7
8
|
* Encodes an additive ciphertext into fixed-width protocol hex.
|
|
8
9
|
*
|
|
@@ -66,8 +67,17 @@ export const decodeDisjunctiveProof = (proof) => ({
|
|
|
66
67
|
branches: proof.branches.map((branch) => decodeCompactProof(branch)),
|
|
67
68
|
});
|
|
68
69
|
/**
|
|
69
|
-
*
|
|
70
|
+
* Expands one inclusive contiguous score range into its allowed plaintext
|
|
71
|
+
* domain.
|
|
70
72
|
*
|
|
71
|
-
*
|
|
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.
|
|
72
76
|
*/
|
|
73
|
-
export const
|
|
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
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ProofContext } from '../proofs/types.js';
|
|
2
|
-
import type { DecryptionSharePayload, ElectionManifest,
|
|
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;
|
|
@@ -8,7 +8,7 @@ import { assertPositiveParticipantIndex, InvalidPayloadError, RISTRETTO_GROUP, }
|
|
|
8
8
|
import { importAuthPublicKey, verifyPayloadSignature } from '../transport/auth.js';
|
|
9
9
|
import { hashElectionManifest, assertSupportedProtocolVersion, SHIPPED_PROTOCOL_VERSION, validateElectionManifest, } from './manifest.js';
|
|
10
10
|
import { signedProtocolPayloadBytes } from './payloads.js';
|
|
11
|
-
import {
|
|
11
|
+
import { scoreRangeDomain } from './voting-codecs.js';
|
|
12
12
|
export const BALLOT_SUBMISSION_PHASE = 5;
|
|
13
13
|
export const BALLOT_CLOSE_PHASE = 6;
|
|
14
14
|
export const DECRYPTION_SHARE_PHASE = 7;
|
|
@@ -53,7 +53,8 @@ export const buildVotingManifestContext = async (manifest, sessionId) => {
|
|
|
53
53
|
manifestHash: await hashElectionManifest(validatedManifest),
|
|
54
54
|
optionCount: validatedManifest.optionList.length,
|
|
55
55
|
protocolVersion: SHIPPED_PROTOCOL_VERSION,
|
|
56
|
-
scoreDomainValues:
|
|
56
|
+
scoreDomainValues: scoreRangeDomain(validatedManifest.scoreRange),
|
|
57
|
+
scoreRangeMax: BigInt(validatedManifest.scoreRange.max),
|
|
57
58
|
sessionId,
|
|
58
59
|
};
|
|
59
60
|
};
|
|
@@ -37,7 +37,7 @@ const recomputePublishedTally = (input) => {
|
|
|
37
37
|
sessionId: input.sessionId,
|
|
38
38
|
optionIndex: input.ballots.optionIndex,
|
|
39
39
|
});
|
|
40
|
-
return combineDecryptionShares(preparedAggregate.ciphertext, input.decryptionShares.map((entry) => entry.share), BigInt(input.ballots.aggregate.ballotCount) *
|
|
40
|
+
return combineDecryptionShares(preparedAggregate.ciphertext, input.decryptionShares.map((entry) => entry.share), BigInt(input.ballots.aggregate.ballotCount) * input.scoreRangeMax);
|
|
41
41
|
};
|
|
42
42
|
const verifyPublishedTallyPayload = (payload, optionIndex, ballots, decryptionShares, tally) => {
|
|
43
43
|
if (payload.transcriptHash !== ballots.aggregate.transcriptHash) {
|
|
@@ -280,6 +280,7 @@ export const verifyElectionCeremony = async (input) => {
|
|
|
280
280
|
jointPublicKey: dkg.jointPublicKey,
|
|
281
281
|
protocolVersion: context.protocolVersion,
|
|
282
282
|
manifestHash: context.manifestHash,
|
|
283
|
+
scoreRangeMax: context.scoreRangeMax,
|
|
283
284
|
sessionId: context.sessionId,
|
|
284
285
|
});
|
|
285
286
|
const publication = tallyPublicationMap.get(optionIndex);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "threshold-elgamal",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.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",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"coverage:badge": "pnpm run coverage:node && tsx ./tools/generate-coverage-badge.ts",
|
|
27
27
|
"smoke:pack": "tsx ./tools/ci/verify-packed-package.ts",
|
|
28
28
|
"prebuild": "pnpm run check && pnpm run test",
|
|
29
|
-
"build": "pnpm exec del-cli dist && tsc --project tsconfig.build.json && tsx ./tools/build/rewrite-dist-relative-imports.ts",
|
|
29
|
+
"build:dist": "pnpm exec del-cli dist && tsc --project tsconfig.build.json && tsx ./tools/build/rewrite-dist-relative-imports.ts",
|
|
30
|
+
"build": "pnpm run build:dist",
|
|
30
31
|
"vectors:threshold": "tsx ./tools/generate-threshold-vectors.ts",
|
|
31
32
|
"vectors:protocol": "tsx ./tools/generate-protocol-vectors.ts",
|
|
32
33
|
"bench:micro": "tsx ./tools/benchmarks/microbench.ts",
|
|
@@ -39,8 +40,8 @@
|
|
|
39
40
|
"prepare": "husky"
|
|
40
41
|
},
|
|
41
42
|
"dependencies": {
|
|
42
|
-
"@noble/curves": "^2.0
|
|
43
|
-
"@noble/hashes": "^2.0
|
|
43
|
+
"@noble/curves": "^2.2.0",
|
|
44
|
+
"@noble/hashes": "^2.2.0"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@astrojs/mdx": "^5.0.3",
|
|
@@ -49,25 +50,25 @@
|
|
|
49
50
|
"@eslint/js": "^10.0.1",
|
|
50
51
|
"@fontsource/ibm-plex-sans": "^5.2.8",
|
|
51
52
|
"@fontsource/jetbrains-mono": "^5.2.8",
|
|
52
|
-
"@types/node": "^25.
|
|
53
|
-
"@typescript-eslint/eslint-plugin": "^8.58.
|
|
54
|
-
"@typescript-eslint/parser": "^8.58.
|
|
53
|
+
"@types/node": "^25.6.0",
|
|
54
|
+
"@typescript-eslint/eslint-plugin": "^8.58.2",
|
|
55
|
+
"@typescript-eslint/parser": "^8.58.2",
|
|
55
56
|
"@vitest/browser-playwright": "^4.1.4",
|
|
56
57
|
"@vitest/coverage-v8": "^4.1.4",
|
|
57
|
-
"astro": "^6.1.
|
|
58
|
+
"astro": "^6.1.7",
|
|
58
59
|
"del-cli": "^7.0.0",
|
|
59
60
|
"eslint": "^10.2.0",
|
|
60
61
|
"eslint-config-prettier": "^10.1.8",
|
|
61
62
|
"eslint-plugin-import-x": "^4.16.2",
|
|
62
63
|
"eslint-plugin-only-error": "^1.0.2",
|
|
63
64
|
"eslint-plugin-prettier": "^5.5.5",
|
|
64
|
-
"globals": "^17.
|
|
65
|
+
"globals": "^17.5.0",
|
|
65
66
|
"husky": "^9.1.7",
|
|
66
|
-
"knip": "^
|
|
67
|
+
"knip": "^6.4.1",
|
|
67
68
|
"playwright": "^1.59.1",
|
|
68
|
-
"prettier": "^3.8.
|
|
69
|
+
"prettier": "^3.8.3",
|
|
69
70
|
"tsx": "^4.21.0",
|
|
70
|
-
"typedoc": "^0.28.
|
|
71
|
+
"typedoc": "^0.28.19",
|
|
71
72
|
"typedoc-plugin-markdown": "^4.11.0",
|
|
72
73
|
"typescript": "^6.0.2",
|
|
73
74
|
"vite": "^8.0.8",
|