mirror of
https://github.com/saymrwulf/swisspost-evoting-go-poc.git
synced 2026-05-14 20:58:03 +00:00
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.
116 lines
3.3 KiB
Go
116 lines
3.3 KiB
Go
package math
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
)
|
|
|
|
// ZqElement represents an element of the group Z_q.
|
|
// Immutable: all operations return new instances.
|
|
type ZqElement struct {
|
|
value *big.Int
|
|
group *ZqGroup
|
|
}
|
|
|
|
// NewZqElement creates a ZqElement from a value, validating group membership.
|
|
func NewZqElement(value *big.Int, group *ZqGroup) (ZqElement, error) {
|
|
if value == nil || group == nil {
|
|
return ZqElement{}, fmt.Errorf("value and group must not be nil")
|
|
}
|
|
if !group.IsGroupMember(value) {
|
|
return ZqElement{}, fmt.Errorf("value %v is not a member of Z_q (q=%v)", value, group.q)
|
|
}
|
|
return ZqElement{value: new(big.Int).Set(value), group: group}, nil
|
|
}
|
|
|
|
// NewZqElementFromInt creates a ZqElement from an int.
|
|
func NewZqElementFromInt(value int, group *ZqGroup) (ZqElement, error) {
|
|
return NewZqElement(big.NewInt(int64(value)), group)
|
|
}
|
|
|
|
// zqElementUnchecked creates a ZqElement without validation.
|
|
func zqElementUnchecked(value *big.Int, group *ZqGroup) ZqElement {
|
|
return ZqElement{value: new(big.Int).Set(value), group: group}
|
|
}
|
|
|
|
// Value returns the element's value as a new big.Int.
|
|
func (e ZqElement) Value() *big.Int {
|
|
return new(big.Int).Set(e.value)
|
|
}
|
|
|
|
// Group returns the element's group.
|
|
func (e ZqElement) Group() *ZqGroup {
|
|
return e.group
|
|
}
|
|
|
|
// Add returns (this + other) mod q.
|
|
func (e ZqElement) Add(other ZqElement) ZqElement {
|
|
e.checkSameGroup(other)
|
|
result := new(big.Int).Add(e.value, other.value)
|
|
result.Mod(result, e.group.q)
|
|
return ZqElement{value: result, group: e.group}
|
|
}
|
|
|
|
// Subtract returns (this - other) mod q.
|
|
func (e ZqElement) Subtract(other ZqElement) ZqElement {
|
|
e.checkSameGroup(other)
|
|
result := new(big.Int).Sub(e.value, other.value)
|
|
result.Mod(result, e.group.q)
|
|
return ZqElement{value: result, group: e.group}
|
|
}
|
|
|
|
// Multiply returns (this * other) mod q.
|
|
func (e ZqElement) Multiply(other ZqElement) ZqElement {
|
|
e.checkSameGroup(other)
|
|
result := new(big.Int).Mul(e.value, other.value)
|
|
result.Mod(result, e.group.q)
|
|
return ZqElement{value: result, group: e.group}
|
|
}
|
|
|
|
// Exponentiate returns this^exponent mod q.
|
|
func (e ZqElement) Exponentiate(exponent *big.Int) ZqElement {
|
|
if exponent.Sign() < 0 {
|
|
panic("exponent must be non-negative")
|
|
}
|
|
result := new(big.Int).Exp(e.value, exponent, e.group.q)
|
|
return ZqElement{value: result, group: e.group}
|
|
}
|
|
|
|
// Negate returns (-this) mod q.
|
|
func (e ZqElement) Negate() ZqElement {
|
|
if e.value.Sign() == 0 {
|
|
return ZqElement{value: big.NewInt(0), group: e.group}
|
|
}
|
|
result := new(big.Int).Sub(e.group.q, e.value)
|
|
return ZqElement{value: result, group: e.group}
|
|
}
|
|
|
|
// Invert returns this^(-1) mod q.
|
|
func (e ZqElement) Invert() ZqElement {
|
|
if e.value.Sign() == 0 {
|
|
panic("cannot invert zero element")
|
|
}
|
|
result := new(big.Int).ModInverse(e.value, e.group.q)
|
|
return ZqElement{value: result, group: e.group}
|
|
}
|
|
|
|
// IsZero checks if this element is zero.
|
|
func (e ZqElement) IsZero() bool {
|
|
return e.value.Sign() == 0
|
|
}
|
|
|
|
// Equals checks if two elements have the same value and group.
|
|
func (e ZqElement) Equals(other ZqElement) bool {
|
|
return e.value.Cmp(other.value) == 0 && e.group.Equals(other.group)
|
|
}
|
|
|
|
func (e ZqElement) checkSameGroup(other ZqElement) {
|
|
if !e.group.Equals(other.group) {
|
|
panic(fmt.Sprintf("elements must be from the same group"))
|
|
}
|
|
}
|
|
|
|
// String returns the string representation.
|
|
func (e ZqElement) String() string {
|
|
return e.value.String()
|
|
}
|