go-dvote/service/censusmanager.go
service/censusmanager.goRaw
1package censusmanager
2
3import (
4 "encoding/json"
5 "fmt"
6 "log"
7 "net/http"
8 "strconv"
9 "strings"
10 "time"
11
12 tree "github.com/vocdoni/dvote-census/tree"
13 signature "github.com/vocdoni/dvote-relay/crypto/signature"
14)
15
16const authTimeWindow = 10 // Time window (seconds) in which TimeStamp will be accepted if auth enabled
17var MkTrees map[string]*tree.Tree // MerkleTree dvote-census library
18var Signatures map[string]string
19var Signature signature.SignKeys // Signature dvote-relay library
20
21type Claim struct {
22 CensusID string `json:"censusID"` // References to MerkleTree namespace
23 ClaimData string `json:"claimData"` // Data to add to the MerkleTree
24 ProofData string `json:"proofData"` // MerkleProof to check
25 TimeStamp string `json:"timeStamp"` // Unix TimeStamp in seconds
26 Signature string `json:"signature"` // Signature as Hexadecimal String
27}
28
29type Result struct {
30 Error bool `json:"error"`
31 Response string `json:"response"`
32}
33
34func AddNamespace(name, pubKey string) {
35 if len(MkTrees) == 0 {
36 MkTrees = make(map[string]*tree.Tree)
37 }
38 if len(Signatures) == 0 {
39 Signatures = make(map[string]string)
40 }
41
42 mkTree := tree.Tree{}
43 mkTree.Init(name)
44 MkTrees[name] = &mkTree
45 Signatures[name] = pubKey
46}
47
48func reply(resp *Result, w http.ResponseWriter) {
49 err := json.NewEncoder(w).Encode(resp)
50 if err != nil {
51 http.Error(w, err.Error(), 500)
52 } else {
53 w.Header().Set("content-type", "application/json")
54 }
55}
56
57func checkRequest(w http.ResponseWriter, req *http.Request) bool {
58 if req.Body == nil {
59 http.Error(w, "Please send a request body", 400)
60 return false
61 }
62 return true
63}
64
65func checkAuth(timestamp, signature, pubKey, message string) bool {
66 if len(pubKey) < 1 {
67 return true
68 }
69 currentTime := int64(time.Now().Unix())
70 timeStampRemote, err := strconv.ParseInt(timestamp, 10, 32)
71 if err != nil {
72 log.Printf("Cannot parse timestamp data %s\n", err)
73 return false
74 }
75 if timeStampRemote < currentTime+authTimeWindow &&
76 timeStampRemote > currentTime-authTimeWindow {
77 v, err := Signature.Verify(message, signature, pubKey)
78 if err != nil {
79 log.Printf("Verification error: %s\n", err)
80 }
81 return v
82 }
83 return false
84}
85
86func claimHandler(w http.ResponseWriter, req *http.Request, op string) {
87 var c Claim
88 var resp Result
89
90 if ok := checkRequest(w, req); !ok {
91 return
92 }
93 // Decode JSON
94 err := json.NewDecoder(req.Body).Decode(&c)
95 if err != nil {
96 http.Error(w, err.Error(), 400)
97 return
98 }
99
100 // Process data
101 log.Printf("Received censusID:{%s} claimData:{%s} proofData:{%s} timeStamp:{%s} signature:{%s}\n",
102 c.CensusID, c.ClaimData, c.ProofData, c.TimeStamp, c.Signature)
103 resp.Error = false
104 resp.Response = ""
105 censusFound := false
106 if len(c.CensusID) > 0 {
107 _, censusFound = MkTrees[c.CensusID]
108 }
109 if !censusFound {
110 resp.Error = true
111 resp.Response = "censusID not valid or not found"
112 reply(&resp, w)
113 return
114 }
115
116 if op == "add" {
117 msg := fmt.Sprintf("%s%s%s", c.CensusID, c.ClaimData, c.TimeStamp)
118 if auth := checkAuth(c.TimeStamp, c.Signature, Signatures[c.CensusID], msg); auth {
119 if strings.HasPrefix(c.CensusID, "0x") {
120 resp.Error = true
121 resp.Response = "add claim to snapshot is not allowed"
122 } else {
123 err = MkTrees[c.CensusID].AddClaim([]byte(c.ClaimData))
124 }
125 } else {
126 resp.Error = true
127 resp.Response = "invalid authentication"
128 }
129 }
130
131 if op == "gen" {
132 resp.Response, err = MkTrees[c.CensusID].GenProof([]byte(c.ClaimData))
133 }
134
135 if op == "root" {
136 resp.Response = MkTrees[c.CensusID].GetRoot()
137 }
138
139 if op == "dump" {
140 values, err := MkTrees[c.CensusID].Dump()
141 if err != nil {
142 resp.Error = true
143 resp.Response = fmt.Sprint(err)
144 } else {
145 jValues, err := json.Marshal(values)
146 if err != nil {
147 resp.Error = true
148 resp.Response = fmt.Sprint(err)
149 } else {
150 resp.Response = string(jValues)
151 }
152 }
153 }
154
155 if op == "snapshot" {
156 msg := fmt.Sprintf("%s%s%s", c.CensusID, c.ClaimData, c.TimeStamp)
157 if auth := checkAuth(c.TimeStamp, c.Signature, Signatures[c.CensusID], msg); auth {
158 if strings.HasPrefix(c.CensusID, "0x") {
159 resp.Error = true
160 resp.Response = "snapshot an snapshot makes no sense"
161 } else {
162 snapshotNamespace, err := MkTrees[c.CensusID].Snapshot()
163 if err != nil {
164 resp.Error = true
165 resp.Response = fmt.Sprint(err)
166 } else {
167 resp.Response = snapshotNamespace
168 }
169 }
170 } else {
171 resp.Error = true
172 resp.Response = "invalid authentication"
173 }
174 }
175
176 if op == "check" {
177 if len(c.ProofData) < 1 {
178 resp.Error = true
179 resp.Response = "proofData not provided"
180 } else {
181 validProof, _ := MkTrees[c.CensusID].CheckProof([]byte(c.ClaimData), c.ProofData)
182 if validProof {
183 resp.Response = "valid"
184 } else {
185 resp.Response = "invalid"
186 }
187 }
188 }
189
190 reply(&resp, w)
191}
192
193func Listen(port int, proto string) {
194 srv := &http.Server{
195 Addr: fmt.Sprintf(":%d", port),
196 ReadHeaderTimeout: 4 * time.Second,
197 ReadTimeout: 4 * time.Second,
198 WriteTimeout: 4 * time.Second,
199 IdleTimeout: 3 * time.Second,
200 }
201
202 http.HandleFunc("/addClaim", func(w http.ResponseWriter, r *http.Request) {
203 claimHandler(w, r, "add")
204 })
205 http.HandleFunc("/genProof", func(w http.ResponseWriter, r *http.Request) {
206 claimHandler(w, r, "gen")
207 })
208 http.HandleFunc("/checkProof", func(w http.ResponseWriter, r *http.Request) {
209 claimHandler(w, r, "check")
210 })
211 http.HandleFunc("/getRoot", func(w http.ResponseWriter, r *http.Request) {
212 claimHandler(w, r, "root")
213 })
214 http.HandleFunc("/snapshot", func(w http.ResponseWriter, r *http.Request) {
215 claimHandler(w, r, "snapshot")
216 })
217 http.HandleFunc("/dump", func(w http.ResponseWriter, r *http.Request) {
218 claimHandler(w, r, "dump")
219 })
220
221 if proto == "https" {
222 log.Print("Starting server in https mode")
223 if err := srv.ListenAndServeTLS("server.crt", "server.key"); err != nil {
224 panic(err)
225 }
226 }
227 if proto == "http" {
228 log.Print("Starting server in http mode")
229 srv.SetKeepAlivesEnabled(false)
230 if err := srv.ListenAndServe(); err != nil {
231 panic(err)
232 }
233 }
234}