arnaucube
arnaucube/go-dvotePublic Self-hosted repository
go-dvote/service/census/censusmanager.go
service/census/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/go-dvote/tree"
 12	signature "github.com/vocdoni/go-dvote/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 go-dvote 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}