tRefactor handshake validation. - tordam - A library for peer discovery inside the Tor network
 (HTM) git clone https://git.parazyd.org/tordam
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 64ad43723f6b233b1830d22ce62879c37e29ddbc
 (DIR) parent 0ba25233d58c34d9c68993f11c9ca302a959e15c
 (HTM) Author: parazyd <parazyd@dyne.org>
       Date:   Tue, 12 Dec 2017 01:56:50 +0100
       
       Refactor handshake validation.
       
       This commit refactors the handshake validation logic. It allows us being
       a lot more robust in error checking and overall validation.
       
       Most importantly, in damlib, the ValidateReq function has been split
       into two: one handling the first handshake, and the other handling the
       second handshake.
       
       Exported redis variables are now located in damlib's redis.go.
       
       We also introduce a config.go file in damlib, which can be a central
       place for holding configurable information.
       
       Diffstat:
         M cmd/dam-client/main.go              |      37 ++++++++++---------------------
         M cmd/dam-dir/main.go                 |     194 ++++++-------------------------
         M cmd/dam-dir/main_test.go            |       2 +-
         A pkg/damlib/config.go                |      18 ++++++++++++++++++
         A pkg/damlib/redis.go                 |      13 +++++++++++++
         M pkg/damlib/validate.go              |     242 ++++++++++++++++++++++++++-----
       
       6 files changed, 284 insertions(+), 222 deletions(-)
       ---
 (DIR) diff --git a/cmd/dam-client/main.go b/cmd/dam-client/main.go
       t@@ -16,18 +16,6 @@ import (
                lib "github.com/parazyd/tor-dam/pkg/damlib"
        )
        
       -// Cwd holds the path to the directory where we will Chdir on startup.
       -var Cwd = os.Getenv("HOME") + "/.dam"
       -
       -// RsaBits holds the size of our RSA private key. Tor standard is 1024.
       -const RsaBits = 1024
       -
       -// Privpath holds the name of where our private key is.
       -const Privpath = "dam-private.key"
       -
       -// Postmsg holds the message we are signing with our private key.
       -const Postmsg = "I am a DAM node!"
       -
        type msgStruct struct {
                Secret string
        }
       t@@ -103,33 +91,32 @@ func announce(dir string, vals map[string]string, privkey *rsa.PrivateKey) (bool
                                log.Printf("%s: Success. 2/2 handshake valid.\n", dir)
                                log.Printf("%s: Reply: %s\n", dir, m.Secret)
                                return true, nil
       -                } else {
       -                        log.Printf("%s: Fail. Reply: %s\n", dir, m.Secret)
       -                        return false, nil
                        }
       +                log.Printf("%s: Fail. Reply: %s\n", dir, m.Secret)
       +                return false, nil
                }
        
                return false, nil
        }
        
        func main() {
       -        if _, err := os.Stat(Cwd); os.IsNotExist(err) {
       -                err := os.Mkdir(Cwd, 0700)
       +        if _, err := os.Stat(lib.Cwd); os.IsNotExist(err) {
       +                err := os.Mkdir(lib.Cwd, 0700)
                        lib.CheckError(err)
                }
       -        err := os.Chdir(Cwd)
       +        err := os.Chdir(lib.Cwd)
                lib.CheckError(err)
        
       -        if _, err := os.Stat(Privpath); os.IsNotExist(err) {
       -                key, err := lib.GenRsa(RsaBits)
       +        if _, err := os.Stat(lib.Privpath); os.IsNotExist(err) {
       +                key, err := lib.GenRsa(lib.RsaBits)
                        lib.CheckError(err)
       -                _, err = lib.SavePrivRsa(Privpath, key)
       +                _, err = lib.SavePrivRsa(lib.Privpath, key)
                        lib.CheckError(err)
                }
        
                // Start up the hidden service
                log.Println("Starting up the hidden service...")
       -        cmd := exec.Command("damhs.py", Privpath)
       +        cmd := exec.Command("damhs.py", lib.Privpath)
                stdout, err := cmd.StdoutPipe()
                lib.CheckError(err)
        
       t@@ -159,10 +146,10 @@ func main() {
                        }
                }
        
       -        key, err := lib.LoadRsaKeyFromFile(Privpath)
       +        key, err := lib.LoadRsaKeyFromFile(lib.Privpath)
                lib.CheckError(err)
        
       -        sig, err := lib.SignMsgRsa([]byte(Postmsg), key)
       +        sig, err := lib.SignMsgRsa([]byte(lib.PostMsg), key)
                lib.CheckError(err)
                encodedSig := base64.StdEncoding.EncodeToString(sig)
        
       t@@ -172,7 +159,7 @@ func main() {
                nodevals := map[string]string{
                        "nodetype":  "node",
                        "address":   string(onionAddr),
       -                "message":   Postmsg,
       +                "message":   lib.PostMsg,
                        "signature": encodedSig,
                        "secret":    "",
                }
 (DIR) diff --git a/cmd/dam-dir/main.go b/cmd/dam-dir/main.go
       t@@ -3,7 +3,6 @@ package main
        // See LICENSE file for copyright and license details.
        
        import (
       -        "encoding/base64"
                "encoding/json"
                "log"
                "net/http"
       t@@ -12,26 +11,12 @@ import (
                "sync"
                "time"
        
       -        "github.com/go-redis/redis"
                lib "github.com/parazyd/tor-dam/pkg/damlib"
        )
        
       -// Cwd holds the path to the directory where we will Chdir on startup.
       -var Cwd = os.Getenv("HOME") + "/.dam"
       -
        // ListenAddress controls where our HTTP API daemon is listening.
        const ListenAddress = "127.0.0.1:49371"
        
       -// RedisAddress points us to our Redis instance.
       -const RedisAddress = "127.0.0.1:6379"
       -
       -// RedisCli is our global Redis client
       -var RedisCli = redis.NewClient(&redis.Options{
       -        Addr:     RedisAddress,
       -        Password: "",
       -        DB:       0,
       -})
       -
        type nodeStruct struct {
                Nodetype  string
                Address   string
       t@@ -52,7 +37,7 @@ func startRedis() {
        
                time.Sleep(500 * time.Millisecond)
        
       -        _, err = RedisCli.Ping().Result()
       +        _, err = lib.RedisCli.Ping().Result()
                lib.CheckError(err)
        }
        
       t@@ -81,34 +66,12 @@ func handlePost(rw http.ResponseWriter, request *http.Request) {
                        return
                }
        
       -        // Drop out ASAP.
       +        // Bail out as soon as possible.
                if len(n.Nodetype) == 0 || len(n.Address) == 0 ||
                        len(n.Message) == 0 || len(n.Signature) == 0 {
                        return
                }
       -        // TODO: When a node wants to promote itself from something it already was,
       -        // what to do?
       -        switch n.Nodetype {
       -        case "node":
       -                log.Println("Client of type:", n.Nodetype)
       -        case "directory":
       -                log.Println("Client of type:", n.Nodetype)
       -        default:
       -                log.Println("Invalid nodetype:", n.Nodetype)
       -                ret = map[string]string{"secret": "Invalid nodetype."}
       -                if err := postback(rw, ret, 400); err != nil {
       -                        lib.CheckError(err)
       -                }
       -                return
       -        }
       -
       -        decSig, err := base64.StdEncoding.DecodeString(n.Signature)
       -        if err != nil {
       -                log.Println("Failed decoding signature:", err)
       -                ret = map[string]string{"secret": err.Error()}
       -                if err := postback(rw, ret, 400); err != nil {
       -                        lib.CheckError(err)
       -                }
       +        if !(lib.ValidateOnionAddress(n.Address)) {
                        return
                }
        
       t@@ -116,139 +79,52 @@ func handlePost(rw http.ResponseWriter, request *http.Request) {
                        "nodetype":  n.Nodetype,
                        "address":   n.Address,
                        "message":   n.Message,
       -                "signature": string(decSig),
       +                "signature": n.Signature,
                        "secret":    n.Secret,
                }
        
       -        // Check if we have seen this node already.
       -        ex, err := RedisCli.Exists(n.Address).Result()
       -        lib.CheckError(err)
       -        var pub = ""
       -        if ex == 1 {
       -                res, err := RedisCli.HGet(n.Address, "pubkey").Result()
       -                pub = string(res)
       -                lib.CheckError(err)
       -        }
       -
       -        pkey, valid := lib.ValidateReq(req, pub)
       -        if !(valid) && pkey == nil {
       -                ret := map[string]string{"secret": "Request is not valid."}
       -                if err := postback(rw, ret, 400); err != nil {
       -                        lib.CheckError(err)
       -                }
       -                return
       -        } else if !(valid) && pkey != nil {
       -                // We couldn't get a descriptor.
       -                ret := map[string]string{"secret": string(pkey)}
       -                if err := postback(rw, ret, 500); err != nil {
       -                        lib.CheckError(err)
       -                }
       -                return
       -        }
       -
       -        pubkey, err := lib.ParsePubkeyRsa(pkey)
       -        lib.CheckError(err)
       -
       -        n.Pubkey = string(pkey)
       -        now := time.Now()
       -        n.Firstseen = now.Unix()
       -        n.Lastseen = now.Unix()
       -
       -        if len(req["secret"]) != 88 {
       -                // Client did not send a decrypted secret.
       -                randString, err := lib.GenRandomASCII(64)
       -                lib.CheckError(err)
       -
       -                secret, err := lib.EncryptMsgRsa([]byte(randString), pubkey)
       -                lib.CheckError(err)
       -
       -                encodedSecret := base64.StdEncoding.EncodeToString(secret)
       -                ret := map[string]string{"secret": encodedSecret}
       -
       -                // Check if we have seen this node already.
       -                ex, err := RedisCli.Exists(n.Address).Result()
       -                lib.CheckError(err)
       -
       -                // Save the node into redis
       -                info := map[string]interface{}{
       -                        "nodetype":  n.Nodetype,
       -                        "address":   n.Address,
       -                        "message":   n.Message,
       -                        "signature": n.Signature,
       -                        "secret":    base64.StdEncoding.EncodeToString([]byte(randString)),
       -                        "pubkey":    n.Pubkey,
       -                        "lastseen":  n.Lastseen,
       -                }
       -
       -                if ex != 1 {
       -                        info["firstseen"] = n.Firstseen
       -                        info["valid"] = 0 // This should be 1 after the node is not considered malicious
       -                }
       -                log.Printf("%s: Writing to Redis\n", n.Address)
       -                redRet, err := RedisCli.HMSet(n.Address, info).Result()
       -                lib.CheckError(err)
       -
       -                if redRet == "OK" {
       -                        log.Println("Returning encrypted secret to", n.Address)
       +        // First handshake
       +        if len(n.Message) != 88 && len(n.Secret) != 88 {
       +                valid, msg := lib.ValidateFirst(req)
       +                ret = map[string]string{"secret": msg}
       +                if valid {
       +                        log.Printf("%s: 1/2 handshake valid.\n", n.Address)
       +                        log.Println("Sending back encrypted secret.")
                                if err := postback(rw, ret, 200); err != nil {
                                        lib.CheckError(err)
                                }
                                return
                        }
       -        }
       -
       -        if len(req["secret"]) == 88 && len(req["message"]) == 88 {
       -                // Client sent a decrypted secret.
       -                var correct = false
       -                localSec, err := RedisCli.HGet(n.Address, "secret").Result()
       +                log.Printf("%s: 1/2 handshake invalid: %s\n", n.Address, msg)
       +                // Delete it all from redis.
       +                _, err := lib.RedisCli.Del(n.Address).Result()
                        lib.CheckError(err)
       -
       -                if localSec == req["secret"] && localSec == req["message"] {
       -                        log.Println("Secrets match!")
       -                        correct = true
       -                } else {
       -                        log.Println("Secrets don't match!")
       -                        correct = false
       -                }
       -
       -                if correct {
       -                        msg := []byte(req["message"])
       -                        sig := []byte(req["signature"])
       -                        pub, err := lib.ParsePubkeyRsa([]byte(n.Pubkey))
       -                        lib.CheckError(err)
       -                        val, err := lib.VerifyMsgRsa(msg, sig, pub)
       +                if err := postback(rw, ret, 400); err != nil {
                                lib.CheckError(err)
       -                        if val {
       -                                correct = true
       -                        } else {
       -                                correct = false
       -                        }
                        }
       +                return
       +        }
        
       -                if correct {
       -                        // Replace the secret in redis to prevent reuse.
       -                        randString, err := lib.GenRandomASCII(64)
       -                        lib.CheckError(err)
       -                        encoded := base64.StdEncoding.EncodeToString([]byte(randString))
       -                        _, err = RedisCli.HSet(n.Address, "secret", encoded).Result()
       -                        lib.CheckError(err)
       -                        log.Printf("Welcoming %s to the network\n", n.Address)
       -                        ret := map[string]string{"secret": "Welcome to the DAM network!"}
       +        // Second handshake
       +        if len(req["secret"]) == 88 && len(req["message"]) == 88 {
       +                valid, msg := lib.ValidateSecond(req)
       +                ret = map[string]string{"secret": msg}
       +                if valid {
       +                        log.Printf("%s: 2/2 handshake valid.\n", n.Address)
       +                        log.Println("Sending back welcome message.")
                                if err := postback(rw, ret, 200); err != nil {
                                        lib.CheckError(err)
                                }
                                return
       -                } else {
       -                        // Delete it all from redis.
       -                        _, err := RedisCli.Del(n.Address).Result()
       +                }
       +                log.Printf("%s: 2/2 handshake invalid.\n", n.Address)
       +                // Delete it all from redis.
       +                _, err := lib.RedisCli.Del(n.Address).Result()
       +                lib.CheckError(err)
       +                if err := postback(rw, ret, 400); err != nil {
                                lib.CheckError(err)
       -                        log.Printf("Verifying %s failed.\n", n.Address)
       -                        ret := map[string]string{"secret": "Verification failed. Bye."}
       -                        if err := postback(rw, ret, 400); err != nil {
       -                                lib.CheckError(err)
       -                        }
       -                        return
                        }
       +                return
                }
        }
        
       t@@ -260,14 +136,14 @@ func handleElse(rw http.ResponseWriter, request *http.Request) {
        func main() {
                var wg sync.WaitGroup
        
       -        if _, err := os.Stat(Cwd); os.IsNotExist(err) {
       -                err := os.Mkdir(Cwd, 0700)
       +        if _, err := os.Stat(lib.Cwd); os.IsNotExist(err) {
       +                err := os.Mkdir(lib.Cwd, 0700)
                        lib.CheckError(err)
                }
       -        err := os.Chdir(Cwd)
       +        err := os.Chdir(lib.Cwd)
                lib.CheckError(err)
        
       -        if _, err := RedisCli.Ping().Result(); err != nil {
       +        if _, err := lib.RedisCli.Ping().Result(); err != nil {
                        // We assume redis is not running. Start it up.
                        startRedis()
                }
 (DIR) diff --git a/cmd/dam-dir/main_test.go b/cmd/dam-dir/main_test.go
       t@@ -146,7 +146,7 @@ func TestValidSecondHandshake(t *testing.T) {
                if err != nil {
                        t.Fatal(err)
                }
       -        if m.Secret == "Welcome to the DAM network!" {
       +        if m.Secret == lib.WelcomeMsg {
                        t.Log("Server replied:", m.Secret)
                } else {
                        t.Fatal(m.Secret)
 (DIR) diff --git a/pkg/damlib/config.go b/pkg/damlib/config.go
       t@@ -0,0 +1,18 @@
       +package damlib
       +
       +import "os"
       +
       +// Cwd holds the path to the directory where we will Chdir on startup.
       +var Cwd = os.Getenv("HOME") + "/.dam"
       +
       +// RsaBits holds the size of our RSA private key. Tor standard is 1024.
       +const RsaBits = 1024
       +
       +// Privpath holds the name of where our private key is.
       +const Privpath = "dam-private.key"
       +
       +// PostMsg holds the message we are signing with our private key.
       +const PostMsg = "I am a DAM node!"
       +
       +// WelcomeMsg holds the message we return when welcoming a node.
       +const WelcomeMsg = "Welcome to the DAM network!"
 (DIR) diff --git a/pkg/damlib/redis.go b/pkg/damlib/redis.go
       t@@ -0,0 +1,13 @@
       +package damlib
       +
       +import "github.com/go-redis/redis"
       +
       +// RedisAddress points us to our Redis instance.
       +const RedisAddress = "127.0.0.1:6379"
       +
       +// RedisCli is our global Redis client
       +var RedisCli = redis.NewClient(&redis.Options{
       +        Addr:     RedisAddress,
       +        Password: "",
       +        DB:       0,
       +})
 (DIR) diff --git a/pkg/damlib/validate.go b/pkg/damlib/validate.go
       t@@ -3,71 +3,239 @@ package damlib
        // See LICENSE file for copyright and license details.
        
        import (
       +        "encoding/base64"
       +        "errors"
                "log"
                "regexp"
                "strings"
                "time"
        )
        
       -// ValidateReq validates our given request against the logic we are checking.
       -// The function takes a request data map, and a public key in the form of a
       -// string. If the public key is an empty string, the function will run an
       -// external program to fetch the node's public key from a Tor HSDir.
       -//
       -// ValidateReq  will first validate "nodetype", looking whether the announcer
       -// is a node or a directory.
       -// Then, it will validate the onion address using a regular expression.
       -// Now, if pubkey is empty, it will run the external program to fetch it. If a
       -// descriptor can't be retrieved, it will retry for 10 times, and fail if those
       -// are not successful.
       -//
       -// Continuing, ValidateReq will verify the RSA signature posted by the
       -// announcer.
       -// If any of the above are invalid, the function will return nil and false.
       -// Otherwise, it will return the pubkey as a slice of bytes, and true.
       -func ValidateReq(req map[string]string, pubkey string) ([]byte, bool) {
       -        // Validate nodetype.
       -        if req["nodetype"] != "node" {
       -                return nil, false
       +// ValidateOnionAddress matches a string against a regular expression matching
       +// a Tor hidden service address. Returns true on success and false on failure.
       +func ValidateOnionAddress(addr string) bool {
       +        re, _ := regexp.Compile("^[a-z2-7]{16}\\.onion$")
       +        if len(re.FindString(addr)) != 22 {
       +                return false
                }
       -        // Validate address.
       -        re, err := regexp.Compile("^[a-z2-7]{16}\\.onion$")
       -        CheckError(err)
       -        if len(re.FindString(req["address"])) != 22 {
       -                return nil, false
       +        return true
       +}
       +
       +// sanityCheck performs basic sanity checks against the incoming request.
       +// Returns a boolean value according to the validity, and a string with an
       +// according message.
       +func sanityCheck(req map[string]string, handshake int) (bool, string) {
       +        if !(ValidateOnionAddress(req["address"])) {
       +                return false, "Invalid onion address"
       +        }
       +        if _, err := base64.StdEncoding.DecodeString(req["signature"]); err != nil {
       +                return false, err.Error()
       +        }
       +        // TODO: When a node wants to promote itself from something it already was,
       +        // what to do?
       +        switch req["nodetype"] {
       +        case "node":
       +                log.Printf("%s is a node.", req["address"])
       +        case "directory":
       +                log.Printf("%s is a directory.", req["address"])
       +        default:
       +                return false, "Invalid nodetype."
                }
       -        log.Println(req["address"], "seems valid")
        
       -        if len(pubkey) == 0 {
       -                // Address is valid, we try to fetch its pubkey from a HSDir
       +        if handshake == 2 {
       +                if _, err := base64.StdEncoding.DecodeString(req["message"]); err != nil {
       +                        return false, err.Error()
       +                }
       +                if _, err := base64.StdEncoding.DecodeString(req["secret"]); err != nil {
       +                        return false, err.Error()
       +                }
       +        }
       +        return true, ""
       +}
       +
       +// ValidateFirst validates the first incoming handshake.
       +// It first calls sanityCheck to validate it's actually working with proper
       +// data.
       +// Next, it will look if the node is already found in redis. If so, it will
       +// fetch its public hey from redis, otherwise it will run an external program to
       +// fetch the node's public key from a Tor HSDir. If that program fails, so will
       +// the function.
       +// Once the public key is retrieved, it will validate the received message
       +// signature against that key. If all is well, we consider the request valid.
       +// Continuing, a random ASCII string will be generated and encrypted with the
       +// retrieved public key. All this data will be written into redis, and finally
       +// the encrypted (and base64 encoded) secret will be returned along with a true
       +// boolean value.
       +// On any failure, the function will return false, and produce an according
       +// string which is to be considered as an error message.
       +func ValidateFirst(req map[string]string) (bool, string) {
       +        sane, what := sanityCheck(req, 1)
       +        if !(sane) {
       +                return false, what
       +        }
       +
       +        // Get the public key.
       +        var pub string
       +        // Check if we have seen this node already.
       +        ex, err := RedisCli.Exists(req["address"]).Result()
       +        CheckError(err)
       +        if ex == 1 {
       +                // We saw it so we should have the public key in redis.
       +                // If we do not, that is an internal error.
       +                pub, err = RedisCli.HGet(req["address"], "pubkey").Result()
       +                CheckError(err)
       +                // FIXME: Do a smarter check
       +                if len(pub) < 20 {
       +                        CheckError(errors.New("Invalid data fetched from redis when requesting pubkey"))
       +                }
       +        } else {
       +                // We fetch it from a HSDir
                        cnt := 0
                        for { // We try until we have it.
                                cnt++
                                if cnt > 10 {
                                        // We probably can't get a good HSDir. The client shall retry
                                        // later on.
       -                                return []byte("Couldn't get a descriptor. Try later."), false
       +                                return false, "Could not get a descriptor. Try later."
                                }
       -                        pubkey = FetchHSPubkey(req["address"])
       -                        if strings.HasPrefix(pubkey, "-----BEGIN RSA PUBLIC KEY-----") &&
       -                                strings.HasSuffix(pubkey, "-----END RSA PUBLIC KEY-----") {
       +                        pub = FetchHSPubkey(req["address"])
       +                        if strings.HasPrefix(pub, "-----BEGIN RSA PUBLIC KEY-----") &&
       +                                strings.HasSuffix(pub, "-----END RSA PUBLIC KEY-----") {
                                        log.Println("Got descriptor!")
                                        break
                                }
                                time.Sleep(2000 * time.Millisecond)
                        }
                }
       +
                // Validate signature.
                msg := []byte(req["message"])
       -        sig := []byte(req["signature"])
       -        pub, err := ParsePubkeyRsa([]byte(pubkey))
       +        decSig, _ := base64.StdEncoding.DecodeString(req["signature"])
       +        sig := []byte(decSig)
       +        pubkey, err := ParsePubkeyRsa([]byte(pub)) // pubkey is their public key in *rsa.PublicKey type
       +        CheckError(err)
       +        val, _ := VerifyMsgRsa(msg, sig, pubkey)
       +        if val != true {
       +                log.Println("crypto/rsa: verification failure")
       +                return false, "Signature verification failure."
       +        }
       +
       +        // The request is valid at this point.
       +
       +        // Make a random secret for them, and save our node info to redis.
       +        randString, err := GenRandomASCII(64)
       +        CheckError(err)
       +        encodedSecret := base64.StdEncoding.EncodeToString([]byte(randString))
       +
       +        var info = map[string]interface{}{
       +                "nodetype":  req["nodetype"],
       +                "address":   req["address"],
       +                "message":   encodedSecret,
       +                "signature": req["signature"],
       +                "secret":    encodedSecret,
       +                "lastseen":  time.Now().Unix(),
       +        } // Can not cast, need this for HMSet
       +        if ex != 1 { // We did not have this node in redis.
       +                info["pubkey"] = pub
       +                info["firstseen"] = time.Now().Unix()
       +                info["valid"] = 0
       +        }
       +
       +        log.Printf("%s: writing to redis\n", req["address"])
       +        redRet, err := RedisCli.HMSet(req["address"], info).Result()
       +        CheckError(err)
       +
       +        if redRet != "OK" {
       +                return false, "Internal server error"
       +        }
       +
       +        encryptedSecret, err := EncryptMsgRsa([]byte(randString), pubkey)
       +        CheckError(err)
       +
       +        encryptedEncodedSecret := base64.StdEncoding.EncodeToString(encryptedSecret)
       +        return true, encryptedEncodedSecret
       +}
       +
       +// ValidateSecond validates the second part of the handshake.
       +// First basic sanity checks are performed to ensure we are working with valid
       +// data.
       +// Next, the according public key will be retrieved from redis. If no key is
       +// found, we will consider the handshake invalid.
       +// Now the decrypted secret that was sent to us will be compared with what we
       +// have saved before. Upon proving they are the same, the RSA signature will now
       +// be validated. If all is well, we consider the request valid.
       +// Further on, we will generate a new random ASCII string and save it in redis
       +// to prevent further reuse of the already known string. Upon success, the
       +// function will return true, and a welcome message. Upon failure, the function
       +// will return false, and an according string which is to be considered an error
       +// message.
       +func ValidateSecond(req map[string]string) (bool, string) {
       +        sane, what := sanityCheck(req, 2)
       +        if !(sane) {
       +                return false, what
       +        }
       +
       +        // Get the public key.
       +        var pub string
       +        // Check if we have seen this node already.
       +        ex, err := RedisCli.Exists(req["address"]).Result()
       +        CheckError(err)
       +        if ex == 1 {
       +                // We saw it so we should have the public key in redis.
       +                // If we do not, that is an internal error.
       +                pub, err = RedisCli.HGet(req["address"], "pubkey").Result()
       +                CheckError(err)
       +                // FIXME: Do a smarter check
       +                if len(pub) < 20 {
       +                        CheckError(errors.New("Invalid data fetched from redis when requesting pubkey"))
       +                }
       +        } else {
       +                return false, "We have not seen you before. Please authenticate properly."
       +        }
       +
       +        localSec, err := RedisCli.HGet(req["address"], "secret").Result()
                CheckError(err)
        
       -        val, _ := VerifyMsgRsa(msg, sig, pub)
       +        if !(localSec == req["secret"] && localSec == req["message"]) {
       +                log.Println("Secrets don't match.")
       +                return false, "Secrets don't match."
       +        }
       +
       +        // Validate signature.
       +        msg := []byte(req["message"])
       +        decSig, _ := base64.StdEncoding.DecodeString(req["signature"])
       +        sig := []byte(decSig)
       +        pubkey, err := ParsePubkeyRsa([]byte(pub)) // pubkey is their public key in *rsa.PublicKey type
       +        CheckError(err)
       +        val, _ := VerifyMsgRsa(msg, sig, pubkey)
                if val != true {
                        log.Println("crypto/rsa: verification failure")
       -                return nil, false
       +                return false, "Signature verification failure."
       +        }
       +
       +        // The request is valid at this point.
       +
       +        // Make a new random secret to prevent reuse.
       +        randString, err := GenRandomASCII(64)
       +        CheckError(err)
       +        encodedSecret := base64.StdEncoding.EncodeToString([]byte(randString))
       +
       +        var info = map[string]interface{}{
       +                "nodetype":  req["nodetype"],
       +                "address":   req["address"],
       +                "message":   encodedSecret,
       +                "signature": req["signature"],
       +                "secret":    encodedSecret,
       +                "lastseen":  time.Now().Unix(),
       +        } // Can not cast, need this for HMSet
       +
       +        log.Printf("%s: writing to redis\n", req["address"])
       +        redRet, err := RedisCli.HMSet(req["address"], info).Result()
       +        CheckError(err)
       +
       +        if redRet != "OK" {
       +                return false, "Internal server error"
                }
        
       -        return []byte(pubkey), true
       +        return true, WelcomeMsg
        }