go-dvote/service/processHttp.go
service/processHttp.goRaw
1package processHttp
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 log.Printf("Msg to check: %s", msg)
111 if auth := checkAuth(c.TimeStamp, c.Signature, msg); auth {
112 err = T.AddClaim([]byte(c.ClaimData))
113 } else {
114 resp.Error = true
115 resp.Response = "invalid authentication"
116 }
117 }
118
119 if op == "gen" {
120 resp.Response, err = T.GenProof([]byte(c.ClaimData))
121 }
122
123 if op == "root" {
124 resp.Response = T.GetRoot()
125 }
126
127 if op == "check" {
128 if len(c.ProofData) < 1 {
129 resp.Error = true
130 resp.Response = "proofData not provided"
131 reply(&resp, w)
132 return
133 }
134 var validProof bool
135 validProof, err = T.CheckProof([]byte(c.ClaimData), c.ProofData)
136 if validProof {
137 resp.Response = "valid"
138 } else {
139 resp.Response = "invalid"
140 }
141 }
142
143 if err != nil {
144 resp.Error = true
145 resp.Response = fmt.Sprint(err)
146 log.Print(err)
147 reply(&resp, w)
148 return
149 }
150
151 reply(&resp, w)
152}
153
154func Listen(port int, proto string, pubKey string) {
155 srv := &http.Server{
156 Addr: fmt.Sprintf(":%d", port),
157 ReadHeaderTimeout: 4 * time.Second,
158 ReadTimeout: 4 * time.Second,
159 WriteTimeout: 4 * time.Second,
160 IdleTimeout: 3 * time.Second,
161 }
162
163 http.HandleFunc("/addClaim", func(w http.ResponseWriter, r *http.Request) {
164 claimHandler(w, r, "add")
165 })
166 http.HandleFunc("/genProof", func(w http.ResponseWriter, r *http.Request) {
167 claimHandler(w, r, "gen")
168 })
169 http.HandleFunc("/checkProof", func(w http.ResponseWriter, r *http.Request) {
170 claimHandler(w, r, "check")
171 })
172 http.HandleFunc("/getRoot", func(w http.ResponseWriter, r *http.Request) {
173 claimHandler(w, r, "root")
174 })
175
176 if len(pubKey) > 1 {
177 log.Printf("Enabling signature authentication with %s\n", pubKey)
178 authPubKey = pubKey
179 } else {
180 authPubKey = ""
181 }
182
183 if proto == "https" {
184 log.Print("Starting server in https mode")
185 if err := srv.ListenAndServeTLS("server.crt", "server.key"); err != nil {
186 panic(err)
187 }
188 }
189 if proto == "http" {
190 log.Print("Starting server in http mode")
191 srv.SetKeepAlivesEnabled(false)
192 if err := srv.ListenAndServe(); err != nil {
193 panic(err)
194 }
195 }
196}