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 "time"
10
11 tree "github.com/vocdoni/dvote-census/tree"
12 signature "github.com/vocdoni/dvote-relay/crypto/signature"
13)
14
15const hashSize = 32
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 RootHash string `json:"rootHash"` // References to MerkleTree rootHash
24 ClaimData string `json:"claimData"` // Data to add to the MerkleTree
25 ProofData string `json:"proofData"` // MerkleProof to check
26 TimeStamp string `json:"timeStamp"` // Unix TimeStamp in seconds
27 Signature string `json:"signature"` // Signature as Hexadecimal String
28}
29
30type Result struct {
31 Error bool `json:"error"`
32 Response string `json:"response"`
33}
34
35func AddNamespace(name, pubKey string) {
36 if len(MkTrees) == 0 {
37 MkTrees = make(map[string]*tree.Tree)
38 }
39 if len(Signatures) == 0 {
40 Signatures = make(map[string]string)
41 }
42
43 mkTree := tree.Tree{}
44 mkTree.Init(name)
45 MkTrees[name] = &mkTree
46 Signatures[name] = pubKey
47}
48
49func reply(resp *Result, w http.ResponseWriter) {
50 err := json.NewEncoder(w).Encode(resp)
51 if err != nil {
52 http.Error(w, err.Error(), 500)
53 } else {
54 w.Header().Set("content-type", "application/json")
55 }
56}
57
58func checkRequest(w http.ResponseWriter, req *http.Request) bool {
59 if req.Body == nil {
60 http.Error(w, "Please send a request body", 400)
61 return false
62 }
63 return true
64}
65
66func checkAuth(timestamp, signature, pubKey, message string) bool {
67 if len(pubKey) < 1 {
68 return true
69 }
70 currentTime := int64(time.Now().Unix())
71 timeStampRemote, err := strconv.ParseInt(timestamp, 10, 32)
72 if err != nil {
73 log.Printf("Cannot parse timestamp data %s\n", err)
74 return false
75 }
76 if timeStampRemote < currentTime+authTimeWindow &&
77 timeStampRemote > currentTime-authTimeWindow {
78 v, err := Signature.Verify(message, signature, pubKey)
79 if err != nil {
80 log.Printf("Verification error: %s\n", err)
81 }
82 return v
83 }
84 return false
85}
86
87func claimHandler(w http.ResponseWriter, req *http.Request, op string) {
88 var c Claim
89 var resp Result
90
91 if ok := checkRequest(w, req); !ok {
92 return
93 }
94 // Decode JSON
95 err := json.NewDecoder(req.Body).Decode(&c)
96 if err != nil {
97 http.Error(w, err.Error(), 400)
98 return
99 }
100
101 // Process data
102 log.Printf("censusId:{%s} rootHash:{%s} claimData:{%s} proofData:{%s} timeStamp:{%s} signature:{%s}\n",
103 c.CensusID, c.RootHash, c.ClaimData, c.ProofData, c.TimeStamp, c.Signature)
104 authString := fmt.Sprintf("%s%s%s%s", c.CensusID, c.RootHash, c.ClaimData, c.TimeStamp)
105 resp.Error = false
106 resp.Response = ""
107 censusFound := false
108 if len(c.CensusID) > 0 {
109 _, censusFound = MkTrees[c.CensusID]
110 }
111 if !censusFound {
112 resp.Error = true
113 resp.Response = "censusId not valid or not found"
114 reply(&resp, w)
115 return
116 }
117
118 if op == "add" {
119 if auth := checkAuth(c.TimeStamp, c.Signature, Signatures[c.CensusID], authString); auth {
120 err = MkTrees[c.CensusID].AddClaim([]byte(c.ClaimData))
121 } else {
122 resp.Error = true
123 resp.Response = "invalid authentication"
124 }
125 }
126
127 if op == "gen" {
128 var t *tree.Tree
129 var err error
130 if len(c.RootHash) > 1 { //if rootHash specified
131 t, err = MkTrees[c.CensusID].Snapshot(c.RootHash)
132 if err != nil {
133 log.Printf("Snapshot error: %s", err.Error())
134 resp.Error = true
135 resp.Response = "invalid root hash"
136 reply(&resp, w)
137 return
138 }
139 } else { //if rootHash not specified use current tree
140 t = MkTrees[c.CensusID]
141 }
142 resp.Response, err = t.GenProof([]byte(c.ClaimData))
143 if err != nil {
144 resp.Error = true
145 resp.Response = err.Error()
146 reply(&resp, w)
147 return
148 }
149 }
150
151 if op == "root" {
152 resp.Response = MkTrees[c.CensusID].GetRoot()
153 }
154
155 if op == "idx" {
156
157 }
158
159 if op == "dump" {
160 var t *tree.Tree
161 if auth := checkAuth(c.TimeStamp, c.Signature, Signatures[c.CensusID], authString); !auth {
162 resp.Error = true
163 resp.Response = "invalid authentication"
164 reply(&resp, w)
165 return
166 }
167
168 if len(c.RootHash) > 1 { //if rootHash specified
169 t, err = MkTrees[c.CensusID].Snapshot(c.RootHash)
170 if err != nil {
171 log.Printf("Snapshot error: %s", err.Error())
172 resp.Error = true
173 resp.Response = "invalid root hash"
174 reply(&resp, w)
175 return
176 }
177 } else { //if rootHash not specified use current merkletree
178 t = MkTrees[c.CensusID]
179 }
180
181 //dump the claim data and return it
182 values, err := t.Dump()
183 if err != nil {
184 resp.Error = true
185 resp.Response = err.Error()
186 } else {
187 jValues, err := json.Marshal(values)
188 if err != nil {
189 resp.Error = true
190 resp.Response = err.Error()
191 } else {
192 resp.Response = fmt.Sprintf("%s", jValues)
193 }
194 }
195 }
196
197 if op == "check" {
198 if len(c.ProofData) < 1 {
199 resp.Error = true
200 resp.Response = "proofData not provided"
201 reply(&resp, w)
202 return
203 }
204 var t *tree.Tree
205 if len(c.RootHash) > 1 { //if rootHash specified
206 t, err = MkTrees[c.CensusID].Snapshot(c.RootHash)
207 if err != nil {
208 log.Printf("Snapshot error: %s", err.Error())
209 resp.Error = true
210 resp.Response = "invalid root hash"
211 reply(&resp, w)
212 return
213 }
214 } else { //if rootHash not specified use current merkletree
215 t = MkTrees[c.CensusID]
216 }
217
218 validProof, err := t.CheckProof([]byte(c.ClaimData), c.ProofData)
219 if err != nil {
220 resp.Error = true
221 resp.Response = err.Error()
222 reply(&resp, w)
223 return
224 }
225 if validProof {
226 resp.Response = "valid"
227 } else {
228 resp.Response = "invalid"
229 }
230 }
231
232 reply(&resp, w)
233}
234
235func addCorsHeaders(w *http.ResponseWriter, req *http.Request) {
236 (*w).Header().Set("Access-Control-Allow-Origin", "*")
237 (*w).Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
238 (*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
239}
240
241func Listen(port int, proto string) {
242 srv := &http.Server{
243 Addr: fmt.Sprintf(":%d", port),
244 ReadHeaderTimeout: 4 * time.Second,
245 ReadTimeout: 4 * time.Second,
246 WriteTimeout: 4 * time.Second,
247 IdleTimeout: 3 * time.Second,
248 }
249
250 http.HandleFunc("/addClaim", func(w http.ResponseWriter, r *http.Request) {
251 addCorsHeaders(&w, r)
252
253 if r.Method == http.MethodPost {
254 claimHandler(w, r, "add")
255 } else if r.Method != http.MethodOptions {
256 http.Error(w, "Not found", http.StatusNotFound)
257 }
258 })
259 http.HandleFunc("/genProof", func(w http.ResponseWriter, r *http.Request) {
260 addCorsHeaders(&w, r)
261
262 if r.Method == http.MethodPost {
263 claimHandler(w, r, "gen")
264 } else if r.Method != http.MethodOptions {
265 http.Error(w, "Not found", http.StatusNotFound)
266 }
267 })
268 http.HandleFunc("/checkProof", func(w http.ResponseWriter, r *http.Request) {
269 addCorsHeaders(&w, r)
270
271 if r.Method == http.MethodPost {
272 claimHandler(w, r, "check")
273 } else if r.Method != http.MethodOptions {
274 http.Error(w, "Not found", http.StatusNotFound)
275 }
276 })
277 http.HandleFunc("/getRoot", func(w http.ResponseWriter, r *http.Request) {
278 addCorsHeaders(&w, r)
279
280 if r.Method == http.MethodPost {
281 claimHandler(w, r, "root")
282 } else if r.Method != http.MethodOptions {
283 http.Error(w, "Not found", http.StatusNotFound)
284 }
285 })
286 http.HandleFunc("/dump", func(w http.ResponseWriter, r *http.Request) {
287 addCorsHeaders(&w, r)
288
289 if r.Method == http.MethodPost {
290 claimHandler(w, r, "dump")
291 } else if r.Method != http.MethodOptions {
292 http.Error(w, "Not found", http.StatusNotFound)
293 }
294 })
295
296 if proto == "https" {
297 log.Print("Starting server in https mode")
298 if err := srv.ListenAndServeTLS("server.crt", "server.key"); err != nil {
299 panic(err)
300 }
301 }
302 if proto == "http" {
303 log.Print("Starting server in http mode")
304 srv.SetKeepAlivesEnabled(false)
305 if err := srv.ListenAndServe(); err != nil {
306 panic(err)
307 }
308 }
309}