You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

233 lines
5.4 KiB

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