swisspost-evoting-go-poc/pkg/elgamal/decrypt.go
saymrwulf e8b6f30871 Swiss Post E-Voting Go PoC
Proof-of-concept reimplementation of the Swiss Post e-voting
cryptographic protocol in Go. Single binary, 52 source files,
2 dependencies. Covers ElGamal encryption, Bayer-Groth verifiable
shuffles, zero-knowledge proofs, return codes, and a full
election ceremony demo.
2026-02-13 19:53:09 +01:00

49 lines
1.4 KiB
Go

package elgamal
import (
emath "github.com/user/evote/pkg/math"
)
// Decrypt computes the ElGamal decryption of a ciphertext.
// m_i = phi_i / gamma^sk_i = phi_i * gamma^(-sk_i)
func Decrypt(ct Ciphertext, sk PrivateKey) Message {
if ct.Size() != sk.Size() {
panic("ciphertext size must match secret key size")
}
elems := make([]emath.GqElement, ct.Size())
for i := 0; i < ct.Size(); i++ {
// gamma^sk_i
gammaSk := ct.Gamma.Exponentiate(sk.Get(i))
// m_i = phi_i / gamma^sk_i
elems[i] = ct.GetPhi(i).Divide(gammaSk)
}
return Message{Elements: emath.GqVectorOf(elems...)}
}
// PartialDecrypt computes partial decryption using one key share.
// For each phi: phi_i' = phi_i / gamma^sk_i
// The gamma is left unchanged for further partial decryptions.
func PartialDecrypt(ct Ciphertext, sk PrivateKey) Ciphertext {
if ct.Size() != sk.Size() {
panic("ciphertext size must match secret key size")
}
phis := make([]emath.GqElement, ct.Size())
for i := 0; i < ct.Size(); i++ {
gammaSk := ct.Gamma.Exponentiate(sk.Get(i))
phis[i] = ct.GetPhi(i).Divide(gammaSk)
}
return Ciphertext{
Gamma: ct.Gamma,
Phis: emath.GqVectorOf(phis...),
}
}
// ExtractMessage extracts the plaintext from a fully decrypted ciphertext.
// After all partial decryptions, the phis contain the plaintext directly.
func ExtractMessage(ct Ciphertext) Message {
return Message{Elements: ct.Phis}
}