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

103 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package mixnet
import (
"math/big"
"github.com/user/evote/pkg/elgamal"
emath "github.com/user/evote/pkg/math"
)
// ProductArgument proves that the product of all elements in a committed matrix equals b.
type ProductArgument struct {
CB *emath.GqElement // Commitment to Hadamard product (nil if m=1)
Hadamard *HadamardArgument // nil if m=1
SVP SingleValueProductArgument // Always present
}
// GenProductArgument generates a product argument.
func GenProductArgument(
cA *emath.GqVector, // Commitments to A columns (size m)
b emath.ZqElement, // Product b = Π A[i,j]
A *emath.ZqMatrix, // n×m matrix
r *emath.ZqVector, // Randomness for A columns
pk elgamal.PublicKey, // Public key (needed for sub-argument hashes)
ck CommitmentKey,
group *emath.GqGroup,
) ProductArgument {
n := A.NumRows()
m := A.NumCols()
if m == 1 {
// Single column: just use SVP directly
svp := GenSingleValueProductArgument(cA.Get(0), b, A.GetColumn(0), r.Get(0), pk, ck, group)
return ProductArgument{SVP: svp}
}
// m > 1: Hadamard + SVP
zqGroup := emath.ZqGroupFromGqGroup(group)
// Compute b_vector = row-wise products (Hadamard product of all columns)
bVector := make([]emath.ZqElement, n)
for i := 0; i < n; i++ {
prod := A.Get(i, 0)
for j := 1; j < m; j++ {
prod = prod.Multiply(A.Get(i, j))
}
bVector[i] = prod
}
bVec := emath.ZqVectorOf(bVector...)
// Commit to Hadamard product
s := emath.RandomZqElement(zqGroup)
cb := ck.Commit(bVec, s)
// Generate Hadamard argument (now with pk)
hadamardArg := GenHadamardArgument(cA, cb, A, bVec, r, s, pk, ck, group)
// Generate SVP argument (now with pk)
svpArg := GenSingleValueProductArgument(cb, b, bVec, s, pk, ck, group)
return ProductArgument{
CB: &cb,
Hadamard: &hadamardArg,
SVP: svpArg,
}
}
// VerifyProductArgument verifies a product argument.
func VerifyProductArgument(
arg ProductArgument,
cA *emath.GqVector,
b emath.ZqElement,
pk elgamal.PublicKey,
ck CommitmentKey,
group *emath.GqGroup,
) bool {
m := cA.Size()
if m == 1 {
return VerifySingleValueProductArgument(arg.SVP, cA.Get(0), b, pk, ck, group)
}
// Verify Hadamard
if arg.CB == nil || arg.Hadamard == nil {
return false
}
if !VerifyHadamardArgument(*arg.Hadamard, cA, *arg.CB, pk, ck, group) {
return false
}
// Verify SVP
return VerifySingleValueProductArgument(arg.SVP, *arg.CB, b, pk, ck, group)
}
func computeProduct(matrix *emath.ZqMatrix) emath.ZqElement {
one, _ := emath.NewZqElement(big.NewInt(1), matrix.Group())
result := one
for i := 0; i < matrix.NumRows(); i++ {
for j := 0; j < matrix.NumCols(); j++ {
result = result.Multiply(matrix.Get(i, j))
}
}
return result
}