Description
What version of Go are you using (go version
)?
$ go version go version go1.17.9 darwin/arm64
Does this issue reproduce with the latest release?
No, but if SSH server extension is implemented (golang/crypto#197), this issue will show up under certain circumstances for older clients.
What did you do?
I applied golang/crypto#197, which implements RFC 3808 (server extension negotiation), to the master
branch. Then I attempted to log into a Go SSH server with an Yubikey RSA key on Ubuntu 18.04. The login failed:
ssh: signature "ssh-rsa" came in for selected algorithm "rsa-sha2-512", public key is type ssh-rsa
In https://github.com/golang/crypto/blob/793ad666bf5ec61392092b27061be9618e4e219b/ssh/server.go#L567, the SSH server performs this check:
if underlyingAlgo(algo) != sig.Format {
authErr = fmt.Errorf("ssh: signature %q not compatible with selected algorithm %q", sig.Format, algo)
break
}
This is a sensible check and works for most cases. However, in Ubuntu 18.04 with gpg-agent
v2.2.4 and OpenSSH v7.6, logins that use an RSA key with gpg-agent
will fail to login. This problem does not happen with an OpenSSH server, however.
What did you expect to see?
As the example from RFC8332 shows, here is an example SSH_MSG_USERAUTH_REQUEST message:
byte SSH_MSG_USERAUTH_REQUEST
string user name
string service name
string "publickey"
boolean TRUE
string "rsa-sha2-512" <--- 1 - Public key algorithm name (algo) (https://datatracker.ietf.org/doc/html/rfc4252#page-8)
string public key blob:
string "ssh-rsa" <--- 2 - Public key type (pubKey.Type())
mpint e
mpint n
string signature:
string "rsa-sha2-512" <--- 3 - Signature format (sig.Format)
string rsa_signature_blob
Why does this happen? Since the server advertises it supports rsa-sha2-512
, the OpenSSH client will attempt to use it in the signature. However, gpg-agent
v2.2.4 does not support rsa-sha2-512
; this was added in v2.2.6 via gpg/gnupg@80b775b. As a result, it appears that the SSH_MSG_USERAUTH_REQUEST
has rsa-sha2-512
in 1 but ssh-sha
for 3.
The Go crypto library expects that 1 and 3 match. However, OpenSSH is a bit more lenient, and it only checks that 1 is an RSA algorithm (rsa-dsa
, rsa-sha2-256
, or rsa-sha2-512
), and that this matches with an RSA key in 2. In more detail:
- In https://github.com/openssh/openssh-portable/blob/abd59663df37a42152e37980113ccaa405b9a282/auth2-pubkey.c#L112,
sshkey_type_from_name(pkalg)
returnsKEY_RSA
type (https://github.com/openssh/openssh-portable/blob/0f3455356bc284d7c6f4d3c1614d31161bd5dcc2/sshkey.c#L91-L94). - The public key is of type
KEY_RSA
, so https://github.com/openssh/openssh-portable/blob/abd59663df37a42152e37980113ccaa405b9a282/auth2-pubkey.c#L127 matches. - https://github.com/openssh/openssh-portable/blob/abd59663df37a42152e37980113ccaa405b9a282/auth2-pubkey.c#L200 attempts to verify the public key with the signature by calling
ssh_rsa_verify
in https://github.com/openssh/openssh-portable/blob/83fa3a044891887369ce8b487ce88d713a04df48/ssh-rsa.c#L222. It determines the signature format in 3.
https://datatracker.ietf.org/doc/html/rfc8332#section-3.2 speaks to the problem we're seeing here:
If the client includes the signature field, the client MUST encode
the same algorithm name in the signature as in
SSH_MSG_USERAUTH_REQUEST -- either "rsa-sha2-256" or "rsa-sha2-512".
If a server receives a mismatching request, it MAY apply arbitrary
authentication penalties, including but not limited to authentication
failure or disconnect.
OpenSSH 7.2 (but not 7.2p2) incorrectly encodes the algorithm in the
signature as "ssh-rsa" when the algorithm in SSH_MSG_USERAUTH_REQUEST
is "rsa-sha2-256" or "rsa-sha2-512". In this case, the signature
does actually use either SHA-256 or SHA-512. A server MAY, but is
not required to, accept this variant or another variant that
corresponds to a good-faith implementation and is considered safe to
accept.
Technically, the spec says here that the strict match on the server side is acceptable, but the spec also says that a server may relax this match since we know that:
- The
SSH_MSG_USERAUTH_REQUEST
public key algorithm isssh-rsa
. - The public key type is
rsa-sha
. - The signature type can be
rsa-sha
,rsa-sha2-256
, orrsa-sha2-512
. - The verification of the public key with the signature still must pass.
I realize this may just be covering up old bugs related to gpg-agent
or OpenSSH, but that's what we're seeing in the wild.