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.

234 lines
5.6 KiB

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