swisspost-evoting-go-poc/pkg/mixnet/product_argument.go
saymrwulf c24650885e Instrument the deep Bayer-Groth layers: commitments, sub-arguments, decryption
Take the live-math cockpit down to the level of the Swiss Post crypto-primitives
class structure. The shuffle proof is no longer one line — you can now watch it
being constructed:

- Pedersen matrix commitment (CommitmentService analog): c_A = Comm(A; r),
  c_{A,j} = h^{r_j} Π g_i^{A_ij}, emitted from CommitMatrix.
- All five Bayer-Groth sub-arguments, mirroring the *ArgumentService classes:
  ShuffleArgument (composition + x,y,z challenges), ProductArgument,
  HadamardArgument (entrywise product), ZeroArgument (bilinear star-map),
  SingleValueProductArgument, MultiExponentiationArgument — each emits its
  defining relation as LaTeX with live dimensions.
- Partial decryption + decryption proof (DecryptionProofService analog):
  φ'_i = φ_i·γ_i^{-sk} with the ZK proof that log_g(pk) = log_γ(γ^sk).

New trace.KindArgument. Low-level Commit stays uninstrumented (called in
verification too — would flood the stream); CommitMatrix is the semantic step.

Test: a 6-voter ceremony (N=6 → 2×3 shuffle matrix, so m>1 and the full argument
tree runs) captures 345 live events across 8 kinds, and asserts all five named
sub-arguments appear.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:26:03 +02:00

109 lines
3 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()
emitArgument("product",
"Product argument: prove the product of all matrix entries equals b",
`\text{ProductArgument}:\ \prod_{i=1}^{n}\prod_{j=1}^{m} A_{ij} = b \quad(\text{via Hadamard} \circ \text{SVP})`,
"ProductArgument: Π_ij A_ij = b (Hadamard ∘ single-value-product)",
dims(m, n))
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
}