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