Skip to content

Commit 1d37c7e

Browse files
committedJun 28, 2023
Adding evoting-admin commands
Showing all elections Allow voting if the private key is known
1 parent 10a13c0 commit 1d37c7e

File tree

3 files changed

+146
-37
lines changed

3 files changed

+146
-37
lines changed
 

‎evoting/api.go

+17-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package evoting
44

55
import (
6+
"go.dedis.ch/cothority/v3/skipchain"
67
"go.dedis.ch/onet/v3"
78

89
"go.dedis.ch/cothority/v3"
@@ -16,16 +17,17 @@ type Client struct {
1617
*onet.Client
1718
// If LookupURL is set, use it for SCIPER lookups (for tests).
1819
LookupURL string
20+
Roster *onet.Roster
1921
}
2022

2123
// NewClient instantiates a new evoting.Client.
22-
func NewClient() *Client {
23-
return &Client{Client: onet.NewClient(cothority.Suite, ServiceName)}
24+
func NewClient(roster *onet.Roster) *Client {
25+
return &Client{Client: onet.NewClient(cothority.Suite, ServiceName), Roster: roster}
2426
}
2527

2628
// Ping a random server which increments the nonce.
27-
func (c *Client) Ping(roster *onet.Roster, nonce uint32) (*Ping, error) {
28-
dest := roster.RandomServerIdentity()
29+
func (c *Client) Ping(nonce uint32) (*Ping, error) {
30+
dest := c.Roster.RandomServerIdentity()
2931
reply := &Ping{}
3032
if err := c.SendProtobuf(dest, &Ping{Nonce: nonce}, reply); err != nil {
3133
return nil, err
@@ -34,8 +36,17 @@ func (c *Client) Ping(roster *onet.Roster, nonce uint32) (*Ping, error) {
3436
}
3537

3638
// LookupSciper returns information about a sciper number.
37-
func (c *Client) LookupSciper(roster *onet.Roster, sciper string) (reply *LookupSciperReply, err error) {
39+
func (c *Client) LookupSciper(sciper string) (reply *LookupSciperReply, err error) {
3840
reply = &LookupSciperReply{}
39-
err = c.SendProtobuf(roster.RandomServerIdentity(), &LookupSciper{Sciper: sciper, LookupURL: c.LookupURL}, reply)
41+
err = c.SendProtobuf(c.Roster.RandomServerIdentity(), &LookupSciper{Sciper: sciper, LookupURL: c.LookupURL}, reply)
42+
return
43+
}
44+
45+
// Reconstruct returns the reconstructed votes.
46+
// If the election is not yet finished, it will return an error
47+
func (c *Client) Reconstruct(id skipchain.SkipBlockID) (rr ReconstructReply, err error) {
48+
err = c.SendProtobuf(c.Roster.List[0], &Reconstruct{
49+
ID: id,
50+
}, &rr)
4051
return
4152
}

‎evoting/api_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func TestPing(t *testing.T) {
2323

2424
_, roster, _ := local.GenTree(3, true)
2525

26-
c := evoting.NewClient()
27-
r, _ := c.Ping(roster, 0)
26+
c := evoting.NewClient(roster)
27+
r, _ := c.Ping(0)
2828
assert.Equal(t, uint32(1), r.Nonce)
2929
}

‎evoting/evoting-admin/app.go

+127-29
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"encoding/json"
88
"flag"
99
"fmt"
10+
"go.dedis.ch/cothority/v3/evoting/lib"
1011
"os"
1112
"strconv"
1213
"strings"
@@ -23,56 +24,104 @@ import (
2324
)
2425

2526
var (
26-
argRoster = flag.String("roster", "", "path to roster toml file")
27-
argAdmins = flag.String("admins", "", "list of admin users")
28-
argPin = flag.String("pin", "", "service pin")
29-
argKey = flag.String("key", "", "public key of authentication server")
30-
argID = flag.String("id", "", "ID of the master chain to modify (optional)")
31-
argUser = flag.Int("user", 0, "The SCIPER of an existing admin of this chain")
32-
argSig = flag.String("sig", "", "A signature proving that you can login to Tequila with the given SCIPER.")
33-
argShow = flag.Bool("show", false, "Show the current Master config")
34-
argDumpVoters = flag.Bool("dumpvoters", false, "Dump a list of voters for election skipchain specified with -id (ballot de-duplication has already been taken into account, order is preserved)")
35-
argDumpElection = flag.Bool("dumpelection", false, "Dump the current election config for the election specified with -id.")
36-
argJSON = flag.Bool("json", false, "Dump in json mode.")
37-
argLoad = flag.String("load", "", "Load the specified json file to modify the election specified with -id.")
27+
argRoster = flag.String("roster", "", "path to roster toml file")
28+
argAdmins = flag.String("admins", "", "list of admin users")
29+
argPin = flag.String("pin", "", "service pin")
30+
argKey = flag.String("key", "", "public key of authentication server")
31+
argPrivate = flag.String("private", "", "private key of authentication server for voting")
32+
argID = flag.String("id", "", "ID of the master chain to modify (optional)")
33+
argUser = flag.Int("user", 0, "The SCIPER of an existing admin of this chain")
34+
argSig = flag.String("sig", "", "A signature proving that you can login to Tequila with the given SCIPER.")
35+
argShow = flag.Bool("show", false, "Show the current Master config")
36+
argDumpVoters = flag.Bool("dumpvoters", false, "Dump a list of voters for election skipchain specified with -id (ballot de-duplication has already been taken into account, order is preserved)")
37+
argDumpElection = flag.Bool("dumpelection", false, "Dump the current election config for the election specified with -id.")
38+
argJSON = flag.Bool("json", false, "Dump in json mode.")
39+
argLoad = flag.String("load", "", "Load the specified json file to modify the election specified with -id.")
40+
argVoteCandidates = flag.String("voteCandidates", "", "Coma delimited list of SCIPERs to vote for")
41+
argDebug = flag.Int("debug", 0, "Debugging level")
3842
)
3943

4044
func main() {
4145
flag.Parse()
4246

47+
if *argDebug > 0 {
48+
log.SetDebugVisible(*argDebug)
49+
}
50+
4351
if *argRoster == "" {
4452
log.Fatal("Roster argument (-roster) is required for create, update, or show.")
4553
}
4654
roster, err := parseRoster(*argRoster)
4755
if err != nil {
4856
log.Fatal("cannot parse roster: ", err)
4957
}
58+
client := onet.NewClient(cothority.Suite, evoting.ServiceName)
59+
cl := evoting.NewClient(roster)
60+
61+
var pub kyber.Point
62+
var priv kyber.Scalar
63+
if *argPrivate != "" {
64+
// If we get the private key, calculate the corresponding public key
65+
b, err := hex.DecodeString(*argPrivate)
66+
log.ErrFatal(err, "while parsing private key")
67+
68+
log.Lvl1("Setting the private key")
69+
priv = cothority.Suite.Scalar()
70+
err = priv.UnmarshalBinary(b)
71+
log.ErrFatal(err, "while unmarshalling private key")
72+
pub = cothority.Suite.Point().Mul(priv, nil)
73+
} else if *argKey != "" {
74+
// If we only get the public key
75+
pub, err = parseKey(*argKey)
76+
if err != nil {
77+
log.Fatal("cannot parse key: ", err)
78+
}
79+
} else {
80+
// If we get no key, create them again
81+
kp := key.NewKeyPair(cothority.Suite)
82+
priv = kp.Private
83+
pub = kp.Public
84+
}
5085

5186
if *argShow {
87+
if *argID == "" {
88+
log.Fatal("Please give ID of master chain")
89+
}
5290
id, err := hex.DecodeString(*argID)
5391
if err != nil {
5492
log.Fatal("id decode", err)
5593
}
5694
request := &evoting.GetElections{Master: id}
95+
if priv != nil && *argUser > 0 {
96+
request.User = uint32(*argUser)
97+
request.Signature = lib.GenerateSignature(priv, id, request.User)
98+
} else {
99+
fmt.Println("You can give '-private PRIVATE_KEY -user ADMINID' for a list of available elections to this user")
100+
}
57101
reply := &evoting.GetElectionsReply{}
58-
client := onet.NewClient(cothority.Suite, evoting.ServiceName)
59102
if err = client.SendProtobuf(roster.List[0], request, reply); err != nil {
60103
log.Fatal("get elections request: ", err)
61104
}
62105
m := reply.Master
63106
fmt.Printf(" Admins: %v\n", m.Admins)
64107
fmt.Printf(" Roster: %v\n", m.Roster.List)
65108
fmt.Printf(" Key: %v\n", m.Key)
109+
for _, election := range reply.Elections {
110+
fmt.Printf("\nElection: %s\n", election.Name)
111+
fmt.Printf(" ID: %x\n", election.ID)
112+
}
66113
return
67114
}
68115

69116
if *argDumpVoters {
117+
if *argID == "" {
118+
log.Fatal("Please give ID of master chain")
119+
}
70120
id, err := hex.DecodeString(*argID)
71121
if err != nil {
72122
log.Fatal("id decode", err)
73123
}
74124
reply := &evoting.GetBoxReply{}
75-
client := onet.NewClient(cothority.Suite, evoting.ServiceName)
76125
if err = client.SendProtobuf(roster.List[0], &evoting.GetBox{ID: id}, reply); err != nil {
77126
log.Fatal("get box request: ", err)
78127
}
@@ -84,12 +133,14 @@ func main() {
84133
}
85134

86135
if *argDumpElection {
136+
if *argID == "" {
137+
log.Fatal("Please give an ID")
138+
}
87139
id, err := hex.DecodeString(*argID)
88140
if err != nil {
89141
log.Fatal("id decode", err)
90142
}
91143
reply := &evoting.GetBoxReply{}
92-
client := onet.NewClient(cothority.Suite, evoting.ServiceName)
93144
if err = client.SendProtobuf(roster.List[0], &evoting.GetBox{ID: id}, reply); err != nil {
94145
log.Fatal("get box request: ", err)
95146
}
@@ -123,19 +174,34 @@ func main() {
123174
out.WriteTo(os.Stdout)
124175
} else {
125176
fmt.Println(reply.Election)
177+
if reply.Election.Stage == lib.Decrypted {
178+
reconstructReply, err := cl.Reconstruct(id)
179+
log.ErrFatal(err, "While getting reconstructed votes")
180+
for i, p := range reconstructReply.Points {
181+
d, _ := p.Data()
182+
fmt.Printf("Vote %d with data %x", i, d)
183+
for _, p := range reconstructReply.AdditionalPoints[i].AdditionalPoints {
184+
d, _ := p.Data()
185+
fmt.Printf(",%x", d)
186+
}
187+
fmt.Println()
188+
}
189+
}
126190
}
127191
return
128192
}
129193

130194
if *argLoad != "" {
195+
if *argID == "" {
196+
log.Fatal("Please give ID of master chain")
197+
}
131198
id, err := hex.DecodeString(*argID)
132199
if err != nil {
133200
log.Fatal("id decode", err)
134201
}
135202

136203
// Look up the election
137204
reply := &evoting.GetBoxReply{}
138-
client := onet.NewClient(cothority.Suite, evoting.ServiceName)
139205
if err = client.SendProtobuf(roster.List[0], &evoting.GetBox{ID: id}, reply); err != nil {
140206
log.Fatal("get box request:", err)
141207
}
@@ -194,6 +260,50 @@ func main() {
194260
return
195261
}
196262

263+
if len(*argVoteCandidates) > 0 {
264+
voteUser := uint32(*argUser)
265+
if len(*argID) != 64 {
266+
log.Fatal("Need an ID of the election chain in hex form of 32 bytes")
267+
}
268+
voteID, err := hex.DecodeString(*argID)
269+
log.ErrFatal(err, "Wrong ID for election chain")
270+
271+
log.Lvl1("Getting election information from the chain")
272+
reply := &evoting.GetBoxReply{}
273+
if err = client.SendProtobuf(roster.List[0], &evoting.GetBox{ID: voteID}, reply); err != nil {
274+
log.Fatal("get box request:", err)
275+
}
276+
277+
if len(*argVoteCandidates) < 6 {
278+
log.Fatal("Need at least one candidate to vote for")
279+
}
280+
voteCandidatesStrings := strings.Split(*argVoteCandidates, ",")
281+
voteCandidates := make([]uint32, len(voteCandidatesStrings))
282+
for i, c := range voteCandidatesStrings {
283+
cand, err := strconv.Atoi(c)
284+
log.ErrFatal(err, "Wrong SCIPER ID for candidate")
285+
voteCandidates[i] = uint32(cand)
286+
}
287+
288+
if priv == nil {
289+
log.Fatal("Need the private key to vote")
290+
}
291+
292+
fmt.Printf("Votes are: %+v\n", voteCandidates)
293+
294+
ballot := lib.CreateBallot(reply.Election.MaxChoices, reply.Election.Key, voteUser, voteCandidates)
295+
request := &evoting.Cast{
296+
ID: voteID,
297+
User: voteUser,
298+
Ballot: &ballot,
299+
Signature: lib.GenerateSignature(priv, reply.Election.Master, voteUser),
300+
}
301+
replyCast := &evoting.CastReply{}
302+
log.ErrFatal(client.SendProtobuf(roster.List[0], request, replyCast))
303+
log.Info("Successfully cast this vote")
304+
return
305+
}
306+
197307
if *argAdmins == "" {
198308
log.Fatal("Admin list (-admins) must have at least one id.")
199309
}
@@ -207,18 +317,6 @@ func main() {
207317
log.Fatal("pin must be set for create and update operations.")
208318
}
209319

210-
var pub kyber.Point
211-
if *argKey != "" {
212-
pub, err = parseKey(*argKey)
213-
if err != nil {
214-
log.Fatal("cannot parse key: ", err)
215-
}
216-
} else {
217-
kp := key.NewKeyPair(cothority.Suite)
218-
log.Infof("Auth-server private key: %v", kp.Private)
219-
pub = kp.Public
220-
}
221-
222320
request := &evoting.Link{Pin: *argPin, Roster: roster, Key: pub, Admins: admins}
223321
if *argID != "" {
224322
id, err := hex.DecodeString(*argID)
@@ -241,11 +339,11 @@ func main() {
241339
}
242340
reply := &evoting.LinkReply{}
243341

244-
client := onet.NewClient(cothority.Suite, evoting.ServiceName)
245342
if err = client.SendProtobuf(roster.List[0], request, reply); err != nil {
246343
log.Fatal("link request: ", err)
247344
}
248345

346+
log.Infof("Auth-server private key: %v", priv)
249347
log.Infof("Auth-server public key: %v", pub)
250348
log.Infof("Master ID: %x", reply.ID)
251349
}

0 commit comments

Comments
 (0)
Please sign in to comment.