threshold-elgamal 0.1.24 → 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 +26 -65
- 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
|
@@ -29,9 +29,8 @@ The JavaScript/TypeScript ecosystem seems to be lacking in modern, functional El
|
|
|
29
29
|
## Production dependencies
|
|
30
30
|
|
|
31
31
|
- [bigint-mod-arith](https://www.npmjs.com/package/bigint-mod-arith)
|
|
32
|
-
- [random-bigint](https://www.npmjs.com/package/random-bigint)
|
|
33
32
|
|
|
34
|
-
It has no other production dependencies apart from
|
|
33
|
+
It has no other production dependencies apart from this one. It could be inlined easily, if needed.
|
|
35
34
|
|
|
36
35
|
## TODO
|
|
37
36
|
|
|
@@ -91,41 +90,28 @@ Threshold scheme for generating a common public key, sharing a secret to 3 parti
|
|
|
91
90
|
import {
|
|
92
91
|
getGroup,
|
|
93
92
|
encrypt,
|
|
94
|
-
|
|
93
|
+
generateKeys,
|
|
95
94
|
combinePublicKeys,
|
|
96
95
|
createDecryptionShare,
|
|
97
96
|
combineDecryptionShares,
|
|
98
97
|
thresholdDecrypt,
|
|
99
98
|
} from "threshold-elgamal";
|
|
100
|
-
import type { PartyKeyPair } from "threshold-elgamal";
|
|
101
99
|
|
|
102
100
|
const primeBits = 2048; // Bit length of the prime modulus
|
|
103
101
|
const threshold = 3; // A scenario for 3 participants with a threshold of 3
|
|
104
102
|
const { prime, generator } = getGroup(2048);
|
|
105
103
|
|
|
106
104
|
// Each participant generates their public key share and private key individually
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
primeBits,
|
|
111
|
-
);
|
|
112
|
-
const participant2KeyShare: PartyKeyPair = generateSingleKeyShare(
|
|
113
|
-
2,
|
|
114
|
-
threshold,
|
|
115
|
-
primeBits,
|
|
116
|
-
);
|
|
117
|
-
const participant3KeyShare: PartyKeyPair = generateSingleKeyShare(
|
|
118
|
-
3,
|
|
119
|
-
threshold,
|
|
120
|
-
primeBits,
|
|
121
|
-
);
|
|
105
|
+
const participant1Keys = generateKeys(1, threshold, primeBits);
|
|
106
|
+
const participant2Keys = generateKeys(2, threshold, primeBits);
|
|
107
|
+
const participant3Keys = generateKeys(3, threshold, primeBits);
|
|
122
108
|
|
|
123
109
|
// Combine the public keys to form a single public key
|
|
124
110
|
const combinedPublicKey = combinePublicKeys(
|
|
125
111
|
[
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
112
|
+
participant1Keys.publicKey,
|
|
113
|
+
participant2Keys.publicKey,
|
|
114
|
+
participant3Keys.publicKey,
|
|
129
115
|
],
|
|
130
116
|
prime,
|
|
131
117
|
);
|
|
@@ -136,21 +122,9 @@ const encryptedMessage = encrypt(secret, prime, generator, combinedPublicKey);
|
|
|
136
122
|
|
|
137
123
|
// Decryption shares
|
|
138
124
|
const decryptionShares = [
|
|
139
|
-
createDecryptionShare(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
prime,
|
|
143
|
-
),
|
|
144
|
-
createDecryptionShare(
|
|
145
|
-
encryptedMessage,
|
|
146
|
-
participant2KeyShare.partyPrivateKey,
|
|
147
|
-
prime,
|
|
148
|
-
),
|
|
149
|
-
createDecryptionShare(
|
|
150
|
-
encryptedMessage,
|
|
151
|
-
participant3KeyShare.partyPrivateKey,
|
|
152
|
-
prime,
|
|
153
|
-
),
|
|
125
|
+
createDecryptionShare(encryptedMessage, participant1Keys.privateKey, prime),
|
|
126
|
+
createDecryptionShare(encryptedMessage, participant2Keys.privateKey, prime),
|
|
127
|
+
createDecryptionShare(encryptedMessage, participant3Keys.privateKey, prime),
|
|
154
128
|
];
|
|
155
129
|
// Combining the decryption shares into one, used to decrypt the message
|
|
156
130
|
const combinedDecryptionShares = combineDecryptionShares(
|
|
@@ -174,7 +148,7 @@ This example demonstrates a 1 to 10 voting scenario where 3 participants cast en
|
|
|
174
148
|
```typescript
|
|
175
149
|
import {
|
|
176
150
|
encrypt,
|
|
177
|
-
|
|
151
|
+
generateKeys,
|
|
178
152
|
combinePublicKeys,
|
|
179
153
|
createDecryptionShare,
|
|
180
154
|
combineDecryptionShares,
|
|
@@ -182,35 +156,22 @@ import {
|
|
|
182
156
|
multiplyEncryptedValues,
|
|
183
157
|
getGroup,
|
|
184
158
|
} from "threshold-elgamal";
|
|
185
|
-
import type { PartyKeyPair } from "threshold-elgamal";
|
|
186
159
|
|
|
187
160
|
const primeBits = 2048; // Bit length of the prime modulus
|
|
188
161
|
const threshold = 3; // A scenario for 3 participants with a threshold of 3
|
|
189
162
|
const { prime, generator } = getGroup(2048);
|
|
190
163
|
|
|
191
164
|
// Each participant generates their public key share and private key individually
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
primeBits,
|
|
196
|
-
);
|
|
197
|
-
const participant2KeyShare: PartyKeyPair = generateSingleKeyShare(
|
|
198
|
-
2,
|
|
199
|
-
threshold,
|
|
200
|
-
primeBits,
|
|
201
|
-
);
|
|
202
|
-
const participant3KeyShare: PartyKeyPair = generateSingleKeyShare(
|
|
203
|
-
3,
|
|
204
|
-
threshold,
|
|
205
|
-
primeBits,
|
|
206
|
-
);
|
|
165
|
+
const participant1Keys = generateKeys(1, threshold, primeBits);
|
|
166
|
+
const participant2Keys = generateKeys(2, threshold, primeBits);
|
|
167
|
+
const participant3Keys = generateKeys(3, threshold, primeBits);
|
|
207
168
|
|
|
208
169
|
// Combine the public keys to form a single public key
|
|
209
170
|
const combinedPublicKey = combinePublicKeys(
|
|
210
171
|
[
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
172
|
+
participant1Keys.publicKey,
|
|
173
|
+
participant2Keys.publicKey,
|
|
174
|
+
participant3Keys.publicKey,
|
|
214
175
|
],
|
|
215
176
|
prime,
|
|
216
177
|
);
|
|
@@ -229,11 +190,11 @@ const encryptedVotesOption2 = voteOption2.map((vote) =>
|
|
|
229
190
|
|
|
230
191
|
// Multiply encrypted votes together to aggregate
|
|
231
192
|
const aggregatedEncryptedVoteOption1 = encryptedVotesOption1.reduce(
|
|
232
|
-
(acc,
|
|
193
|
+
(acc, encryptedVote) => multiplyEncryptedValues(acc, encryptedVote, prime),
|
|
233
194
|
{ c1: 1n, c2: 1n },
|
|
234
195
|
);
|
|
235
196
|
const aggregatedEncryptedVoteOption2 = encryptedVotesOption2.reduce(
|
|
236
|
-
(acc,
|
|
197
|
+
(acc, encryptedVote) => multiplyEncryptedValues(acc, encryptedVote, prime),
|
|
237
198
|
{ c1: 1n, c2: 1n },
|
|
238
199
|
);
|
|
239
200
|
|
|
@@ -243,34 +204,34 @@ const aggregatedEncryptedVoteOption2 = encryptedVotesOption2.reduce(
|
|
|
243
204
|
const decryptionSharesOption1 = [
|
|
244
205
|
createDecryptionShare(
|
|
245
206
|
aggregatedEncryptedVoteOption1,
|
|
246
|
-
|
|
207
|
+
participant1Keys.privateKey,
|
|
247
208
|
prime,
|
|
248
209
|
),
|
|
249
210
|
createDecryptionShare(
|
|
250
211
|
aggregatedEncryptedVoteOption1,
|
|
251
|
-
|
|
212
|
+
participant2Keys.privateKey,
|
|
252
213
|
prime,
|
|
253
214
|
),
|
|
254
215
|
createDecryptionShare(
|
|
255
216
|
aggregatedEncryptedVoteOption1,
|
|
256
|
-
|
|
217
|
+
participant3Keys.privateKey,
|
|
257
218
|
prime,
|
|
258
219
|
),
|
|
259
220
|
];
|
|
260
221
|
const decryptionSharesOption2 = [
|
|
261
222
|
createDecryptionShare(
|
|
262
223
|
aggregatedEncryptedVoteOption2,
|
|
263
|
-
|
|
224
|
+
participant1Keys.privateKey,
|
|
264
225
|
prime,
|
|
265
226
|
),
|
|
266
227
|
createDecryptionShare(
|
|
267
228
|
aggregatedEncryptedVoteOption2,
|
|
268
|
-
|
|
229
|
+
participant2Keys.privateKey,
|
|
269
230
|
prime,
|
|
270
231
|
),
|
|
271
232
|
createDecryptionShare(
|
|
272
233
|
aggregatedEncryptedVoteOption2,
|
|
273
|
-
|
|
234
|
+
participant3Keys.privateKey,
|
|
274
235
|
prime,
|
|
275
236
|
),
|
|
276
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)];
|