Description
What version of Go are you using (go version
)?
# go version go version go1.18 linux/amd64
(running in the go:1.18
docker image)
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
go env
Output
# go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/root/.cache/go-build" GOENV="/root/.config/go/env" GOEXE="" GOEXPERIMENT="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/local/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64" GOVCS="" GOVERSION="go1.18" GCCGO="gccgo" GOAMD64="v1" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="/work/go.mod" GOWORK="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build3649815783=/tmp/go-build -gno-record-gcc-switches"
What did you do?
Gist: https://gist.github.com/jeffreytolar/9fd9ed8c01802fb7d36d98ed268f3a18
This gist has three approaches for configuring SSH authentication using an ECDSA-backed certificate (ecdsa-sha2-nistp256-cert-v01@openssh.com
):
- Using
ssh.NewCertSigner
- Using an
agent.Keyring
- Using an
agent.NewClient
talking to anagent.Keyring
served viaagent.ServeAgent
Run using go run .
; as written, the gist uses the last approach, with the other two commented out.
What did you expect to see?
Successful running of the gist:
2022/04/06 17:15:27 [server] Completed handshake
2022/04/06 17:15:27 [client] Completed handshake
What did you see instead?
An error during authentication:
panic: ssh: handshake failed: agent: unsupported algorithm "ecdsa-sha2-nistp256"
goroutine 1 [running]:
main.check(...)
/tmp/9fd9ed8c01802fb7d36d98ed268f3a18/test.go:19
main.client({0x5ef2f8, 0xc000100800}, {0xc000100920, 0x1, 0x1})
/tmp/9fd9ed8c01802fb7d36d98ed268f3a18/test.go:80 +0x271
main.main()
/tmp/9fd9ed8c01802fb7d36d98ed268f3a18/test.go:124 +0x90
exit status 2
Using one of the other approaches (adjust the comments in main
) works. go get golang.org/x/crypto@a5774263c1e06050d4c71b5794b2b5a321289a8f
also works (with all three approaches).
I think this is a regression caused by golang/crypto@5d542ad; previously, (*agentKeyringSigner).SignWithOpts
always fell back on s.agent.SignWithFlags
with no flags, whereas the new SignWithAlgorithm
errors out.
In doing some debugging, SignWithAlgorithm
is getting called with algorithm="ecdsa-sha2-nistp256"
, whereas s.pub.Type()
is "ecdsa-sha2-nistp256-cert-v01@openssh.com"
.
Currently, my workaround is calling ssh.underlyingAlgo(s.pub.Type())
, which works, although might not be the correct fix:
type patchedAgent struct {
agent.Agent
}
func (p patchedAgent) Signers() (signers []ssh.Signer, err error) {
signers, err = p.Agent.Signers()
for i := range signers {
signers[i] = patchedSigner{signers[i]}
}
return
}
type patchedSigner struct {
ssh.Signer
}
func (s patchedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*ssh.Signature, error) {
// missing check in crypto/ssh
if algorithm == underlyingAlgo(s.Signer.PublicKey().Type()) {
return s.Sign(rand, data)
}
if as, ok := s.Signer.(ssh.AlgorithmSigner); ok {
return as.SignWithAlgorithm(rand, data, algorithm)
}
return s.Sign(rand, data)
}
//go:linkname underlyingAlgo golang.org/x/crypto/ssh.underlyingAlgo
func underlyingAlgo(algo string) string