From 35b918f574464eeca89fcf0d5f3a07a58a4b5a38 Mon Sep 17 00:00:00 2001 From: zeripath Date: Wed, 20 Oct 2021 20:55:33 +0100 Subject: [PATCH] Offer rsa-sha2-512 and rsa-sha2-256 algorithms in internal SSH (#17281) * Offer rsa-sha2-512 and rsa-sha2-256 algorithms in internal SSH There is a subtle bug in the SSH library x/crypto/ssh which makes the incorrect assumption that the public key type is the same as the signature algorithm type. This means that only ssh-rsa signatures are offered by default. This PR adds a workaround around this problem. Fix #17175 Signed-off-by: Andrew Thornton * as per review Signed-off-by: Andrew Thornton --- modules/ssh/ssh.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go index efe9525345..909ed32c5e 100644 --- a/modules/ssh/ssh.go +++ b/modules/ssh/ssh.go @@ -316,10 +316,66 @@ func Listen(host string, port int, ciphers []string, keyExchanges []string, macs } } + // Workaround slightly broken behaviour in x/crypto/ssh/handshake.go:458-463 + // + // Fundamentally the issue here is that HostKeyAlgos make the incorrect assumption + // that the PublicKey().Type() matches the signature algorithm. + // + // Therefore we need to add duplicates for the RSA with different signing algorithms. + signers := make([]ssh.Signer, 0, len(srv.HostSigners)) + for _, signer := range srv.HostSigners { + if signer.PublicKey().Type() == "ssh-rsa" { + signers = append(signers, + &wrapSigner{ + Signer: signer, + algorithm: gossh.SigAlgoRSASHA2512, + }, + &wrapSigner{ + Signer: signer, + algorithm: gossh.SigAlgoRSASHA2256, + }, + ) + } + signers = append(signers, signer) + } + srv.HostSigners = signers + go listen(&srv) } +// wrapSigner wraps a signer and overrides its public key type with the provided algorithm +type wrapSigner struct { + ssh.Signer + algorithm string +} + +// PublicKey returns an associated PublicKey instance. +func (s *wrapSigner) PublicKey() gossh.PublicKey { + return &wrapPublicKey{ + PublicKey: s.Signer.PublicKey(), + algorithm: s.algorithm, + } +} + +// Sign returns raw signature for the given data. This method +// will apply the hash specified for the keytype to the data using +// the algorithm assigned for this key +func (s *wrapSigner) Sign(rand io.Reader, data []byte) (*gossh.Signature, error) { + return s.Signer.(gossh.AlgorithmSigner).SignWithAlgorithm(rand, data, s.algorithm) +} + +// wrapPublicKey wraps a PublicKey and overrides its type +type wrapPublicKey struct { + gossh.PublicKey + algorithm string +} + +// Type returns the algorithm +func (k *wrapPublicKey) Type() string { + return k.algorithm +} + // GenKeyPair make a pair of public and private keys for SSH access. // Public key is encoded in the format for inclusion in an OpenSSH authorized_keys file. // Private Key generated is PEM encoded