diff options
author | Özgür Kesim <oec@kesim.org> | 2024-03-19 21:08:48 +0100 |
---|---|---|
committer | Özgür Kesim <oec@kesim.org> | 2024-03-19 21:08:48 +0100 |
commit | d0560b68aec9528181ba57f24f4eaa35401246bb (patch) | |
tree | edb543dd39f50b0ae819e61bc9327b45cfb2ce79 | |
parent | e92c9886af82d5d9bc64b715677588e2643271bd (diff) |
vote: proof generation and verification added
Generation of a Commitment is implemented, together with the
generation and verification of proofs, i.e. their Schnorr's signatures
-rw-r--r-- | avnet/avnet.go | 123 | ||||
-rw-r--r-- | vote/vote.go | 191 | ||||
-rw-r--r-- | vote/vote_test.go (renamed from avnet/avnet_test.go) | 9 |
3 files changed, 197 insertions, 126 deletions
diff --git a/avnet/avnet.go b/avnet/avnet.go deleted file mode 100644 index e40759f..0000000 --- a/avnet/avnet.go +++ /dev/null @@ -1,123 +0,0 @@ -package avnet - -import ( - "crypto/rand" - "crypto/sha256" - "encoding/base32" - "encoding/binary" - "fmt" - "io" - - curve "filippo.io/edwards25519" -) - -var b32 = base32.StdEncoding.WithPadding(base32.NoPadding) - -// A Schnorr signature to prove knowledge of x for given g^x and i. - -type Proof struct { - PointV curve.Point - ScalarR curve.Scalar - I uint64 -} - -type Commitment struct { - PubX curve.Point - PubR curve.Point - ProofX Proof - ProofR Proof -} - -type Vote struct { - bit bool - - privX curve.Scalar - privR curve.Scalar - - Commitment -} - -func newPriv(s *curve.Scalar, random io.Reader) error { - var buf [64]byte - if random == nil { - random = rand.Reader - } - random.Read(buf[:]) - _, e := s.SetUniformBytes(buf[:]) - return e -} - -func setPub(p *curve.Scalar, P *curve.Point) *curve.Point { - return P.ScalarBaseMult(p) -} - -// Generates the proof, aka Schnorr signature, for given priv 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) -func proof(pr *Proof, x *curve.Scalar, i uint64) error { - pr.I = i - var v curve.Scalar - e := newPriv(&v, nil) - if e != nil { - return e - } - setPub(&v, &pr.PointV) - gx := curve.Point{} - setPub(x, &gx) - - // Calculate h := H(g, g^v, g^x, i) - h256 := sha256.New() - h256.Write(curve.NewGeneratorPoint().Bytes()) - h256.Write(pr.PointV.Bytes()) - h256.Write(gx.Bytes()) - e = binary.Write(h256, binary.BigEndian, i) - if e != nil { - return e - } - // h := h256.Bytes() - - // TODO: calculate r - return fmt.Errorf("proof not implemented") - -} - -func newVoteWithRand(bit bool, rand io.Reader) (vote *Vote, e error) { - vote = &Vote{ - bit: bit, - } - - e = newPriv(&vote.privX, rand) - if e != nil { - return nil, e - } - e = newPriv(&vote.privR, rand) - if e != nil { - return nil, e - } - - setPub(&vote.privX, &vote.Commitment.PubX) - setPub(&vote.privR, &vote.Commitment.PubR) - - return vote, nil -} - -func NewVote(bit bool) (vote *Vote, e error) { - return newVoteWithRand(bit, nil) -} - -func pubStr(p *curve.Point) string { - return b32.EncodeToString(p.Bytes()) -} - -func (c *Commitment) String() string { - return fmt.Sprintf(`{"PubX": "%s", "PubR": "%s"}`, pubStr(&c.PubX), pubStr(&c.PubR)) -} - -func (c *Commitment) MarshalJSON() ([]byte, error) { - s := c.String() - return []byte(s), nil -}
\ No newline at end of file diff --git a/vote/vote.go b/vote/vote.go new file mode 100644 index 0000000..2ca35a6 --- /dev/null +++ b/vote/vote.go @@ -0,0 +1,191 @@ +package vote + +import ( + "crypto/rand" + "crypto/sha512" + "encoding/base32" + "encoding/binary" + "fmt" + "io" + + curve "filippo.io/edwards25519" +) + +var b32 = base32.StdEncoding.WithPadding(base32.NoPadding) + +// A Schnorr signature to prove knowledge of x for given g^x and i. + +type Proof struct { + PointV curve.Point + ScalarR curve.Scalar + I uint64 +} + +type Commitment struct { + Index uint64 + PubX curve.Point + PubR curve.Point + ProofX Proof + ProofR Proof +} + +type Vote struct { + bit bool + + privX curve.Scalar + privR curve.Scalar + + Commitment +} + +func newPriv(s *curve.Scalar, random io.Reader) error { + var buf [64]byte + if random == nil { + random = rand.Reader + } + random.Read(buf[:]) + _, e := s.SetUniformBytes(buf[:]) + return e +} + +func setPub(p *curve.Scalar, P *curve.Point) *curve.Point { + return P.ScalarBaseMult(p) +} + +// Generates the proof, aka Schnorr signature, for given priv 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) +func genProof(pr *Proof, x *curve.Scalar, i uint64) error { + pr.I = i + var v = new(curve.Scalar) + e := newPriv(v, nil) + if e != nil { + return e + } + setPub(v, &pr.PointV) + gx := new(curve.Point) + setPub(x, gx) + + // Calculate h := H(g, g^v, g^x, i) + h, e := hash(&pr.PointV, gx, i) + + if e != nil { + return e + } + + // Calculate r := v - x*h + xh := new(curve.Scalar).Multiply(x, h) + (&pr.ScalarR).Subtract(v, xh) + + return nil +} + +// Calculate h := H(g, g^v, g^x, i) +func hash(gv, gx *curve.Point, i uint64) (*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) + + hb := h512.Sum(nil) + return new(curve.Scalar).SetUniformBytes(hb) +} + +func (v *Vote) genProofs() (e error) { + e = genProof(&v.ProofX, &v.privX, v.Index) + if e != nil { + return e + } + return genProof(&v.ProofR, &v.privR, v.Index) +} + +// 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) + setPub(x, gx) + h, e := hash(V, gx, i) + if e != nil { + return false + } + + // Calculate g^(x*h) + xh := new(curve.Scalar).Multiply(x, h) + gxh := new(curve.Point) + setPub(xh, gxh) + + // Calculate g^r + gr := new(curve.Point) + setPub(r, gr) + + // Calculate g^r*g^(x*h) + // Note that the edwards25519 package uses Addtion as the group operation + grgxh := new(curve.Point).Add(gr, gxh) + + return V.Equal(grgxh) == 1 +} + +func combineErr(e1, e2 error) error { + if e1 == nil && e2 == nil { + return nil + } else if e1 != nil { + if e2 == nil { + return e1 + } + return fmt.Errorf("%v and %v", e1, e2) + } + return e2 +} + +// Verify checks for both, ProofX and ProofY that +// TODO +func (v *Vote) VerifyProofs() (ok bool) { + okX := verifyProof(&v.ProofX.PointV, &v.ProofX.ScalarR, &v.privX, v.Index) + okR := verifyProof(&v.ProofR.PointV, &v.ProofR.ScalarR, &v.privR, v.Index) + return okX && okR +} + +func newVoteWithRand(bit bool, index uint64, rand io.Reader) (vote *Vote, e error) { + vote = &Vote{ + bit: bit, + } + vote.Commitment.Index = index + + e = newPriv(&vote.privX, rand) + if e != nil { + return nil, e + } + e = newPriv(&vote.privR, rand) + if e != nil { + return nil, e + } + + setPub(&vote.privX, &vote.Commitment.PubX) + setPub(&vote.privR, &vote.Commitment.PubR) + + e = vote.genProofs() + + return vote, nil +} + +func NewVote(bit bool, index uint64) (vote *Vote, e error) { + return newVoteWithRand(bit, index, nil) +} + +func pubStr(p *curve.Point) string { + return b32.EncodeToString(p.Bytes()) +} + +func (c *Commitment) String() string { + return fmt.Sprintf(`{"PubX": "%s", "PubR": "%s"}`, pubStr(&c.PubX), pubStr(&c.PubR)) +} + +func (c *Commitment) MarshalJSON() ([]byte, error) { + s := c.String() + return []byte(s), nil +} diff --git a/avnet/avnet_test.go b/vote/vote_test.go index 8800b8b..7a00ee3 100644 --- a/avnet/avnet_test.go +++ b/vote/vote_test.go @@ -1,11 +1,11 @@ -package avnet +package vote import ( "testing" ) func TestRound(t *testing.T) { - v, e := newVoteWithRand(false, nil) + v, e := newVoteWithRand(false, 0, nil) if e != nil { t.Fatalf("unexpected error: %v", e) @@ -13,4 +13,7 @@ func TestRound(t *testing.T) { if v.bit { t.Fatal("expected vote false, but got true") } -}
\ No newline at end of file + if !v.VerifyProofs() { + t.Fatalf("Proofs not correct! %+v", v) + } +} |