Skip to content

Commit 0f0ed50

Browse files
author
Janis Erdmanis
committed
implementing secret sharing
1 parent 4941efe commit 0f0ed50

File tree

10 files changed

+308
-152
lines changed

10 files changed

+308
-152
lines changed

README.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ The latter division can be used as plaintext equivalence test for the package. T
7777

7878
```julia
7979
using CryptoGroups
80-
using SigmaProofs.ElGamal: Enc, ElGamalRow
80+
using SigmaProofs.ElGamal: Enc
8181
using SigmaProofs.DecryptionProofs: exponentiate, verify
8282

8383
g = @ECGroup{P_192}()
@@ -89,8 +89,8 @@ pk = g^sk
8989

9090
encryptor = Enc(pk, g)
9191

92-
plaintexts = [g^4, g^2, g^3]
93-
cyphertexts = encryptor(plaintexts, rand(2:order(g)-1, length(plaintexts))) .|> ElGamalRow
92+
plaintexts = [g^4, g^2, g^3] .|> tuple
93+
cyphertexts = encryptor(plaintexts, rand(2:order(g)-1, length(plaintexts)))
9494

9595
simulator = decrypt(g, cyphertexts, sk, verifier)
9696

@@ -126,7 +126,21 @@ The last piece of puzzle is range proof use in homomorphic tallying methods usin
126126

127127
## SecretSharing
128128

129-
Verifiable secret sharing. Comming soon...
129+
The `SigmaProofs.SecretSharing` module provides an implementation of the Feldman Verifiable Secret Sharing (VSS) scheme, a cryptographic protocol that extends Shamir's Secret Sharing with a verification mechanism.
130+
131+
The Feldman VSS scheme allows a dealer to distribute shares of a secret among participants in a way that enables the participants to verify the consistency of their shares without revealing the secret. This is achieved by the dealer publishing commitments to the coefficients of the polynomial used to generate the shares.
132+
133+
The module includes the following key functionality:
134+
135+
1. **Secret Sharing Setup**: The `sharding_setup(g::G, nodes::Vector{G}, coeff::Vector{G}, ::Verifier)::Simulator{ShardingSetup}` function generates the necessary components for the secret sharing scheme, including the dealer's public key, the participants' public keys, and the commitments to the polynomial coefficients.
136+
137+
2. **Share Verification**: The `verify` function allows participants to verify the consistency of their shares by checking the published commitments.
138+
139+
3. **Secret Reconstruction**: The `merge_exponentiations(::Simulator{ShardingSetup}, ::Vector{G}, ::Vector{Exponentiation{G}}, proof::Vector{ChaumPedersenProof{G}})::Simulator{Exponentiation}` and `merge_decryptions(::Simulator{ShardingSetup}, ::Vector{<:ElGamalRow{G}}, ::Vector{Exponentiation{G}}, proof::Vector{ChaumPedersenProof{G}})::Simulator{Decryption}` functions enable the reconstruction of the secret by combining a threshold number of shares using Lagrange interpolation.
140+
141+
4. **Utility Functions**: The module provides helper functions for polynomial evaluation (`evaluate_poly`) and Lagrange coefficient computation (`lagrange_coef`), which are essential for both the sharing and reconstruction processes.
142+
143+
The module also includes support for generating proofs and verifying the consistency of the scheme using the `SigmaProofs` framework. For more detailed use see `test/secretsharing.jl` file.
130144

131145
## CommitmentShuffle
132146

src/DecryptionProofs.jl

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,39 +15,39 @@ b(e::ElGamalRow{<:Group, N}) where N = ntuple(n -> e[n].b, N)
1515
struct Decryption{G <: Group, N} <: Proposition
1616
g::G
1717
pk::G
18-
cyphertexts::Vector{ElGamalRow{G, N}}
18+
ciphertexts::Vector{ElGamalRow{G, N}}
1919
plaintexts::Vector{NTuple{N, G}}
2020
end
2121

2222
proof_type(::Type{Decryption}) = ChaumPedersenProof
2323
proof_type(::Type{<:Decryption{G}}) where G <: Group = ChaumPedersenProof{G}
2424

25-
Base.:(==)(x::T, y::T) where T <: Decryption = x.g == y.g && x.pk == y.pk && x.cyphertexts == y.cyphertexts && x.plaintexts == y.plaintexts
25+
Base.:(==)(x::T, y::T) where T <: Decryption = x.g == y.g && x.pk == y.pk && x.ciphertexts == y.ciphertexts && x.plaintexts == y.plaintexts
2626

2727
function Base.permute!(decr::Decryption, perm::AbstractVector{<:Integer})
2828

29-
permute!(decr.cyphertexts, perm)
29+
permute!(decr.ciphertexts, perm)
3030
permute!(decr.plaintexts, perm)
3131

3232
return
3333
end
3434

35-
verify(proposition::Decryption, secret::Integer) = decrypt(proposition.g, proposition.cyphertexts, secret) == proposition
35+
verify(proposition::Decryption, secret::Integer) = decrypt(proposition.g, proposition.ciphertexts, secret) == proposition
3636

37-
axinv(proposition::Decryption) = (mi ./ b(ei) for (ei, mi) in zip(proposition.cyphertexts, proposition.plaintexts))
37+
axinv(proposition::Decryption) = (mi ./ b(ei) for (ei, mi) in zip(proposition.ciphertexts, proposition.plaintexts))
3838
axinv(e::Vector{<:ElGamalRow}, secret::Integer) = (a(ei) .^ (-secret) for ei in e)
3939

40-
function decrypt(g::G, cyphertexts::Vector{<:ElGamalRow{G}}, secret::Integer; axinv = axinv(cyphertexts, secret)) where G <: Group
40+
function decrypt(g::G, ciphertexts::Vector{<:ElGamalRow{G}}, secret::Integer; axinv = axinv(ciphertexts, secret)) where G <: Group
4141

42-
plaintexts = [b(ci) .* axi for (ci, axi) in zip(cyphertexts, axinv)]
42+
plaintexts = [b(ci) .* axi for (ci, axi) in zip(ciphertexts, axinv)]
4343
pk = g^secret
4444

45-
return Decryption(g, pk, cyphertexts, plaintexts)
45+
return Decryption(g, pk, ciphertexts, plaintexts)
4646
end
4747

4848
function prove(proposition::Decryption{G}, verifier::Verifier, secret::Integer; axinv = axinv(proposition)) where G <: Group
4949

50-
g_vec = [proposition.g, flatten(a(c) for c in proposition.cyphertexts)...]
50+
g_vec = [proposition.g, flatten(a(c) for c in proposition.ciphertexts)...]
5151
y_vec = [inv(proposition.pk), flatten(axinv)...]
5252

5353
log_proposition = LogEquality(g_vec, y_vec)
@@ -57,9 +57,9 @@ function prove(proposition::Decryption{G}, verifier::Verifier, secret::Integer;
5757
return log_proof
5858
end
5959

60-
function decrypt(g::G, cyphertexts::Vector{<:ElGamalRow{G}}, secret::Integer, verifier::Verifier; axinv = axinv(cyphertexts, secret)) where G <: Group
60+
function decrypt(g::G, ciphertexts::Vector{<:ElGamalRow{G}}, secret::Integer, verifier::Verifier; axinv = axinv(ciphertexts, secret)) where G <: Group
6161

62-
proposition = decrypt(g, cyphertexts, secret; axinv)
62+
proposition = decrypt(g, ciphertexts, secret; axinv)
6363
proof = prove(proposition, verifier, secret; axinv)
6464

6565
return Simulator(proposition, proof, verifier)
@@ -68,7 +68,7 @@ end
6868

6969
function verify(proposition::Decryption, proof::ChaumPedersenProof, verifier::Verifier)
7070

71-
g_vec = [proposition.g, flatten(a(c) for c in proposition.cyphertexts)...]
71+
g_vec = [proposition.g, flatten(a(c) for c in proposition.ciphertexts)...]
7272
y_vec = [inv(proposition.pk), flatten(axinv(proposition))...]
7373

7474
log_proposition = LogEquality(g_vec, y_vec)
@@ -83,38 +83,38 @@ end
8383
struct DecryptionInv{G <: Group, N} <: Proposition
8484
g::G
8585
pk::G
86-
cyphertexts::Vector{ElGamalRow{G, N}}
86+
ciphertexts::Vector{ElGamalRow{G, N}}
8787
trackers::Vector{NTuple{N, G}}
8888
end
8989

9090
proof_type(::Type{DecryptionInv}) = ChaumPedersenProof
9191
proof_type(::Type{<:DecryptionInv{G}}) where G <: Group = ChaumPedersenProof{G}
9292

93-
Base.:(==)(x::T, y::T) where T <: DecryptionInv = x.g == y.g && x.pk == y.pk && x.cyphertexts == y.cyphertexts && x.trackers == y.trackers
93+
Base.:(==)(x::T, y::T) where T <: DecryptionInv = x.g == y.g && x.pk == y.pk && x.ciphertexts == y.ciphertexts && x.trackers == y.trackers
9494

9595
function Base.permute!(decr::DecryptionInv, perm::AbstractVector{<:Integer})
9696

97-
permute!(decr.cyphertexts, perm)
97+
permute!(decr.ciphertexts, perm)
9898
permute!(decr.trackers, perm)
9999

100100
return
101101
end
102102

103-
ax(proposition::DecryptionInv) = (ti .* b(ei) for (ei, ti) in zip(proposition.cyphertexts, proposition.trackers))
103+
ax(proposition::DecryptionInv) = (ti .* b(ei) for (ei, ti) in zip(proposition.ciphertexts, proposition.trackers))
104104
ax(e::Vector{<:ElGamalRow}, secret::Integer) = (a(ei) .^ secret for ei in e)
105105

106-
function decryptinv(g::G, cyphertexts::Vector{<:ElGamalRow{G}}, secret::Integer; ax = ax(cyphertexts, secret)) where G <: Group
106+
function decryptinv(g::G, ciphertexts::Vector{<:ElGamalRow{G}}, secret::Integer; ax = ax(ciphertexts, secret)) where G <: Group
107107

108-
trackers = [axi ./ b(ci) for (ci, axi) in zip(cyphertexts, ax)]
108+
trackers = [axi ./ b(ci) for (ci, axi) in zip(ciphertexts, ax)]
109109
pk = g^secret
110110

111-
return DecryptionInv(g, pk, cyphertexts, trackers)
111+
return DecryptionInv(g, pk, ciphertexts, trackers)
112112
end
113113

114114
# The same actually
115115
function prove(proposition::DecryptionInv{G}, verifier::Verifier, secret::Integer; ax = ax(proposition)) where G <: Group
116116

117-
g_vec = [proposition.g, flatten(a(c) for c in proposition.cyphertexts)...]
117+
g_vec = [proposition.g, flatten(a(c) for c in proposition.ciphertexts)...]
118118
y_vec = [proposition.pk, flatten(ax)...]
119119

120120
log_proposition = LogEquality(g_vec, y_vec)
@@ -125,20 +125,20 @@ function prove(proposition::DecryptionInv{G}, verifier::Verifier, secret::Intege
125125
end
126126

127127

128-
function decryptinv(g::G, cyphertexts::Vector{<:ElGamalRow{G}}, secret::Integer, verifier::Verifier; ax = ax(cyphertexts, secret)) where G <: Group
128+
function decryptinv(g::G, ciphertexts::Vector{<:ElGamalRow{G}}, secret::Integer, verifier::Verifier; ax = ax(ciphertexts, secret)) where G <: Group
129129

130-
proposition = decryptinv(g, cyphertexts, secret; ax)
130+
proposition = decryptinv(g, ciphertexts, secret; ax)
131131

132132
proof = prove(proposition, verifier, secret; ax)
133133

134134
return Simulator(proposition, proof, verifier)
135135
end
136136

137-
verify(proposition::DecryptionInv, secret::Integer) = decryptinv(proposition.g, proposition.cyphertexts, secret) == proposition
137+
verify(proposition::DecryptionInv, secret::Integer) = decryptinv(proposition.g, proposition.ciphertexts, secret) == proposition
138138

139139
function verify(proposition::DecryptionInv, proof::ChaumPedersenProof, verifier::Verifier)
140140

141-
g_vec = [proposition.g, flatten(a(c) for c in proposition.cyphertexts)...]
141+
g_vec = [proposition.g, flatten(a(c) for c in proposition.ciphertexts)...]
142142
y_vec = [proposition.pk, flatten(ax(proposition))...]
143143

144144
log_proposition = LogEquality(g_vec, y_vec)
@@ -149,12 +149,12 @@ end
149149

150150
function Serializer.save(proposition::Decryption, dir::Path)
151151

152-
(; g, pk, cyphertexts, plaintexts) = proposition
152+
(; g, pk, ciphertexts, plaintexts) = proposition
153153

154154
pbkey_tree = Parser.marshal_publickey(pk, g)
155155
write(joinpath(dir, "publicKey.bt"), pbkey_tree)
156156

157-
write(joinpath(dir, "Ciphertexts.bt"), Tree(cyphertexts))
157+
write(joinpath(dir, "Ciphertexts.bt"), Tree(ciphertexts))
158158
write(joinpath(dir, "Decryption.bt"), Tree(plaintexts)) # Decryption could be renamed to Plaintexts
159159

160160
return
@@ -169,15 +169,15 @@ function Serializer.load(::Type{Decryption}, basedir::Path)
169169

170170
G = typeof(g)
171171

172-
cyphertexts_tree = Parser.decode(read(joinpath(basedir, "Ciphertexts.bt")))
172+
ciphertexts_tree = Parser.decode(read(joinpath(basedir, "Ciphertexts.bt")))
173173
plaintexts_tree = Parser.decode(read(joinpath(basedir, "Decryption.bt")))
174174

175175
N = 1 # ToDo: extract that from the tree
176176

177-
cyphertexts = convert(Vector{ElGamalRow{G, N}}, cyphertexts_tree)
177+
ciphertexts = convert(Vector{ElGamalRow{G, N}}, ciphertexts_tree)
178178
plaintexts = convert(Vector{NTuple{N, G}}, plaintexts_tree)
179179

180-
return Decryption(g, pk, cyphertexts, plaintexts)
180+
return Decryption(g, pk, ciphertexts, plaintexts)
181181
end
182182

183183
Serializer.load(::Type{P}, ::Type{Decryption}, path::Path) where P <: ChaumPedersenProof = Serializer.load(P, path; prefix="Decryption")
@@ -186,12 +186,12 @@ Serializer.load(::Type{P}, ::Type{Decryption}, path::Path) where P <: ChaumPeder
186186

187187
function Serializer.save(proposition::DecryptionInv, dir::Path)
188188

189-
(; g, pk, cyphertexts, trackers) = proposition
189+
(; g, pk, ciphertexts, trackers) = proposition
190190

191191
pbkey_tree = Parser.marshal_publickey(pk, g)
192192
write(joinpath(dir, "publicKey.bt"), pbkey_tree)
193193

194-
write(joinpath(dir, "Ciphertexts.bt"), Tree(cyphertexts))
194+
write(joinpath(dir, "Ciphertexts.bt"), Tree(ciphertexts))
195195
write(joinpath(dir, "DecryptionInv.bt"), Tree(trackers)) # Decryption could be renamed to Plaintexts
196196

197197
return
@@ -206,15 +206,15 @@ function Serializer.load(::Type{DecryptionInv}, basedir::Path)
206206

207207
G = typeof(g)
208208

209-
cyphertexts_tree = Parser.decode(read(joinpath(basedir, "Ciphertexts.bt")))
209+
ciphertexts_tree = Parser.decode(read(joinpath(basedir, "Ciphertexts.bt")))
210210
plaintexts_tree = Parser.decode(read(joinpath(basedir, "DecryptionInv.bt")))
211211

212212
N = 1 # ToDo: extract that from the tree
213213

214-
cyphertexts = convert(Vector{ElGamalRow{G, N}}, cyphertexts_tree)
214+
ciphertexts = convert(Vector{ElGamalRow{G, N}}, ciphertexts_tree)
215215
plaintexts = convert(Vector{NTuple{N, G}}, plaintexts_tree)
216216

217-
return DecryptionInv(g, pk, cyphertexts, plaintexts)
217+
return DecryptionInv(g, pk, ciphertexts, plaintexts)
218218
end
219219

220220
Serializer.load(::Type{P}, ::Type{DecryptionInv}, path::Path) where P <: ChaumPedersenProof = Serializer.load(P, path; prefix="DecryptionInv")

src/ElGamal.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ ElGamalRow(row::AbstractVector{<:ElGamalElement{G}}) where {G <: Group} = conver
4343
width(::Type{ElGamalRow{<:Group, N}}) where N = N
4444
width(::Type{<:AbstractVector{<:ElGamalRow{<:Group, N}}}) where N = N
4545

46+
a(e::ElGamalRow{<:Group, N}) where N = ntuple(n -> e[n].a, N)
47+
b(e::ElGamalRow{<:Group, N}) where N = ntuple(n -> e[n].b, N)
48+
4649
Base.convert(::Type{ElGamalRow{G, N}}, row::NTuple{N, G}) where {N, G<:Group} = ElGamalRow(tuple([ElGamalElement(mi) for mi in row]...))
4750
Base.convert(::Type{ElGamalRow{G, 1}}, element::ElGamalElement{G}) where G <: Group = ElGamalRow((element,))
4851
Base.convert(::Type{ElGamalRow{G}}, row::AbstractVector{<:ElGamalElement{G}}) where G <: Group = ElGamalRow(tuple(row...))

src/LogProofs.jl

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,6 @@ proof_type(::Type{LogEquality{G}}) where G <: Group = ChaumPedersenProof{G}
7272

7373
Base.:(==)(x::T, y::T) where T <: ChaumPedersenProof = x.commitment == y.commitment && x.response == y.response
7474

75-
76-
77-
7875
function prove(proposition::LogEquality{G}, verifier::Verifier, power::Integer; roprg = gen_roprg())::ChaumPedersenProof where G <: Group
7976

8077
(; g, y) = proposition
@@ -123,6 +120,51 @@ function exponentiate(g::Vector{<:Group}, power::Integer, verifier::Verifier; ro
123120
end
124121

125122

123+
struct Exponentiation{G <: Group} <: Proposition
124+
g::G
125+
pk::G
126+
x::Vector{G}
127+
y::Vector{G}
128+
end
129+
130+
proof_type(::Type{<:Exponentiation{G}}) where G <: Group = ChaumPedersenProof{G}
131+
132+
133+
function exponentiate(g::G, x::Vector{G}, power::Integer)::Exponentiation where G <: Group
134+
135+
pk = g ^ power
136+
y = x .^ power
137+
138+
return Exponentiation(g, pk, x, y)
139+
end
140+
141+
142+
function exponentiate(g::G, x::Vector{G}, power::Integer, verifier::Verifier; roprg = gen_roprg()) where G <: Group
143+
144+
proposition = exponentiate(g, x, power)
145+
proof = prove(proposition, verifier, power; roprg)
146+
147+
return Simulator(proposition, proof, verifier)
148+
end
149+
150+
151+
function prove(proposition::Exponentiation, verifier::Verifier, power::Integer; roprg = gen_roprg())
152+
153+
g_vec = [proposition.g, proposition.x...]
154+
y_vec = [proposition.pk, proposition.y...]
155+
156+
return prove(LogEquality(g_vec, y_vec), verifier, power)
157+
end
158+
159+
function verify(proposition::Exponentiation{G}, proof::ChaumPedersenProof{G}, verifier::Verifier) where G <: Group
160+
161+
g_vec = [proposition.g, proposition.x...]
162+
y_vec = [proposition.pk, proposition.y...]
163+
164+
return verify(LogEquality(g_vec, y_vec), proof, verifier)
165+
end
166+
167+
126168
function Serializer.save(proof::ChaumPedersenProof, dir::Path; prefix = "ChaumPedersen")
127169

128170
(; commitment, response) = proof

src/RangeProofs.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ end
492492

493493

494494
### Now let's implement plaintext equivalence proofs
495-
# In case one can ensure that the input cyphertexts are correct ElGamal encryptions this is unnecessary as
495+
# In case one can ensure that the input ciphertexts are correct ElGamal encryptions this is unnecessary as
496496
# one then simply proves `dec(e1/e2) = 1`. To perform this proof one does not need to know the blinding factors, but the knowledge
497497
# of the exponent of encrypted value is necessary. Depending of available knowledge this proposition can have multiple proof types.
498498
struct PlaintextEquivalence{G<:Group} <: Proposition

0 commit comments

Comments
 (0)