swisspost-evoting-go-poc/pkg/mixnet/multiexp_test.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

133 lines
3.3 KiB
Go

package mixnet
import (
"crypto/rand"
"math/big"
"testing"
"github.com/user/evote/pkg/elgamal"
emath "github.com/user/evote/pkg/math"
)
func TestMultiExponentiationArgument(t *testing.T) {
q := testSafePrimeQ(256)
p := new(big.Int).Mul(big.NewInt(2), q)
p.Add(p, big.NewInt(1))
g := big.NewInt(4)
group, err := emath.NewGqGroup(p, q, g)
if err != nil {
t.Fatal(err)
}
zqGroup := emath.ZqGroupFromGqGroup(group)
l := 2
pk := elgamal.GenKeyPair(group, l).PK
n := 3
m := 1
ck := GenCommitmentKey(n, group)
cMatrix := make([]*elgamal.CiphertextVector, m)
for i := 0; i < m; i++ {
cts := make([]elgamal.Ciphertext, n)
for j := 0; j < n; j++ {
r := emath.RandomZqElement(zqGroup)
cts[j] = elgamal.EncryptOnes(r, pk)
}
cMatrix[i] = elgamal.NewCiphertextVector(cts)
}
aCols := make([]*emath.ZqVector, m)
for j := 0; j < m; j++ {
aCols[j] = emath.RandomZqVector(n, zqGroup)
}
A := emath.ZqMatrixFromColumns(aCols)
r := emath.RandomZqVector(m, zqGroup)
cA := ck.CommitMatrix(A, r)
// Target = Enc(1; rho) * Π multiExp(cMatrix[i], A[:,i])
rhoVal := emath.RandomZqElement(zqGroup)
cTarget := identityCiphertext(l, group)
for i := 0; i < m; i++ {
ct := multiExpCiphertextRow(cMatrix[i], A.GetColumn(i))
cTarget = cTarget.Multiply(ct)
}
encRho := elgamal.EncryptOnes(rhoVal, pk)
cTarget = cTarget.Multiply(encRho)
arg := GenMultiExponentiationArgument(cMatrix, cTarget, cA, A, r, rhoVal, pk, ck, group)
ok := VerifyMultiExponentiationArgument(arg, cMatrix, cTarget, cA, pk, ck, group)
if !ok {
t.Fatal("MultiExponentiationArgument verification failed")
}
t.Log("MultiExponentiationArgument verification PASSED!")
}
func TestMultiExpLargerMatrix(t *testing.T) {
q := testSafePrimeQ(256)
p := new(big.Int).Mul(big.NewInt(2), q)
p.Add(p, big.NewInt(1))
g := big.NewInt(4)
group, err := emath.NewGqGroup(p, q, g)
if err != nil {
t.Fatal(err)
}
zqGroup := emath.ZqGroupFromGqGroup(group)
l := 2
pk := elgamal.GenKeyPair(group, l).PK
n := 2
m := 2
ck := GenCommitmentKey(n, group)
cMatrix := make([]*elgamal.CiphertextVector, m)
for i := 0; i < m; i++ {
cts := make([]elgamal.Ciphertext, n)
for j := 0; j < n; j++ {
r := emath.RandomZqElement(zqGroup)
cts[j] = elgamal.EncryptOnes(r, pk)
}
cMatrix[i] = elgamal.NewCiphertextVector(cts)
}
aCols := make([]*emath.ZqVector, m)
for j := 0; j < m; j++ {
aCols[j] = emath.RandomZqVector(n, zqGroup)
}
A := emath.ZqMatrixFromColumns(aCols)
r := emath.RandomZqVector(m, zqGroup)
cA := ck.CommitMatrix(A, r)
rhoVal := emath.RandomZqElement(zqGroup)
cTarget := identityCiphertext(l, group)
for i := 0; i < m; i++ {
ct := multiExpCiphertextRow(cMatrix[i], A.GetColumn(i))
cTarget = cTarget.Multiply(ct)
}
encRho := elgamal.EncryptOnes(rhoVal, pk)
cTarget = cTarget.Multiply(encRho)
arg := GenMultiExponentiationArgument(cMatrix, cTarget, cA, A, r, rhoVal, pk, ck, group)
ok := VerifyMultiExponentiationArgument(arg, cMatrix, cTarget, cA, pk, ck, group)
if !ok {
t.Fatal("MultiExponentiationArgument (2x2) verification failed")
}
t.Log("MultiExponentiationArgument (2x2) verification PASSED!")
}
func testSafePrimeQ(bits int) *big.Int {
for {
q, err := rand.Prime(rand.Reader, bits)
if err != nil {
panic(err)
}
p := new(big.Int).Mul(big.NewInt(2), q)
p.Add(p, big.NewInt(1))
if p.ProbablyPrime(64) {
return q
}
}
}