swisspost-evoting-go-poc/pkg/mixnet/verifiable_shuffle.go
saymrwulf bc43c3d3aa Instrument real crypto ops to emit live LaTeX events
The running cryptography now narrates itself. Each headline operation emits a
trace.Event carrying its LaTeX notation plus the actual runtime values, the
instant it executes:

- Ed25519 signature on every inter-party message (envelope.Seal)
- X25519 ECDH key agreement for confidential card delivery (NewSecureChannel)
- ElGamal ballot encryption E1 = (g^r, pk^r·m) with the real r (castBallot)
- Fiat-Shamir challenge e = H(...) mod q (Schnorr proof)
- Bayer-Groth verifiable shuffle C' = {ReEnc_pk(C_π(i))} with N (mix-net)

Ceremony phases set trace phase/party context so events are attributed to the
acting stakeholder and phase. Instrumentation is behind the enabled-check, so
normal runs pay nothing.

Test: a full traced ceremony captures 184 live events across all five headline
kinds, each with non-empty LaTeX and live values, correctly phase/party-tagged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:06:08 +02:00

70 lines
1.8 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 (
"fmt"
"github.com/user/evote/pkg/elgamal"
emath "github.com/user/evote/pkg/math"
"github.com/user/evote/pkg/trace"
)
// VerifiableShuffle holds the result of a verifiable shuffle.
type VerifiableShuffle struct {
ShuffledCiphertexts *elgamal.CiphertextVector
Argument ShuffleArgument
}
// GenVerifiableShuffle performs a shuffle and generates a proof.
func GenVerifiableShuffle(
C *elgamal.CiphertextVector,
pk elgamal.PublicKey,
group *emath.GqGroup,
) VerifiableShuffle {
// Determine matrix dimensions
N := C.Size()
_, n := GetMatrixDimensions(N)
// Generate commitment key of size n
ck := GenCommitmentKey(n, group)
// Perform shuffle
shuffle := GenShuffle(C, pk)
// Generate shuffle argument
arg := GenShuffleArgument(C, shuffle.Shuffled, shuffle.Perm, shuffle.Rho, pk, ck, group)
trace.EmitFunc(func() trace.Event {
return trace.Event{
Kind: trace.KindShuffle,
Caption: fmt.Sprintf("Bayer-Groth verifiable shuffle of %d ciphertexts", N),
LaTeX: `\mathbf{C}' = \big\{\, \mathrm{ReEnc}_{pk}\!\big(C_{\pi(i)};\, \rho_i\big) \,\big\}_{i=1}^{\VAL{N}}, \qquad \pi \xleftarrow{\$} S_{\VAL{N}}`,
ASCII: "C' = { ReEnc_pk(C_π(i); ρ_i) } for a secret permutation π",
Values: map[string]string{
"N": fmt.Sprintf("%d", N),
"m": fmt.Sprintf("%d", N/n),
"n": fmt.Sprintf("%d", n),
},
}
})
return VerifiableShuffle{
ShuffledCiphertexts: shuffle.Shuffled,
Argument: arg,
}
}
// VerifyShuffle verifies a verifiable shuffle.
func VerifyShuffle(
C *elgamal.CiphertextVector,
vs VerifiableShuffle,
pk elgamal.PublicKey,
group *emath.GqGroup,
) bool {
N := C.Size()
_, n := GetMatrixDimensions(N)
// Regenerate commitment key (deterministic from group)
ck := GenCommitmentKey(n, group)
return VerifyShuffleArgument(vs.Argument, C, vs.ShuffledCiphertexts, pk, ck, group)
}