threshold-elgamal 0.1.25 → 0.1.26
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 +25 -63
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/thresholdElgamal.d.ts +13 -7
- package/dist/thresholdElgamal.js +11 -11
- package/dist/types.d.ts +0 -8
- package/dist/utils/testUtils.d.ts +4 -2
- package/dist/utils/testUtils.js +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,41 +90,28 @@ Threshold scheme for generating a common public key, sharing a secret to 3 parti
|
|
|
90
90
|
import {
|
|
91
91
|
getGroup,
|
|
92
92
|
encrypt,
|
|
93
|
-
|
|
93
|
+
generateKeys,
|
|
94
94
|
combinePublicKeys,
|
|
95
95
|
createDecryptionShare,
|
|
96
96
|
combineDecryptionShares,
|
|
97
97
|
thresholdDecrypt,
|
|
98
98
|
} from "threshold-elgamal";
|
|
99
|
-
import type { PartyKeyPair } from "threshold-elgamal";
|
|
100
99
|
|
|
101
100
|
const primeBits = 2048; // Bit length of the prime modulus
|
|
102
101
|
const threshold = 3; // A scenario for 3 participants with a threshold of 3
|
|
103
102
|
const { prime, generator } = getGroup(2048);
|
|
104
103
|
|
|
105
104
|
// Each participant generates their public key share and private key individually
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
primeBits,
|
|
110
|
-
);
|
|
111
|
-
const participant2KeyShare: PartyKeyPair = generateSingleKeyShare(
|
|
112
|
-
2,
|
|
113
|
-
threshold,
|
|
114
|
-
primeBits,
|
|
115
|
-
);
|
|
116
|
-
const participant3KeyShare: PartyKeyPair = generateSingleKeyShare(
|
|
117
|
-
3,
|
|
118
|
-
threshold,
|
|
119
|
-
primeBits,
|
|
120
|
-
);
|
|
105
|
+
const participant1Keys = generateKeys(1, threshold, primeBits);
|
|
106
|
+
const participant2Keys = generateKeys(2, threshold, primeBits);
|
|
107
|
+
const participant3Keys = generateKeys(3, threshold, primeBits);
|
|
121
108
|
|
|
122
109
|
// Combine the public keys to form a single public key
|
|
123
110
|
const combinedPublicKey = combinePublicKeys(
|
|
124
111
|
[
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
112
|
+
participant1Keys.publicKey,
|
|
113
|
+
participant2Keys.publicKey,
|
|
114
|
+
participant3Keys.publicKey,
|
|
128
115
|
],
|
|
129
116
|
prime,
|
|
130
117
|
);
|
|
@@ -135,21 +122,9 @@ const encryptedMessage = encrypt(secret, prime, generator, combinedPublicKey);
|
|
|
135
122
|
|
|
136
123
|
// Decryption shares
|
|
137
124
|
const decryptionShares = [
|
|
138
|
-
createDecryptionShare(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
prime,
|
|
142
|
-
),
|
|
143
|
-
createDecryptionShare(
|
|
144
|
-
encryptedMessage,
|
|
145
|
-
participant2KeyShare.partyPrivateKey,
|
|
146
|
-
prime,
|
|
147
|
-
),
|
|
148
|
-
createDecryptionShare(
|
|
149
|
-
encryptedMessage,
|
|
150
|
-
participant3KeyShare.partyPrivateKey,
|
|
151
|
-
prime,
|
|
152
|
-
),
|
|
125
|
+
createDecryptionShare(encryptedMessage, participant1Keys.privateKey, prime),
|
|
126
|
+
createDecryptionShare(encryptedMessage, participant2Keys.privateKey, prime),
|
|
127
|
+
createDecryptionShare(encryptedMessage, participant3Keys.privateKey, prime),
|
|
153
128
|
];
|
|
154
129
|
// Combining the decryption shares into one, used to decrypt the message
|
|
155
130
|
const combinedDecryptionShares = combineDecryptionShares(
|
|
@@ -173,7 +148,7 @@ This example demonstrates a 1 to 10 voting scenario where 3 participants cast en
|
|
|
173
148
|
```typescript
|
|
174
149
|
import {
|
|
175
150
|
encrypt,
|
|
176
|
-
|
|
151
|
+
generateKeys,
|
|
177
152
|
combinePublicKeys,
|
|
178
153
|
createDecryptionShare,
|
|
179
154
|
combineDecryptionShares,
|
|
@@ -181,35 +156,22 @@ import {
|
|
|
181
156
|
multiplyEncryptedValues,
|
|
182
157
|
getGroup,
|
|
183
158
|
} from "threshold-elgamal";
|
|
184
|
-
import type { PartyKeyPair } from "threshold-elgamal";
|
|
185
159
|
|
|
186
160
|
const primeBits = 2048; // Bit length of the prime modulus
|
|
187
161
|
const threshold = 3; // A scenario for 3 participants with a threshold of 3
|
|
188
162
|
const { prime, generator } = getGroup(2048);
|
|
189
163
|
|
|
190
164
|
// Each participant generates their public key share and private key individually
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
primeBits,
|
|
195
|
-
);
|
|
196
|
-
const participant2KeyShare: PartyKeyPair = generateSingleKeyShare(
|
|
197
|
-
2,
|
|
198
|
-
threshold,
|
|
199
|
-
primeBits,
|
|
200
|
-
);
|
|
201
|
-
const participant3KeyShare: PartyKeyPair = generateSingleKeyShare(
|
|
202
|
-
3,
|
|
203
|
-
threshold,
|
|
204
|
-
primeBits,
|
|
205
|
-
);
|
|
165
|
+
const participant1Keys = generateKeys(1, threshold, primeBits);
|
|
166
|
+
const participant2Keys = generateKeys(2, threshold, primeBits);
|
|
167
|
+
const participant3Keys = generateKeys(3, threshold, primeBits);
|
|
206
168
|
|
|
207
169
|
// Combine the public keys to form a single public key
|
|
208
170
|
const combinedPublicKey = combinePublicKeys(
|
|
209
171
|
[
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
172
|
+
participant1Keys.publicKey,
|
|
173
|
+
participant2Keys.publicKey,
|
|
174
|
+
participant3Keys.publicKey,
|
|
213
175
|
],
|
|
214
176
|
prime,
|
|
215
177
|
);
|
|
@@ -228,11 +190,11 @@ const encryptedVotesOption2 = voteOption2.map((vote) =>
|
|
|
228
190
|
|
|
229
191
|
// Multiply encrypted votes together to aggregate
|
|
230
192
|
const aggregatedEncryptedVoteOption1 = encryptedVotesOption1.reduce(
|
|
231
|
-
(acc,
|
|
193
|
+
(acc, encryptedVote) => multiplyEncryptedValues(acc, encryptedVote, prime),
|
|
232
194
|
{ c1: 1n, c2: 1n },
|
|
233
195
|
);
|
|
234
196
|
const aggregatedEncryptedVoteOption2 = encryptedVotesOption2.reduce(
|
|
235
|
-
(acc,
|
|
197
|
+
(acc, encryptedVote) => multiplyEncryptedValues(acc, encryptedVote, prime),
|
|
236
198
|
{ c1: 1n, c2: 1n },
|
|
237
199
|
);
|
|
238
200
|
|
|
@@ -242,34 +204,34 @@ const aggregatedEncryptedVoteOption2 = encryptedVotesOption2.reduce(
|
|
|
242
204
|
const decryptionSharesOption1 = [
|
|
243
205
|
createDecryptionShare(
|
|
244
206
|
aggregatedEncryptedVoteOption1,
|
|
245
|
-
|
|
207
|
+
participant1Keys.privateKey,
|
|
246
208
|
prime,
|
|
247
209
|
),
|
|
248
210
|
createDecryptionShare(
|
|
249
211
|
aggregatedEncryptedVoteOption1,
|
|
250
|
-
|
|
212
|
+
participant2Keys.privateKey,
|
|
251
213
|
prime,
|
|
252
214
|
),
|
|
253
215
|
createDecryptionShare(
|
|
254
216
|
aggregatedEncryptedVoteOption1,
|
|
255
|
-
|
|
217
|
+
participant3Keys.privateKey,
|
|
256
218
|
prime,
|
|
257
219
|
),
|
|
258
220
|
];
|
|
259
221
|
const decryptionSharesOption2 = [
|
|
260
222
|
createDecryptionShare(
|
|
261
223
|
aggregatedEncryptedVoteOption2,
|
|
262
|
-
|
|
224
|
+
participant1Keys.privateKey,
|
|
263
225
|
prime,
|
|
264
226
|
),
|
|
265
227
|
createDecryptionShare(
|
|
266
228
|
aggregatedEncryptedVoteOption2,
|
|
267
|
-
|
|
229
|
+
participant2Keys.privateKey,
|
|
268
230
|
prime,
|
|
269
231
|
),
|
|
270
232
|
createDecryptionShare(
|
|
271
233
|
aggregatedEncryptedVoteOption2,
|
|
272
|
-
|
|
234
|
+
participant3Keys.privateKey,
|
|
273
235
|
prime,
|
|
274
236
|
),
|
|
275
237
|
];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { generateParameters, encrypt, decrypt } from './elgamal';
|
|
2
|
-
import {
|
|
3
|
-
import type { EncryptedMessage, Parameters
|
|
2
|
+
import { generateKeys, generateKeyShares, combinePublicKeys, createDecryptionShare, combineDecryptionShares, thresholdDecrypt } from './thresholdElgamal';
|
|
3
|
+
import type { EncryptedMessage, Parameters } from './types';
|
|
4
4
|
import { getRandomBigIntegerInRange, multiplyEncryptedValues, getGroup } from './utils/utils';
|
|
5
|
-
export { generateParameters, encrypt, decrypt,
|
|
6
|
-
export type { EncryptedMessage, Parameters
|
|
5
|
+
export { generateParameters, encrypt, decrypt, generateKeys, generateKeyShares, combinePublicKeys, createDecryptionShare, combineDecryptionShares, thresholdDecrypt, getRandomBigIntegerInRange, multiplyEncryptedValues, getGroup, };
|
|
6
|
+
export type { EncryptedMessage, Parameters };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { generateParameters, encrypt, decrypt } from "./elgamal.js";
|
|
2
|
-
import {
|
|
2
|
+
import { generateKeys, generateKeyShares, combinePublicKeys, createDecryptionShare, combineDecryptionShares, thresholdDecrypt, } from "./thresholdElgamal.js";
|
|
3
3
|
import { getRandomBigIntegerInRange, multiplyEncryptedValues, getGroup, } from "./utils/utils.js";
|
|
4
|
-
export { generateParameters, encrypt, decrypt,
|
|
4
|
+
export { generateParameters, encrypt, decrypt, generateKeys, generateKeyShares, combinePublicKeys, createDecryptionShare, combineDecryptionShares, thresholdDecrypt, getRandomBigIntegerInRange, multiplyEncryptedValues, getGroup, };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EncryptedMessage
|
|
1
|
+
import type { EncryptedMessage } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Evaluates a polynomial at a given point using modular arithmetic.
|
|
4
4
|
*
|
|
@@ -14,18 +14,24 @@ export declare const evaluatePolynomial: (polynomial: bigint[], x: number, prime
|
|
|
14
14
|
* @param {number} index - The unique index of the participant (starting from 1).
|
|
15
15
|
* @param {number} threshold - The minimum number of key shares required for decryption.
|
|
16
16
|
* @param {2048 | 3072 | 4096} primeBits - The bit length of the prime modulus (default: 2048).
|
|
17
|
-
* @returns {
|
|
17
|
+
* @returns { privateKey: bigint; publicKey: bigint} The key share containing a private and public key share for the participant.
|
|
18
18
|
*/
|
|
19
|
-
export declare const
|
|
19
|
+
export declare const generateKeys: (index: number, threshold: number, primeBits?: 2048 | 3072 | 4096) => {
|
|
20
|
+
privateKey: bigint;
|
|
21
|
+
publicKey: bigint;
|
|
22
|
+
};
|
|
20
23
|
/**
|
|
21
24
|
* Generates key shares for a threshold ElGamal cryptosystem.
|
|
22
25
|
*
|
|
23
26
|
* @param {number} n - The total number of key shares.
|
|
24
27
|
* @param {number} threshold - The minimum number of key shares required for decryption.
|
|
25
28
|
* @param {2048 | 3072 | 4096} primeBits - The bit length of the prime modulus (default: 2048).
|
|
26
|
-
* @returns {
|
|
29
|
+
* @returns {{ privateKey: bigint; publicKey: bigint }[]} An array of key shares, each containing a private and public key share.
|
|
27
30
|
*/
|
|
28
|
-
export declare const generateKeyShares: (n: number, threshold: number, primeBits?: 2048 | 3072 | 4096) =>
|
|
31
|
+
export declare const generateKeyShares: (n: number, threshold: number, primeBits?: 2048 | 3072 | 4096) => {
|
|
32
|
+
privateKey: bigint;
|
|
33
|
+
publicKey: bigint;
|
|
34
|
+
}[];
|
|
29
35
|
/**
|
|
30
36
|
* Combines multiple public keys into a single public key.
|
|
31
37
|
*
|
|
@@ -38,11 +44,11 @@ export declare const combinePublicKeys: (publicKeys: bigint[], prime: bigint) =>
|
|
|
38
44
|
* Performs a partial decryption on a ciphertext using an individual's private key share.
|
|
39
45
|
*
|
|
40
46
|
* @param {EncryptedMessage} encryptedMessage - The encrypted secret.
|
|
41
|
-
* @param {bigint}
|
|
47
|
+
* @param {bigint} privateKey - The private key share of the decrypting party.
|
|
42
48
|
* @param {bigint} prime - The prime modulus used in the ElGamal system.
|
|
43
49
|
* @returns {bigint} The result of the partial decryption.
|
|
44
50
|
*/
|
|
45
|
-
export declare const createDecryptionShare: (encryptedMessage: EncryptedMessage,
|
|
51
|
+
export declare const createDecryptionShare: (encryptedMessage: EncryptedMessage, privateKey: bigint, prime: bigint) => bigint;
|
|
46
52
|
/**
|
|
47
53
|
* Combines partial decryptions from multiple parties into a single decryption factor.
|
|
48
54
|
*
|
package/dist/thresholdElgamal.js
CHANGED
|
@@ -21,20 +21,20 @@ export const evaluatePolynomial = (polynomial, x, prime) => {
|
|
|
21
21
|
* @param {number} index - The unique index of the participant (starting from 1).
|
|
22
22
|
* @param {number} threshold - The minimum number of key shares required for decryption.
|
|
23
23
|
* @param {2048 | 3072 | 4096} primeBits - The bit length of the prime modulus (default: 2048).
|
|
24
|
-
* @returns {
|
|
24
|
+
* @returns { privateKey: bigint; publicKey: bigint} The key share containing a private and public key share for the participant.
|
|
25
25
|
*/
|
|
26
|
-
export const
|
|
26
|
+
export const generateKeys = (index, threshold, primeBits = 2048) => {
|
|
27
27
|
const group = getGroup(primeBits);
|
|
28
28
|
const prime = group.prime;
|
|
29
29
|
const generator = group.generator;
|
|
30
30
|
const polynomial = generatePolynomial(threshold, prime);
|
|
31
|
-
let
|
|
31
|
+
let privateKey = evaluatePolynomial(polynomial, index, prime);
|
|
32
32
|
// Ensure non-zero private key, adjusting index if necessary
|
|
33
|
-
while (
|
|
34
|
-
|
|
33
|
+
while (privateKey === 0n) {
|
|
34
|
+
privateKey = evaluatePolynomial(polynomial, index + 1, prime);
|
|
35
35
|
}
|
|
36
|
-
const
|
|
37
|
-
return {
|
|
36
|
+
const publicKey = modPow(generator, privateKey, prime);
|
|
37
|
+
return { privateKey, publicKey };
|
|
38
38
|
};
|
|
39
39
|
/**
|
|
40
40
|
* Generates key shares for a threshold ElGamal cryptosystem.
|
|
@@ -42,12 +42,12 @@ export const generateSingleKeyShare = (index, threshold, primeBits = 2048) => {
|
|
|
42
42
|
* @param {number} n - The total number of key shares.
|
|
43
43
|
* @param {number} threshold - The minimum number of key shares required for decryption.
|
|
44
44
|
* @param {2048 | 3072 | 4096} primeBits - The bit length of the prime modulus (default: 2048).
|
|
45
|
-
* @returns {
|
|
45
|
+
* @returns {{ privateKey: bigint; publicKey: bigint }[]} An array of key shares, each containing a private and public key share.
|
|
46
46
|
*/
|
|
47
47
|
export const generateKeyShares = (n, threshold, primeBits = 2048) => {
|
|
48
48
|
const keyShares = [];
|
|
49
49
|
for (let i = 1; i <= n; i++) {
|
|
50
|
-
const keyShare =
|
|
50
|
+
const keyShare = generateKeys(i, threshold, primeBits);
|
|
51
51
|
keyShares.push(keyShare);
|
|
52
52
|
}
|
|
53
53
|
return keyShares;
|
|
@@ -64,11 +64,11 @@ export const combinePublicKeys = (publicKeys, prime) => publicKeys.reduce((acc,
|
|
|
64
64
|
* Performs a partial decryption on a ciphertext using an individual's private key share.
|
|
65
65
|
*
|
|
66
66
|
* @param {EncryptedMessage} encryptedMessage - The encrypted secret.
|
|
67
|
-
* @param {bigint}
|
|
67
|
+
* @param {bigint} privateKey - The private key share of the decrypting party.
|
|
68
68
|
* @param {bigint} prime - The prime modulus used in the ElGamal system.
|
|
69
69
|
* @returns {bigint} The result of the partial decryption.
|
|
70
70
|
*/
|
|
71
|
-
export const createDecryptionShare = (encryptedMessage,
|
|
71
|
+
export const createDecryptionShare = (encryptedMessage, privateKey, prime) => modPow(encryptedMessage.c1, privateKey, prime);
|
|
72
72
|
/**
|
|
73
73
|
* Combines partial decryptions from multiple parties into a single decryption factor.
|
|
74
74
|
*
|
package/dist/types.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import type { PartyKeyPair } from '../types';
|
|
2
1
|
export declare const getRandomScore: (min?: number, max?: number) => number;
|
|
3
2
|
export declare const thresholdSetup: (partiesCount: number, threshold: number, primeBits?: 2048 | 3072 | 4096) => {
|
|
4
|
-
keyShares:
|
|
3
|
+
keyShares: {
|
|
4
|
+
privateKey: bigint;
|
|
5
|
+
publicKey: bigint;
|
|
6
|
+
}[];
|
|
5
7
|
combinedPublicKey: bigint;
|
|
6
8
|
prime: bigint;
|
|
7
9
|
generator: bigint;
|
package/dist/utils/testUtils.js
CHANGED
|
@@ -6,7 +6,7 @@ export const getRandomScore = (min = 1, max = 10) => Math.floor(Math.random() *
|
|
|
6
6
|
export const thresholdSetup = (partiesCount, threshold, primeBits = 2048) => {
|
|
7
7
|
const { prime, generator } = getGroup(primeBits);
|
|
8
8
|
const keyShares = generateKeyShares(partiesCount, threshold, primeBits);
|
|
9
|
-
const publicKeys = keyShares.map((ks) => ks.
|
|
9
|
+
const publicKeys = keyShares.map((ks) => ks.publicKey);
|
|
10
10
|
const combinedPublicKey = combinePublicKeys(publicKeys, prime);
|
|
11
11
|
return { keyShares, combinedPublicKey, prime, generator };
|
|
12
12
|
};
|
|
@@ -16,7 +16,7 @@ export const testSecureEncryptionAndDecryption = (participantsCount, threshold,
|
|
|
16
16
|
const selectedDecryptionShares = keyShares
|
|
17
17
|
.sort(() => Math.random() - 0.5)
|
|
18
18
|
.slice(0, threshold)
|
|
19
|
-
.map((
|
|
19
|
+
.map(({ privateKey }) => createDecryptionShare(encryptedMessage, privateKey, prime));
|
|
20
20
|
const combinedDecryptionShares = combineDecryptionShares(selectedDecryptionShares, prime);
|
|
21
21
|
const decryptedMessage = thresholdDecrypt(encryptedMessage, combinedDecryptionShares, prime);
|
|
22
22
|
expect(decryptedMessage).toBe(secret);
|
|
@@ -29,7 +29,7 @@ export const homomorphicMultiplicationTest = (participantsCount, threshold, mess
|
|
|
29
29
|
const selectedDecryptionShares = keyShares
|
|
30
30
|
.sort(() => Math.random() - 0.5)
|
|
31
31
|
.slice(0, threshold)
|
|
32
|
-
.map((
|
|
32
|
+
.map(({ privateKey }) => createDecryptionShare(encryptedProduct, privateKey, prime));
|
|
33
33
|
const combinedDecryptionShares = combineDecryptionShares(selectedDecryptionShares, prime);
|
|
34
34
|
const decryptedProduct = thresholdDecrypt(encryptedProduct, combinedDecryptionShares, prime);
|
|
35
35
|
expect(decryptedProduct).toBe(expectedProduct);
|
|
@@ -42,7 +42,7 @@ export const votingTest = (participantsCount, threshold, candidatesCount) => {
|
|
|
42
42
|
const encryptedProducts = Array.from({ length: candidatesCount }, (_, candidateIndex) => encryptedVotesMatrix.reduce((product, encryptedVotes) => multiplyEncryptedValues(product, encryptedVotes[candidateIndex], prime), { c1: 1n, c2: 1n }));
|
|
43
43
|
const partialDecryptionsMatrix = encryptedProducts.map((product) => keyShares
|
|
44
44
|
.slice(0, threshold)
|
|
45
|
-
.map((keyShare) => createDecryptionShare(product, keyShare.
|
|
45
|
+
.map((keyShare) => createDecryptionShare(product, keyShare.privateKey, prime)));
|
|
46
46
|
const decryptedProducts = partialDecryptionsMatrix.map((decryptionShares) => {
|
|
47
47
|
const combinedDecryptionShares = combineDecryptionShares(decryptionShares, prime);
|
|
48
48
|
const encryptedProduct = encryptedProducts[partialDecryptionsMatrix.indexOf(decryptionShares)];
|