arnaucube
arnaucube/go-dvotePublic Self-hosted repository
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}