this repo has no description

implement two-way signature verification

Akshay f9cea457 0d751a41

Changed files
+62 -18
appview
db
state
cmd
knotserver
knotserver
+4 -2
appview/db/db.go
··· 108 108 109 109 var secret string 110 110 err := res.Scan(&secret) 111 - if err != nil { 112 - return "", nil 111 + if err != nil || secret == "" { 112 + return "", err 113 113 } 114 + 115 + log.Println("domain, secret: ", domain, secret) 114 116 115 117 return secret, nil 116 118 }
+45 -12
appview/state/state.go
··· 4 4 "crypto/hmac" 5 5 "crypto/sha256" 6 6 "encoding/hex" 7 + "fmt" 7 8 "log" 8 9 "net/http" 9 - "net/url" 10 10 "time" 11 11 12 12 "github.com/go-chi/chi/v5" ··· 83 83 84 84 // check if domain is valid url, and strip extra bits down to just host 85 85 domain := r.FormValue("domain") 86 - url, err := url.Parse(domain) 87 86 if domain == "" || err != nil { 87 + log.Println(err) 88 88 http.Error(w, "Invalid form", http.StatusBadRequest) 89 89 return 90 90 } 91 91 92 - key, err := s.Db.GenerateRegistrationKey(url.Host, did) 92 + key, err := s.Db.GenerateRegistrationKey(domain, did) 93 93 94 94 if err != nil { 95 95 log.Println(err) ··· 112 112 return 113 113 } 114 114 115 + log.Println("checking ", domain) 116 + 115 117 secret, err := s.Db.GetRegistrationKey(domain) 116 118 if err != nil { 117 119 log.Printf("no key found for domain %s: %s\n", domain, err) 118 120 return 119 121 } 120 - 121 - hmac := hmac.New(sha256.New, []byte(secret)) 122 - signature := hex.EncodeToString(hmac.Sum(nil)) 122 + log.Println("has secret ", secret) 123 123 124 124 // make a request do the knotserver with an empty body and above signature 125 - url, _ := url.Parse(domain) 126 - url = url.JoinPath("check") 127 - pingRequest, err := http.NewRequest("GET", url.String(), nil) 125 + url := fmt.Sprintf("http://%s/internal/health", domain) 126 + 127 + pingRequest, err := buildPingRequest(url, secret) 128 128 if err != nil { 129 - log.Println("failed to create ping request for ", url.String()) 129 + log.Println("failed to build ping request", err) 130 130 return 131 131 } 132 - pingRequest.Header.Set("X-Signature", signature) 133 132 134 133 client := &http.Client{ 135 134 Timeout: 5 * time.Second, ··· 142 141 } 143 142 144 143 if resp.StatusCode != http.StatusOK { 145 - log.Println("status nok") 144 + log.Println("status nok", resp.StatusCode) 146 145 w.Write([]byte("no dice")) 147 146 return 148 147 } 148 + 149 + // verify response mac 150 + signature := resp.Header.Get("X-Signature") 151 + signatureBytes, err := hex.DecodeString(signature) 152 + if err != nil { 153 + return 154 + } 155 + 156 + expectedMac := hmac.New(sha256.New, []byte(secret)) 157 + expectedMac.Write([]byte("ok")) 158 + 159 + if !hmac.Equal(expectedMac.Sum(nil), signatureBytes) { 160 + log.Printf("response body signature mismatch: %x\n", signatureBytes) 161 + return 162 + } 163 + 149 164 w.Write([]byte("check success")) 150 165 151 166 // mark as registered 152 167 s.Db.Register(domain) 153 168 154 169 return 170 + } 171 + 172 + func buildPingRequest(url, secret string) (*http.Request, error) { 173 + pingRequest, err := http.NewRequest("GET", url, nil) 174 + if err != nil { 175 + return nil, err 176 + } 177 + 178 + timestamp := time.Now().Format(time.RFC3339) 179 + mac := hmac.New(sha256.New, []byte(secret)) 180 + message := pingRequest.Method + pingRequest.URL.Path + timestamp 181 + mac.Write([]byte(message)) 182 + signature := hex.EncodeToString(mac.Sum(nil)) 183 + 184 + pingRequest.Header.Set("X-Signature", signature) 185 + pingRequest.Header.Set("X-Timestamp", timestamp) 186 + 187 + return pingRequest, nil 155 188 } 156 189 157 190 func (s *State) Router() http.Handler {
+3 -4
cmd/knotserver/main.go
··· 7 7 "log/slog" 8 8 "net/http" 9 9 "os" 10 - "os/signal" 11 - "syscall" 12 10 13 11 "github.com/icyphox/bild/knotserver" 14 12 "github.com/icyphox/bild/knotserver/config" 15 13 ) 16 14 17 15 func main() { 18 - ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) 19 - defer stop() 16 + ctx := context.Background() 17 + // ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) 18 + // defer stop() 20 19 21 20 slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil))) 22 21
+2
knotserver/middleware.go
··· 4 4 "crypto/hmac" 5 5 "crypto/sha256" 6 6 "encoding/hex" 7 + "log" 7 8 "net/http" 8 9 "time" 9 10 ) ··· 11 12 func (h *Handle) VerifySignature(next http.Handler) http.Handler { 12 13 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 13 14 signature := r.Header.Get("X-Signature") 15 + log.Println(signature) 14 16 if signature == "" || !h.verifyHMAC(signature, r) { 15 17 writeError(w, "signature verification failed", http.StatusForbidden) 16 18 return
+8
knotserver/routes.go
··· 2 2 3 3 import ( 4 4 "compress/gzip" 5 + "crypto/hmac" 6 + "crypto/sha256" 7 + "encoding/hex" 5 8 "encoding/json" 6 9 "errors" 7 10 "fmt" ··· 404 407 // } 405 408 406 409 func (h *Handle) Health(w http.ResponseWriter, r *http.Request) { 410 + log.Println("got health check") 411 + mac := hmac.New(sha256.New, []byte(h.c.Secret)) 412 + mac.Write([]byte("ok")) 413 + w.Header().Add("X-Signature", hex.EncodeToString(mac.Sum(nil))) 414 + 407 415 w.Write([]byte("ok")) 408 416 }