tor-dam

tor distributed announce mechanism (not a dht)
git clone https://git.parazyd.org/tor-dam
Log | Files | Refs | README | LICENSE

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 }