From 0ec4fba0bded082b243a3285abc878d24855e1f2 Mon Sep 17 00:00:00 2001 From: Sam Anthony Date: Mon, 14 Apr 2025 19:38:49 -0400 Subject: receive signature verification key in handshake --- .gitignore | 1 + handshake.go | 79 ++++++++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 38bf9c6..3b96afd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /hose acme.dump +.plan diff --git a/handshake.go b/handshake.go index d26d62a..c12d8e7 100644 --- a/handshake.go +++ b/handshake.go @@ -3,10 +3,12 @@ package main import ( "bufio" "context" + "errors" "fmt" "golang.org/x/sync/errgroup" "io" "net" + "net/netip" "os" "slices" "strings" @@ -22,6 +24,15 @@ const ( retryInterval = 500 * time.Millisecond ) +var errHostKey = errors.New("host key verification failed") + +type keyType string + +const ( + boxPublicKey keyType = "Public encryption key" + sigPublicKey = "Public signature verification key" +) + // handshake exchanges public keys with a remote host. // The user is asked to verify the received key // before it is saved in the known hosts file. @@ -54,10 +65,10 @@ func handshake(rhost string) error { } } -// handshakeSend sends the local public key to a remote host. +// handshakeSend sends the local public box (encryption) key to a remote host. func handshakeSend(rhost string) error { - util.Logf("loading public key...") - pubkey, err := key.LoadPublicKey() + util.Logf("loading public encryption key...") + pubBoxkey, err := key.LoadBoxPublicKey() if err != nil { return err } @@ -71,11 +82,11 @@ func handshakeSend(rhost string) error { defer conn.Close() util.Logf("connected to %s", raddr) - if _, err := conn.Write(pubkey[:]); err != nil { + if _, err := conn.Write(pubBoxkey[:]); err != nil { return err } - util.Logf("sent public key to %s", rhost) + util.Logf("sent public encryption key to %s", rhost) return nil } @@ -115,38 +126,58 @@ func handshakeRecv(rhost string) error { defer conn.Close() util.Logf("accepted connection from %s", conn.RemoteAddr()) - // Receive public key from remote host. - var rpubkey [32]byte - _, err = io.ReadFull(conn, rpubkey[:]) + // Receive public box (encryption) key from remote host. + var rBoxPubKey key.BoxPublicKey + _, err = io.ReadFull(conn, rBoxPubKey[:]) if err != nil { return err } - util.Logf("received public key from %s", conn.RemoteAddr()) + util.Logf("received public encryption key from %s", conn.RemoteAddr()) - // Ask user to verify the key. - ok, err := verifyPublicKey(conn.RemoteAddr(), rpubkey) + // Receive public signature verification key from remote host. + var rSigPubKey key.SigPublicKey + _, err = io.ReadFull(conn, rSigPubKey[:]) if err != nil { return err } - if !ok { - // User rejected the key. - return fmt.Errorf("host key verification failed") - } + util.Logf("receive public signature verification key from %s", conn.RemoteAddr()) - return hosts.Set(conn.RemoteAddr(), rpubkey) -} + // Ask user to verify the keys. + host, _, err := net.SplitHostPort(conn.RemoteAddr().String()) + if err != nil { + return err + } + // Verify box key. + ok, err := verifyKey(host, rBoxPubKey[:], boxPublicKey) + if err != nil { + return err + } + if !ok { // user rejected the key. + return errHostKey + } + // Verify signature verification key. + ok, err = verifyKey(host, rSigPubKey[:], sigPublicKey) + if err != nil { + return err + } + if !ok { // user rejected the key. + return errHostKey + } -// verifyPublicKey asks the user to verify the public key of a remote host. -// It returns true if the user accepts the key, or false if they don't, or a non-nil error. -func verifyPublicKey(addr net.Addr, pubkey [32]byte) (bool, error) { - host, _, err := net.SplitHostPort(addr.String()) + // Save in known hosts file. + rAddr, err := netip.ParseAddr(conn.RemoteAddr().String()) if err != nil { - return false, err + return err } + return hosts.Add(hosts.Host{rAddr, rBoxPubKey, rSigPubKey}) +} +// verifyKey asks the user to verify a key received from a remote host. +// It returns true if the user accepts the key, or false if they don't, or a non-nil error. +func verifyKey(host string, key []byte, kt keyType) (bool, error) { // Ask host to verify the key. - util.Logf("Public key of host %q: %x\nIs this the correct key (yes/[no])?", - host, pubkey[:]) + util.Logf("%s key of host %q: %x\nIs this the correct key (yes/[no])?", + kt, host, key[:]) response, err := scan([]string{"yes", "no", ""}) if err != nil { return false, err -- cgit v1.2.3