aboutsummaryrefslogtreecommitdiffstats
path: root/handshake.go
diff options
context:
space:
mode:
Diffstat (limited to 'handshake.go')
-rw-r--r--handshake.go87
1 files changed, 86 insertions, 1 deletions
diff --git a/handshake.go b/handshake.go
index e47813f..cfe7085 100644
--- a/handshake.go
+++ b/handshake.go
@@ -1,7 +1,9 @@
package main
import (
+ "fmt"
"golang.org/x/sync/errgroup"
+ "io"
"net"
"git.samanthony.xyz/hose/key"
@@ -46,5 +48,88 @@ func handshakeSend(rhost string) error {
// The user is asked to verify the fingerprint of the key before
// it is saved to the known hosts file.
func handshakeRecv(rhost string) error {
- // TODO
+ // Listen for connection.
+ laddr := net.JoinHostPort("", port)
+ ln, err := net.Listen(network, laddr)
+ if err != nil {
+ return err
+ }
+ defer ln.Close()
+ logf("listening on %s", laddr)
+
+ conn, err := ln.Accept()
+ if err != nil {
+ return err
+ }
+ defer conn.Close()
+ logf("accepted connection from %s", conn.RemoteAddr())
+
+ // Receive public key from remote host.
+ var rpubkey [32]byte
+ _, err = io.ReadFull(conn, rpubkey[:])
+ if err != nil {
+ return err
+ }
+ logf("received public key from $s", conn.RemoteAddr())
+
+ // Ask user to verify the fingerprint of the key.
+ ok, err := verifyPublicKey(conn.RemoteAddr(), rpubkey)
+ if err != nil {
+ return err
+ }
+ if !ok {
+ // User rejected the fingerprint.
+ return fmt.Errorf("host key verification failed")
+ }
+
+ return addKnownHost(conn.RemoteAddr(), rpubkey)
+}
+
+// verifyPublicKey asks the user to verify the fingerprint of a public key belonging to a remote host.
+// It returns true if the user accepts the fingerprint, or false if they don't, or a non-nil error.
+func verifyPublicKey(addr net.Addr, pubkey [32]byte) (bool, error) {
+ // Lookup human-friendly name of remote host, or fall back to the address.
+ hostname, err := lookupAddr(addr.String())
+ if err != nil {
+ return false, err
+ }
+
+ // Ask host to verify fingerprint.
+ logf("Fingerprint of host %q: %s\nIs this the correct fingerprint (yes/[no])?",
+ hostname, fingerprint(pubkey))
+ var response string
+ n, err := fmt.Scanln(&response)
+ if err != nil {
+ return false, err
+ }
+ for n > 0 && response != "yes" && response != "no" {
+ logf("Please type 'yes' or 'no'")
+ n, err = fmt.Scanln(&response)
+ if err != nil {
+ return false, err
+ }
+ }
+ if n == 0 {
+ response = "no" // default response (pressed enter without typing anything)
+ }
+ switch response {
+ case "yes":
+ return true, nil
+ case "no":
+ return false, nil
+ }
+ panic("unreachable")
+}
+
+// lookupAddr attempts to resolve the host name of an IP address.
+// If no names are mapped to the address, the address itself is returned.
+func lookupAddr(addr string) (string, error) {
+ hostNames, err := net.LookupAddr(addr)
+ if err != nil {
+ return "", err
+ }
+ if len(hostNames) > 0 {
+ return hostNames[0], nil
+ }
+ return addr, nil
}