aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÖzgür Kesim <oec@codeblau.de>2024-03-20 14:48:25 +0100
committerÖzgür Kesim <oec@codeblau.de>2024-03-20 14:48:25 +0100
commit60f58c25788c273caddd36dfc3bce44757170a67 (patch)
treecdf39e063b217a328d86973b4d11379dde08cbef
parent1184e02e6f8b6d8f3fd9881f29b7f2d646088088 (diff)
vote: use a public key as identity
A shortcoming in the design of AV-net and SEAL is that identities of participants are known to them _prior_ to the initial commitment/round 1. F.e. the NIZK proofs as proposed in the papers require the index of a participant as an input parameter to the hash function. We propose to use randomly generated public keys in the same curve as identifiers which can than be used to a) sort the identifiers b) sign commitments and round-data. This allows for a participant to provide their NIKZ proof right away with the commitment. The ordering of participants is implicitly given by the public keys, during the calculations in the rounds.
-rw-r--r--vote/vote.go223
-rw-r--r--vote/vote_test.go4
2 files changed, 120 insertions, 107 deletions
diff --git a/vote/vote.go b/vote/vote.go
index a97a66e..97367c0 100644
--- a/vote/vote.go
+++ b/vote/vote.go
@@ -1,60 +1,75 @@
package vote
import (
+ "bytes"
"crypto/rand"
"crypto/sha512"
- "encoding/base32"
- "encoding/binary"
+ "encoding/base64"
+ "encoding/json"
"fmt"
"io"
curve "filippo.io/edwards25519"
)
-var b32 = base32.StdEncoding.WithPadding(base32.NoPadding)
+var b64 = base64.StdEncoding.WithPadding(base64.NoPadding)
-// A Schnorr signature to prove knowledge of x for given g^x and i.
-
-type Proof struct {
- PV curve.Point
- Sr curve.Scalar
- I uint64
-}
-
-type Commitment struct {
- Index uint64
- Points struct {
- X curve.Point
- R curve.Point
- }
- Proofs struct {
- X Proof
- R Proof
- }
-}
+type point curve.Point
+type scalar curve.Scalar
+// Representation of a vote (true or false) of an individual.
+// The .Commitment is sent as data for round1 of the protocol.
type Vote struct {
bit bool
private struct {
- X curve.Scalar
- R curve.Scalar
+ id *scalar
+ x *scalar
+ r *scalar
}
Commitment
}
-func newPriv(s *curve.Scalar, random io.Reader) error {
+// Commitment represents the public data sent by a participant
+// in round 1 of the protocol.
+type Commitment struct {
+ Id *point `json:"index"`
+ Points struct {
+ X *point
+ R *point
+ } `json:"points"`
+ Proofs struct {
+ X *Proof
+ R *Proof
+ } `json:"proofs"`
+}
+
+// A Schnorr signature to prove knowledge of v for given g^v and i.
+// Choosing a scalar v randomly, the signature consists of (V, r) with
+//
+// V := g^v, with randomly chosen v
+// r := (v - x*h), with h := H(g, g^v, g^x, i), where i is given by the context.
+//
+// Verification of the signature is by comparing V =?= g^r * g^(x*h)
+type Proof struct {
+ PV *point `json:"V"`
+ Sr *scalar `json:"r"`
+ Id *point `json:"id"`
+}
+
+func randomScalar(random io.Reader) (*scalar, error) {
var buf [64]byte
if random == nil {
random = rand.Reader
}
random.Read(buf[:])
- _, e := s.SetUniformBytes(buf[:])
- return e
+ s, e := new(curve.Scalar).SetUniformBytes(buf[:])
+ return (*scalar)(s), e
}
-func setPoint(p *curve.Scalar, P *curve.Point) *curve.Point {
- return P.ScalarBaseMult(p)
+func (s *scalar) point() *point {
+ p := new(curve.Point).ScalarBaseMult((*curve.Scalar)(s))
+ return (*point)(p)
}
// Generates the proof, aka Schnorr signature, for given priv and i.
@@ -64,75 +79,76 @@ func setPoint(p *curve.Scalar, P *curve.Point) *curve.Point {
// r := (v - x*h), with h := H(g, g^v, g^x, i), where i is given by the context.
//
// Verification of the signature is by comparing V =?= g^r * g^(x*h)
-func genProof(pr *Proof, x *curve.Scalar, i uint64) error {
- pr.I = i
- var v = new(curve.Scalar)
- e := newPriv(v, nil)
+func (x *scalar) proof(id *point) (pr *Proof, e error) {
+ pr = &Proof{Id: id}
+
+ // choose random v
+ v, e := randomScalar(nil)
if e != nil {
- return e
+ return nil, e
}
- setPoint(v, &pr.PV)
- gx := new(curve.Point)
- setPoint(x, gx)
- // Calculate h := H(g, g^v, g^x, i)
- h, e := hash(&pr.PV, gx, i)
+ // calculate g^v
+ pr.PV = v.point()
+
+ // calculate g^x
+ gx := x.point()
+ // calculate h := H(g, g^v, g^x, i)
+ h, e := hash(pr.PV, gx, id)
if e != nil {
- return e
+ return nil, e
}
// Calculate r := v - x*h
- xh := new(curve.Scalar).Multiply(x, h)
- (&pr.Sr).Subtract(v, xh)
+ xh := new(curve.Scalar).Multiply((*curve.Scalar)(x), h)
+ r := new(curve.Scalar).Subtract((*curve.Scalar)(v), xh)
+ pr.Sr = (*scalar)(r)
- return nil
+ return pr, nil
}
// Calculate h := H(g, g^v, g^x, i)
-func hash(gv, gx *curve.Point, i uint64) (*curve.Scalar, error) {
+func hash(gv, gx *point, id *point) (*curve.Scalar, error) {
h512 := sha512.New()
h512.Write(curve.NewGeneratorPoint().Bytes())
- h512.Write(gv.Bytes())
- h512.Write(gx.Bytes())
- _ = binary.Write(h512, binary.BigEndian, i)
-
+ h512.Write(((*curve.Point)(gv)).Bytes())
+ h512.Write(((*curve.Point)(gx)).Bytes())
+ h512.Write(((*curve.Point)(id)).Bytes())
hb := h512.Sum(nil)
return new(curve.Scalar).SetUniformBytes(hb)
}
+// Generate the proofs for both, the g^x and g^r points.
func (v *Vote) genProofs() (e error) {
- e = genProof(&v.Proofs.X, &v.private.X, v.Index)
+ v.Proofs.X, e = v.private.x.proof(v.Id)
if e != nil {
return e
}
- return genProof(&v.Proofs.R, &v.private.R, v.Index)
+ v.Proofs.R, e = v.private.r.proof(v.Id)
+ return e
}
// Verifies that g^v == g^r*g^(x*h)
-func verifyProof(V *curve.Point, r, x *curve.Scalar, i uint64) (ok bool) {
- // Calculate h = H(g, g^v, g^x, i)
- gx := new(curve.Point)
- setPoint(x, gx)
- h, e := hash(V, gx, i)
+func verifyProof(V *point, Gx *point, r *scalar, id *point) (ok bool) {
+ // Calculate h = H(g, g^v, g^x, id)
+ h, e := hash(V, Gx, id)
if e != nil {
return false
}
- // Calculate g^(x*h)
- xh := new(curve.Scalar).Multiply(x, h)
- gxh := new(curve.Point)
- setPoint(xh, gxh)
+ // Calculate g^(x*h) = (g^x)^h
+ gxh := new(curve.Point).ScalarMult(h, (*curve.Point)(Gx))
// Calculate g^r
- gr := new(curve.Point)
- setPoint(r, gr)
+ gr := r.point()
// Calculate g^r*g^(x*h)
// Note that the edwards25519 package uses Addtion as the group
- grgxh := new(curve.Point).Add(gr, gxh)
+ grgxh := new(curve.Point).Add((*curve.Point)(gr), gxh)
- return V.Equal(grgxh) == 1
+ // Return true if g^v == g^r*g^(x*h)
+ return ((*curve.Point)(V)).Equal(grgxh) == 1
}
func combineErr(e1, e2 error) error {
@@ -147,74 +163,71 @@ func combineErr(e1, e2 error) error {
return e2
}
-// Verify checks for both, ProofX and ProofY that
-// TODO
+// Verify verifies the proofs for both, g^x and g^r
func (v *Vote) VerifyProofs() (ok bool) {
- okX := verifyProof(&v.Proofs.X.PV, &v.Proofs.X.Sr, &v.private.X, v.Index)
- okR := verifyProof(&v.Proofs.R.PV, &v.Proofs.R.Sr, &v.private.R, v.Index)
+ okX := verifyProof(v.Proofs.X.PV, v.Points.X, v.Proofs.X.Sr, v.Id)
+ okR := verifyProof(v.Proofs.R.PV, v.Points.R, v.Proofs.R.Sr, v.Id)
return okX && okR
}
-func newVoteWithRand(bit bool, index uint64, rand io.Reader) (vote *Vote, e error) {
+// Generates a vote with commitments and proofs and takes the input for
+// the randomness from the given io.Reader
+func newVoteWithRand(bit bool, rand io.Reader) (vote *Vote, e error) {
vote = &Vote{
bit: bit,
}
- vote.Commitment.Index = index
- e = newPriv(&vote.private.X, rand)
+ vote.private.id, e = randomScalar(rand)
if e != nil {
return nil, e
}
- e = newPriv(&vote.private.R, rand)
+ vote.private.x, e = randomScalar(rand)
+ if e != nil {
+ return nil, e
+ }
+ vote.private.r, e = randomScalar(rand)
if e != nil {
return nil, e
}
- setPoint(&vote.private.X, &vote.Commitment.Points.X)
- setPoint(&vote.private.R, &vote.Commitment.Points.R)
+ vote.Commitment.Id = vote.private.id.point()
+ vote.Commitment.Points.X = vote.private.x.point()
+ vote.Commitment.Points.R = vote.private.r.point()
e = vote.genProofs()
return vote, nil
}
-func NewVote(bit bool, index uint64) (vote *Vote, e error) {
- return newVoteWithRand(bit, index, nil)
+// NewVote generates a vote for given bit and index, taking crypt/Reader as
+// source for randomness
+func NewVote(bit bool) (vote *Vote, e error) {
+ return newVoteWithRand(bit, nil)
+}
+
+func (p *point) String() string {
+ return b64.EncodeToString(((*curve.Point)(p)).Bytes())
}
-func ptStr(p *curve.Point) string {
- return b32.EncodeToString(p.Bytes())
+func (s *scalar) String() string {
+ return b64.EncodeToString(((*curve.Scalar)(s)).Bytes())
}
-func scStr(s *curve.Scalar) string {
- return b32.EncodeToString(s.Bytes())
+func (s *scalar) MarshalJSON() ([]byte, error) {
+ return []byte(fmt.Sprintf(`"%s"`, s)), nil
+
+}
+func (p *point) MarshalJSON() ([]byte, error) {
+ return []byte(fmt.Sprintf(`"%s"`, p)), nil
}
func (c *Commitment) String() string {
- return fmt.Sprintf(`{
- "Index": %d,
- "Points": {
- "X": "%s",
- "R": "%s" },
- "Proofs": {
- "X": {
- "PV": "%s",
- "Sr": "%s" },
- "Y": {
- "PV": "%s",
- "Sr": "%s" }
- }
-}`,
- c.Index,
- ptStr(&c.Points.X),
- ptStr(&c.Points.R),
- ptStr(&c.Proofs.X.PV),
- scStr(&c.Proofs.X.Sr),
- ptStr(&c.Proofs.R.PV),
- scStr(&c.Proofs.R.Sr))
-}
-
-func (c *Commitment) MarshalJSON() ([]byte, error) {
- s := c.String()
- return []byte(s), nil
+ buf := &bytes.Buffer{}
+ dec := json.NewEncoder(buf)
+ dec.SetIndent("", " ")
+ e := dec.Encode(c)
+ if e != nil {
+ return fmt.Sprintf("<error encoding: %v>", e)
+ }
+ return buf.String()
}
diff --git a/vote/vote_test.go b/vote/vote_test.go
index 6ff41b8..437dbe5 100644
--- a/vote/vote_test.go
+++ b/vote/vote_test.go
@@ -8,7 +8,7 @@ func TestVoteGeneration(t *testing.T) {
for i := range 100 {
bit := i%3 == 1
- vote, e := newVoteWithRand(bit, uint64(i), nil)
+ vote, e := newVoteWithRand(bit, nil)
if e != nil {
t.Fatalf("unexpected error: %v", e)
@@ -20,6 +20,6 @@ func TestVoteGeneration(t *testing.T) {
t.Fatalf("Proofs not correct! %+v", vote)
}
- t.Logf("Generated %+v\n", vote)
+ t.Logf("Generated %s\n", vote)
}
}