Skip to content

Commit

Permalink
ssh: deprecate and replace SigAlgo constants
Browse files Browse the repository at this point in the history
RFC 8332, Section 2 sets up two overlapping namespaces: public key
formats and public key algorithms.

* The formats are what we currently have KeyAlgo constants for, and they
  appear in PublicKey.Type.

* The algorithms are the set of both KeyAlgo and SigAlgo constants, and
  they appear in Signature.Format (amongst other places).

This is incoherent, because that means Signature.Format can be both a
KeyAlgo (like KeyAlgoECDSA256) or a SigAlgo (like SigAlgoRSASHA2256).

One solution would be to duplicate all the KeyAlgo constants into the
SigAlgo namespace, but that would be confusing because applications are
currently using KeyAlgos where they'd be supposed to use the new
SigAlgos (while we can't deprecate the KeyAlgos because they are still
necessary for the PublicKey.Type namespace).

Instead, drop the separate namespaces, and use KeyAlgos throughout.
There are simply some KeyAlgos that can't be a PublicKey.Type.

Take the opportunity to fix the stuttering SHA22565/SHA2512 names. It's
totally ok to call those hashes SHA-256 and SHA-512 without the family
infix.

For golang/go#49952

Change-Id: Ia1fce3912a7e60aa70a88f75ed311be331fd19d5
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/392354
Trust: Filippo Valsorda <[email protected]>
Run-TryBot: Filippo Valsorda <[email protected]>
Reviewed-by: Roland Shoemaker <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
  • Loading branch information
FiloSottile committed Mar 14, 2022
1 parent cff7455 commit cd32840
Show file tree
Hide file tree
Showing 13 changed files with 93 additions and 82 deletions.
6 changes: 3 additions & 3 deletions ssh/agent/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,9 @@ func testAgentInterface(t *testing.T, agent ExtendedAgent, key interface{}, cert
t.Fatalf("Verify(%s): %v", pubKey.Type(), err)
}
}
sshFlagTest(0, ssh.SigAlgoRSA)
sshFlagTest(SignatureFlagRsaSha256, ssh.SigAlgoRSASHA2256)
sshFlagTest(SignatureFlagRsaSha512, ssh.SigAlgoRSASHA2512)
sshFlagTest(0, ssh.KeyAlgoRSA)
sshFlagTest(SignatureFlagRsaSha256, ssh.KeyAlgoRSASHA256)
sshFlagTest(SignatureFlagRsaSha512, ssh.KeyAlgoRSASHA512)
}

// If the key has a lifetime, is it removed when it should be?
Expand Down
4 changes: 2 additions & 2 deletions ssh/agent/keyring.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,9 @@ func (r *keyring) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureF
var algorithm string
switch flags {
case SignatureFlagRsaSha256:
algorithm = ssh.SigAlgoRSASHA2256
algorithm = ssh.KeyAlgoRSASHA256
case SignatureFlagRsaSha512:
algorithm = ssh.SigAlgoRSASHA2512
algorithm = ssh.KeyAlgoRSASHA512
default:
return nil, fmt.Errorf("agent: unsupported signature flags: %d", flags)
}
Expand Down
37 changes: 22 additions & 15 deletions ssh/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import (
"time"
)

