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.
- package/README.md +33 -35
- package/dist/core/bigint.d.ts +2 -1
- package/dist/core/bigint.js +6 -1
- package/dist/core/bytes.js +4 -0
- package/dist/core/crypto.js +5 -0
- package/dist/core/errors.d.ts +50 -11
- package/dist/core/errors.js +50 -11
- package/dist/core/groups.d.ts +6 -1
- package/dist/core/groups.js +13 -2
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +4 -0
- package/dist/core/public.d.ts +13 -0
- package/dist/core/public.js +12 -0
- package/dist/core/random.js +4 -0
- package/dist/core/ristretto.d.ts +7 -0
- package/dist/core/ristretto.js +7 -0
- package/dist/core/types.d.ts +14 -6
- package/dist/core/validation.d.ts +45 -12
- package/dist/core/validation.js +52 -12
- package/dist/dkg/pedersen-share-codec.d.ts +12 -2
- package/dist/dkg/pedersen-share-codec.js +19 -2
- package/dist/dkg/public.d.ts +12 -0
- package/dist/dkg/public.js +11 -0
- package/dist/dkg/verification.d.ts +33 -14
- package/dist/dkg/verification.js +16 -12
- package/dist/elgamal/additive.d.ts +15 -3
- package/dist/elgamal/additive.js +27 -23
- package/dist/elgamal/bsgs.js +7 -0
- package/dist/elgamal/public.d.ts +11 -0
- package/dist/elgamal/public.js +10 -0
- package/dist/elgamal/types.d.ts +6 -0
- package/dist/index.d.ts +27 -25
- package/dist/index.js +20 -19
- package/dist/proofs/disjunctive.d.ts +11 -16
- package/dist/proofs/disjunctive.js +12 -17
- package/dist/proofs/dleq.d.ts +17 -13
- package/dist/proofs/dleq.js +11 -12
- package/dist/proofs/helpers.d.ts +1 -1
- package/dist/proofs/helpers.js +5 -4
- package/dist/proofs/nonces.js +6 -0
- package/dist/proofs/public.d.ts +12 -0
- package/dist/proofs/public.js +11 -0
- package/dist/proofs/schnorr.d.ts +10 -11
- package/dist/proofs/schnorr.js +10 -11
- package/dist/proofs/types.d.ts +30 -5
- package/dist/protocol/board-audit.d.ts +8 -3
- package/dist/protocol/board-audit.js +8 -2
- package/dist/protocol/builders.d.ts +77 -13
- package/dist/protocol/builders.js +83 -13
- package/dist/protocol/canonical-json.js +4 -0
- package/dist/protocol/manifest.d.ts +32 -15
- package/dist/protocol/manifest.js +65 -18
- package/dist/protocol/ordering.js +6 -0
- package/dist/protocol/payloads.d.ts +3 -3
- package/dist/protocol/payloads.js +7 -3
- package/dist/protocol/public.d.ts +17 -0
- package/dist/protocol/public.js +16 -0
- package/dist/protocol/score-range.d.ts +6 -0
- package/dist/protocol/score-range.js +24 -0
- package/dist/protocol/transcript.js +4 -0
- package/dist/protocol/types.d.ts +110 -17
- package/dist/protocol/verification.d.ts +14 -7
- package/dist/protocol/verification.js +7 -5
- package/dist/protocol/voting-ballot-aggregation.d.ts +10 -2
- package/dist/protocol/voting-ballot-aggregation.js +6 -0
- package/dist/protocol/voting-ballots.d.ts +3 -5
- package/dist/protocol/voting-ballots.js +6 -5
- package/dist/protocol/voting-codecs.d.ts +8 -3
- package/dist/protocol/voting-codecs.js +18 -2
- package/dist/protocol/voting-decryption.d.ts +3 -5
- package/dist/protocol/voting-decryption.js +7 -5
- package/dist/protocol/voting-shared.d.ts +2 -1
- package/dist/protocol/voting-shared.js +9 -2
- package/dist/protocol/voting-verification.d.ts +27 -30
- package/dist/protocol/voting-verification.js +13 -26
- package/dist/serialize/encoding.js +4 -0
- package/dist/threshold/decrypt.d.ts +13 -5
- package/dist/threshold/decrypt.js +20 -5
- package/dist/threshold/public.d.ts +11 -0
- package/dist/threshold/public.js +10 -0
- package/dist/threshold/types.d.ts +23 -4
- package/dist/transport/auth.d.ts +10 -16
- package/dist/transport/auth.js +16 -16
- package/dist/transport/complaints.d.ts +2 -4
- package/dist/transport/complaints.js +8 -4
- package/dist/transport/envelopes.d.ts +5 -7
- package/dist/transport/envelopes.js +9 -7
- package/dist/transport/key-agreement.d.ts +15 -23
- package/dist/transport/key-agreement.js +21 -23
- package/dist/transport/types.d.ts +16 -2
- package/dist/vss/feldman.d.ts +11 -1
- package/dist/vss/feldman.js +11 -1
- package/dist/vss/pedersen.d.ts +13 -0
- package/dist/vss/pedersen.js +13 -0
- package/dist/vss/public.d.ts +10 -0
- package/dist/vss/public.js +9 -0
- package/dist/vss/types.d.ts +4 -0
- package/package.json +43 -14
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Top-level full-ceremony verification for the supported voting workflow.
|
|
3
|
+
*
|
|
4
|
+
* This module is the shortest path for auditors and bulletin-board readers who
|
|
5
|
+
* want one call that replays manifest validation, board audit, DKG, ballots,
|
|
6
|
+
* decryption shares, and tally checks.
|
|
7
|
+
*/
|
|
1
8
|
import { InvalidPayloadError } from '../core/index.js';
|
|
2
9
|
import { decodeScalar } from '../core/ristretto.js';
|
|
3
10
|
import { verifyDKGTranscript, } from '../dkg/verification.js';
|
|
@@ -30,7 +37,7 @@ const recomputePublishedTally = (input) => {
|
|
|
30
37
|
sessionId: input.sessionId,
|
|
31
38
|
optionIndex: input.ballots.optionIndex,
|
|
32
39
|
});
|
|
33
|
-
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);
|
|
34
41
|
};
|
|
35
42
|
const verifyPublishedTallyPayload = (payload, optionIndex, ballots, decryptionShares, tally) => {
|
|
36
43
|
if (payload.transcriptHash !== ballots.aggregate.transcriptHash) {
|
|
@@ -123,17 +130,8 @@ const verifyBallotClosePayload = (input) => {
|
|
|
123
130
|
* DKG verification, ballot verification, decryption-share verification, and
|
|
124
131
|
* per-option tally checks.
|
|
125
132
|
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* ```ts
|
|
131
|
-
* const verified = await verifyElectionCeremony(bundle);
|
|
132
|
-
*
|
|
133
|
-
* console.log(verified.boardAudit.overall.fingerprint);
|
|
134
|
-
* console.log(verified.countedParticipantIndices);
|
|
135
|
-
* console.log(verified.perOptionTallies);
|
|
136
|
-
* ```
|
|
133
|
+
* This is the main verifier entry point for callers that want failures to
|
|
134
|
+
* abort immediately.
|
|
137
135
|
*/
|
|
138
136
|
export const verifyElectionCeremony = async (input) => {
|
|
139
137
|
let context;
|
|
@@ -282,6 +280,7 @@ export const verifyElectionCeremony = async (input) => {
|
|
|
282
280
|
jointPublicKey: dkg.jointPublicKey,
|
|
283
281
|
protocolVersion: context.protocolVersion,
|
|
284
282
|
manifestHash: context.manifestHash,
|
|
283
|
+
scoreRangeMax: context.scoreRangeMax,
|
|
285
284
|
sessionId: context.sessionId,
|
|
286
285
|
});
|
|
287
286
|
const publication = tallyPublicationMap.get(optionIndex);
|
|
@@ -333,20 +332,8 @@ export const verifyElectionCeremony = async (input) => {
|
|
|
333
332
|
* Runs the full ceremony verifier and returns a structured success-or-failure
|
|
334
333
|
* result without throwing.
|
|
335
334
|
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
* @example
|
|
340
|
-
* ```ts
|
|
341
|
-
* const result = await tryVerifyElectionCeremony(bundle);
|
|
342
|
-
*
|
|
343
|
-
* if (!result.ok) {
|
|
344
|
-
* console.error(result.error.stage, result.error.code, result.error.reason);
|
|
345
|
-
* return;
|
|
346
|
-
* }
|
|
347
|
-
*
|
|
348
|
-
* console.log(result.verified.qualifiedParticipantIndices);
|
|
349
|
-
* ```
|
|
335
|
+
* This is the preferred integration point for applications that want stable
|
|
336
|
+
* failure stages and codes instead of exception handling.
|
|
350
337
|
*/
|
|
351
338
|
export const tryVerifyElectionCeremony = async (input) => {
|
|
352
339
|
try {
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic byte encoders used for transcript hashing, proof challenges,
|
|
3
|
+
* session identifiers, and other canonical digest inputs.
|
|
4
|
+
*/
|
|
1
5
|
import { bytesToBigInt, hexToBytes } from '../core/bytes.js';
|
|
2
6
|
import { utf8ToBytes } from '../core/crypto.js';
|
|
3
7
|
import { InvalidPayloadError, InvalidScalarError } from '../core/errors.js';
|
|
@@ -10,20 +10,28 @@ import { type AggregateDecryptionPreparationInput, type DecryptionShare, type Sh
|
|
|
10
10
|
*/
|
|
11
11
|
export declare const lagrangeCoefficient: (participantIndex: bigint, allIndices: readonly bigint[], q: bigint) => bigint;
|
|
12
12
|
/**
|
|
13
|
-
* Creates
|
|
13
|
+
* Creates one trustee's partial decryption share `d_i = x_i C_1`.
|
|
14
|
+
*
|
|
15
|
+
* This is the low-level share value that applications typically prove with
|
|
16
|
+
* DLEQ and then publish inside a signed `decryption-share` payload.
|
|
14
17
|
*/
|
|
15
18
|
export declare const createDecryptionShare: (ciphertext: ElGamalCiphertext, share: Share) => DecryptionShare;
|
|
16
19
|
/**
|
|
17
|
-
* Prepares a verified aggregate for decryption
|
|
20
|
+
* Prepares a verified aggregate for decryption-share generation and DLEQ
|
|
21
|
+
* proving.
|
|
18
22
|
*
|
|
19
23
|
* When the accepted aggregate has identity `c1`, the raw DLEQ statement would
|
|
20
24
|
* degenerate because every participant would obtain the same identity-valued
|
|
21
|
-
* partial share. This helper deterministically adds a public encryption of
|
|
22
|
-
* in that corner case so the plaintext stays unchanged while the
|
|
23
|
-
* proof statement remains meaningful.
|
|
25
|
+
* partial share. This helper deterministically adds a public encryption of
|
|
26
|
+
* zero in that corner case so the plaintext stays unchanged while the
|
|
27
|
+
* decryption proof statement remains meaningful.
|
|
24
28
|
*/
|
|
25
29
|
export declare const prepareAggregateForDecryption: (input: AggregateDecryptionPreparationInput) => AggregateDecryptionPreparationInput["aggregate"];
|
|
26
30
|
/**
|
|
27
31
|
* Combines indexed decryption shares via Lagrange interpolation at `x = 0`.
|
|
32
|
+
*
|
|
33
|
+
* This is the final bounded plaintext-recovery step. In the supported voting
|
|
34
|
+
* workflow it runs only after the transcript, ballot aggregation, and
|
|
35
|
+
* decryption-share proofs have already been verified.
|
|
28
36
|
*/
|
|
29
37
|
export declare const combineDecryptionShares: (ciphertext: ElGamalCiphertext, decryptionShares: readonly DecryptionShare[], bound: bigint) => bigint;
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threshold decryption and bounded tally-recovery helpers.
|
|
3
|
+
*
|
|
4
|
+
* These functions are used after ballot aggregation has produced a verified
|
|
5
|
+
* additive ciphertext and before tally publication or verifier-side tally
|
|
6
|
+
* recomputation.
|
|
7
|
+
*/
|
|
1
8
|
import { hexToBytes } from '../core/bytes.js';
|
|
2
9
|
import { assertInSubgroupOrIdentity, assertScalarInZq, assertValidPublicKey, IndexOutOfRangeError, InvalidShareError, PlaintextDomainError, RISTRETTO_GROUP, modInvQ, modQ, } from '../core/index.js';
|
|
3
10
|
import { decodePoint, encodePoint, hashChallengeToScalar, pointAdd, pointMultiply, pointSubtract, RISTRETTO_ZERO, } from '../core/ristretto.js';
|
|
@@ -57,7 +64,10 @@ export const lagrangeCoefficient = (participantIndex, allIndices, q) => {
|
|
|
57
64
|
return modQ(numerator * modInvQ(denominator, q), q);
|
|
58
65
|
};
|
|
59
66
|
/**
|
|
60
|
-
* Creates
|
|
67
|
+
* Creates one trustee's partial decryption share `d_i = x_i C_1`.
|
|
68
|
+
*
|
|
69
|
+
* This is the low-level share value that applications typically prove with
|
|
70
|
+
* DLEQ and then publish inside a signed `decryption-share` payload.
|
|
61
71
|
*/
|
|
62
72
|
export const createDecryptionShare = (ciphertext, share) => {
|
|
63
73
|
assertPositiveIndex(share.index, 'Share');
|
|
@@ -69,13 +79,14 @@ export const createDecryptionShare = (ciphertext, share) => {
|
|
|
69
79
|
};
|
|
70
80
|
};
|
|
71
81
|
/**
|
|
72
|
-
* Prepares a verified aggregate for decryption
|
|
82
|
+
* Prepares a verified aggregate for decryption-share generation and DLEQ
|
|
83
|
+
* proving.
|
|
73
84
|
*
|
|
74
85
|
* When the accepted aggregate has identity `c1`, the raw DLEQ statement would
|
|
75
86
|
* degenerate because every participant would obtain the same identity-valued
|
|
76
|
-
* partial share. This helper deterministically adds a public encryption of
|
|
77
|
-
* in that corner case so the plaintext stays unchanged while the
|
|
78
|
-
* proof statement remains meaningful.
|
|
87
|
+
* partial share. This helper deterministically adds a public encryption of
|
|
88
|
+
* zero in that corner case so the plaintext stays unchanged while the
|
|
89
|
+
* decryption proof statement remains meaningful.
|
|
79
90
|
*/
|
|
80
91
|
export const prepareAggregateForDecryption = (input) => {
|
|
81
92
|
assertVerifiedAggregate(input.aggregate);
|
|
@@ -92,6 +103,10 @@ export const prepareAggregateForDecryption = (input) => {
|
|
|
92
103
|
};
|
|
93
104
|
/**
|
|
94
105
|
* Combines indexed decryption shares via Lagrange interpolation at `x = 0`.
|
|
106
|
+
*
|
|
107
|
+
* This is the final bounded plaintext-recovery step. In the supported voting
|
|
108
|
+
* workflow it runs only after the transcript, ballot aggregation, and
|
|
109
|
+
* decryption-share proofs have already been verified.
|
|
95
110
|
*/
|
|
96
111
|
export const combineDecryptionShares = (ciphertext, decryptionShares, bound) => {
|
|
97
112
|
if (decryptionShares.length === 0) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threshold-share and bounded tally-reconstruction helpers.
|
|
3
|
+
*
|
|
4
|
+
* Use this module when you need to prepare aggregates, create decryption
|
|
5
|
+
* shares, or reconstruct tallies directly.
|
|
6
|
+
*
|
|
7
|
+
* @module threshold-elgamal/threshold
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
export { combineDecryptionShares, createDecryptionShare, prepareAggregateForDecryption, } from './decrypt.js';
|
|
11
|
+
export type { AggregateDecryptionPreparationInput, DecryptionShare, Share, VerifiedAggregateCiphertext, } from './types.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threshold-share and bounded tally-reconstruction helpers.
|
|
3
|
+
*
|
|
4
|
+
* Use this module when you need to prepare aggregates, create decryption
|
|
5
|
+
* shares, or reconstruct tallies directly.
|
|
6
|
+
*
|
|
7
|
+
* @module threshold-elgamal/threshold
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
export { combineDecryptionShares, createDecryptionShare, prepareAggregateForDecryption, } from './decrypt.js';
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threshold-decryption types that bridge verified ballot aggregates, indexed
|
|
3
|
+
* shares, and the final bounded plaintext reconstruction step.
|
|
4
|
+
*/
|
|
1
5
|
import type { EncodedPoint } from '../core/types.js';
|
|
2
6
|
import type { ElGamalCiphertext } from '../elgamal/types.js';
|
|
3
7
|
/** A single indexed Shamir share over `Z_q`. */
|
|
@@ -7,7 +11,12 @@ export type Share = {
|
|
|
7
11
|
/** Share value `f(index) mod q`. */
|
|
8
12
|
readonly value: bigint;
|
|
9
13
|
};
|
|
10
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* A participant's partial decryption contribution for one verified aggregate.
|
|
16
|
+
*
|
|
17
|
+
* These values are the low-level shares later wrapped into signed
|
|
18
|
+
* `decryption-share` protocol payloads.
|
|
19
|
+
*/
|
|
11
20
|
export type DecryptionShare = {
|
|
12
21
|
/** 1-based participant index matching the source share. */
|
|
13
22
|
readonly index: number;
|
|
@@ -15,7 +24,12 @@ export type DecryptionShare = {
|
|
|
15
24
|
readonly value: EncodedPoint;
|
|
16
25
|
};
|
|
17
26
|
declare const verifiedAggregateBrand: unique symbol;
|
|
18
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* A threshold aggregate tied to a verified additive ciphertext.
|
|
29
|
+
*
|
|
30
|
+
* The branding step prevents callers from skipping ballot verification and
|
|
31
|
+
* constructing arbitrary objects that merely look like verified aggregates.
|
|
32
|
+
*/
|
|
19
33
|
export type VerifiedAggregateCiphertext = {
|
|
20
34
|
/** Canonical transcript hash that anchors the accepted ballot log. */
|
|
21
35
|
readonly transcriptHash: string;
|
|
@@ -26,13 +40,18 @@ export type VerifiedAggregateCiphertext = {
|
|
|
26
40
|
/** Opaque brand preventing arbitrary object-literal construction. */
|
|
27
41
|
readonly [verifiedAggregateBrand]: true;
|
|
28
42
|
};
|
|
29
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Public context needed to prepare a verified aggregate for decryption.
|
|
45
|
+
*
|
|
46
|
+
* This binds the rerandomization corner case to the exact ceremony, manifest,
|
|
47
|
+
* and option slot being processed.
|
|
48
|
+
*/
|
|
30
49
|
export type AggregateDecryptionPreparationInput = {
|
|
31
50
|
/** Verified aggregate recomputed from the accepted ballot transcript. */
|
|
32
51
|
readonly aggregate: VerifiedAggregateCiphertext;
|
|
33
52
|
/** Joint public key used to encrypt the original ballots. */
|
|
34
53
|
readonly publicKey: EncodedPoint;
|
|
35
|
-
/**
|
|
54
|
+
/** Protocol namespace carried by the ceremony transcript. */
|
|
36
55
|
readonly protocolVersion: string;
|
|
37
56
|
/** Manifest hash that anchors the ceremony context. */
|
|
38
57
|
readonly manifestHash: string;
|
package/dist/transport/auth.d.ts
CHANGED
|
@@ -7,38 +7,32 @@ type GenerateAuthKeyPairOptions = {
|
|
|
7
7
|
/**
|
|
8
8
|
* Generates a fresh per-ceremony authentication key pair.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Trustees use this key pair to sign every public payload they publish during
|
|
11
|
+
* setup, DKG, voting, and tally publication.
|
|
11
12
|
*/
|
|
12
13
|
export declare const generateAuthKeyPair: (options?: GenerateAuthKeyPairOptions) => Promise<CryptoKeyPair>;
|
|
13
14
|
/**
|
|
14
|
-
* Exports an authentication public key as SPKI hex
|
|
15
|
-
*
|
|
16
|
-
* @param publicKey Authentication public key.
|
|
17
|
-
* @returns Lowercase hexadecimal SPKI bytes.
|
|
15
|
+
* Exports an authentication public key as SPKI hex so it can be published in a
|
|
16
|
+
* registration payload and later imported by verifiers.
|
|
18
17
|
*/
|
|
19
18
|
export declare const exportAuthPublicKey: (publicKey: CryptoKey) => Promise<EncodedAuthPublicKey>;
|
|
20
19
|
/**
|
|
21
|
-
* Imports an authentication public key from SPKI
|
|
22
|
-
*
|
|
23
|
-
* @param spkiHex Lowercase hexadecimal SPKI bytes.
|
|
24
|
-
* @returns Imported public key.
|
|
20
|
+
* Imports an authentication public key from the canonical published SPKI
|
|
21
|
+
* encoding.
|
|
25
22
|
*/
|
|
26
23
|
export declare const importAuthPublicKey: (spkiHex: EncodedAuthPublicKey) => Promise<CryptoKey>;
|
|
27
24
|
/**
|
|
28
25
|
* Signs canonical payload bytes with an authentication private key.
|
|
29
26
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* @returns Lowercase hexadecimal raw Ed25519 signature bytes.
|
|
27
|
+
* Protocol builders call this after they have frozen the payload shape and
|
|
28
|
+
* canonical serialization.
|
|
33
29
|
*/
|
|
34
30
|
export declare const signPayloadBytes: (privateKey: CryptoKey, payloadBytes: Uint8Array) => Promise<string>;
|
|
35
31
|
/**
|
|
36
32
|
* Verifies canonical payload bytes against an authentication signature.
|
|
37
33
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* @param signatureHex Lowercase hexadecimal raw Ed25519 signature bytes.
|
|
41
|
-
* @returns `true` when the signature verifies.
|
|
34
|
+
* Signature verification for published board payloads ultimately routes
|
|
35
|
+
* through this helper.
|
|
42
36
|
*/
|
|
43
37
|
export declare const verifyPayloadSignature: (publicKey: CryptoKey, payloadBytes: Uint8Array, signatureHex: string) => Promise<boolean>;
|
|
44
38
|
export {};
|
package/dist/transport/auth.js
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ed25519 authentication-key and signature helpers.
|
|
3
|
+
*
|
|
4
|
+
* Every published protocol payload is signed through this layer, either
|
|
5
|
+
* directly or via the higher-level protocol builders.
|
|
6
|
+
*/
|
|
1
7
|
import { bytesToHex, hexToBytes, toBufferSource } from '../core/bytes.js';
|
|
2
8
|
import { getWebCrypto } from '../core/index.js';
|
|
3
9
|
/**
|
|
4
10
|
* Generates a fresh per-ceremony authentication key pair.
|
|
5
11
|
*
|
|
6
|
-
*
|
|
12
|
+
* Trustees use this key pair to sign every public payload they publish during
|
|
13
|
+
* setup, DKG, voting, and tally publication.
|
|
7
14
|
*/
|
|
8
15
|
export const generateAuthKeyPair = async (options = {}) => getWebCrypto().subtle.generateKey({
|
|
9
16
|
name: 'Ed25519',
|
|
10
17
|
}, options.extractable ?? false, ['sign', 'verify']);
|
|
11
18
|
/**
|
|
12
|
-
* Exports an authentication public key as SPKI hex
|
|
13
|
-
*
|
|
14
|
-
* @param publicKey Authentication public key.
|
|
15
|
-
* @returns Lowercase hexadecimal SPKI bytes.
|
|
19
|
+
* Exports an authentication public key as SPKI hex so it can be published in a
|
|
20
|
+
* registration payload and later imported by verifiers.
|
|
16
21
|
*/
|
|
17
22
|
export const exportAuthPublicKey = async (publicKey) => bytesToHex(new Uint8Array(await getWebCrypto().subtle.exportKey('spki', publicKey)));
|
|
18
23
|
/**
|
|
19
|
-
* Imports an authentication public key from SPKI
|
|
20
|
-
*
|
|
21
|
-
* @param spkiHex Lowercase hexadecimal SPKI bytes.
|
|
22
|
-
* @returns Imported public key.
|
|
24
|
+
* Imports an authentication public key from the canonical published SPKI
|
|
25
|
+
* encoding.
|
|
23
26
|
*/
|
|
24
27
|
export const importAuthPublicKey = async (spkiHex) => getWebCrypto().subtle.importKey('spki', toBufferSource(hexToBytes(spkiHex)), {
|
|
25
28
|
name: 'Ed25519',
|
|
@@ -27,17 +30,14 @@ export const importAuthPublicKey = async (spkiHex) => getWebCrypto().subtle.impo
|
|
|
27
30
|
/**
|
|
28
31
|
* Signs canonical payload bytes with an authentication private key.
|
|
29
32
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* @returns Lowercase hexadecimal raw Ed25519 signature bytes.
|
|
33
|
+
* Protocol builders call this after they have frozen the payload shape and
|
|
34
|
+
* canonical serialization.
|
|
33
35
|
*/
|
|
34
36
|
export const signPayloadBytes = async (privateKey, payloadBytes) => bytesToHex(new Uint8Array(await getWebCrypto().subtle.sign('Ed25519', privateKey, toBufferSource(payloadBytes))));
|
|
35
37
|
/**
|
|
36
38
|
* Verifies canonical payload bytes against an authentication signature.
|
|
37
39
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* @param signatureHex Lowercase hexadecimal raw Ed25519 signature bytes.
|
|
41
|
-
* @returns `true` when the signature verifies.
|
|
40
|
+
* Signature verification for published board payloads ultimately routes
|
|
41
|
+
* through this helper.
|
|
42
42
|
*/
|
|
43
43
|
export const verifyPayloadSignature = async (publicKey, payloadBytes, signatureHex) => getWebCrypto().subtle.verify('Ed25519', publicKey, toBufferSource(hexToBytes(signatureHex)), toBufferSource(payloadBytes));
|
|
@@ -8,10 +8,8 @@ type ComplaintResolution = {
|
|
|
8
8
|
* Resolves a dealer challenge using only public transcript material plus the
|
|
9
9
|
* dealer-revealed sender-ephemeral private key.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* @param revealedEphemeralPrivateKeyHex Revealed sender-ephemeral private key.
|
|
14
|
-
* @returns Complaint resolution result.
|
|
11
|
+
* The DKG verifier uses this to decide whether a complaint indicates dealer
|
|
12
|
+
* misconduct or a complaining recipient that published an invalid accusation.
|
|
15
13
|
*/
|
|
16
14
|
export declare const resolveDealerChallengeFromPublicKey: (envelope: EncryptedEnvelope, recipientPublicKeyHex: EncodedTransportPublicKey, revealedEphemeralPrivateKeyHex: EncodedTransportPrivateKey) => Promise<ComplaintResolution>;
|
|
17
15
|
export {};
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Complaint-resolution helpers for the encrypted share-transport layer.
|
|
3
|
+
*
|
|
4
|
+
* DKG transcript verification uses this module when it needs to reconstruct
|
|
5
|
+
* the expected public challenge material for a dealer complaint.
|
|
6
|
+
*/
|
|
1
7
|
import { hexToBytes, toBufferSource } from '../core/bytes.js';
|
|
2
8
|
import { getWebCrypto } from '../core/index.js';
|
|
3
9
|
import { deriveEnvelopeKey, encodeEnvelopeContext } from './envelopes.js';
|
|
@@ -14,10 +20,8 @@ const decryptEnvelopeFromSharedSecret = async (envelope, sharedSecret) => {
|
|
|
14
20
|
* Resolves a dealer challenge using only public transcript material plus the
|
|
15
21
|
* dealer-revealed sender-ephemeral private key.
|
|
16
22
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* @param revealedEphemeralPrivateKeyHex Revealed sender-ephemeral private key.
|
|
20
|
-
* @returns Complaint resolution result.
|
|
23
|
+
* The DKG verifier uses this to decide whether a complaint indicates dealer
|
|
24
|
+
* misconduct or a complaining recipient that published an invalid accusation.
|
|
21
25
|
*/
|
|
22
26
|
export const resolveDealerChallengeFromPublicKey = async (envelope, recipientPublicKeyHex, revealedEphemeralPrivateKeyHex) => {
|
|
23
27
|
const ephemeralKeyMatches = await verifyLocalTransportKey(revealedEphemeralPrivateKeyHex, envelope.ephemeralPublicKey);
|
|
@@ -4,10 +4,9 @@ export declare const deriveEnvelopeKey: (sharedSecret: Uint8Array, context: Enve
|
|
|
4
4
|
/**
|
|
5
5
|
* Encrypts a payload into a sender-ephemeral authenticated envelope.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* @returns Envelope plus the sender-ephemeral private key for complaint recovery.
|
|
7
|
+
* DKG dealers use this when publishing encrypted Pedersen shares for one
|
|
8
|
+
* recipient. The returned ephemeral private key is retained so complaint
|
|
9
|
+
* resolution can later prove what was sent.
|
|
11
10
|
*/
|
|
12
11
|
export declare const encryptEnvelope: (plaintext: Uint8Array, recipientPublicKeyHex: EncodedTransportPublicKey, context: EnvelopeContext) => Promise<{
|
|
13
12
|
readonly envelope: EncryptedEnvelope;
|
|
@@ -16,8 +15,7 @@ export declare const encryptEnvelope: (plaintext: Uint8Array, recipientPublicKey
|
|
|
16
15
|
/**
|
|
17
16
|
* Decrypts an authenticated envelope with the recipient transport private key.
|
|
18
17
|
*
|
|
19
|
-
* @
|
|
20
|
-
*
|
|
21
|
-
* @returns Decrypted plaintext bytes.
|
|
18
|
+
* This is the recipient-side counterpart to {@link encryptEnvelope} and is
|
|
19
|
+
* typically followed by Pedersen share decoding.
|
|
22
20
|
*/
|
|
23
21
|
export declare const decryptEnvelope: (envelope: EncryptedEnvelope, recipientPrivateKey: CryptoKey | EncodedTransportPrivateKey) => Promise<Uint8Array>;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authenticated sender-ephemeral envelope helpers for dealer-to-recipient
|
|
3
|
+
* transport during DKG share distribution.
|
|
4
|
+
*/
|
|
1
5
|
import { bytesToHex, hexToBytes, toBufferSource } from '../core/bytes.js';
|
|
2
6
|
import { getWebCrypto, hkdfSha256, randomBytes } from '../core/index.js';
|
|
3
7
|
import { encodeForChallenge } from '../serialize/encoding.js';
|
|
@@ -8,10 +12,9 @@ export const deriveEnvelopeKey = async (sharedSecret, context, usages = ['encryp
|
|
|
8
12
|
/**
|
|
9
13
|
* Encrypts a payload into a sender-ephemeral authenticated envelope.
|
|
10
14
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @returns Envelope plus the sender-ephemeral private key for complaint recovery.
|
|
15
|
+
* DKG dealers use this when publishing encrypted Pedersen shares for one
|
|
16
|
+
* recipient. The returned ephemeral private key is retained so complaint
|
|
17
|
+
* resolution can later prove what was sent.
|
|
15
18
|
*/
|
|
16
19
|
export const encryptEnvelope = async (plaintext, recipientPublicKeyHex, context) => {
|
|
17
20
|
const ephemeral = await generateTransportKeyPair({
|
|
@@ -39,9 +42,8 @@ export const encryptEnvelope = async (plaintext, recipientPublicKeyHex, context)
|
|
|
39
42
|
/**
|
|
40
43
|
* Decrypts an authenticated envelope with the recipient transport private key.
|
|
41
44
|
*
|
|
42
|
-
* @
|
|
43
|
-
*
|
|
44
|
-
* @returns Decrypted plaintext bytes.
|
|
45
|
+
* This is the recipient-side counterpart to {@link encryptEnvelope} and is
|
|
46
|
+
* typically followed by Pedersen share decoding.
|
|
45
47
|
*/
|
|
46
48
|
export const decryptEnvelope = async (envelope, recipientPrivateKey) => {
|
|
47
49
|
const resolvedRecipientPrivateKey = typeof recipientPrivateKey === 'string'
|
|
@@ -14,53 +14,45 @@ export declare const assertNonZeroSharedSecret: (sharedSecret: Uint8Array) => vo
|
|
|
14
14
|
/**
|
|
15
15
|
* Generates an X25519 transport key pair.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* Trustees publish the public half in their registration payloads and keep the
|
|
18
|
+
* private half for decrypting inbound dealer envelopes.
|
|
19
19
|
*/
|
|
20
20
|
export declare const generateTransportKeyPair: (options?: GenerateTransportKeyPairOptions) => Promise<TransportKeyPair>;
|
|
21
21
|
/**
|
|
22
|
-
* Exports a transport public key as raw lowercase hexadecimal bytes
|
|
23
|
-
*
|
|
24
|
-
* @param publicKey Transport public key.
|
|
25
|
-
* @returns Lowercase hexadecimal public key bytes.
|
|
22
|
+
* Exports a transport public key as raw lowercase hexadecimal bytes so it can
|
|
23
|
+
* be published in a registration payload.
|
|
26
24
|
*/
|
|
27
25
|
export declare const exportTransportPublicKey: (publicKey: CryptoKey) => Promise<EncodedTransportPublicKey>;
|
|
28
26
|
/**
|
|
29
27
|
* Exports a transport private key as PKCS#8 lowercase hexadecimal bytes.
|
|
30
28
|
*
|
|
31
|
-
*
|
|
32
|
-
* @returns Lowercase hexadecimal PKCS#8 bytes.
|
|
29
|
+
* This is mainly useful for persistence and tests.
|
|
33
30
|
*/
|
|
34
31
|
export declare const exportTransportPrivateKey: (privateKey: CryptoKey) => Promise<EncodedTransportPrivateKey>;
|
|
35
32
|
/**
|
|
36
|
-
* Imports a transport public key from raw hexadecimal
|
|
37
|
-
*
|
|
38
|
-
* @param publicKeyHex Lowercase hexadecimal public key bytes.
|
|
39
|
-
* @returns Imported transport public key.
|
|
33
|
+
* Imports a transport public key from the canonical raw hexadecimal encoding
|
|
34
|
+
* published on the board.
|
|
40
35
|
*/
|
|
41
36
|
export declare const importTransportPublicKey: (publicKeyHex: EncodedTransportPublicKey) => Promise<CryptoKey>;
|
|
42
37
|
export declare const assertSupportedTransportPublicKeyEncoding: (publicKeyHex: EncodedTransportPublicKey, label?: string) => void;
|
|
43
38
|
/**
|
|
44
|
-
* Imports a transport private key from PKCS#8 hexadecimal
|
|
45
|
-
*
|
|
46
|
-
* @param privateKeyHex Lowercase hexadecimal PKCS#8 bytes.
|
|
47
|
-
* @returns Imported transport private key.
|
|
39
|
+
* Imports a transport private key from the canonical PKCS#8 hexadecimal
|
|
40
|
+
* encoding.
|
|
48
41
|
*/
|
|
49
42
|
export declare const importTransportPrivateKey: (privateKeyHex: EncodedTransportPrivateKey) => Promise<CryptoKey>;
|
|
50
43
|
/**
|
|
51
44
|
* Derives a raw shared secret for X25519.
|
|
52
45
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* @returns Raw shared secret bytes.
|
|
46
|
+
* Envelope encryption and decryption both route through this shared-secret
|
|
47
|
+
* derivation step before expanding the result with HKDF.
|
|
56
48
|
*/
|
|
57
49
|
export declare const deriveTransportSharedSecret: (privateKey: CryptoKey, publicKey: CryptoKey) => Promise<Uint8Array>;
|
|
58
50
|
/**
|
|
59
|
-
* Verifies that a local transport private key matches the registered public
|
|
51
|
+
* Verifies that a local transport private key matches the registered public
|
|
52
|
+
* key.
|
|
60
53
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* @returns `true` when the private key expands to `expectedPublicKeyHex`.
|
|
54
|
+
* This is useful when loading persisted local key material for an already
|
|
55
|
+
* published registration record.
|
|
64
56
|
*/
|
|
65
57
|
export declare const verifyLocalTransportKey: (privateKey: CryptoKey | EncodedTransportPrivateKey, expectedPublicKeyHex: EncodedTransportPublicKey) => Promise<boolean>;
|
|
66
58
|
export {};
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* X25519 transport-key helpers used for encrypted DKG share delivery.
|
|
3
|
+
*
|
|
4
|
+
* These functions stay lower-level than the protocol builders so applications
|
|
5
|
+
* and tests can manage key export, import, and local verification directly.
|
|
6
|
+
*/
|
|
1
7
|
import { bytesToHex, hexToBytes, toBufferSource } from '../core/bytes.js';
|
|
2
8
|
import { InvalidPayloadError, getWebCrypto } from '../core/index.js';
|
|
3
9
|
const X25519_BASE_POINT = (() => {
|
|
@@ -30,8 +36,8 @@ export const assertNonZeroSharedSecret = (sharedSecret) => {
|
|
|
30
36
|
/**
|
|
31
37
|
* Generates an X25519 transport key pair.
|
|
32
38
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
39
|
+
* Trustees publish the public half in their registration payloads and keep the
|
|
40
|
+
* private half for decrypting inbound dealer envelopes.
|
|
35
41
|
*/
|
|
36
42
|
export const generateTransportKeyPair = async (options = {}) => {
|
|
37
43
|
const keyPair = await getWebCrypto().subtle.generateKey(x25519Algorithm, options.extractable ?? false, ['deriveBits']);
|
|
@@ -42,24 +48,19 @@ export const generateTransportKeyPair = async (options = {}) => {
|
|
|
42
48
|
};
|
|
43
49
|
};
|
|
44
50
|
/**
|
|
45
|
-
* Exports a transport public key as raw lowercase hexadecimal bytes
|
|
46
|
-
*
|
|
47
|
-
* @param publicKey Transport public key.
|
|
48
|
-
* @returns Lowercase hexadecimal public key bytes.
|
|
51
|
+
* Exports a transport public key as raw lowercase hexadecimal bytes so it can
|
|
52
|
+
* be published in a registration payload.
|
|
49
53
|
*/
|
|
50
54
|
export const exportTransportPublicKey = async (publicKey) => bytesToHex(new Uint8Array(await getWebCrypto().subtle.exportKey('raw', publicKey)));
|
|
51
55
|
/**
|
|
52
56
|
* Exports a transport private key as PKCS#8 lowercase hexadecimal bytes.
|
|
53
57
|
*
|
|
54
|
-
*
|
|
55
|
-
* @returns Lowercase hexadecimal PKCS#8 bytes.
|
|
58
|
+
* This is mainly useful for persistence and tests.
|
|
56
59
|
*/
|
|
57
60
|
export const exportTransportPrivateKey = async (privateKey) => bytesToHex(new Uint8Array(await getWebCrypto().subtle.exportKey('pkcs8', privateKey)));
|
|
58
61
|
/**
|
|
59
|
-
* Imports a transport public key from raw hexadecimal
|
|
60
|
-
*
|
|
61
|
-
* @param publicKeyHex Lowercase hexadecimal public key bytes.
|
|
62
|
-
* @returns Imported transport public key.
|
|
62
|
+
* Imports a transport public key from the canonical raw hexadecimal encoding
|
|
63
|
+
* published on the board.
|
|
63
64
|
*/
|
|
64
65
|
export const importTransportPublicKey = async (publicKeyHex) => {
|
|
65
66
|
const publicKeyBytes = hexToBytes(publicKeyHex);
|
|
@@ -77,18 +78,15 @@ export const assertSupportedTransportPublicKeyEncoding = (publicKeyHex, label =
|
|
|
77
78
|
assertValidX25519PublicKeyBytes(publicKeyBytes, label);
|
|
78
79
|
};
|
|
79
80
|
/**
|
|
80
|
-
* Imports a transport private key from PKCS#8 hexadecimal
|
|
81
|
-
*
|
|
82
|
-
* @param privateKeyHex Lowercase hexadecimal PKCS#8 bytes.
|
|
83
|
-
* @returns Imported transport private key.
|
|
81
|
+
* Imports a transport private key from the canonical PKCS#8 hexadecimal
|
|
82
|
+
* encoding.
|
|
84
83
|
*/
|
|
85
84
|
export const importTransportPrivateKey = async (privateKeyHex) => getWebCrypto().subtle.importKey('pkcs8', toBufferSource(hexToBytes(privateKeyHex)), x25519Algorithm, true, ['deriveBits']);
|
|
86
85
|
/**
|
|
87
86
|
* Derives a raw shared secret for X25519.
|
|
88
87
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* @returns Raw shared secret bytes.
|
|
88
|
+
* Envelope encryption and decryption both route through this shared-secret
|
|
89
|
+
* derivation step before expanding the result with HKDF.
|
|
92
90
|
*/
|
|
93
91
|
export const deriveTransportSharedSecret = async (privateKey, publicKey) => {
|
|
94
92
|
const sharedSecret = new Uint8Array(await getWebCrypto().subtle.deriveBits({
|
|
@@ -109,11 +107,11 @@ const deriveTransportPublicKey = async (privateKey) => {
|
|
|
109
107
|
return bytesToHex(await deriveTransportSharedSecret(privateKey, basePoint));
|
|
110
108
|
};
|
|
111
109
|
/**
|
|
112
|
-
* Verifies that a local transport private key matches the registered public
|
|
110
|
+
* Verifies that a local transport private key matches the registered public
|
|
111
|
+
* key.
|
|
113
112
|
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
* @returns `true` when the private key expands to `expectedPublicKeyHex`.
|
|
113
|
+
* This is useful when loading persisted local key material for an already
|
|
114
|
+
* published registration record.
|
|
117
115
|
*/
|
|
118
116
|
export const verifyLocalTransportKey = async (privateKey, expectedPublicKeyHex) => {
|
|
119
117
|
try {
|