- 
                Notifications
    You must be signed in to change notification settings 
- Fork 18.4k
Open
golang/crypto
#322Labels
BugReportIssues describing a possible bug in the Go implementation.Issues describing a possible bug in the Go implementation.NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone
Description
Go version
go version go1.24.6 linux/amd64
Output of go env in your module/workspace:
AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/home/heshamcli/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/heshamcli/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build4145496535=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/home/heshamcli/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/heshamcli/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/go'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/heshamcli/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.24.6'
GOWORK=''
PKG_CONFIG='pkg-config'What did you do?
I set up an SSH server using golang.org/x/crypto/ssh and attempted to connect with a client (Paramiko) that tries multiple keys within a single SSH connection.
Paramiko sends a new SSH_MSG_SERVICE_REQUEST before each key attempt.
The following is a minimal, reduced version of a real production use case that reproduces the issue.
Minimal reproducible Go server example:
package main
import (
	"fmt"
	"log"
	"net"
	"golang.org/x/crypto/ssh"
)
func main() {
	private, err := ssh.ParsePrivateKey([]byte(testServerKey))
	if err != nil {
		log.Fatalf("failed to parse private key: %v", err)
	}
	config := &ssh.ServerConfig{NoClientAuth: false}
	config.AddHostKey(private)
	config.PublicKeyCallback = func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
		// No keys are accepted — force failure to test multiple attempts.
		fmt.Println("Key attempt from:", conn.User())
		return nil, fmt.Errorf("unauthorized")
	}
	listener, err := net.Listen("tcp", ":2222")
	if err != nil {
		log.Fatalf("failed to listen on 2222: %v", err)
	}
	log.Println("Listening on :2222")
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Fatalf("failed to accept incoming connection: %v", err)
		}
		go func(c net.Conn) {
			_, chans, reqs, err := ssh.NewServerConn(c, config)
			if err != nil {
				log.Printf("handshake failed: %v", err)
				return
			}
			go ssh.DiscardRequests(reqs)
			for ch := range chans {
				ch.Reject(ssh.Prohibited, "no channels accepted")
			}
		}(conn)
	}
}
const testServerKey = `-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBANlfqF8A3gBzgU6bEqkG6nRzrtNdEPr+HMX3/fOjYbt5tI/gA2w7
s+pu5PxtjLMToCbMSqXnsRUtCTy3s+N3LfkCAwEAAQJAAOso8bEqBuYkqWolHpw3
s1+Zp0xDZklnwQkk+blO2f4VokFHVf0EheG45KkQAfIZK0GnxK5v5X9vQb6PtX2z
wQIhAPM7hpE0DwHHZbT+zG8cEoVazDNZdPpjU1wJ7HKrPu2DAiEA3ZyxB7KhSvn1
bA9UURccYmn7ppHJ3FQogZhTY7v8sokCIQDaxl00GV4g8ow6HxWfVHtLgQYVyxGi
gFRcHyPuPGvTiQIhAIAvdfmXoiAEAKLyH2VG9r5L6fDZzOVlK3kU4IJK1ahJAiBZ
3Ghuo1VGeF2hHpiFci0JHk3akGczRGwQ9+QXJFx+0g==
-----END RSA PRIVATE KEY-----`Minimal Paramiko client example (Python):
import paramiko
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Create a single underlying transport (one connection)
sock = client._get_socket("localhost", 2222, timeout=5)
transport = paramiko.Transport(sock)
transport.start_client()
# Load multiple keys and try them one by one on the same transport
keys = [
    paramiko.RSAKey.from_private_key_file("/path/to/key1"),
    paramiko.Ed25519Key.from_private_key_file("/path/to/key2"),
]
for key in keys:
    try:
        transport.auth_publickey("test", key)
        print(f"Authenticated with key {key.get_name()}")
        break
    except Exception as e:
        print("Auth failed with key:", e)What did you see happen?
- The server logged the first key attempt, then closed the connection immediately.
- The second key attempt never reached the server.
- Paramiko retried authentication in the same connection by sending a new SSH_MSG_SERVICE_REQUEST, but the server did not accept it.
Server log example:
Listening on :2222
Key attempt from: test
handshake failed: ssh: handshake failed: ssh: no auth passed yet
Client output:
Auth failed with key: Authentication failed.
Auth failed with key: [Errno 104] Connection reset by peer
Additionally
- OpenSSH server works as expected when using sshd, multiple key attempts in one connection work fine.
 This is because OpenSSH does not send anotherSSH_MSG_SERVICE_REQUESTafter the first authentication attempt; it simply proceeds to send additionalSSH_MSG_USERAUTH_REQUESTpackets for each key.
What did you expect to see?
- The server should accept the repeated SSH_MSG_SERVICE_REQUESTpackets and allow multiple key attempts in the same session.
- The connection should only be closed after all attempts fail (or after exceeding MaxAuthTries).
Metadata
Metadata
Assignees
Labels
BugReportIssues describing a possible bug in the Go implementation.Issues describing a possible bug in the Go implementation.NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.