aboutsummaryrefslogtreecommitdiff
path: root/vote/vote.go
diff options
context:
space:
mode:
Diffstat (limited to 'vote/vote.go')
-rw-r--r--vote/vote.go223
1 files changed, 118 insertions, 105 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()
}