// These constants from [PROTOCOL.certkeys] represent the key algorithm names
// for certificate types supported by this package.
// Certificate algorithm names from [PROTOCOL.certkeys]. These values can appear
// in Certificate.Type, PublicKey.Type, and ClientConfig.HostKeyAlgorithms.
// Unlike key algorithm names, these are not passed to AlgorithmSigner and don't
// appear in the Signature.Format field.
const (
CertAlgoRSAv01 = "[email protected]"
CertAlgoDSAv01 = "[email protected]"
Expand All @@ -25,14 +27,21 @@ const (
CertAlgoSKECDSA256v01 = "[email protected]"
CertAlgoED25519v01 = "[email protected]"
CertAlgoSKED25519v01 = "[email protected]"

// CertAlgoRSASHA256v01 and CertAlgoRSASHA512v01 can't appear as a
// Certificate.Type (or PublicKey.Type), but only in
// ClientConfig.HostKeyAlgorithms.
CertAlgoRSASHA256v01 = "[email protected]"
CertAlgoRSASHA512v01 = "[email protected]"
)

// These constants from [PROTOCOL.certkeys] represent additional signature
// algorithm names for certificate types supported by this package.
const (
CertSigAlgoRSAv01 = "[email protected]"
CertSigAlgoRSASHA2256v01 = "[email protected]"
CertSigAlgoRSASHA2512v01 = "[email protected]"
// Deprecated: use CertAlgoRSAv01.
CertSigAlgoRSAv01 = CertAlgoRSAv01
// Deprecated: use CertAlgoRSASHA256v01.
CertSigAlgoRSASHA2256v01 = CertAlgoRSASHA256v01
// Deprecated: use CertAlgoRSASHA512v01.
CertSigAlgoRSASHA2512v01 = CertAlgoRSASHA512v01
)

// Certificate types distinguish between host and user
Expand Down Expand Up @@ -433,7 +442,7 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {

if v, ok := authority.(AlgorithmSigner); ok {
if v.PublicKey().Type() == KeyAlgoRSA {
authority = &rsaSigner{v, SigAlgoRSASHA2512}
authority = &rsaSigner{v, KeyAlgoRSASHA512}
}
}

Expand All @@ -446,13 +455,11 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
}

// certAlgoNames includes a mapping from signature algorithms to the
// corresponding certificate signature algorithm. When a key type (such
// as ED25516) is associated with only one algorithm, the KeyAlgo
// constant is used instead of the SigAlgo.
// corresponding certificate signature algorithm.
var certAlgoNames = map[string]string{
SigAlgoRSA: CertSigAlgoRSAv01,
SigAlgoRSASHA2256: CertSigAlgoRSASHA2256v01,
SigAlgoRSASHA2512: CertSigAlgoRSASHA2512v01,
KeyAlgoRSA: CertAlgoRSAv01,
KeyAlgoRSASHA256: CertAlgoRSASHA256v01,
KeyAlgoRSASHA512: CertAlgoRSASHA512v01,
KeyAlgoDSA: CertAlgoDSAv01,
KeyAlgoECDSA256: CertAlgoECDSA256v01,
KeyAlgoECDSA384: CertAlgoECDSA384v01,
Expand Down Expand Up @@ -514,7 +521,7 @@ func (c *Certificate) Marshal() []byte {
return result
}

// Type returns the key name. It is part of the PublicKey interface.
// Type returns the certificate algorithm name. It is part of the PublicKey interface.
func (c *Certificate) Type() string {
algo, ok := certAlgoNames[c.Key.Type()]
if !ok {
Expand Down
10 changes: 5 additions & 5 deletions ssh/certs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func (s *legacyRSASigner) Sign(rand io.Reader, data []byte) (*Signature, error)
if !ok {
return nil, fmt.Errorf("invalid signer")
}
return v.SignWithAlgorithm(rand, data, SigAlgoRSA)
return v.SignWithAlgorithm(rand, data, KeyAlgoRSA)
}

func TestCertTypes(t *testing.T) {
Expand All @@ -248,10 +248,10 @@ func TestCertTypes(t *testing.T) {
{CertAlgoECDSA384v01, testSigners["ecdsap384"], ""},
{CertAlgoECDSA521v01, testSigners["ecdsap521"], ""},
{CertAlgoED25519v01, testSigners["ed25519"], ""},
{CertAlgoRSAv01, testSigners["rsa"], SigAlgoRSASHA2512},
{CertAlgoRSAv01, &legacyRSASigner{testSigners["rsa"]}, SigAlgoRSA},
{CertAlgoRSAv01, testSigners["rsa-sha2-256"], SigAlgoRSASHA2512},
{CertAlgoRSAv01, testSigners["rsa-sha2-512"], SigAlgoRSASHA2512},
{CertAlgoRSAv01, testSigners["rsa"], KeyAlgoRSASHA512},
{CertAlgoRSAv01, &legacyRSASigner{testSigners["rsa"]}, KeyAlgoRSA},
{CertAlgoRSAv01, testSigners["rsa-sha2-256"], KeyAlgoRSASHA512},
{CertAlgoRSAv01, testSigners["rsa-sha2-512"], KeyAlgoRSASHA512},
{CertAlgoDSAv01, testSigners["dsa"], ""},
}

Expand Down
8 changes: 4 additions & 4 deletions ssh/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,11 @@ type ClientConfig struct {
// be used for the connection. If empty, a reasonable default is used.
ClientVersion string

// HostKeyAlgorithms lists the key types that the client will
// accept from the server as host key, in order of
// HostKeyAlgorithms lists the public key algorithms that the client will
// accept from the server for host key authentication, in order of
// preference. If empty, a reasonable default is used. Any
// string returned from PublicKey.Type method may be used, or
// any of the CertAlgoXxxx and KeyAlgoXxxx constants.
// string returned from a PublicKey.Type method may be used, or
// any of the CertAlgo and KeyAlgo constants.
HostKeyAlgorithms []string

// Timeout is the maximum amount of time for the TCP connection to establish.
Expand Down
6 changes: 3 additions & 3 deletions ssh/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ func TestVerifyHostKeySignature(t *testing.T) {
verifyAlgo string
wantError string
}{
{"rsa", SigAlgoRSA, SigAlgoRSA, ""},
{"rsa", SigAlgoRSASHA2256, SigAlgoRSASHA2256, ""},
{"rsa", SigAlgoRSA, SigAlgoRSASHA2512, `ssh: invalid signature algorithm "ssh-rsa", expected "rsa-sha2-512"`},
{"rsa", KeyAlgoRSA, KeyAlgoRSA, ""},
{"rsa", KeyAlgoRSASHA256, KeyAlgoRSASHA256, ""},
{"rsa", KeyAlgoRSA, KeyAlgoRSASHA512, `ssh: invalid signature algorithm "ssh-rsa", expected "rsa-sha2-512"`},
{"ed25519", KeyAlgoED25519, KeyAlgoED25519, ""},
} {
key := testSigners[tt.key].PublicKey()
Expand Down
36 changes: 18 additions & 18 deletions ssh/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ var preferredKexAlgos = []string{
// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
// of authenticating servers) in preference order.
var supportedHostKeyAlgos = []string{
CertSigAlgoRSASHA2512v01, CertSigAlgoRSASHA2256v01,
CertSigAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
CertAlgoRSASHA512v01, CertAlgoRSASHA256v01,
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,

KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
SigAlgoRSASHA2512, SigAlgoRSASHA2256,
SigAlgoRSA, KeyAlgoDSA,
KeyAlgoRSASHA512, KeyAlgoRSASHA256,
KeyAlgoRSA, KeyAlgoDSA,

KeyAlgoED25519,
}
Expand All @@ -92,20 +92,20 @@ var supportedCompressions = []string{compressionNone}
// hashFuncs keeps the mapping of supported algorithms to their respective
// hashes needed for signature verification.
var hashFuncs = map[string]crypto.Hash{
SigAlgoRSA: crypto.SHA1,
SigAlgoRSASHA2256: crypto.SHA256,
SigAlgoRSASHA2512: crypto.SHA512,
KeyAlgoDSA: crypto.SHA1,
KeyAlgoECDSA256: crypto.SHA256,
KeyAlgoECDSA384: crypto.SHA384,
KeyAlgoECDSA521: crypto.SHA512,
CertSigAlgoRSAv01: crypto.SHA1,
CertSigAlgoRSASHA2256v01: crypto.SHA256,
CertSigAlgoRSASHA2512v01: crypto.SHA512,
CertAlgoDSAv01: crypto.SHA1,
CertAlgoECDSA256v01: crypto.SHA256,
CertAlgoECDSA384v01: crypto.SHA384,
CertAlgoECDSA521v01: crypto.SHA512,
KeyAlgoRSA: crypto.SHA1,
KeyAlgoRSASHA256: crypto.SHA256,
KeyAlgoRSASHA512: crypto.SHA512,
KeyAlgoDSA: crypto.SHA1,
KeyAlgoECDSA256: crypto.SHA256,
KeyAlgoECDSA384: crypto.SHA384,
KeyAlgoECDSA521: crypto.SHA512,
CertAlgoRSAv01: crypto.SHA1,
CertAlgoRSASHA256v01: crypto.SHA256,
CertAlgoRSASHA512v01: crypto.SHA512,
CertAlgoDSAv01: crypto.SHA1,
CertAlgoECDSA256v01: crypto.SHA256,
CertAlgoECDSA384v01: crypto.SHA384,
CertAlgoECDSA521v01: crypto.SHA512,
}

// unexpectedMessageError results when the SSH message that we received didn't
Expand Down
8 changes: 4 additions & 4 deletions ssh/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,9 +460,9 @@ func (t *handshakeTransport) sendKexInit() error {
algo := k.PublicKey().Type()
switch algo {
case KeyAlgoRSA:
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, []string{SigAlgoRSASHA2512, SigAlgoRSASHA2256, SigAlgoRSA}...)
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, []string{KeyAlgoRSASHA512, KeyAlgoRSASHA256, KeyAlgoRSA}...)
case CertAlgoRSAv01:
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, []string{CertSigAlgoRSASHA2512v01, CertSigAlgoRSASHA2256v01, CertSigAlgoRSAv01}...)
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, []string{CertAlgoRSASHA512v01, CertAlgoRSASHA256v01, CertAlgoRSAv01}...)
default:
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, algo)
}
Expand Down Expand Up @@ -629,11 +629,11 @@ func (t *handshakeTransport) server(kex kexAlgorithm, algs *algorithms, magics *
// so we have to manually check for a compatible host key.
switch kt {
case KeyAlgoRSA:
if algs.hostKey == SigAlgoRSASHA2256 || algs.hostKey == SigAlgoRSASHA2512 {
if algs.hostKey == KeyAlgoRSASHA256 || algs.hostKey == KeyAlgoRSASHA512 {
hostKey = &rsaSigner{signer, algs.hostKey}
}
case CertAlgoRSAv01:
if algs.hostKey == CertSigAlgoRSASHA2256v01 || algs.hostKey == CertSigAlgoRSASHA2512v01 {
if algs.hostKey == CertAlgoRSASHA256v01 || algs.hostKey == CertAlgoRSASHA512v01 {
hostKey = &rsaSigner{signer, certToPrivAlgo(algs.hostKey)}
}
}
Expand Down
46 changes: 25 additions & 21 deletions ssh/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ import (
"golang.org/x/crypto/ssh/internal/bcrypt_pbkdf"
)

// These constants represent the algorithm names for key types supported by this
// package.
// Public key algorithms names. These values can appear in PublicKey.Type,
// ClientConfig.HostKeyAlgorithms, Signature.Format, or as AlgorithmSigner
// arguments.
const (
KeyAlgoRSA = "ssh-rsa"
KeyAlgoDSA = "ssh-dss"
Expand All @@ -41,16 +42,21 @@ const (
KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
KeyAlgoED25519 = "ssh-ed25519"
KeyAlgoSKED25519 = "[email protected]"

// KeyAlgoRSASHA256 and KeyAlgoRSASHA512 are only public key algorithms, not
// public key formats, so they can't appear as a PublicKey.Type. The
// corresponding PublicKey.Type is KeyAlgoRSA. See RFC 8332, Section 2.
KeyAlgoRSASHA256 = "rsa-sha2-256"
KeyAlgoRSASHA512 = "rsa-sha2-512"
)

// These constants represent non-default signature algorithms that are supported
// as algorithm parameters to AlgorithmSigner.SignWithAlgorithm methods. See
// [PROTOCOL.agent] section 4.5.1 and
// https://tools.ietf.org/html/draft-ietf-curdle-rsa-sha2-10
const (
SigAlgoRSA = "ssh-rsa"
SigAlgoRSASHA2256 = "rsa-sha2-256"
SigAlgoRSASHA2512 = "rsa-sha2-512"
// Deprecated: use KeyAlgoRSA.
SigAlgoRSA = KeyAlgoRSA
// Deprecated: use KeyAlgoRSASHA256.
SigAlgoRSASHA2256 = KeyAlgoRSASHA256
// Deprecated: use KeyAlgoRSASHA512.
SigAlgoRSASHA2512 = KeyAlgoRSASHA512
)

// parsePubKey parses a public key of the given algorithm.
Expand Down Expand Up @@ -325,11 +331,9 @@ type Signer interface {
type AlgorithmSigner interface {
Signer

// SignWithAlgorithm is like Signer.Sign, but allows specification of a
// non-default signing algorithm. See the SigAlgo* constants in this
// package for signature algorithms supported by this package. Callers may
// pass an empty string for the algorithm in which case the AlgorithmSigner
// will use its default algorithm.
// SignWithAlgorithm is like Signer.Sign, but allows specifying a desired
// signing algorithm. Callers may pass an empty string for the algorithm in
// which case the AlgorithmSigner will use a default algorithm.
SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error)
}

Expand Down Expand Up @@ -383,11 +387,11 @@ func (r *rsaPublicKey) Marshal() []byte {
func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error {
var hash crypto.Hash
switch sig.Format {
case SigAlgoRSA:
case KeyAlgoRSA:
hash = crypto.SHA1
case SigAlgoRSASHA2256:
case KeyAlgoRSASHA256:
hash = crypto.SHA256
case SigAlgoRSASHA2512:
case KeyAlgoRSASHA512:
hash = crypto.SHA512
default:
return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type())
Expand Down Expand Up @@ -979,12 +983,12 @@ func (s *wrappedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm
if _, ok := s.pubKey.(*rsaPublicKey); ok {
// RSA keys support a few hash functions determined by the requested signature algorithm
switch algorithm {
case "", SigAlgoRSA:
algorithm = SigAlgoRSA
case "", KeyAlgoRSA:
algorithm = KeyAlgoRSA
hashFunc = crypto.SHA1
case SigAlgoRSASHA2256:
case KeyAlgoRSASHA256:
hashFunc = crypto.SHA256
case SigAlgoRSASHA2512:
case KeyAlgoRSASHA512:
hashFunc = crypto.SHA512
default:
return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm)
Expand Down
2 changes: 1 addition & 1 deletion ssh/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func TestKeySignWithAlgorithmVerify(t *testing.T) {

// RSA keys are the only ones which currently support more than one signing algorithm
if pub.Type() == KeyAlgoRSA {
for _, algorithm := range []string{SigAlgoRSA, SigAlgoRSASHA2256, SigAlgoRSASHA2512} {
for _, algorithm := range []string{KeyAlgoRSA, KeyAlgoRSASHA256, KeyAlgoRSASHA512} {
signWithAlgTestCase(algorithm, algorithm)
}
}
Expand Down
4 changes: 2 additions & 2 deletions ssh/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ type ServerConfig struct {
}

// AddHostKey adds a private key as a host key. If an existing host
// key exists with the same algorithm, it is overwritten. Each server
// key exists with the same public key format, it is replaced. Each server
// config must have at least one host key.
func (s *ServerConfig) AddHostKey(key Signer) {
for i, k := range s.hostKeys {
Expand Down Expand Up @@ -284,7 +284,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)

func isAcceptableAlgo(algo string) bool {
switch algo {
case SigAlgoRSA, SigAlgoRSASHA2256, SigAlgoRSASHA2512, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoSKECDSA256, KeyAlgoED25519, KeyAlgoSKED25519,
case KeyAlgoRSA, KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoSKECDSA256, KeyAlgoED25519, KeyAlgoSKED25519,
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01:
return true
}
Expand Down
4 changes: 2 additions & 2 deletions ssh/session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -757,11 +757,11 @@ func TestHostKeyAlgorithms(t *testing.T) {
connect(clientConf, KeyAlgoECDSA256)

// Client asks for RSA explicitly.
clientConf.HostKeyAlgorithms = []string{SigAlgoRSA}
clientConf.HostKeyAlgorithms = []string{KeyAlgoRSA}
connect(clientConf, KeyAlgoRSA)

// Client asks for RSA-SHA2-512 explicitly.
clientConf.HostKeyAlgorithms = []string{SigAlgoRSASHA2512}
clientConf.HostKeyAlgorithms = []string{KeyAlgoRSASHA512}
// We get back an "ssh-rsa" key but the verification happened
// with an RSA-SHA2-512 signature.
connect(clientConf, KeyAlgoRSA)
Expand Down
4 changes: 2 additions & 2 deletions ssh/testdata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ func init() {
if v, ok := testSigners[t].(*rsaSigner); ok {
switch t {
case "rsa-sha2-256":
testSigners[t] = &rsaSigner{v, SigAlgoRSASHA2256}
testSigners[t] = &rsaSigner{v, KeyAlgoRSASHA256}
case "rsa-sha2-512":
testSigners[t] = &rsaSigner{v, SigAlgoRSASHA2512}
testSigners[t] = &rsaSigner{v, KeyAlgoRSASHA512}
}
}
if err != nil {
Expand Down

0 comments on commit cd32840

Please sign in to comment.