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.

309 lines
7.8 KiB

  1. package censusmanager
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "log"
  6. "net/http"
  7. "strconv"
  8. "time"
  9. tree "github.com/vocdoni/go-dvote/tree"
  10. signature "github.com/vocdoni/go-dvote/crypto/signature"
  11. )
  12. const hashSize = 32
  13. const authTimeWindow = 10 // Time window (seconds) in which TimeStamp will be accepted if auth enabled
  14. var MkTrees map[string]*tree.Tree // MerkleTree dvote-census library
  15. var Signatures map[string]string
  16. var Signature signature.SignKeys // Signature go-dvote library
  17. type Claim struct {
  18. CensusID string `json:"censusId"` // References to MerkleTree namespace
  19. RootHash string `json:"rootHash"` // References to MerkleTree rootHash
  20. ClaimData string `json:"claimData"` // Data to add to the MerkleTree
  21. ProofData string `json:"proofData"` // MerkleProof to check
  22. TimeStamp string `json:"timeStamp"` // Unix TimeStamp in seconds
  23. Signature string `json:"signature"` // Signature as Hexadecimal String
  24. }
  25. type Result struct {
  26. Error bool `json:"error"`
  27. Response string `json:"response"`
  28. }
  29. func AddNamespace(name, pubKey string) {
  30. if len(MkTrees) == 0 {
  31. MkTrees = make(map[string]*tree.Tree)
  32. }
  33. if len(Signatures) == 0 {
  34. Signatures = make(map[string]string)
  35. }
  36. mkTree := tree.Tree{}
  37. mkTree.Init(name)
  38. MkTrees[name] = &mkTree
  39. Signatures[name] = pubKey
  40. }
  41. func reply(resp *Result, w http.ResponseWriter) {
  42. err := json.NewEncoder(w).Encode(resp)
  43. if err != nil {
  44. http.Error(w, err.Error(), 500)
  45. } else {
  46. w.Header().Set("content-type", "application/json")
  47. }
  48. }
  49. func checkRequest(w http.ResponseWriter, req *http.Request) bool {
  50. if req.Body == nil {
  51. http.Error(w, "Please send a request body", 400)
  52. return false
  53. }
  54. return true
  55. }
  56. func checkAuth(timestamp, signature, pubKey, message string) bool {
  57. if len(pubKey) < 1 {
  58. return true
  59. }
  60. currentTime := int64(time.Now().Unix())
  61. timeStampRemote, err := strconv.ParseInt(timestamp, 10, 32)
  62. if err != nil {
  63. log.Printf("Cannot parse timestamp data %s\n", err)
  64. return false
  65. }
  66. if timeStampRemote < currentTime+authTimeWindow &&
  67. timeStampRemote > currentTime-authTimeWindow {
  68. v, err := Signature.Verify(message, signature, pubKey)
  69. if err != nil {
  70. log.Printf("Verification error: %s\n", err)
  71. }
  72. return v
  73. }
  74. return false
  75. }
  76. func claimHandler(w http.ResponseWriter, req *http.Request, op string) {
  77. var c Claim
  78. var resp Result
  79. if ok := checkRequest(w, req); !ok {
  80. return
  81. }
  82. // Decode JSON
  83. err := json.NewDecoder(req.Body).Decode(&c)
  84. if err != nil {
  85. http.Error(w, err.Error(), 400)
  86. return
  87. }
  88. // Process data
  89. log.Printf("censusId:{%s} rootHash:{%s} claimData:{%s} proofData:{%s} timeStamp:{%s} signature:{%s}\n",
  90. c.CensusID, c.RootHash, c.ClaimData, c.ProofData, c.TimeStamp, c.Signature)
  91. authString := fmt.Sprintf("%s%s%s%s", c.CensusID, c.RootHash, c.ClaimData, c.TimeStamp)
  92. resp.Error = false
  93. resp.Response = ""
  94. censusFound := false
  95. if len(c.CensusID) > 0 {
  96. _, censusFound = MkTrees[c.CensusID]
  97. }
  98. if !censusFound {
  99. resp.Error = true
  100. resp.Response = "censusId not valid or not found"
  101. reply(&resp, w)
  102. return
  103. }
  104. if op == "add" {
  105. if auth := checkAuth(c.TimeStamp, c.Signature, Signatures[c.CensusID], authString); auth {
  106. err = MkTrees[c.CensusID].AddClaim([]byte(c.ClaimData))
  107. } else {
  108. resp.Error = true
  109. resp.Response = "invalid authentication"
  110. }
  111. }
  112. if op == "gen" {
  113. var t *tree.Tree
  114. var err error
  115. if len(c.RootHash) > 1 { //if rootHash specified
  116. t, err = MkTrees[c.CensusID].Snapshot(c.RootHash)
  117. if err != nil {
  118. log.Printf("Snapshot error: %s", err.Error())
  119. resp.Error = true
  120. resp.Response = "invalid root hash"
  121. reply(&resp, w)
  122. return
  123. }
  124. } else { //if rootHash not specified use current tree
  125. t = MkTrees[c.CensusID]
  126. }
  127. resp.Response, err = t.GenProof([]byte(c.ClaimData))
  128. if err != nil {
  129. resp.Error = true
  130. resp.Response = err.Error()
  131. reply(&resp, w)
  132. return
  133. }
  134. }
  135. if op == "root" {
  136. resp.Response = MkTrees[c.CensusID].GetRoot()
  137. }
  138. if op == "idx" {
  139. }
  140. if op == "dump" {
  141. var t *tree.Tree
  142. if auth := checkAuth(c.TimeStamp, c.Signature, Signatures[c.CensusID], authString); !auth {
  143. resp.Error = true
  144. resp.Response = "invalid authentication"
  145. reply(&resp, w)
  146. return
  147. }
  148. if len(c.RootHash) > 1 { //if rootHash specified
  149. t, err = MkTrees[c.CensusID].Snapshot(c.RootHash)
  150. if err != nil {
  151. log.Printf("Snapshot error: %s", err.Error())
  152. resp.Error = true
  153. resp.Response = "invalid root hash"
  154. reply(&resp, w)
  155. return
  156. }
  157. } else { //if rootHash not specified use current merkletree
  158. t = MkTrees[c.CensusID]
  159. }
  160. //dump the claim data and return it
  161. values, err := t.Dump()
  162. if err != nil {
  163. resp.Error = true
  164. resp.Response = err.Error()
  165. } else {
  166. jValues, err := json.Marshal(values)
  167. if err != nil {
  168. resp.Error = true
  169. resp.Response = err.Error()
  170. } else {
  171. resp.Response = fmt.Sprintf("%s", jValues)
  172. }
  173. }
  174. }
  175. if op == "check" {
  176. if len(c.ProofData) < 1 {
  177. resp.Error = true
  178. resp.Response = "proofData not provided"
  179. reply(&resp, w)
  180. return
  181. }
  182. var t *tree.Tree
  183. if len(c.RootHash) > 1 { //if rootHash specified
  184. t, err = MkTrees[c.CensusID].Snapshot(c.RootHash)
  185. if err != nil {
  186. log.Printf("Snapshot error: %s", err.Error())
  187. resp.Error = true
  188. resp.Response = "invalid root hash"
  189. reply(&resp, w)
  190. return
  191. }
  192. } else { //if rootHash not specified use current merkletree
  193. t = MkTrees[c.CensusID]
  194. }
  195. validProof, err := t.CheckProof([]byte(c.ClaimData), c.ProofData)
  196. if err != nil {
  197. resp.Error = true
  198. resp.Response = err.Error()
  199. reply(&resp, w)
  200. return
  201. }
  202. if validProof {
  203. resp.Response = "valid"
  204. } else {
  205. resp.Response = "invalid"
  206. }
  207. }
  208. reply(&resp, w)
  209. }
  210. func addCorsHeaders(w *http.ResponseWriter, req *http.Request) {
  211. (*w).Header().Set("Access-Control-Allow-Origin", "*")
  212. (*w).Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
  213. (*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
  214. }
  215. func Listen(port int, proto string) {
  216. srv := &http.Server{
  217. Addr: fmt.Sprintf(":%d", port),
  218. ReadHeaderTimeout: 4 * time.Second,
  219. ReadTimeout: 4 * time.Second,
  220. WriteTimeout: 4 * time.Second,
  221. IdleTimeout: 3 * time.Second,
  222. }
  223. http.HandleFunc("/addClaim", func(w http.ResponseWriter, r *http.Request) {
  224. addCorsHeaders(&w, r)
  225. if r.Method == http.MethodPost {
  226. claimHandler(w, r, "add")
  227. } else if r.Method != http.MethodOptions {
  228. http.Error(w, "Not found", http.StatusNotFound)
  229. }
  230. })
  231. http.HandleFunc("/genProof", func(w http.ResponseWriter, r *http.Request) {
  232. addCorsHeaders(&w, r)
  233. if r.Method == http.MethodPost {
  234. claimHandler(w, r, "gen")
  235. } else if r.Method != http.MethodOptions {
  236. http.Error(w, "Not found", http.StatusNotFound)
  237. }
  238. })
  239. http.HandleFunc("/checkProof", func(w http.ResponseWriter, r *http.Request) {
  240. addCorsHeaders(&w, r)
  241. if r.Method == http.MethodPost {
  242. claimHandler(w, r, "check")
  243. } else if r.Method != http.MethodOptions {
  244. http.Error(w, "Not found", http.StatusNotFound)
  245. }
  246. })
  247. http.HandleFunc("/getRoot", func(w http.ResponseWriter, r *http.Request) {
  248. addCorsHeaders(&w, r)
  249. if r.Method == http.MethodPost {
  250. claimHandler(w, r, "root")
  251. } else if r.Method != http.MethodOptions {
  252. http.Error(w, "Not found", http.StatusNotFound)
  253. }
  254. })
  255. http.HandleFunc("/dump", func(w http.ResponseWriter, r *http.Request) {
  256. addCorsHeaders(&w, r)
  257. if r.Method == http.MethodPost {
  258. claimHandler(w, r, "dump")
  259. } else if r.Method != http.MethodOptions {
  260. http.Error(w, "Not found", http.StatusNotFound)
  261. }
  262. })
  263. if proto == "https" {
  264. log.Print("Starting server in https mode")
  265. if err := srv.ListenAndServeTLS("server.crt", "server.key"); err != nil {
  266. panic(err)
  267. }
  268. }
  269. if proto == "http" {
  270. log.Print("Starting server in http mode")
  271. srv.SetKeepAlivesEnabled(false)
  272. if err := srv.ListenAndServe(); err != nil {
  273. panic(err)
  274. }
  275. }
  276. }