arnaucube
arnaucube/go-dvotePublic Self-hosted repository
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 = string(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 Listen(port int, proto string) {
236	srv := &http.Server{
237		Addr:              fmt.Sprintf(":%d", port),
238		ReadHeaderTimeout: 4 * time.Second,
239		ReadTimeout:       4 * time.Second,
240		WriteTimeout:      4 * time.Second,
241		IdleTimeout:       3 * time.Second,
242	}
243
244	http.HandleFunc("/addClaim", func(w http.ResponseWriter, r *http.Request) {
245		claimHandler(w, r, "add")
246	})
247	http.HandleFunc("/genProof", func(w http.ResponseWriter, r *http.Request) {
248		claimHandler(w, r, "gen")
249	})
250	http.HandleFunc("/checkProof", func(w http.ResponseWriter, r *http.Request) {
251		claimHandler(w, r, "check")
252	})
253	http.HandleFunc("/getRoot", func(w http.ResponseWriter, r *http.Request) {
254		claimHandler(w, r, "root")
255	})
256	http.HandleFunc("/dump", func(w http.ResponseWriter, r *http.Request) {
257		claimHandler(w, r, "dump")
258	})
259
260	if proto == "https" {
261		log.Print("Starting server in https mode")
262		if err := srv.ListenAndServeTLS("server.crt", "server.key"); err != nil {
263			panic(err)
264		}
265	}
266	if proto == "http" {
267		log.Print("Starting server in http mode")
268		srv.SetKeepAlivesEnabled(false)
269		if err := srv.ListenAndServe(); err != nil {
270			panic(err)
271		}
272	}
273}