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