ssh/knownhosts: improve IPv6 support in Normalize

Correctly converts bracketed IPv6:

- [abcd::abcd:abcd:abcd] => abcd::abcd:abcd:abcd
- [abcd::abcd:abcd:abcd]:22 => abcd::abcd:abcd:abcd
- [abcd::abcd:abcd:abcd]:23 => [abcd::abcd:abcd:abcd]:23

Fixes golang/go#53463

Change-Id: Id0a7460d8448a72e2a8c6d46137245bead9ecf9f
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/694575
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Carlos Amedee <carlos@golang.org>
diff --git a/ssh/knownhosts/knownhosts.go b/ssh/knownhosts/knownhosts.go
index c022e41..1ebd7e6 100644
--- a/ssh/knownhosts/knownhosts.go
+++ b/ssh/knownhosts/knownhosts.go
@@ -421,20 +421,26 @@
 	return certChecker.CheckHostKey, nil
 }
 
-// Normalize normalizes an address into the form used in known_hosts
+// Normalize normalizes an address into the form used in known_hosts. Supports
+// IPv4, hostnames, bracketed IPv6. Any other non-standard formats are returned
+// with minimal transformation.
 func Normalize(address string) string {
+	const defaultSSHPort = "22"
+
 	host, port, err := net.SplitHostPort(address)
 	if err != nil {
 		host = address
-		port = "22"
+		port = defaultSSHPort
 	}
-	entry := host
-	if port != "22" {
-		entry = "[" + entry + "]:" + port
-	} else if strings.Contains(host, ":") && !strings.HasPrefix(host, "[") {
-		entry = "[" + entry + "]"
+
+	if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
+		host = host[1 : len(host)-1]
 	}
-	return entry
+
+	if port == defaultSSHPort {
+		return host
+	}
+	return "[" + host + "]:" + port
 }
 
 // Line returns a line to add append to the known_hosts files.
diff --git a/ssh/knownhosts/knownhosts_test.go b/ssh/knownhosts/knownhosts_test.go
index 156576a..552a556 100644
--- a/ssh/knownhosts/knownhosts_test.go
+++ b/ssh/knownhosts/knownhosts_test.go
@@ -236,7 +236,7 @@
 		"server.org":                             "server.org " + edKeyStr,
 		"server.org:22":                          "server.org " + edKeyStr,
 		"server.org:23":                          "[server.org]:23 " + edKeyStr,
-		"[c629:1ec4:102:304:102:304:102:304]:22": "[c629:1ec4:102:304:102:304:102:304] " + edKeyStr,
+		"[c629:1ec4:102:304:102:304:102:304]:22": "c629:1ec4:102:304:102:304:102:304 " + edKeyStr,
 		"[c629:1ec4:102:304:102:304:102:304]:23": "[c629:1ec4:102:304:102:304:102:304]:23 " + edKeyStr,
 	} {
 		if got := Line([]string{in}, edKey); got != want {
@@ -310,14 +310,25 @@
 
 func TestNormalize(t *testing.T) {
 	for in, want := range map[string]string{
-		"127.0.0.1:22":             "127.0.0.1",
-		"[127.0.0.1]:22":           "127.0.0.1",
-		"[127.0.0.1]:23":           "[127.0.0.1]:23",
-		"127.0.0.1:23":             "[127.0.0.1]:23",
-		"[a.b.c]:22":               "a.b.c",
-		"[abcd:abcd:abcd:abcd]":    "[abcd:abcd:abcd:abcd]",
-		"[abcd:abcd:abcd:abcd]:22": "[abcd:abcd:abcd:abcd]",
-		"[abcd:abcd:abcd:abcd]:23": "[abcd:abcd:abcd:abcd]:23",
+		"127.0.0.1":                 "127.0.0.1",
+		"127.0.0.1:22":              "127.0.0.1",
+		"[127.0.0.1]:22":            "127.0.0.1",
+		"[127.0.0.1]:23":            "[127.0.0.1]:23",
+		"127.0.0.1:23":              "[127.0.0.1]:23",
+		"[a.b.c]:22":                "a.b.c",
+		"[a.b.c]:23":                "[a.b.c]:23",
+		"abcd::abcd:abcd:abcd":      "abcd::abcd:abcd:abcd",
+		"[abcd::abcd:abcd:abcd]":    "abcd::abcd:abcd:abcd",
+		"[abcd::abcd:abcd:abcd]:22": "abcd::abcd:abcd:abcd",
+		"[abcd::abcd:abcd:abcd]:23": "[abcd::abcd:abcd:abcd]:23",
+		"2001:db8::1":               "2001:db8::1",
+		"2001:db8::1:22":            "2001:db8::1:22",
+		"[2001:db8::1]:22":          "2001:db8::1",
+		"2001:db8::1:2200":          "2001:db8::1:2200",
+		"a.b.c.d.com:2200":          "[a.b.c.d.com]:2200",
+		"2001::db8:1":               "2001::db8:1",
+		"2001::db8:1:22":            "2001::db8:1:22",
+		"2001::db8:1:2200":          "2001::db8:1:2200",
 	} {
 		got := Normalize(in)
 		if got != want {