diff options
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 |
commit | 60f58c25788c273caddd36dfc3bce44757170a67 (patch) | |
tree | cdf39e063b217a328d86973b4d11379dde08cbef | |
parent | 1184e02e6f8b6d8f3fd9881f29b7f2d646088088 (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.go | 223 | ||||
-rw-r--r-- | vote/vote_test.go | 4 |
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) } } |