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	"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}