@ -8,17 +8,24 @@ import (
"strconv"
"strconv"
"time"
"time"
signature "github.com/vocdoni/go-dvote/crypto/signature_ecdsa"
tree "github.com/vocdoni/go-dvote/tree"
tree "github.com/vocdoni/go-dvote/tree"
signature "github.com/vocdoni/go-dvote/crypto/signature"
)
)
const hashSize = 32
const authTimeWindow = 10 // Time window (seconds) in which TimeStamp will be accepted if auth enabled
var MkTrees map [ string ] * tree . Tree // MerkleTree dvote-census library
// Time window (seconds) in which TimeStamp will be accepted if auth enabled
const authTimeWindow = 10
// MkTrees map of merkle trees indexed by censusId
var MkTrees map [ string ] * tree . Tree
// Signatures map of management pubKeys indexed by censusId
var Signatures map [ string ] string
var Signatures map [ string ] string
var Signature signature . SignKeys // Signature go-dvote library
var currentSignature signature . SignKeys
// Claim type represents a JSON object with all possible fields
type Claim struct {
type Claim struct {
Method string ` json:"method" ` // method to call
CensusID string ` json:"censusId" ` // References to MerkleTree namespace
CensusID string ` json:"censusId" ` // References to MerkleTree namespace
RootHash string ` json:"rootHash" ` // References to MerkleTree rootHash
RootHash string ` json:"rootHash" ` // References to MerkleTree rootHash
ClaimData string ` json:"claimData" ` // Data to add to the MerkleTree
ClaimData string ` json:"claimData" ` // Data to add to the MerkleTree
@ -27,11 +34,13 @@ type Claim struct {
Signature string ` json:"signature" ` // Signature as Hexadecimal String
Signature string ` json:"signature" ` // Signature as Hexadecimal String
}
}
// Result type represents a JSON object with the result of the method executed
type Result struct {
type Result struct {
Error bool ` json:"error" `
Error bool ` json:"error" `
Response string ` json:"response" `
Response string ` json:"response" `
}
}
// AddNamespace adds a new merkletree identified by a censusId (name)
func AddNamespace ( name , pubKey string ) {
func AddNamespace ( name , pubKey string ) {
if len ( MkTrees ) == 0 {
if len ( MkTrees ) == 0 {
MkTrees = make ( map [ string ] * tree . Tree )
MkTrees = make ( map [ string ] * tree . Tree )
@ -63,7 +72,7 @@ func checkRequest(w http.ResponseWriter, req *http.Request) bool {
return true
return true
}
}
func checkAuth ( timestamp , signatur e , pubKey , message string ) bool {
func checkAuth ( timestamp , signed , pubKey , message string ) bool {
if len ( pubKey ) < 1 {
if len ( pubKey ) < 1 {
return true
return true
}
}
@ -75,7 +84,7 @@ func checkAuth(timestamp, signature, pubKey, message string) bool {
}
}
if timeStampRemote < currentTime + authTimeWindow &&
if timeStampRemote < currentTime + authTimeWindow &&
timeStampRemote > currentTime - authTimeWindow {
timeStampRemote > currentTime - authTimeWindow {
v , err := Signature . Verify ( message , signatur e , pubKey )
v , err := current Signature. Verify ( message , signed , pubKey )
if err != nil {
if err != nil {
log . Printf ( "Verification error: %s\n" , err )
log . Printf ( "Verification error: %s\n" , err )
}
}
@ -84,10 +93,8 @@ func checkAuth(timestamp, signature, pubKey, message string) bool {
return false
return false
}
}
func claim Handler( w http . ResponseWriter , req * http . Request , op string ) {
func http Handler( w http . ResponseWriter , req * http . Request ) {
var c Claim
var c Claim
var resp Result
if ok := checkRequest ( w , req ) ; ! ok {
if ok := checkRequest ( w , req ) ; ! ok {
return
return
}
}
@ -97,6 +104,18 @@ func claimHandler(w http.ResponseWriter, req *http.Request, op string) {
http . Error ( w , err . Error ( ) , 400 )
http . Error ( w , err . Error ( ) , 400 )
return
return
}
}
if len ( c . Method ) < 1 {
http . Error ( w , "method must be specified" , 400 )
return
}
resp := opHandler ( & c )
reply ( resp , w )
}
func opHandler ( c * Claim ) * Result {
var resp Result
op := c . Method
var err error
// Process data
// Process data
log . Printf ( "censusId:{%s} rootHash:{%s} claimData:{%s} proofData:{%s} timeStamp:{%s} signature:{%s}\n" ,
log . Printf ( "censusId:{%s} rootHash:{%s} claimData:{%s} proofData:{%s} timeStamp:{%s} signature:{%s}\n" ,
@ -111,73 +130,61 @@ func claimHandler(w http.ResponseWriter, req *http.Request, op string) {
if ! censusFound {
if ! censusFound {
resp . Error = true
resp . Error = true
resp . Response = "censusId not valid or not found"
resp . Response = "censusId not valid or not found"
reply ( & resp , w )
return
return & resp
}
//Methods without rootHash
if op == "getRoot" {
resp . Response = MkTrees [ c . CensusID ] . GetRoot ( )
return & resp
}
}
if op == "add" {
if op == "addClaim " {
if auth := checkAuth ( c . TimeStamp , c . Signature , Signatures [ c . CensusID ] , authString ) ; auth {
if auth := checkAuth ( c . TimeStamp , c . Signature , Signatures [ c . CensusID ] , authString ) ; auth {
err = MkTrees [ c . CensusID ] . AddClaim ( [ ] byte ( c . ClaimData ) )
err = MkTrees [ c . CensusID ] . AddClaim ( [ ] byte ( c . ClaimData ) )
} else {
} else {
resp . Error = true
resp . Error = true
resp . Response = "invalid authentication"
resp . Response = "invalid authentication"
}
}
return & resp
}
}
if op == "gen" {
var t * tree . Tree
var err error
if len ( c . RootHash ) > 1 { //if rootHash specified
t , err = MkTrees [ c . CensusID ] . Snapshot ( c . RootHash )
if err != nil {
log . Printf ( "Snapshot error: %s" , err . Error ( ) )
resp . Error = true
resp . Response = "invalid root hash"
reply ( & resp , w )
return
}
} else { //if rootHash not specified use current tree
t = MkTrees [ c . CensusID ]
//Methods with rootHash, if rootHash specified snapshot the tree
var t * tree . Tree
if len ( c . RootHash ) > 1 { //if rootHash specified
t , err = MkTrees [ c . CensusID ] . Snapshot ( c . RootHash )
if err != nil {
log . Printf ( "Snapshot error: %s" , err . Error ( ) )
resp . Error = true
resp . Response = "invalid root hash"
return & resp
}
}
} else { //if rootHash not specified use current tree
t = MkTrees [ c . CensusID ]
}
if op == "genProof" {
resp . Response , err = t . GenProof ( [ ] byte ( c . ClaimData ) )
resp . Response , err = t . GenProof ( [ ] byte ( c . ClaimData ) )
if err != nil {
if err != nil {
resp . Error = true
resp . Error = true
resp . Response = err . Error ( )
resp . Response = err . Error ( )
reply ( & resp , w )
return
}
}
return & resp
}
}
if op == "root" {
resp . Response = MkTrees [ c . CensusID ] . GetRoot ( )
}
if op == "idx" {
if op == "getIdx" {
resp . Response , err = t . GetIndex ( [ ] byte ( c . ClaimData ) )
return & resp
}
}
if op == "dump" {
if op == "dump" {
var t * tree . Tree
if auth := checkAuth ( c . TimeStamp , c . Signature , Signatures [ c . CensusID ] , authString ) ; ! auth {
if auth := checkAuth ( c . TimeStamp , c . Signature , Signatures [ c . CensusID ] , authString ) ; ! auth {
resp . Error = true
resp . Error = true
resp . Response = "invalid authentication"
resp . Response = "invalid authentication"
reply ( & resp , w )
return
}
if len ( c . RootHash ) > 1 { //if rootHash specified
t , err = MkTrees [ c . CensusID ] . Snapshot ( c . RootHash )
if err != nil {
log . Printf ( "Snapshot error: %s" , err . Error ( ) )
resp . Error = true
resp . Response = "invalid root hash"
reply ( & resp , w )
return
}
} else { //if rootHash not specified use current merkletree
t = MkTrees [ c . CensusID ]
return & resp
}
}
//dump the claim data and return it
//dump the claim data and return it
values , err := t . Dump ( )
values , err := t . Dump ( )
if err != nil {
if err != nil {
@ -192,44 +199,31 @@ func claimHandler(w http.ResponseWriter, req *http.Request, op string) {
resp . Response = fmt . Sprintf ( "%s" , jValues )
resp . Response = fmt . Sprintf ( "%s" , jValues )
}
}
}
}
return & resp
}
}
if op == "check" {
if op == "checkProof " {
if len ( c . ProofData ) < 1 {
if len ( c . ProofData ) < 1 {
resp . Error = true
resp . Error = true
resp . Response = "proofData not provided"
resp . Response = "proofData not provided"
reply ( & resp , w )
return
}
var t * tree . Tree
if len ( c . RootHash ) > 1 { //if rootHash specified
t , err = MkTrees [ c . CensusID ] . Snapshot ( c . RootHash )
if err != nil {
log . Printf ( "Snapshot error: %s" , err . Error ( ) )
resp . Error = true
resp . Response = "invalid root hash"
reply ( & resp , w )
return
}
} else { //if rootHash not specified use current merkletree
t = MkTrees [ c . CensusID ]
return & resp
}
}
// Generate proof and return it
validProof , err := t . CheckProof ( [ ] byte ( c . ClaimData ) , c . ProofData )
validProof , err := t . CheckProof ( [ ] byte ( c . ClaimData ) , c . ProofData )
if err != nil {
if err != nil {
resp . Error = true
resp . Error = true
resp . Response = err . Error ( )
resp . Response = err . Error ( )
reply ( & resp , w )
return
return & resp
}
}
if validProof {
if validProof {
resp . Response = "valid"
resp . Response = "valid"
} else {
} else {
resp . Response = "invalid"
resp . Response = "invalid"
}
}
return & resp
}
}
reply ( & resp , w )
return & resp
}
}
func addCorsHeaders ( w * http . ResponseWriter , req * http . Request ) {
func addCorsHeaders ( w * http . ResponseWriter , req * http . Request ) {
@ -238,6 +232,7 @@ func addCorsHeaders(w *http.ResponseWriter, req *http.Request) {
( * w ) . Header ( ) . Set ( "Access-Control-Allow-Headers" , "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization" )
( * w ) . Header ( ) . Set ( "Access-Control-Allow-Headers" , "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization" )
}
}
// Listen starts a census service defined of type "proto"
func Listen ( port int , proto string ) {
func Listen ( port int , proto string ) {
srv := & http . Server {
srv := & http . Server {
Addr : fmt . Sprintf ( ":%d" , port ) ,
Addr : fmt . Sprintf ( ":%d" , port ) ,
@ -247,52 +242,14 @@ func Listen(port int, proto string) {
IdleTimeout : 3 * time . Second ,
IdleTimeout : 3 * time . Second ,
}
}
http . HandleFunc ( "/addClaim" , func ( w http . ResponseWriter , r * http . Request ) {
addCorsHeaders ( & w , r )
if r . Method == http . MethodPost {
claimHandler ( w , r , "add" )
} else if r . Method != http . MethodOptions {
http . Error ( w , "Not found" , http . StatusNotFound )
}
} )
http . HandleFunc ( "/genProof" , func ( w http . ResponseWriter , r * http . Request ) {
addCorsHeaders ( & w , r )
if r . Method == http . MethodPost {
claimHandler ( w , r , "gen" )
} else if r . Method != http . MethodOptions {
http . Error ( w , "Not found" , http . StatusNotFound )
}
} )
http . HandleFunc ( "/checkProof" , func ( w http . ResponseWriter , r * http . Request ) {
addCorsHeaders ( & w , r )
if r . Method == http . MethodPost {
claimHandler ( w , r , "check" )
} else if r . Method != http . MethodOptions {
http . Error ( w , "Not found" , http . StatusNotFound )
}
} )
http . HandleFunc ( "/getRoot" , func ( w http . ResponseWriter , r * http . Request ) {
http . HandleFunc ( "/" , func ( w http . ResponseWriter , r * http . Request ) {
addCorsHeaders ( & w , r )
addCorsHeaders ( & w , r )
if r . Method == http . MethodPost {
if r . Method == http . MethodPost {
claim Handler( w , r , "root" )
httpHandler ( w , r )
} else if r . Method != http . MethodOptions {
} else if r . Method != http . MethodOptions {
http . Error ( w , "Not found" , http . StatusNotFound )
http . Error ( w , "Not found" , http . StatusNotFound )
}
}
} )
} )
http . HandleFunc ( "/dump" , func ( w http . ResponseWriter , r * http . Request ) {
addCorsHeaders ( & w , r )
if r . Method == http . MethodPost {
claimHandler ( w , r , "dump" )
} else if r . Method != http . MethodOptions {
http . Error ( w , "Not found" , http . StatusNotFound )
}
} )
if proto == "https" {
if proto == "https" {
log . Print ( "Starting server in https mode" )
log . Print ( "Starting server in https mode" )
if err := srv . ListenAndServeTLS ( "server.crt" , "server.key" ) ; err != nil {
if err := srv . ListenAndServeTLS ( "server.crt" , "server.key" ) ; err != nil {