validate.go (4394B)
1 package main 2 3 /* 4 * Copyright (c) 2017-2021 Ivan Jelincic <parazyd@dyne.org> 5 * 6 * This file is part of tor-dam 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Affero General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Affero General Public License for more details. 17 * 18 * You should have received a copy of the GNU Affero General Public License 19 * along with this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 22 import ( 23 "crypto/ed25519" 24 "encoding/base64" 25 "log" 26 "regexp" 27 "time" 28 ) 29 30 func validateOnionAddress(addr string) bool { 31 re, _ := regexp.Compile(`^[a-z2-7](?:.{55})\.onion`) 32 return len(re.FindString(addr)) == 62 33 } 34 35 // firstHandshake will take the incoming public key either from the request 36 // or, if found, from redis. This key is stored, and a nonce is generated. 37 // This nonce is returned back to the client to sign with the key. In the 38 // second handshake, we verify this nonce signature against the retrieved 39 // public key. 40 func firstHandshake(req map[string]string) (bool, string) { 41 var pubstr string 42 43 // Check if we have seen this node already 44 ex, err := rcli.Exists(rctx, req["address"]).Result() 45 if err != nil { 46 log.Fatal(err) 47 } 48 49 if ex == 1 { 50 // We saw it so we should hae the public key stored in redis. 51 // If we do not, that is an internal error. 52 pubstr, err = rcli.HGet(rctx, req["address"], "pubkey").Result() 53 if err != nil { 54 log.Fatal(err) 55 } 56 } else { 57 // We take it from the request 58 pubstr = req["pubkey"] 59 } 60 61 randString, err := genRandomASCII(64) 62 if err != nil { 63 log.Fatal(err) 64 } 65 66 enc := base64.StdEncoding.EncodeToString([]byte(randString)) 67 68 var info = map[string]interface{}{ 69 "address": req["address"], 70 "message": enc, 71 "signature": req["signature"], 72 "secret": enc, 73 "lastseen": time.Now().Unix(), 74 } // Can not cast, need this for HSet 75 76 if ex != 1 { 77 // We did not have this node in redis 78 info["pubkey"] = pubstr 79 info["firstseen"] = time.Now().Unix() 80 if *trustall { 81 info["trusted"] = 1 82 } else { 83 info["trusted"] = 0 84 } 85 } 86 87 log.Printf("%s: Writing to redis\n", req["address"]) 88 if _, err := rcli.HSet(rctx, req["address"], info).Result(); err != nil { 89 log.Fatal(err) 90 } 91 92 return true, enc 93 } 94 95 func secondHandshake(req map[string]string) (bool, string) { 96 // Check if we have seen this node already 97 ex, err := rcli.Exists(rctx, req["address"]).Result() 98 if err != nil { 99 log.Fatal(err) 100 } 101 if ex != 1 { 102 log.Printf("%s tried to jump in 2/2 handshake before getting a nonce\n", 103 req["address"]) 104 return false, "We have not seen you before. Authenticate properly." 105 } 106 107 // We saw it so we should have the public key in redis. If we do not, 108 // then it's an internal error. 109 pubstr, err := rcli.HGet(rctx, req["address"], "pubkey").Result() 110 if err != nil { 111 log.Fatal(err) 112 } 113 114 lSec, err := rcli.HGet(rctx, req["address"], "secret").Result() 115 if err != nil { 116 log.Fatal(err) 117 } 118 119 if lSec != req["secret"] || lSec != req["message"] { 120 log.Printf("%s: Secrets didn't match\n", req["address"]) 121 return false, "Secrets didn't match." 122 } 123 124 // Validate signature. 125 msg := []byte(lSec) 126 sig, _ := base64.StdEncoding.DecodeString(req["signature"]) 127 deckey, err := base64.StdEncoding.DecodeString(pubstr) 128 if err != nil { 129 log.Fatal(err) 130 } 131 pubkey := ed25519.PublicKey(deckey) 132 133 if !ed25519.Verify(pubkey, msg, sig) { 134 log.Println("crypto/ed25519: Signature verification failure") 135 return false, "Signature verification failure" 136 } 137 138 // The request is valid at this point 139 140 // Make a new random secret to prevent reuse. 141 randString, _ := genRandomASCII(64) 142 encSecret := base64.StdEncoding.EncodeToString([]byte(randString)) 143 144 var info = map[string]interface{}{ 145 "address": req["address"], 146 "message": encSecret, 147 "signature": req["signature"], 148 "secret": encSecret, 149 "lastseen": time.Now().Unix(), 150 } // TODO: Use struct 151 152 log.Printf("Adding %s to redis\n", req["address"]) 153 if _, err := rcli.HSet(rctx, req["address"], info).Result(); err != nil { 154 log.Fatal(err) 155 } 156 157 return true, "Welcome to tor-dam" 158 }