Skip to content

Commit

Permalink
implementing secret sharing
Browse files Browse the repository at this point in the history
  • Loading branch information
Janis Erdmanis committed Oct 21, 2024
1 parent 4941efe commit 0f0ed50
Show file tree
Hide file tree
Showing 10 changed files with 308 additions and 152 deletions.
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ The latter division can be used as plaintext equivalence test for the package. T

```julia
using CryptoGroups
using SigmaProofs.ElGamal: Enc, ElGamalRow
using SigmaProofs.ElGamal: Enc
using SigmaProofs.DecryptionProofs: exponentiate, verify

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

encryptor = Enc(pk, g)

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

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

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

## SecretSharing

Verifiable secret sharing. Comming soon...
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.

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.

The module includes the following key functionality:

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.

2. **Share Verification**: The `verify` function allows participants to verify the consistency of their shares by checking the published commitments.

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.

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.

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.

## CommitmentShuffle

Expand Down
68 changes: 34 additions & 34 deletions src/DecryptionProofs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,39 @@ b(e::ElGamalRow{<:Group, N}) where N = ntuple(n -> e[n].b, N)
struct Decryption{G <: Group, N} <: Proposition
g::G
pk::G
cyphertexts::Vector{ElGamalRow{G, N}}
ciphertexts::Vector{ElGamalRow{G, N}}
plaintexts::Vector{NTuple{N, G}}
end

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

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

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

permute!(decr.cyphertexts, perm)
permute!(decr.ciphertexts, perm)
permute!(decr.plaintexts, perm)

return
end

verify(proposition::Decryption, secret::Integer) = decrypt(proposition.g, proposition.cyphertexts, secret) == proposition
verify(proposition::Decryption, secret::Integer) = decrypt(proposition.g, proposition.ciphertexts, secret) == proposition

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

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

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

return Decryption(g, pk, cyphertexts, plaintexts)
return Decryption(g, pk, ciphertexts, plaintexts)
end

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

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

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

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

proposition = decrypt(g, cyphertexts, secret; axinv)
proposition = decrypt(g, ciphertexts, secret; axinv)
proof = prove(proposition, verifier, secret; axinv)

return Simulator(proposition, proof, verifier)
Expand All @@ -68,7 +68,7 @@ end

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

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

log_proposition = LogEquality(g_vec, y_vec)
Expand All @@ -83,38 +83,38 @@ end
struct DecryptionInv{G <: Group, N} <: Proposition
g::G
pk::G
cyphertexts::Vector{ElGamalRow{G, N}}
ciphertexts::Vector{ElGamalRow{G, N}}
trackers::Vector{NTuple{N, G}}
end

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

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

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

permute!(decr.cyphertexts, perm)
permute!(decr.ciphertexts, perm)
permute!(decr.trackers, perm)

return
end

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

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

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

return DecryptionInv(g, pk, cyphertexts, trackers)
return DecryptionInv(g, pk, ciphertexts, trackers)
end

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

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

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


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

proposition = decryptinv(g, cyphertexts, secret; ax)
proposition = decryptinv(g, ciphertexts, secret; ax)

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

return Simulator(proposition, proof, verifier)
end

verify(proposition::DecryptionInv, secret::Integer) = decryptinv(proposition.g, proposition.cyphertexts, secret) == proposition
verify(proposition::DecryptionInv, secret::Integer) = decryptinv(proposition.g, proposition.ciphertexts, secret) == proposition

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

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

log_proposition = LogEquality(g_vec, y_vec)
Expand All @@ -149,12 +149,12 @@ end

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

(; g, pk, cyphertexts, plaintexts) = proposition
(; g, pk, ciphertexts, plaintexts) = proposition

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

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

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

G = typeof(g)

cyphertexts_tree = Parser.decode(read(joinpath(basedir, "Ciphertexts.bt")))
ciphertexts_tree = Parser.decode(read(joinpath(basedir, "Ciphertexts.bt")))
plaintexts_tree = Parser.decode(read(joinpath(basedir, "Decryption.bt")))

N = 1 # ToDo: extract that from the tree

cyphertexts = convert(Vector{ElGamalRow{G, N}}, cyphertexts_tree)
ciphertexts = convert(Vector{ElGamalRow{G, N}}, ciphertexts_tree)
plaintexts = convert(Vector{NTuple{N, G}}, plaintexts_tree)

return Decryption(g, pk, cyphertexts, plaintexts)
return Decryption(g, pk, ciphertexts, plaintexts)
end

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

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

(; g, pk, cyphertexts, trackers) = proposition
(; g, pk, ciphertexts, trackers) = proposition

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

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

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

G = typeof(g)

cyphertexts_tree = Parser.decode(read(joinpath(basedir, "Ciphertexts.bt")))
ciphertexts_tree = Parser.decode(read(joinpath(basedir, "Ciphertexts.bt")))
plaintexts_tree = Parser.decode(read(joinpath(basedir, "DecryptionInv.bt")))

N = 1 # ToDo: extract that from the tree

cyphertexts = convert(Vector{ElGamalRow{G, N}}, cyphertexts_tree)
ciphertexts = convert(Vector{ElGamalRow{G, N}}, ciphertexts_tree)
plaintexts = convert(Vector{NTuple{N, G}}, plaintexts_tree)

return DecryptionInv(g, pk, cyphertexts, plaintexts)
return DecryptionInv(g, pk, ciphertexts, plaintexts)
end

Serializer.load(::Type{P}, ::Type{DecryptionInv}, path::Path) where P <: ChaumPedersenProof = Serializer.load(P, path; prefix="DecryptionInv")
Expand Down
3 changes: 3 additions & 0 deletions src/ElGamal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ ElGamalRow(row::AbstractVector{<:ElGamalElement{G}}) where {G <: Group} = conver
width(::Type{ElGamalRow{<:Group, N}}) where N = N
width(::Type{<:AbstractVector{<:ElGamalRow{<:Group, N}}}) where N = N

a(e::ElGamalRow{<:Group, N}) where N = ntuple(n -> e[n].a, N)
b(e::ElGamalRow{<:Group, N}) where N = ntuple(n -> e[n].b, N)

Base.convert(::Type{ElGamalRow{G, N}}, row::NTuple{N, G}) where {N, G<:Group} = ElGamalRow(tuple([ElGamalElement(mi) for mi in row]...))
Base.convert(::Type{ElGamalRow{G, 1}}, element::ElGamalElement{G}) where G <: Group = ElGamalRow((element,))
Base.convert(::Type{ElGamalRow{G}}, row::AbstractVector{<:ElGamalElement{G}}) where G <: Group = ElGamalRow(tuple(row...))
Expand Down
48 changes: 45 additions & 3 deletions src/LogProofs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,6 @@ proof_type(::Type{LogEquality{G}}) where G <: Group = ChaumPedersenProof{G}

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




function prove(proposition::LogEquality{G}, verifier::Verifier, power::Integer; roprg = gen_roprg())::ChaumPedersenProof where G <: Group

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


struct Exponentiation{G <: Group} <: Proposition
g::G
pk::G
x::Vector{G}
y::Vector{G}
end

proof_type(::Type{<:Exponentiation{G}}) where G <: Group = ChaumPedersenProof{G}


function exponentiate(g::G, x::Vector{G}, power::Integer)::Exponentiation where G <: Group

pk = g ^ power
y = x .^ power

return Exponentiation(g, pk, x, y)
end


function exponentiate(g::G, x::Vector{G}, power::Integer, verifier::Verifier; roprg = gen_roprg()) where G <: Group

proposition = exponentiate(g, x, power)
proof = prove(proposition, verifier, power; roprg)

return Simulator(proposition, proof, verifier)
end


function prove(proposition::Exponentiation, verifier::Verifier, power::Integer; roprg = gen_roprg())

g_vec = [proposition.g, proposition.x...]
y_vec = [proposition.pk, proposition.y...]

return prove(LogEquality(g_vec, y_vec), verifier, power)
end

function verify(proposition::Exponentiation{G}, proof::ChaumPedersenProof{G}, verifier::Verifier) where G <: Group

g_vec = [proposition.g, proposition.x...]
y_vec = [proposition.pk, proposition.y...]

return verify(LogEquality(g_vec, y_vec), proof, verifier)
end


function Serializer.save(proof::ChaumPedersenProof, dir::Path; prefix = "ChaumPedersen")

(; commitment, response) = proof
Expand Down
2 changes: 1 addition & 1 deletion src/RangeProofs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ end


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

0 comments on commit 0f0ed50

Please sign in to comment.