ssh: Make error message deterministic

By using a slice instead of a map for tried authentication methods the order is always the same. Small slices are also faster than maps.

Before the change sometimes I get the error:

    ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain

and sometimes:

    ssh: handshake failed: ssh: unable to authenticate, attempted methods [publickey none], no supported methods remain

Change-Id: I06507d57e9eef497ff05bce088d52607e69dde3e
GitHub-Last-Rev: 3a46aae4c6e3e5f52ca4b04384b3cc4efc039aa6
GitHub-Pull-Request: golang/crypto#142
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/239171
Reviewed-by: Han-Wen Nienhuys <hanwen@google.com>
Run-TryBot: Han-Wen Nienhuys <hanwen@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/ssh/client_auth.go b/ssh/client_auth.go
index 0590070..f326565 100644
--- a/ssh/client_auth.go
+++ b/ssh/client_auth.go
@@ -36,7 +36,7 @@
 
 	// during the authentication phase the client first attempts the "none" method
 	// then any untried methods suggested by the server.
-	tried := make(map[string]bool)
+	var tried []string
 	var lastMethods []string
 
 	sessionID := c.transport.getSessionID()
@@ -49,7 +49,9 @@
 			// success
 			return nil
 		} else if ok == authFailure {
-			tried[auth.method()] = true
+			if m := auth.method(); !contains(tried, m) {
+				tried = append(tried, m)
+			}
 		}
 		if methods == nil {
 			methods = lastMethods
@@ -61,7 +63,7 @@
 	findNext:
 		for _, a := range config.Auth {
 			candidateMethod := a.method()
-			if tried[candidateMethod] {
+			if contains(tried, candidateMethod) {
 				continue
 			}
 			for _, meth := range methods {
@@ -72,16 +74,16 @@
 			}
 		}
 	}
-	return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", keys(tried))
+	return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", tried)
 }
 
-func keys(m map[string]bool) []string {
-	s := make([]string, 0, len(m))
-
-	for key := range m {
-		s = append(s, key)
+func contains(list []string, e string) bool {
+	for _, s := range list {
+		if s == e {
+			return true
+		}
 	}
-	return s
+	return false
 }
 
 // An AuthMethod represents an instance of an RFC 4252 authentication method.