go.crypto/ssh: fix certificate parsing/marshaling.

The change to add the PublicKey interface accidentally caused certificate handling to expect an extra copy of the private key algorithm name in the binary representation. This change adapts a suitable parsing API and adds a test to ensure that cert handling isn't easily broken in the future.

R=agl, hanwen, jmpittman
CC=golang-dev
https://golang.org/cl/13272055
diff --git a/ssh/keys.go b/ssh/keys.go
index 0c6c6b3..fa1e236 100644
--- a/ssh/keys.go
+++ b/ssh/keys.go
@@ -31,14 +31,10 @@
 	KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
 )
 
-// parsePubKey parses a public key according to RFC 4253, section 6.6.
-func parsePubKey(in []byte) (pubKey PublicKey, rest []byte, ok bool) {
-	algo, in, ok := parseString(in)
-	if !ok {
-		return
-	}
-
-	switch string(algo) {
+// parsePubKey parses a public key of the given algorithm.
+// Use ParsePublicKey for keys with prepended algorithm.
+func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, ok bool) {
+	switch algo {
 	case KeyAlgoRSA:
 		return parseRSA(in)
 	case KeyAlgoDSA:
@@ -46,7 +42,7 @@
 	case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
 		return parseECDSA(in)
 	case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
-		return parseOpenSSHCertV01(in, string(algo))
+		return parseOpenSSHCertV01(in, algo)
 	}
 	return nil, nil, false
 }
@@ -54,7 +50,7 @@
 // parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
 // (see sshd(8) manual page) once the options and key type fields have been
 // removed.
-func parseAuthorizedKey(in []byte) (out interface{}, comment string, ok bool) {
+func parseAuthorizedKey(in []byte) (out PublicKey, comment string, ok bool) {
 	in = bytes.TrimSpace(in)
 
 	i := bytes.IndexAny(in, " \t")
@@ -69,7 +65,7 @@
 		return
 	}
 	key = key[:n]
-	out, _, ok = parsePubKey(key)
+	out, _, ok = ParsePublicKey(key)
 	if !ok {
 		return nil, "", false
 	}
@@ -79,7 +75,7 @@
 
 // ParseAuthorizedKeys parses a public key from an authorized_keys
 // file used in OpenSSH according to the sshd(8) manual page.
-func ParseAuthorizedKey(in []byte) (out interface{}, comment string, options []string, rest []byte, ok bool) {
+func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, ok bool) {
 	for len(in) > 0 {
 		end := bytes.IndexByte(in, '\n')
 		if end != -1 {
@@ -179,9 +175,14 @@
 }
 
 // ParsePublicKey parses an SSH public key formatted for use in
-// the SSH wire protocol.
+// the SSH wire protocol according to RFC 4253, section 6.6.
 func ParsePublicKey(in []byte) (out PublicKey, rest []byte, ok bool) {
-	return parsePubKey(in)
+	algo, in, ok := parseString(in)
+	if !ok {
+		return
+	}
+
+	return parsePubKey(in, string(algo))
 }
 
 // MarshalAuthorizedKey returns a byte stream suitable for inclusion