threshold-elgamal 0.1.11 → 0.1.12
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/dist/constants.d.ts +23 -0
- package/dist/constants.js +40 -0
- package/dist/elgamal.d.ts +28 -0
- package/dist/elgamal.js +65 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/testUtils.d.ts +11 -0
- package/dist/testUtils.js +52 -0
- package/dist/thresholdElgamal.d.ts +75 -0
- package/dist/thresholdElgamal.js +117 -0
- package/dist/types.d.ts +18 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +25 -0
- package/dist/utils.js +45 -0
- package/package.json +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const GROUPS: {
|
|
2
|
+
readonly ffdhe2048: {
|
|
3
|
+
readonly primeBits: 2048;
|
|
4
|
+
readonly prime: 32317006071311007300153513477825163362488057133489075174588434139269806834136210002792056362640164685458556357935330816928829023080573472625273554742461245741026202527916572972862706300325263428213145766931414223654220941111348629991657478268034230553086349050635557712219187890332729569696129743856241741236237225197346402691855797767976823014625397933058015226858730761197532436467475855460715043896844940366130497697812854295958659597567051283852132784468522925504568272879113720098931873959143374175837826000278034973198552060607533234122603254684088120031105907484281003994966956119696956248629032338072839127039n;
|
|
5
|
+
readonly generator: 2n;
|
|
6
|
+
readonly modulus: 16158503035655503650076756738912581681244028566744537587294217069634903417068105001396028181320082342729278178967665408464414511540286736312636777371230622870513101263958286486431353150162631714106572883465707111827110470555674314995828739134017115276543174525317778856109593945166364784848064871928120870618118612598673201345927898883988411507312698966529007613429365380598766218233737927730357521948422470183065248848906427147979329798783525641926066392234261462752284136439556860049465936979571687087918913000139017486599276030303766617061301627342044060015552953742140501997483478059848478124314516169036419563519n;
|
|
7
|
+
readonly symmetricEquivalentBitsSecurity: 103;
|
|
8
|
+
};
|
|
9
|
+
readonly ffdhe3072: {
|
|
10
|
+
readonly primeBits: 3072;
|
|
11
|
+
readonly prime: 5809605995369958062758586654274580047791722104970656507438869740087793294939022179753100900150316602414836960597893531254315756065700170507943025794723871619068282822579148207659984331724286057133800207014820356957933334364535176201393094406964280368146360322417397201921556656310696298417414318434929392806928868314831784332237038568260988712237196665742900353512788403877776568945491183287529096888884348887176901995757588549340219807606149955056871781046117195453427070254533858964729101754281121787330325506574928503501334937579191349178901801866451262831560570379780282604068262795024384318599710948857446185134652829941527736472860172354516733867877780829051346167153594329592339252295871976889069885964128038593002336846153522149026229984394781638501125312676451837144945451331832522946684620954184360294871798125320434686136230055213248587935623124338652624786221871129902570119964134282018641257113252046271726747647n;
|
|
12
|
+
readonly generator: 2n;
|
|
13
|
+
readonly modulus: 2904802997684979031379293327137290023895861052485328253719434870043896647469511089876550450075158301207418480298946765627157878032850085253971512897361935809534141411289574103829992165862143028566900103507410178478966667182267588100696547203482140184073180161208698600960778328155348149208707159217464696403464434157415892166118519284130494356118598332871450176756394201938888284472745591643764548444442174443588450997878794274670109903803074977528435890523058597726713535127266929482364550877140560893665162753287464251750667468789595674589450900933225631415780285189890141302034131397512192159299855474428723092567326414970763868236430086177258366933938890414525673083576797164796169626147935988444534942982064019296501168423076761074513114992197390819250562656338225918572472725665916261473342310477092180147435899062660217343068115027606624293967811562169326312393110935564951285059982067141009320628556626023135863373823n;
|
|
14
|
+
readonly symmetricEquivalentBitsSecurity: 125;
|
|
15
|
+
};
|
|
16
|
+
readonly ffdhe4096: {
|
|
17
|
+
readonly primeBits: 4096;
|
|
18
|
+
readonly prime: 1044388881413152506673611132423542708364181673367771525125030890756881099188024532056304793061869328458723091803972939229793654985168401497491717574483844225116618212565649899896238061528255690984013755361148305106047581812557457571303413897964307070369153233034916545609049161117676542252417034306148432734874401682098205055813065377495410934435776008569464677021023433005437163880753068613673525551966829473007537177831003494630326494021352410947409155250518131329542947165352164089215019548909074312164647627938366550236314760864116934087960021077839688388383033906117940935023026686459274599124189299486771919466921436930468113859003854695674493896608503326776616230412252016237753188005160515672431703429026925450722225213972891936880551722374424500117253400391608019951133386097176734162660461073160502839490488652900367939577292447038637156268014222959401811270825513710710113193757653852931049810187522670964988718456427706279024201400130351029277257873323362974483425793829163819060563081096261611614988801585554385004830748976181157545121697905898543562330970182151097394600286811868072516047394404389555706298311761588649133904051123770516767707951778179308436153604841663369568605395358405635911568855382987714763476172799n;
|
|
19
|
+
readonly generator: 2n;
|
|
20
|
+
readonly modulus: 522194440706576253336805566211771354182090836683885762562515445378440549594012266028152396530934664229361545901986469614896827492584200748745858787241922112558309106282824949948119030764127845492006877680574152553023790906278728785651706948982153535184576616517458272804524580558838271126208517153074216367437200841049102527906532688747705467217888004284732338510511716502718581940376534306836762775983414736503768588915501747315163247010676205473704577625259065664771473582676082044607509774454537156082323813969183275118157380432058467043980010538919844194191516953058970467511513343229637299562094649743385959733460718465234056929501927347837246948304251663388308115206126008118876594002580257836215851714513462725361112606986445968440275861187212250058626700195804009975566693048588367081330230536580251419745244326450183969788646223519318578134007111479700905635412756855355056596878826926465524905093761335482494359228213853139512100700065175514638628936661681487241712896914581909530281540548130805807494400792777192502415374488090578772560848952949271781165485091075548697300143405934036258023697202194777853149155880794324566952025561885258383853975889089654218076802420831684784302697679202817955784427691493857381738086399n;
|
|
21
|
+
readonly symmetricEquivalentBitsSecurity: 150;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The GROUPS object defines cryptographic parameters for different levels
|
|
3
|
+
of finite field Diffie-Hellman (FFDHE) key exchanges.
|
|
4
|
+
|
|
5
|
+
The parameters are standardized in RFC 7919 to ensure security,
|
|
6
|
+
interoperability, and a balance between computational efficiency
|
|
7
|
+
and resistance against cryptographic attacks.
|
|
8
|
+
|
|
9
|
+
Each subgroup (ffdhe2048, ffdhe3072, ffdhe4096) corresponds to a security
|
|
10
|
+
level, with larger primes offering stronger security but requiring more processing power.
|
|
11
|
+
|
|
12
|
+
Reference: https://datatracker.ietf.org/doc/rfc7919/
|
|
13
|
+
*/
|
|
14
|
+
export const GROUPS = {
|
|
15
|
+
// Parameters for 2048-bit prime modulus group
|
|
16
|
+
ffdhe2048: {
|
|
17
|
+
primeBits: 2048, // Size of the prime in bits, default security level suitable for current applications
|
|
18
|
+
prime: 32317006071311007300153513477825163362488057133489075174588434139269806834136210002792056362640164685458556357935330816928829023080573472625273554742461245741026202527916572972862706300325263428213145766931414223654220941111348629991657478268034230553086349050635557712219187890332729569696129743856241741236237225197346402691855797767976823014625397933058015226858730761197532436467475855460715043896844940366130497697812854295958659597567051283852132784468522925504568272879113720098931873959143374175837826000278034973198552060607533234122603254684088120031105907484281003994966956119696956248629032338072839127039n,
|
|
19
|
+
generator: 2n, // A small, primitive root modulo prime, used to generate public keys
|
|
20
|
+
// Same as `prime` in this context, used for modulo operations in encryption/decryption
|
|
21
|
+
modulus: 16158503035655503650076756738912581681244028566744537587294217069634903417068105001396028181320082342729278178967665408464414511540286736312636777371230622870513101263958286486431353150162631714106572883465707111827110470555674314995828739134017115276543174525317778856109593945166364784848064871928120870618118612598673201345927898883988411507312698966529007613429365380598766218233737927730357521948422470183065248848906427147979329798783525641926066392234261462752284136439556860049465936979571687087918913000139017486599276030303766617061301627342044060015552953742140501997483478059848478124314516169036419563519n,
|
|
22
|
+
symmetricEquivalentBitsSecurity: 103, // The estimated security level equivalent to symmetric key cryptography
|
|
23
|
+
},
|
|
24
|
+
// Parameters for 3072-bit prime modulus group
|
|
25
|
+
ffdhe3072: {
|
|
26
|
+
primeBits: 3072, // Provides higher security/complexity level
|
|
27
|
+
prime: 5809605995369958062758586654274580047791722104970656507438869740087793294939022179753100900150316602414836960597893531254315756065700170507943025794723871619068282822579148207659984331724286057133800207014820356957933334364535176201393094406964280368146360322417397201921556656310696298417414318434929392806928868314831784332237038568260988712237196665742900353512788403877776568945491183287529096888884348887176901995757588549340219807606149955056871781046117195453427070254533858964729101754281121787330325506574928503501334937579191349178901801866451262831560570379780282604068262795024384318599710948857446185134652829941527736472860172354516733867877780829051346167153594329592339252295871976889069885964128038593002336846153522149026229984394781638501125312676451837144945451331832522946684620954184360294871798125320434686136230055213248587935623124338652624786221871129902570119964134282018641257113252046271726747647n,
|
|
28
|
+
generator: 2n,
|
|
29
|
+
modulus: 2904802997684979031379293327137290023895861052485328253719434870043896647469511089876550450075158301207418480298946765627157878032850085253971512897361935809534141411289574103829992165862143028566900103507410178478966667182267588100696547203482140184073180161208698600960778328155348149208707159217464696403464434157415892166118519284130494356118598332871450176756394201938888284472745591643764548444442174443588450997878794274670109903803074977528435890523058597726713535127266929482364550877140560893665162753287464251750667468789595674589450900933225631415780285189890141302034131397512192159299855474428723092567326414970763868236430086177258366933938890414525673083576797164796169626147935988444534942982064019296501168423076761074513114992197390819250562656338225918572472725665916261473342310477092180147435899062660217343068115027606624293967811562169326312393110935564951285059982067141009320628556626023135863373823n,
|
|
30
|
+
symmetricEquivalentBitsSecurity: 125,
|
|
31
|
+
},
|
|
32
|
+
// Parameters for 4096-bit prime modulus group
|
|
33
|
+
ffdhe4096: {
|
|
34
|
+
primeBits: 4096, // High security/complexity level
|
|
35
|
+
prime: 1044388881413152506673611132423542708364181673367771525125030890756881099188024532056304793061869328458723091803972939229793654985168401497491717574483844225116618212565649899896238061528255690984013755361148305106047581812557457571303413897964307070369153233034916545609049161117676542252417034306148432734874401682098205055813065377495410934435776008569464677021023433005437163880753068613673525551966829473007537177831003494630326494021352410947409155250518131329542947165352164089215019548909074312164647627938366550236314760864116934087960021077839688388383033906117940935023026686459274599124189299486771919466921436930468113859003854695674493896608503326776616230412252016237753188005160515672431703429026925450722225213972891936880551722374424500117253400391608019951133386097176734162660461073160502839490488652900367939577292447038637156268014222959401811270825513710710113193757653852931049810187522670964988718456427706279024201400130351029277257873323362974483425793829163819060563081096261611614988801585554385004830748976181157545121697905898543562330970182151097394600286811868072516047394404389555706298311761588649133904051123770516767707951778179308436153604841663369568605395358405635911568855382987714763476172799n,
|
|
36
|
+
generator: 2n,
|
|
37
|
+
modulus: 522194440706576253336805566211771354182090836683885762562515445378440549594012266028152396530934664229361545901986469614896827492584200748745858787241922112558309106282824949948119030764127845492006877680574152553023790906278728785651706948982153535184576616517458272804524580558838271126208517153074216367437200841049102527906532688747705467217888004284732338510511716502718581940376534306836762775983414736503768588915501747315163247010676205473704577625259065664771473582676082044607509774454537156082323813969183275118157380432058467043980010538919844194191516953058970467511513343229637299562094649743385959733460718465234056929501927347837246948304251663388308115206126008118876594002580257836215851714513462725361112606986445968440275861187212250058626700195804009975566693048588367081330230536580251419745244326450183969788646223519318578134007111479700905635412756855355056596878826926465524905093761335482494359228213853139512100700065175514638628936661681487241712896914581909530281540548130805807494400792777192502415374488090578772560848952949271781165485091075548697300143405934036258023697202194777853149155880794324566952025561885258383853975889089654218076802420831684784302697679202817955784427691493857381738086399n,
|
|
38
|
+
symmetricEquivalentBitsSecurity: 150,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { EncryptedMessage, Parameters } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Generates the parameters for the ElGamal encryption, including the prime, generator,
|
|
4
|
+
* and key pair (public and private keys).
|
|
5
|
+
*
|
|
6
|
+
* @param {2048 | 3072 | 4096} primeBits - The bit length for the prime number. Supports 2048, 3072, or 4096 bits.
|
|
7
|
+
* @returns {Parameters} The generated parameters including the prime, generator, publicKey, and privateKey.
|
|
8
|
+
*/
|
|
9
|
+
export declare const generateParameters: (primeBits?: 2048 | 3072 | 4096) => Parameters;
|
|
10
|
+
/**
|
|
11
|
+
* Encrypts a secret using ElGamal encryption.
|
|
12
|
+
*
|
|
13
|
+
* @param {number} secret - The secret to be encrypted.
|
|
14
|
+
* @param {bigint} prime - The prime number used in the encryption system.
|
|
15
|
+
* @param {bigint} generator - The generator used in the encryption system.
|
|
16
|
+
* @param {bigint} publicKey - The public key used for encryption.
|
|
17
|
+
* @returns {EncryptedMessage} The encrypted secret, consisting of two BigIntegers (c1 and c2).
|
|
18
|
+
*/
|
|
19
|
+
export declare const encrypt: (secret: number, prime: bigint, generator: bigint, publicKey: bigint) => EncryptedMessage;
|
|
20
|
+
/**
|
|
21
|
+
* Decrypts an ElGamal encrypted secret.
|
|
22
|
+
*
|
|
23
|
+
* @param {EncryptedMessage} secret - The encrypted secret to decrypt.
|
|
24
|
+
* @param {bigint} prime - The prime number used in the encryption system.
|
|
25
|
+
* @param {bigint} privateKey - The private key used for decryption.
|
|
26
|
+
* @returns {number} The decrypted secret as an integer.
|
|
27
|
+
*/
|
|
28
|
+
export declare const decrypt: (encryptedMessage: EncryptedMessage, prime: bigint, privateKey: bigint) => number;
|
package/dist/elgamal.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { modPow, modInv } from 'bigint-mod-arith';
|
|
2
|
+
import { GROUPS } from './constants';
|
|
3
|
+
import { getRandomBigIntegerInRange } from './utils';
|
|
4
|
+
/**
|
|
5
|
+
* Generates the parameters for the ElGamal encryption, including the prime, generator,
|
|
6
|
+
* and key pair (public and private keys).
|
|
7
|
+
*
|
|
8
|
+
* @param {2048 | 3072 | 4096} primeBits - The bit length for the prime number. Supports 2048, 3072, or 4096 bits.
|
|
9
|
+
* @returns {Parameters} The generated parameters including the prime, generator, publicKey, and privateKey.
|
|
10
|
+
*/
|
|
11
|
+
export const generateParameters = (primeBits = 2048) => {
|
|
12
|
+
let prime;
|
|
13
|
+
let generator;
|
|
14
|
+
switch (primeBits) {
|
|
15
|
+
case 2048:
|
|
16
|
+
prime = BigInt(GROUPS.ffdhe2048.prime);
|
|
17
|
+
generator = BigInt(GROUPS.ffdhe2048.generator);
|
|
18
|
+
break;
|
|
19
|
+
case 3072:
|
|
20
|
+
prime = BigInt(GROUPS.ffdhe3072.prime);
|
|
21
|
+
generator = BigInt(GROUPS.ffdhe3072.generator);
|
|
22
|
+
break;
|
|
23
|
+
case 4096:
|
|
24
|
+
prime = BigInt(GROUPS.ffdhe4096.prime);
|
|
25
|
+
generator = BigInt(GROUPS.ffdhe4096.generator);
|
|
26
|
+
break;
|
|
27
|
+
default:
|
|
28
|
+
throw new Error('Unsupported bit length');
|
|
29
|
+
}
|
|
30
|
+
const privateKey = getRandomBigIntegerInRange(2n, prime - 1n);
|
|
31
|
+
const publicKey = modPow(generator, privateKey, prime);
|
|
32
|
+
return { prime, generator, publicKey, privateKey };
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Encrypts a secret using ElGamal encryption.
|
|
36
|
+
*
|
|
37
|
+
* @param {number} secret - The secret to be encrypted.
|
|
38
|
+
* @param {bigint} prime - The prime number used in the encryption system.
|
|
39
|
+
* @param {bigint} generator - The generator used in the encryption system.
|
|
40
|
+
* @param {bigint} publicKey - The public key used for encryption.
|
|
41
|
+
* @returns {EncryptedMessage} The encrypted secret, consisting of two BigIntegers (c1 and c2).
|
|
42
|
+
*/
|
|
43
|
+
export const encrypt = (secret, prime, generator, publicKey) => {
|
|
44
|
+
if (secret >= Number(prime)) {
|
|
45
|
+
throw new Error('Message is too large for direct encryption');
|
|
46
|
+
}
|
|
47
|
+
const randomNumber = getRandomBigIntegerInRange(1n, prime - 1n);
|
|
48
|
+
const c1 = modPow(generator, randomNumber, prime);
|
|
49
|
+
const messageBigInt = BigInt(secret);
|
|
50
|
+
const c2 = (modPow(publicKey, randomNumber, prime) * messageBigInt) % prime;
|
|
51
|
+
return { c1, c2 };
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Decrypts an ElGamal encrypted secret.
|
|
55
|
+
*
|
|
56
|
+
* @param {EncryptedMessage} secret - The encrypted secret to decrypt.
|
|
57
|
+
* @param {bigint} prime - The prime number used in the encryption system.
|
|
58
|
+
* @param {bigint} privateKey - The private key used for decryption.
|
|
59
|
+
* @returns {number} The decrypted secret as an integer.
|
|
60
|
+
*/
|
|
61
|
+
export const decrypt = (encryptedMessage, prime, privateKey) => {
|
|
62
|
+
const ax = modPow(encryptedMessage.c1, privateKey, prime);
|
|
63
|
+
const plaintext = (modInv(ax, prime) * encryptedMessage.c2) % prime;
|
|
64
|
+
return Number(plaintext);
|
|
65
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { generateParameters, encrypt, decrypt } from './elgamal';
|
|
2
|
+
export { generateSingleKeyShare, generateKeyShares, combinePublicKeys, createDecryptionShare, combineDecryptionShares, thresholdDecrypt, } from './thresholdElgamal';
|
|
3
|
+
export { getRandomBigIntegerInRange, multiplyEncryptedValues } from './utils';
|
|
4
|
+
export type { EncryptedMessage, Parameters, KeyPair, PartyKeyPair, } from './types';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { generateParameters, encrypt, decrypt } from './elgamal';
|
|
2
|
+
export { generateSingleKeyShare, generateKeyShares, combinePublicKeys, createDecryptionShare, combineDecryptionShares, thresholdDecrypt, } from './thresholdElgamal';
|
|
3
|
+
export { getRandomBigIntegerInRange, multiplyEncryptedValues } from './utils';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PartyKeyPair } from './types';
|
|
2
|
+
export declare const getRandomScore: (min?: number, max?: number) => number;
|
|
3
|
+
export declare const thresholdSetup: (partiesCount: number, threshold: number, primeBits?: 2048 | 3072 | 4096) => {
|
|
4
|
+
keyShares: PartyKeyPair[];
|
|
5
|
+
combinedPublicKey: bigint;
|
|
6
|
+
prime: bigint;
|
|
7
|
+
generator: bigint;
|
|
8
|
+
};
|
|
9
|
+
export declare const testSecureEncryptionAndDecryption: (participantsCount: number, threshold: number, secret: number) => void;
|
|
10
|
+
export declare const homomorphicMultiplicationTest: (participantsCount: number, threshold: number, messages: number[]) => void;
|
|
11
|
+
export declare const votingTest: (participantsCount: number, threshold: number, candidatesCount: number) => void;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { expect } from 'vitest';
|
|
2
|
+
import { encrypt } from './elgamal';
|
|
3
|
+
import { generateKeyShares, combinePublicKeys, createDecryptionShare, combineDecryptionShares, thresholdDecrypt, getGroup, } from './thresholdElgamal';
|
|
4
|
+
import { multiplyEncryptedValues } from './utils';
|
|
5
|
+
export const getRandomScore = (min = 1, max = 10) => Math.floor(Math.random() * (max - min + 1)) + min;
|
|
6
|
+
export const thresholdSetup = (partiesCount, threshold, primeBits = 2048) => {
|
|
7
|
+
const { prime, generator } = getGroup(primeBits);
|
|
8
|
+
const keyShares = generateKeyShares(partiesCount, threshold, primeBits);
|
|
9
|
+
const publicKeys = keyShares.map((ks) => ks.partyPublicKey);
|
|
10
|
+
const combinedPublicKey = combinePublicKeys(publicKeys, prime);
|
|
11
|
+
return { keyShares, combinedPublicKey, prime, generator };
|
|
12
|
+
};
|
|
13
|
+
export const testSecureEncryptionAndDecryption = (participantsCount, threshold, secret) => {
|
|
14
|
+
const { keyShares, combinedPublicKey, prime, generator } = thresholdSetup(participantsCount, threshold);
|
|
15
|
+
const encryptedMessage = encrypt(secret, prime, generator, combinedPublicKey);
|
|
16
|
+
const selectedDecryptionShares = keyShares
|
|
17
|
+
.sort(() => Math.random() - 0.5)
|
|
18
|
+
.slice(0, threshold)
|
|
19
|
+
.map((keyShare) => createDecryptionShare(encryptedMessage, keyShare.partyPrivateKey, prime));
|
|
20
|
+
const combinedDecryptionShares = combineDecryptionShares(selectedDecryptionShares, prime);
|
|
21
|
+
const decryptedMessage = thresholdDecrypt(encryptedMessage, combinedDecryptionShares, prime);
|
|
22
|
+
expect(decryptedMessage).toBe(secret);
|
|
23
|
+
};
|
|
24
|
+
export const homomorphicMultiplicationTest = (participantsCount, threshold, messages) => {
|
|
25
|
+
const expectedProduct = messages.reduce((product, secret) => product * secret, 1);
|
|
26
|
+
const { keyShares, combinedPublicKey, prime, generator } = thresholdSetup(participantsCount, threshold);
|
|
27
|
+
const encryptedMessages = messages.map((secret) => encrypt(secret, prime, generator, combinedPublicKey));
|
|
28
|
+
const encryptedProduct = encryptedMessages.reduce((product, encryptedMessage) => multiplyEncryptedValues(product, encryptedMessage, prime), { c1: 1n, c2: 1n });
|
|
29
|
+
const selectedDecryptionShares = keyShares
|
|
30
|
+
.sort(() => Math.random() - 0.5)
|
|
31
|
+
.slice(0, threshold)
|
|
32
|
+
.map((keyShare) => createDecryptionShare(encryptedProduct, keyShare.partyPrivateKey, prime));
|
|
33
|
+
const combinedDecryptionShares = combineDecryptionShares(selectedDecryptionShares, prime);
|
|
34
|
+
const decryptedProduct = thresholdDecrypt(encryptedProduct, combinedDecryptionShares, prime);
|
|
35
|
+
expect(decryptedProduct).toBe(expectedProduct);
|
|
36
|
+
};
|
|
37
|
+
export const votingTest = (participantsCount, threshold, candidatesCount) => {
|
|
38
|
+
const { keyShares, combinedPublicKey, prime, generator } = thresholdSetup(participantsCount, threshold);
|
|
39
|
+
const votesMatrix = Array.from({ length: participantsCount }, () => Array.from({ length: candidatesCount }, () => getRandomScore(1, 10)));
|
|
40
|
+
const expectedProducts = Array.from({ length: candidatesCount }, (_, candidateIndex) => votesMatrix.reduce((product, votes) => product * votes[candidateIndex], 1));
|
|
41
|
+
const encryptedVotesMatrix = votesMatrix.map((votes) => votes.map((vote) => encrypt(vote, prime, generator, combinedPublicKey)));
|
|
42
|
+
const encryptedProducts = Array.from({ length: candidatesCount }, (_, candidateIndex) => encryptedVotesMatrix.reduce((product, encryptedVotes) => multiplyEncryptedValues(product, encryptedVotes[candidateIndex], prime), { c1: 1n, c2: 1n }));
|
|
43
|
+
const partialDecryptionsMatrix = encryptedProducts.map((product) => keyShares
|
|
44
|
+
.slice(0, threshold)
|
|
45
|
+
.map((keyShare) => createDecryptionShare(product, keyShare.partyPrivateKey, prime)));
|
|
46
|
+
const decryptedProducts = partialDecryptionsMatrix.map((decryptionShares) => {
|
|
47
|
+
const combinedDecryptionShares = combineDecryptionShares(decryptionShares, prime);
|
|
48
|
+
const encryptedProduct = encryptedProducts[partialDecryptionsMatrix.indexOf(decryptionShares)];
|
|
49
|
+
return thresholdDecrypt(encryptedProduct, combinedDecryptionShares, prime);
|
|
50
|
+
});
|
|
51
|
+
expect(decryptedProducts).toEqual(expectedProducts);
|
|
52
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { EncryptedMessage, PartyKeyPair } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Retrieves the group parameters for a given prime bit length.
|
|
4
|
+
*
|
|
5
|
+
* @param {2048 | 3072 | 4096} primeBits - The bit length of the prime modulus (2048, 3072, or 4096).
|
|
6
|
+
* @returns {Object} The group parameters including prime and generator.
|
|
7
|
+
*/
|
|
8
|
+
export declare const getGroup: (primeBits: 2048 | 3072 | 4096) => {
|
|
9
|
+
prime: bigint;
|
|
10
|
+
generator: bigint;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Evaluates a polynomial at a given point using modular arithmetic.
|
|
14
|
+
*
|
|
15
|
+
* @param {bigint[]} polynomial - The coefficients of the polynomial.
|
|
16
|
+
* @param {number} x - The point at which to evaluate the polynomial.
|
|
17
|
+
* @param {bigint} prime - The prime modulus.
|
|
18
|
+
* @returns {bigint} The result of the polynomial evaluation.
|
|
19
|
+
*/
|
|
20
|
+
export declare const evaluatePolynomial: (polynomial: bigint[], x: number, prime: bigint) => bigint;
|
|
21
|
+
/**
|
|
22
|
+
* Generates a single key share for a participant in a threshold ElGamal cryptosystem.
|
|
23
|
+
*
|
|
24
|
+
* @param {number} index - The unique index of the participant (starting from 1).
|
|
25
|
+
* @param {number} threshold - The minimum number of key shares required for decryption.
|
|
26
|
+
* @param {2048 | 3072 | 4096} primeBits - The bit length of the prime modulus (default: 2048).
|
|
27
|
+
* @returns {PartyKeyPair} The key share containing a private and public key share for the participant.
|
|
28
|
+
*/
|
|
29
|
+
export declare const generateSingleKeyShare: (index: number, threshold: number, primeBits?: 2048 | 3072 | 4096) => PartyKeyPair;
|
|
30
|
+
/**
|
|
31
|
+
* Generates key shares for a threshold ElGamal cryptosystem.
|
|
32
|
+
*
|
|
33
|
+
* @param {number} n - The total number of key shares.
|
|
34
|
+
* @param {number} threshold - The minimum number of key shares required for decryption.
|
|
35
|
+
* @param {2048 | 3072 | 4096} primeBits - The bit length of the prime modulus (default: 2048).
|
|
36
|
+
* @returns {PartyKeyPair[]} An array of key shares, each containing a private and public key share.
|
|
37
|
+
*/
|
|
38
|
+
export declare const generateKeyShares: (n: number, threshold: number, primeBits?: 2048 | 3072 | 4096) => PartyKeyPair[];
|
|
39
|
+
/**
|
|
40
|
+
* Combines multiple public keys into a single public key.
|
|
41
|
+
*
|
|
42
|
+
* @param {bigint[]} publicKeys - An array of public keys to combine.
|
|
43
|
+
* @param {bigint} prime - The prime modulus used in the ElGamal system.
|
|
44
|
+
* @returns {bigint} The combined public key.
|
|
45
|
+
*/
|
|
46
|
+
export declare const combinePublicKeys: (publicKeys: bigint[], prime: bigint) => bigint;
|
|
47
|
+
/**
|
|
48
|
+
* Performs a partial decryption on a ciphertext using an individual's private key share.
|
|
49
|
+
*
|
|
50
|
+
* @param {EncryptedMessage} encryptedMessage - The encrypted secret.
|
|
51
|
+
* @param {bigint} partyPrivateKey - The private key share of the decrypting party.
|
|
52
|
+
* @param {bigint} prime - The prime modulus used in the ElGamal system.
|
|
53
|
+
* @returns {bigint} The result of the partial decryption.
|
|
54
|
+
*/
|
|
55
|
+
export declare const createDecryptionShare: (encryptedMessage: EncryptedMessage, partyPrivateKey: bigint, prime: bigint) => bigint;
|
|
56
|
+
/**
|
|
57
|
+
* Combines partial decryptions from multiple parties into a single decryption factor.
|
|
58
|
+
*
|
|
59
|
+
* @param {bigint[]} decryptionShares - An array of partial decryption results.
|
|
60
|
+
* @param {bigint} prime - The prime modulus used in the ElGamal system.
|
|
61
|
+
* @returns {bigint} The combined decryption factor.
|
|
62
|
+
*/
|
|
63
|
+
export declare const combineDecryptionShares: (decryptionShares: bigint[], prime: bigint) => bigint;
|
|
64
|
+
/**
|
|
65
|
+
* Decrypts an encrypted secret using the combined partial decryptions in a threshold ElGamal scheme.
|
|
66
|
+
*
|
|
67
|
+
* @param {{ c1: bigint; c2: bigint }} encryptedMessage - The encrypted secret components.
|
|
68
|
+
* @param {bigint} combinedDecryptionShares - The combined partial decryptions from all parties.
|
|
69
|
+
* @param {bigint} prime - The prime modulus used in the ElGamal system.
|
|
70
|
+
* @returns {number} The decrypted secret, assuming it was small enough to be directly encrypted.
|
|
71
|
+
*/
|
|
72
|
+
export declare const thresholdDecrypt: (encryptedMessage: {
|
|
73
|
+
c1: bigint;
|
|
74
|
+
c2: bigint;
|
|
75
|
+
}, combinedDecryptionShares: bigint, prime: bigint) => number;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { modPow, modInv } from 'bigint-mod-arith';
|
|
2
|
+
import { GROUPS } from './constants';
|
|
3
|
+
import { generatePolynomial } from './utils';
|
|
4
|
+
/**
|
|
5
|
+
* Retrieves the group parameters for a given prime bit length.
|
|
6
|
+
*
|
|
7
|
+
* @param {2048 | 3072 | 4096} primeBits - The bit length of the prime modulus (2048, 3072, or 4096).
|
|
8
|
+
* @returns {Object} The group parameters including prime and generator.
|
|
9
|
+
*/
|
|
10
|
+
export const getGroup = (primeBits) => {
|
|
11
|
+
switch (primeBits) {
|
|
12
|
+
case 2048:
|
|
13
|
+
return GROUPS.ffdhe2048;
|
|
14
|
+
case 3072:
|
|
15
|
+
return GROUPS.ffdhe3072;
|
|
16
|
+
case 4096:
|
|
17
|
+
return GROUPS.ffdhe4096;
|
|
18
|
+
default:
|
|
19
|
+
throw new Error('Unsupported bit length');
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Evaluates a polynomial at a given point using modular arithmetic.
|
|
24
|
+
*
|
|
25
|
+
* @param {bigint[]} polynomial - The coefficients of the polynomial.
|
|
26
|
+
* @param {number} x - The point at which to evaluate the polynomial.
|
|
27
|
+
* @param {bigint} prime - The prime modulus.
|
|
28
|
+
* @returns {bigint} The result of the polynomial evaluation.
|
|
29
|
+
*/
|
|
30
|
+
export const evaluatePolynomial = (polynomial, x, prime) => {
|
|
31
|
+
let result = 0n;
|
|
32
|
+
for (let i = 0; i < polynomial.length; i++) {
|
|
33
|
+
result = (result + polynomial[i] * BigInt(x) ** BigInt(i)) % prime;
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Generates a single key share for a participant in a threshold ElGamal cryptosystem.
|
|
39
|
+
*
|
|
40
|
+
* @param {number} index - The unique index of the participant (starting from 1).
|
|
41
|
+
* @param {number} threshold - The minimum number of key shares required for decryption.
|
|
42
|
+
* @param {2048 | 3072 | 4096} primeBits - The bit length of the prime modulus (default: 2048).
|
|
43
|
+
* @returns {PartyKeyPair} The key share containing a private and public key share for the participant.
|
|
44
|
+
*/
|
|
45
|
+
export const generateSingleKeyShare = (index, threshold, primeBits = 2048) => {
|
|
46
|
+
const group = getGroup(primeBits);
|
|
47
|
+
const prime = group.prime;
|
|
48
|
+
const generator = group.generator;
|
|
49
|
+
const polynomial = generatePolynomial(threshold, prime);
|
|
50
|
+
let partyPrivateKey = evaluatePolynomial(polynomial, index, prime);
|
|
51
|
+
// Ensure non-zero private key, adjusting index if necessary
|
|
52
|
+
while (partyPrivateKey === 0n) {
|
|
53
|
+
partyPrivateKey = evaluatePolynomial(polynomial, index + 1, prime);
|
|
54
|
+
}
|
|
55
|
+
const partyPublicKey = modPow(generator, partyPrivateKey, prime);
|
|
56
|
+
return { partyPrivateKey, partyPublicKey };
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Generates key shares for a threshold ElGamal cryptosystem.
|
|
60
|
+
*
|
|
61
|
+
* @param {number} n - The total number of key shares.
|
|
62
|
+
* @param {number} threshold - The minimum number of key shares required for decryption.
|
|
63
|
+
* @param {2048 | 3072 | 4096} primeBits - The bit length of the prime modulus (default: 2048).
|
|
64
|
+
* @returns {PartyKeyPair[]} An array of key shares, each containing a private and public key share.
|
|
65
|
+
*/
|
|
66
|
+
export const generateKeyShares = (n, threshold, primeBits = 2048) => {
|
|
67
|
+
const keyShares = [];
|
|
68
|
+
for (let i = 1; i <= n; i++) {
|
|
69
|
+
const keyShare = generateSingleKeyShare(i, threshold, primeBits);
|
|
70
|
+
keyShares.push(keyShare);
|
|
71
|
+
}
|
|
72
|
+
return keyShares;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Combines multiple public keys into a single public key.
|
|
76
|
+
*
|
|
77
|
+
* @param {bigint[]} publicKeys - An array of public keys to combine.
|
|
78
|
+
* @param {bigint} prime - The prime modulus used in the ElGamal system.
|
|
79
|
+
* @returns {bigint} The combined public key.
|
|
80
|
+
*/
|
|
81
|
+
export const combinePublicKeys = (publicKeys, prime) => publicKeys.reduce((acc, current) => (acc * current) % prime, 1n);
|
|
82
|
+
/**
|
|
83
|
+
* Performs a partial decryption on a ciphertext using an individual's private key share.
|
|
84
|
+
*
|
|
85
|
+
* @param {EncryptedMessage} encryptedMessage - The encrypted secret.
|
|
86
|
+
* @param {bigint} partyPrivateKey - The private key share of the decrypting party.
|
|
87
|
+
* @param {bigint} prime - The prime modulus used in the ElGamal system.
|
|
88
|
+
* @returns {bigint} The result of the partial decryption.
|
|
89
|
+
*/
|
|
90
|
+
export const createDecryptionShare = (encryptedMessage, partyPrivateKey, prime) => modPow(encryptedMessage.c1, partyPrivateKey, prime);
|
|
91
|
+
/**
|
|
92
|
+
* Combines partial decryptions from multiple parties into a single decryption factor.
|
|
93
|
+
*
|
|
94
|
+
* @param {bigint[]} decryptionShares - An array of partial decryption results.
|
|
95
|
+
* @param {bigint} prime - The prime modulus used in the ElGamal system.
|
|
96
|
+
* @returns {bigint} The combined decryption factor.
|
|
97
|
+
*/
|
|
98
|
+
export const combineDecryptionShares = (decryptionShares, prime) => {
|
|
99
|
+
let result = 1n;
|
|
100
|
+
for (const partialDecryption of decryptionShares) {
|
|
101
|
+
result = (result * partialDecryption) % prime;
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Decrypts an encrypted secret using the combined partial decryptions in a threshold ElGamal scheme.
|
|
107
|
+
*
|
|
108
|
+
* @param {{ c1: bigint; c2: bigint }} encryptedMessage - The encrypted secret components.
|
|
109
|
+
* @param {bigint} combinedDecryptionShares - The combined partial decryptions from all parties.
|
|
110
|
+
* @param {bigint} prime - The prime modulus used in the ElGamal system.
|
|
111
|
+
* @returns {number} The decrypted secret, assuming it was small enough to be directly encrypted.
|
|
112
|
+
*/
|
|
113
|
+
export const thresholdDecrypt = (encryptedMessage, combinedDecryptionShares, prime) => {
|
|
114
|
+
const combinedDecryptionInverse = modInv(combinedDecryptionShares, prime);
|
|
115
|
+
const plaintext = (encryptedMessage.c2 * combinedDecryptionInverse) % prime;
|
|
116
|
+
return Number(plaintext);
|
|
117
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type EncryptedMessage = {
|
|
2
|
+
c1: bigint;
|
|
3
|
+
c2: bigint;
|
|
4
|
+
};
|
|
5
|
+
export type Parameters = {
|
|
6
|
+
prime: bigint;
|
|
7
|
+
generator: bigint;
|
|
8
|
+
publicKey: bigint;
|
|
9
|
+
privateKey: bigint;
|
|
10
|
+
};
|
|
11
|
+
export type KeyPair = {
|
|
12
|
+
privateKey: bigint;
|
|
13
|
+
publicKey: bigint;
|
|
14
|
+
};
|
|
15
|
+
export type PartyKeyPair = {
|
|
16
|
+
partyPrivateKey: bigint;
|
|
17
|
+
partyPublicKey: bigint;
|
|
18
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { EncryptedMessage } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Generates a random bigint within a specified range.
|
|
4
|
+
* @param {bigint} min - The minimum value (inclusive).
|
|
5
|
+
* @param {bigint} max - The maximum value (exclusive).
|
|
6
|
+
* @returns {bigint} A random bigint within the specified range.
|
|
7
|
+
*/
|
|
8
|
+
export declare const getRandomBigIntegerInRange: (min: bigint, max: bigint) => bigint;
|
|
9
|
+
/**
|
|
10
|
+
* Performs homomorphic multiplication on two encrypted values, allowing for encrypted arithmetic operations.
|
|
11
|
+
* @param {EncryptedMessage} value1 - The first encrypted value.
|
|
12
|
+
* @param {EncryptedMessage} value2 - The second encrypted value.
|
|
13
|
+
* @param {bigint} prime - The prime modulus used in the encryption system.
|
|
14
|
+
* @returns {EncryptedMessage} The result of the multiplication, as a new encrypted message.
|
|
15
|
+
*/
|
|
16
|
+
export declare const multiplyEncryptedValues: (value1: EncryptedMessage, value2: EncryptedMessage, prime: bigint) => EncryptedMessage;
|
|
17
|
+
/**
|
|
18
|
+
* Generates a random polynomial of a specified degree, to be used in Shamir's Secret Sharing scheme.
|
|
19
|
+
* The polynomial is of the form f(x) = a0 + a1*x + a2*x^2 + ... + a_{threshold-1}*x^{threshold-1},
|
|
20
|
+
* where a0 is the "master" secret, and the rest of the coefficients are randomly chosen.
|
|
21
|
+
* @param {number} threshold - The degree of the polynomial (also, the number of shares required to reconstruct the secret).
|
|
22
|
+
* @param {bigint} prime - The prime modulus used in the system, to ensure operations are performed in a finite field.
|
|
23
|
+
* @returns {bigint[]} An array representing the polynomial coefficients `[a0, a1, ..., a_{threshold-1}]`.
|
|
24
|
+
*/
|
|
25
|
+
export declare const generatePolynomial: (threshold: number, prime: bigint) => bigint[];
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import randomBigint from 'random-bigint';
|
|
2
|
+
/**
|
|
3
|
+
* Generates a random bigint within a specified range.
|
|
4
|
+
* @param {bigint} min - The minimum value (inclusive).
|
|
5
|
+
* @param {bigint} max - The maximum value (exclusive).
|
|
6
|
+
* @returns {bigint} A random bigint within the specified range.
|
|
7
|
+
*/
|
|
8
|
+
export const getRandomBigIntegerInRange = (min, max) => {
|
|
9
|
+
const range = max - min + 1n;
|
|
10
|
+
// Determine the number of bits needed for the range
|
|
11
|
+
const bitsNeeded = range.toString(2).length;
|
|
12
|
+
// Generate a random bigint within the calculated bits
|
|
13
|
+
let num = randomBigint(bitsNeeded);
|
|
14
|
+
// Adjust the number to our range
|
|
15
|
+
num = num % range;
|
|
16
|
+
// Add the minimum to align with our desired range
|
|
17
|
+
return min + num;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Performs homomorphic multiplication on two encrypted values, allowing for encrypted arithmetic operations.
|
|
21
|
+
* @param {EncryptedMessage} value1 - The first encrypted value.
|
|
22
|
+
* @param {EncryptedMessage} value2 - The second encrypted value.
|
|
23
|
+
* @param {bigint} prime - The prime modulus used in the encryption system.
|
|
24
|
+
* @returns {EncryptedMessage} The result of the multiplication, as a new encrypted message.
|
|
25
|
+
*/
|
|
26
|
+
export const multiplyEncryptedValues = (value1, value2, prime) => {
|
|
27
|
+
const c1Multiplied = (value1.c1 * value2.c1) % prime;
|
|
28
|
+
const c2Multiplied = (value1.c2 * value2.c2) % prime;
|
|
29
|
+
return { c1: c1Multiplied, c2: c2Multiplied };
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Generates a random polynomial of a specified degree, to be used in Shamir's Secret Sharing scheme.
|
|
33
|
+
* The polynomial is of the form f(x) = a0 + a1*x + a2*x^2 + ... + a_{threshold-1}*x^{threshold-1},
|
|
34
|
+
* where a0 is the "master" secret, and the rest of the coefficients are randomly chosen.
|
|
35
|
+
* @param {number} threshold - The degree of the polynomial (also, the number of shares required to reconstruct the secret).
|
|
36
|
+
* @param {bigint} prime - The prime modulus used in the system, to ensure operations are performed in a finite field.
|
|
37
|
+
* @returns {bigint[]} An array representing the polynomial coefficients `[a0, a1, ..., a_{threshold-1}]`.
|
|
38
|
+
*/
|
|
39
|
+
export const generatePolynomial = (threshold, prime) => {
|
|
40
|
+
const polynomial = [getRandomBigIntegerInRange(2n, prime - 1n)]; // constant term is the "master" private key
|
|
41
|
+
for (let i = 1; i < threshold; i++) {
|
|
42
|
+
polynomial.push(getRandomBigIntegerInRange(0n, prime - 1n)); // random coefficients
|
|
43
|
+
}
|
|
44
|
+
return polynomial;
|
|
45
|
+
};
|