golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.46k stars 17.59k forks source link

x/crypto/ssh: export supported algorithms #61537

Open drakkan opened 1 year ago

drakkan commented 1 year ago

Currently KEXs, MACs and ciphers are private, some of them are defined as constants and others as simple strings, for example take a look at the supported ciphers list

// supportedCiphers lists ciphers we support but might not recommend.
var supportedCiphers = []string{
    "aes128-ctr", "aes192-ctr", "aes256-ctr",
    "aes128-gcm@openssh.com", gcm256CipherID,
    chacha20Poly1305ID,
    "arcfour256", "arcfour128", "arcfour",
    aes128cbcID,
    tripledescbcID,
}

I propose defining all supported algorithms as constants and exporting them for better discoverability. We should also export the list of supported ciphers, KEXs, MACs, host key, public key algorithms and so on, so an application using the library can simply check if an algorithm is supported.

cc @golang/security

FiloSottile commented 1 year ago

I definitely agree the supported algorithms should be somewhere on the godoc, either in the constants block or in the package description, but I am mildly opposed to exposing a list of "supported" algorithms as a slice or the like. First, it encourages enabling all supported algorithms by making it easy, and we have a lot of bad supported algorithms; second, "supported" is a complex state that can depend on when and how they are used. See #46232.

Can we see the list of proposed constants?

drakkan commented 1 year ago

Here is the list of proposed constants

// Supported ciphers. Ciphers based on AES CBC and RC4 are no longer considered
// secure.
const (
    CipherAlgoAES128GCM        = "aes128-gcm@openssh.com"
    CipherAlgoAES256GCM        = "aes256-gcm@openssh.com"
    CipherAlgoChacha20Poly1305 = "chacha20-poly1305@openssh.com"
    CipherAlgoAES128CTR        = "aes128-ctr"
    CipherAlgoAES192CTR        = "aes192-ctr"
    CipherAlgoAES256CTR        = "aes256-ctr"
    CipherAlgoAES128CBC        = "aes128-cbc"
    CipherAlgoTripleDESCBC     = "3des-cbc"
    CipherAlgoRC4              = "arcfour"
    CipherAlgoRC4128           = "arcfour128"
    CipherAlgoRC4256           = "arcfour256"
)

// Supported key exchanges algorithms. SHA-1 based KEXs are no longer considered
// secure.
const (
    KEXAlgoDH1SHA1    = "diffie-hellman-group1-sha1"
    KEXAlgoDH14SHA1   = "diffie-hellman-group14-sha1"
    KEXAlgoDH14SHA256 = "diffie-hellman-group14-sha256"
    KEXAlgoECDH256    = "ecdh-sha2-nistp256"
    KEXAlgoECDH384    = "ecdh-sha2-nistp384"
    KEXAlgoECDH521    = "ecdh-sha2-nistp521"
    // This KEX enables both curve25519-sha256 and curve25519-sha256@libssh.org
    KEXAlgoCurve25519SHA256 = "curve25519-sha256"
    // For the following kex only the client half contains a production ready
    // implementation. The server half only consists of a minimal implementation
    // to satisfy the automated tests and it is not accepted.
    KEXAlgoDHGEXSHA1   = "diffie-hellman-group-exchange-sha1"
    KEXAlgoDHGEXSHA256 = "diffie-hellman-group-exchange-sha256"
)

// Supported message authentication code (MAC) algorithms. SHA-1 based MACs are
// no longer considered secure.
const (
    MACAlgoHMACSHA256ETM = "hmac-sha2-256-etm@openssh.com"
    MACAlgoHMACSHA512ETM = "hmac-sha2-512-etm@openssh.com"
    MACAlgoHMACSHA256    = "hmac-sha2-256"
    MACAlgoHMACSHA512    = "hmac-sha2-512"
    MACAlgoHMACSHA1      = "hmac-sha1"
    MACAlgoHMACSHA196    = "hmac-sha1-96"
)
FiloSottile commented 1 year ago

Do we need both KEXAlgoCurve25519SHA256LibSSH and KEXAlgoCurve25519SHA256? Can we just have KEXAlgoCurve25519SHA256 instead and always negotiate both if enabled, assuming the wire protocol is the same or equivalent?

drakkan commented 1 year ago

Do we need both KEXAlgoCurve25519SHA256LibSSH and KEXAlgoCurve25519SHA256? Can we just have KEXAlgoCurve25519SHA256 instead and always negotiate both if enabled, assuming the wire protocol is the same or equivalent?

curve25519-sha256 is just the new name for curve25519-sha256@libssh.org, they already share the same implementation

https://github.com/golang/crypto/blob/d08e19beaccde615f2f1458b1b0df8fe75e20c8a/ssh/kex.go#L436

kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{}
kexAlgoMap[kexAlgoCurve25519SHA256LibSSH] = &curve25519sha256{}

we can expose only KEXAlgoCurve25519SHA256 and document that it will also enable the other variant. I'll update the proposed constants. Thank you

rsc commented 1 year ago

This proposal has been added to the active column of the proposals project and will now be reviewed at the weekly proposal review meetings. — rsc for the proposal review group

hanwen commented 1 year ago

First, it encourages enabling all supported algorithms by making it easy, and we have a lot of bad supported algorithms

We could counter this by selecting a sensible default configuration for both client and server.

If library users decide to override this default, we should provide them information to let them make informed choices.

Maybe the TLS package is a good example: see https://pkg.go.dev/crypto/tls#CipherSuites and https://pkg.go.dev/crypto/tls#InsecureCipherSuites. We could do something like

type Algorithms {
  KEX []string
  Cipher []string
  MAC []string
  PublicKey []string
  PublicKeyAlgo []string
}

// Algorithms implemented, excluding ones with security problems.
func SupportedAlgorithms() Algorithms

// Algorithms implemented with security problems.
func InsecureAlgorithms() Algorithms

second, "supported" is a complex state that can depend on when and how they are used. See #46232.

if we have a clear distinction between secure/insecure algorithms, we could move the server implementation of 'client-only' algorithms out of the test files.

rsc commented 1 year ago

Any more thoughts on this? What is the proposal we've converged on?

drakkan commented 1 year ago

I think we agree on exporting all supported algorithms as constants but we are not yet sure if exporting or not a list of supported algorithms.

Internally we have a list of supported and preferred algorithms and use the preferred ones if no algorithm is explicitly configured.

The weird thing for me the first time I used x/crypto as a user was that I had to look at the code to figure out which algorithms are supported and preferred. I would favour exporting also the lists of algorithms I would do only a minor change to the @hanwen proposal

type Algorithms {
  KEX []string
  Cipher []string
  MAC []string
  PublicKey []string
  PublicKeyAlgo []string
}

// SupportedAlgorithms returns algorithms we support but might not recommend.
func SupportedAlgorithms() Algorithms

// PreferredAlgorithms returns the default preference for algorithms.
func PreferredAlgorithms() Algorithms

We could also change the preferred algorithms so that sha-1 based algorithms are disabled by default

hanwen commented 1 year ago

re. Supported/Preferred vs. Supported/Insecure: following the pattern set by the TLS package will be less cognitive overhead in the long run, for folks that use both TLS and SSH package.

I am assuming that the way the TLS package does it hasn't been a problem so far.

drakkan commented 1 year ago

re. Supported/Preferred vs. Supported/Insecure: following the pattern set by the TLS package will be less cognitive overhead in the long run, for folks that use both TLS and SSH package.

I am assuming that the way the TLS package does it hasn't been a problem so far.

That's fine with me too, the only difference is that if an application wants to check if a user-supplied algorithm is acceptable (crypto/ssh ignores unknown algorithms), it has to look at two lists. But at the same time this approach makes more difficult to enable "insecure" algorithms, which is perhaps what we want to achieve

hanwen commented 1 year ago

nit: []string feels more straightforward, but does the ordering mean something? In practice, map[string]struct{} may be slightly more practical to use.

drakkan commented 1 year ago

nit: []string feels more straightforward, but does the ordering mean something? In practice, map[string]struct{} may be slightly more practical to use.

order matters. The first common algorithm between client and server is selected, see findCommon

hanwen commented 1 year ago

order matters. The first common algorithm between client and server is selected, see findCommon

order matters if you set it in ssh.Config, but maybe not for

an application using the library can simply check if an algorithm is supported.

drakkan commented 1 year ago

order matters. The first common algorithm between client and server is selected, see findCommon

order matters if you set it in ssh.Config, but maybe not for

an application using the library can simply check if an algorithm is supported.

ah ok, do you mean something like CipherSuites() []*CipherSuite in crypto/tls. This is useful if we want to add some additional info, for example if the cipher is supported only on the client side

hanwen commented 1 year ago

nit: []string feels more straightforward, but does the ordering mean something? In practice, map[string]struct{} may be slightly more practical to use.

we can probably address that in your proposal as follows

type Algorithms {
  KEX []string
  Cipher []string
  MAC []string
  PublicKey []string
  PublicKeyAlgo []string
}

func (a *Algorithms) IsSupportedCipher(alg string) bool
func (a *Algorithms) IsSupportedKEX(alg string) bool
...

so your proposal sounds fine.

hanwen commented 1 year ago

note, https://github.com/golang/go/issues/58523 should be addressed together (ie in 2 reviews on top of each other) so we don't paint ourselves in an awkward corner with naming. other than that, I think we are in agreement on the general shape of things here.

drakkan commented 1 year ago

@hanwen thank you. I'll start working on this, so we can also see a test implementation before accepting the proposal.

Since #61244 has been accepted, it would be useful to also review CL 510775 so that Supported/InsecureAlgorithms can be implemented for public keys as well. thanks

gopherbot commented 1 year ago

Change https://go.dev/cl/531935 mentions this issue: ssh: export supported algorithms

drakkan commented 1 year ago

Hello,

I tried a first implementation. Here are the exported constants

// Supported ciphers. Ciphers based on AES CBC and RC4 are no longer considered
// secure.
const (
    CipherAlgoAES128GCM        = "aes128-gcm@openssh.com"
    CipherAlgoAES256GCM        = "aes256-gcm@openssh.com"
    CipherAlgoChacha20Poly1305 = "chacha20-poly1305@openssh.com"
    CipherAlgoAES128CTR        = "aes128-ctr"
    CipherAlgoAES192CTR        = "aes192-ctr"
    CipherAlgoAES256CTR        = "aes256-ctr"
    CipherAlgoAES128CBC        = "aes128-cbc"
    CipherAlgoTripleDESCBC     = "3des-cbc"
    CipherAlgoRC4              = "arcfour"
    CipherAlgoRC4128           = "arcfour128"
    CipherAlgoRC4256           = "arcfour256"
)

// Supported key exchanges algorithms. SHA-1 based KEXs are no longer considered
// secure.
const (
    KexAlgoDH1SHA1    = "diffie-hellman-group1-sha1"
    KexAlgoDH14SHA1   = "diffie-hellman-group14-sha1"
    KexAlgoDH14SHA256 = "diffie-hellman-group14-sha256"
    KexAlgoDH16SHA512 = "diffie-hellman-group16-sha512"
    KexAlgoECDH256    = "ecdh-sha2-nistp256"
    KexAlgoECDH384    = "ecdh-sha2-nistp384"
    KexAlgoECDH521    = "ecdh-sha2-nistp521"
    // This KEX enables both curve25519-sha256 and curve25519-sha256@libssh.org
    KexAlgoCurve25519SHA256       = "curve25519-sha256"
    // For the following KEXs only the client half contains a production ready
    // implementation. The server half only consists of a minimal implementation
    // to satisfy the automated tests and it is not accepted.
    KexAlgoDHGEXSHA1   = "diffie-hellman-group-exchange-sha1"
    KexAlgoDHGEXSHA256 = "diffie-hellman-group-exchange-sha256"
)

// Supported message authentication code (MAC) algorithms. SHA-1 based MACs are
// no longer considered secure.
const (
    MACAlgoHMACSHA256ETM = "hmac-sha2-256-etm@openssh.com"
    MACAlgoHMACSHA512ETM = "hmac-sha2-512-etm@openssh.com"
    MACAlgoHMACSHA256    = "hmac-sha2-256"
    MACAlgoHMACSHA512    = "hmac-sha2-512"
    MACAlgoHMACSHA1      = "hmac-sha1"
    MACAlgoHMACSHA196    = "hmac-sha1-96"
)

// Supported compression algorithms.
const (
    CompressionNone = "none"
)

and here the exported struct and methods

// Algorithms defines the algorithms for an SSH connection.
type Algorithms struct {
    KEXs         []string
    Ciphers      []string
    MACs         []string
    HostKeys     []string
    PublicKeys   []string
    Compressions []string
}

// SupportedAlgorithms returns algorithms currently implemented by this package,
// excluding those with security issues, which are returned by
// InsecureAlgorithms. The algorithms listed here are in preference order.
// Please note that the algorithms used by default may not match these ones for
// backward compatibility reasons.
func SupportedAlgorithms(isServer bool) Algorithms {
    algos := Algorithms{
        Ciphers:      supportedCiphers,
        MACs:         supportedMACs,
        HostKeys:     supportedHostKeyAlgos,
        PublicKeys:   supportedPubKeyAuthAlgos,
        Compressions: supportedCompressions,
    }
    if isServer {
        algos.KEXs = supportedServerKexAlgos
    } else {
        algos.KEXs = supportedClientKexAlgos
    }
    return algos
}

// InsecureAlgorithms returns algorithms currently implemented by this package
// and which have security issues.
func InsecureAlgorithms(isServer bool) Algorithms {
    kexs := []string{KexAlgoDH14SHA1, KexAlgoDH1SHA1}
    if !isServer {
        kexs = append(kexs, KexAlgoDHGEXSHA1)
    }
    return Algorithms{
        KEXs: kexs,
        Ciphers: []string{
            CipherAlgoAES128CBC,
            CipherAlgoTripleDESCBC,
            CipherAlgoRC4256, CipherAlgoRC4128, CipherAlgoRC4,
        },
        MACs:         []string{MACAlgoHMACSHA196, MACAlgoHMACSHA1},
        HostKeys:     []string{CertAlgoRSAv01, CertAlgoDSAv01, KeyAlgoRSA, KeyAlgoDSA},
        PublicKeys:   []string{KeyAlgoRSA, KeyAlgoDSA},
        Compressions: nil,
    }
}
rsc commented 11 months ago

The bool argument is odd; no one is going to remember at a call site what InsecureAlgorithms(true) does versus InsecureAlgorithms(false). Should we have four methods instead? (InsecureServerAlgorithms, ...)?

drakkan commented 11 months ago

The bool argument is odd; no one is going to remember at a call site what InsecureAlgorithms(true) does versus InsecureAlgorithms(false). Should we have four methods instead? (InsecureServerAlgorithms, ...)?

The bool argument is not required, we are also trying to add server-side DHGEX (see CL 532415).

I forgot to update this proposal, sorry. Here is the new proposal as implemented in CL 531935

// Implemented ciphers algorithms. Ciphers based on AES CBC and RC4 are no
// longer considered secure.
const (
    CipherAlgoAES128GCM        = "aes128-gcm@openssh.com"
    CipherAlgoAES256GCM        = "aes256-gcm@openssh.com"
    CipherAlgoChacha20Poly1305 = "chacha20-poly1305@openssh.com"
    CipherAlgoAES128CTR        = "aes128-ctr"
    CipherAlgoAES192CTR        = "aes192-ctr"
    CipherAlgoAES256CTR        = "aes256-ctr"
    CipherAlgoAES128CBC        = "aes128-cbc"
    CipherAlgoTripleDESCBC     = "3des-cbc"
    CipherAlgoRC4              = "arcfour"
    CipherAlgoRC4128           = "arcfour128"
    CipherAlgoRC4256           = "arcfour256"
)

// Implemented key exchanges algorithms. SHA-1 based KEXs are no longer
// considered secure.
const (
    KexAlgoDH1SHA1          = "diffie-hellman-group1-sha1"
    KexAlgoDH14SHA1         = "diffie-hellman-group14-sha1"
    KexAlgoDH14SHA256       = "diffie-hellman-group14-sha256"
    KexAlgoDH16SHA512       = "diffie-hellman-group16-sha512"
    KexAlgoECDH256          = "ecdh-sha2-nistp256"
    KexAlgoECDH384          = "ecdh-sha2-nistp384"
    KexAlgoECDH521          = "ecdh-sha2-nistp521"
    KexAlgoCurve25519SHA256 = "curve25519-sha256"
    KexAlgoDHGEXSHA1        = "diffie-hellman-group-exchange-sha1"
    KexAlgoDHGEXSHA256      = "diffie-hellman-group-exchange-sha256"

    // An alias for KexAlgoCurve25519SHA256. This kex ID will be added if
    // KexAlgoCurve25519SHA256 is requested for backward compatibility with
    // OpenSSH versions up to 7.2.
    kexAlgoCurve25519SHA256LibSSH = "curve25519-sha256@libssh.org"
)

// Implemented message authentication code (MAC) algorithms. SHA-1 based MACs are
// no longer considered secure.
const (
    MACAlgoHMACSHA256ETM = "hmac-sha2-256-etm@openssh.com"
    MACAlgoHMACSHA512ETM = "hmac-sha2-512-etm@openssh.com"
    MACAlgoHMACSHA256    = "hmac-sha2-256"
    MACAlgoHMACSHA512    = "hmac-sha2-512"
    MACAlgoHMACSHA1      = "hmac-sha1"
    MACAlgoHMACSHA196    = "hmac-sha1-96"
)

// Implemented compression algorithms.
const (
    CompressionNone = "none"
)

// Algorithms defines a set of algorithms that can be configured in the client
// or server config for negotiation during a handshake.
type Algorithms struct {
    KEXs         []string
    Ciphers      []string
    MACs         []string
    HostKeys     []string
    PublicKeys   []string
    Compressions []string
}

// SupportedAlgorithms returns algorithms currently implemented by this package,
// excluding those with security issues, which are returned by
// InsecureAlgorithms. The algorithms listed here are in preference order.
func SupportedAlgorithms() Algorithms {
    return Algorithms{
        Ciphers:      supportedCiphers,
        MACs:         supportedMACs,
        KEXs:         supportedKexAlgos,
        HostKeys:     supportedHostKeyAlgos,
        PublicKeys:   supportedPubKeyAuthAlgos,
        Compressions: supportedCompressions,
    }
}

// InsecureAlgorithms returns algorithms currently implemented by this package
// and which have security issues.
func InsecureAlgorithms() Algorithms {
    return Algorithms{
        KEXs: []string{KexAlgoDH14SHA1, KexAlgoDH1SHA1, KexAlgoDHGEXSHA1},
        Ciphers: []string{
            CipherAlgoAES128CBC,
            CipherAlgoTripleDESCBC,
            CipherAlgoRC4256, CipherAlgoRC4128, CipherAlgoRC4,
        },
        MACs:         []string{MACAlgoHMACSHA196, MACAlgoHMACSHA1},
        HostKeys:     []string{CertAlgoRSAv01, CertAlgoDSAv01, KeyAlgoRSA, KeyAlgoDSA},
        PublicKeys:   []string{KeyAlgoRSA, KeyAlgoDSA},
        Compressions: nil,
    }
}
rsc commented 11 months ago

There is a casing inconsistency that should be fixed: some names use "Kex" and others use "KEX". They should all use the same casing.

drakkan commented 11 months ago

There is a casing inconsistency that should be fixed: some names use "Kex" and others use "KEX". They should all use the same casing.

You're right, do we agree on "KEX"?

hanwen commented 11 months ago

https://pkg.go.dev/golang.org/x/crypto/ssh#Config says "KeyExchange", which is probably better than either KEX or Kex. It also avoids the dilemma of kexes vs. kexs

hanwen commented 11 months ago

in the review I also suggested Insecure as prefix for known insecure algorithms, eg. InsecureCipherAlgoRC4

drakkan commented 11 months ago

Thank you @hanwenI would prefer to keep KexAlgoCurve25519SHA256 etc. to avoid too long names. Here is the updated proposal

// Implemented ciphers algorithms. Ciphers based on AES CBC and RC4 are no
// longer considered secure.
const (
    CipherAlgoAES128GCM        = "aes128-gcm@openssh.com"
    CipherAlgoAES256GCM        = "aes256-gcm@openssh.com"
    CipherAlgoChacha20Poly1305 = "chacha20-poly1305@openssh.com"
    CipherAlgoAES128CTR        = "aes128-ctr"
    CipherAlgoAES192CTR        = "aes192-ctr"
    CipherAlgoAES256CTR        = "aes256-ctr"
    CipherAlgoAES128CBC        = "aes128-cbc"
    CipherAlgoTripleDESCBC     = "3des-cbc"
    CipherAlgoRC4              = "arcfour"
    CipherAlgoRC4128           = "arcfour128"
    CipherAlgoRC4256           = "arcfour256"
)

// Implemented key exchanges algorithms. SHA-1 based KEXs are no longer
// considered secure.
const (
    KexAlgoDH1SHA1          = "diffie-hellman-group1-sha1"
    KexAlgoDH14SHA1         = "diffie-hellman-group14-sha1"
    KexAlgoDH14SHA256       = "diffie-hellman-group14-sha256"
    KexAlgoDH16SHA512       = "diffie-hellman-group16-sha512"
    KexAlgoECDH256          = "ecdh-sha2-nistp256"
    KexAlgoECDH384          = "ecdh-sha2-nistp384"
    KexAlgoECDH521          = "ecdh-sha2-nistp521"
    KexAlgoCurve25519SHA256 = "curve25519-sha256"
    KexAlgoDHGEXSHA1        = "diffie-hellman-group-exchange-sha1"
    KexAlgoDHGEXSHA256      = "diffie-hellman-group-exchange-sha256"

    // An alias for KexAlgoCurve25519SHA256. This kex ID will be added if
    // KexAlgoCurve25519SHA256 is requested for backward compatibility with
    // OpenSSH versions up to 7.2.
    kexAlgoCurve25519SHA256LibSSH = "curve25519-sha256@libssh.org"
)

// Implemented message authentication code (MAC) algorithms. SHA-1 based MACs are
// no longer considered secure.
const (
    MACAlgoHMACSHA256ETM = "hmac-sha2-256-etm@openssh.com"
    MACAlgoHMACSHA512ETM = "hmac-sha2-512-etm@openssh.com"
    MACAlgoHMACSHA256    = "hmac-sha2-256"
    MACAlgoHMACSHA512    = "hmac-sha2-512"
    MACAlgoHMACSHA1      = "hmac-sha1"
    MACAlgoHMACSHA196    = "hmac-sha1-96"
)

// Implemented compression algorithms.
const (
    CompressionNone = "none"
)

type Algorithms struct {
    KeyExchanges []string
    Ciphers      []string
    MACs         []string
    HostKeys     []string
    PublicKeys   []string
    Compressions []string
}

// SupportedAlgorithms returns algorithms currently implemented by this package,
// excluding those with security issues, which are returned by
// InsecureAlgorithms. The algorithms listed here are in preference order.
func SupportedAlgorithms() Algorithms {
    return Algorithms{
        Ciphers:      supportedCiphers,
        MACs:         supportedMACs,
        KeyExchanges: supportedKexAlgos,
        HostKeys:     supportedHostKeyAlgos,
        PublicKeys:   supportedPubKeyAuthAlgos,
        Compressions: supportedCompressions,
    }
}

// InsecureAlgorithms returns algorithms currently implemented by this package
// and which have security issues.
func InsecureAlgorithms() Algorithms {
    return Algorithms{
        KeyExchanges: []string{KexAlgoDH14SHA1, KexAlgoDH1SHA1, KexAlgoDHGEXSHA1},
        Ciphers: []string{
            CipherAlgoAES128CBC,
            CipherAlgoTripleDESCBC,
            CipherAlgoRC4256, CipherAlgoRC4128, CipherAlgoRC4,
        },
        MACs:         []string{MACAlgoHMACSHA196, MACAlgoHMACSHA1},
        HostKeys:     []string{CertAlgoRSAv01, CertAlgoDSAv01, KeyAlgoRSA, KeyAlgoDSA},
        PublicKeys:   []string{KeyAlgoRSA, KeyAlgoDSA},
        Compressions: nil,
    }
}
hanwen commented 11 months ago

KexAlgoCurve25519SHA256 etc. to avoid too long names.

I don't see the problem with a long name here; it's not like anyone would use these identifiers repetitively, and 'kex' is a jargon that we can avoid.

rsc commented 11 months ago

I know we have CertAlgoZzz and KeyAlgoZzz already, but in general Go does not use abbreviations like Algorithm -> Algo, and I think we don't have to continue that pattern in new enumerations.

How about CipherAlgoAES128GCM -> CipherAES128GCM and MACAlgoHMACSHA256 -> HMACSHA256?

At that point, KexAlgoDH1SHA1 -> KeyExchangeDH1SHA1 seems good too: KeyExchange is not much longer than KexAlgo.

drakkan commented 11 months ago

in the review I also suggested Insecure as prefix for known insecure algorithms, eg. InsecureCipherAlgoRC4

would this mean we have to change the exported name when an algorithm becomes insecure in the future?

drakkan commented 11 months ago

I know we have CertAlgoZzz and KeyAlgoZzz already, but in general Go does not use abbreviations like Algorithm -> Algo, and I think we don't have to continue that pattern in new enumerations.

How about CipherAlgoAES128GCM -> CipherAES128GCM and MACAlgoHMACSHA256 -> HMACSHA256?

At that point, KexAlgoDH1SHA1 -> KeyExchangeDH1SHA1 seems good too: KeyExchange is not much longer than KexAlgo.

Looks good, thank you

rsc commented 11 months ago

Re InsecureCipherRC4 vs CipherRC4, I think that would mean if we already had CipherRC4 and realized it had become insecure, we'd add InsecureCipherRC4 and mark CipherRC4 as deprecated. I thought that would be annoying, but actually it has some nice properties for editors and other tools warning about use of deprecated symbols, much the same way that deprecating math/rand.Read flags uses where people should be using crypto/rand.Read instead. This would probably be a net win for security of ssh clients. People will be able to go into their code and decide whether to keep using the insecure cipher. If so they just need to add "Insecure" at the mention to remove the deprecation warning.

hanwen commented 11 months ago

I was (more simply minded) thinking of breaking clients in the name of security (eg. https://github.com/golang/go/issues/19767).

If we already have annotations for 'deprecated', can't we simply add those to the constants of insecure algorithms?

rsc commented 11 months ago

I don't think we should break people in the name of security anymore. I am fine with just adding Deprecated tags and not including the Insecure-prefixed name. The Insecure-prefixed name gives people a way to "silence" the deprecation.

drakkan commented 11 months ago

Thank you both. So we should also deprecate things like KeyAlgoRSA, KeyAlgoDSA etc. in this proposal/related CL and add InsecureKeyAlgoRSA, InsecureKeyAlgoDSA, etc.

rsc commented 11 months ago

Have all remaining concerns about this proposal been addressed?

The proposal details are in https://github.com/golang/go/issues/61537#issuecomment-1789399757.

drakkan commented 11 months ago

Have all remaining concerns about this proposal been addressed?

The proposal details are in #61537 (comment).

My ony remaining concern is if we have to deprecate the following algos

HostKeys:     []string{CertAlgoRSAv01, CertAlgoDSAv01, KeyAlgoRSA, KeyAlgoDSA},
PublicKeys:   []string{KeyAlgoRSA, KeyAlgoDSA},

and add InsecureKeyAlgoRSA as you proposed above so that func InsecureAlgorithms() Algorithms { returns only algorithms with the Insecure prefix

hanwen commented 11 months ago

If we are going to leave the constants without Insecure prefix alone, we don't have to add any right now, so that makes https://github.com/golang/go/issues/61537#issuecomment-1791186573 moot? Or do we also want to settle this in this proposal/change?

drakkan commented 11 months ago

If we are going to leave the constants without Insecure prefix alone, we don't have to add any right now,

yes

Or do we also want to settle this in this proposal/change?

Adding the Insecure prefix and deprecating insecure algorithms is a smart idea, maybe we can do it here too.

drakkan commented 11 months ago

We may do this, but I'm also fine without the Insecure variants

const (
    // Deprecated: this algorithm is insecure.
    CertAlgoRSAv01         = InsecureCertAlgoRSAv01
    InsecureCertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
    // Deprecated: this algorithm is insecure.
    CertAlgoDSAv01         = InsecureCertAlgoDSAv01
    InsecureCertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
       ...
)

const (
    // Deprecated: this algorithm is insecure.
    KeyAlgoRSA         = InsecureKeyAlgoRSA
    InsecureKeyAlgoRSA = "ssh-rsa"
    // Deprecated: this algorithm is insecure.
    KeyAlgoDSA         = InsecureKeyAlgoDSA
        InsecureKeyAlgoDSA = "ssh-dss"
        ....
)

// Implemented ciphers algorithms.
const (
    CipherAES128GCM            = "aes128-gcm@openssh.com"
    CipherAES256GCM            = "aes256-gcm@openssh.com"
    CipherChacha20Poly1305     = "chacha20-poly1305@openssh.com"
    CipherAES128CTR            = "aes128-ctr"
    CipherAES192CTR            = "aes192-ctr"
    CipherAES256CTR            = "aes256-ctr"
    InsecureCipherAES128CBC    = "aes128-cbc"
    InsecureCipherTripleDESCBC = "3des-cbc"
    InsecureCipherRC4          = "arcfour"
    InsecureCipherRC4128       = "arcfour128"
    InsecureCipherRC4256       = "arcfour256"
)

// Implemented key exchanges algorithms.
const (
    InsecureKeyExchangeDH1SHA1   = "diffie-hellman-group1-sha1"
    InsecureKeyExchangeDH14SHA1  = "diffie-hellman-group14-sha1"
    KeyExchangeDH14SHA256        = "diffie-hellman-group14-sha256"
    KeyExchangeDH16SHA512        = "diffie-hellman-group16-sha512"
    KeyExchangeECDH256           = "ecdh-sha2-nistp256"
    KeyExchangeECDH384           = "ecdh-sha2-nistp384"
    KeyExchangeECDH521           = "ecdh-sha2-nistp521"
    KeyExchangeCurve25519SHA256  = "curve25519-sha256"
    InsecureKeyExchangeDHGEXSHA1 = "diffie-hellman-group-exchange-sha1"
    KeyExchangeDHGEXSHA256       = "diffie-hellman-group-exchange-sha256"

    // An alias for KeyExchangeCurve25519SHA256. This kex ID will be added if
    // KeyExchangeCurve25519SHA256 is requested for backward compatibility with
    // OpenSSH versions up to 7.2.
    keyExchangeCurve25519SHA256LibSSH = "curve25519-sha256@libssh.org"
)

// Implemented message authentication code (MAC) algorithms. SHA-1 based MACs are
// no longer considered secure.
const (
    HMACSHA256ETM      = "hmac-sha2-256-etm@openssh.com"
    HMACSHA512ETM      = "hmac-sha2-512-etm@openssh.com"
    HMACSHA256         = "hmac-sha2-256"
    HMACSHA512         = "hmac-sha2-512"
    InsecureHMACSHA1   = "hmac-sha1"
    InsecureHMACSHA196 = "hmac-sha1-96"
)

// Implemented compression algorithms.
const (
    CompressionNone = "none"
)

// Algorithms defines a set of algorithms that can be configured in the client
// or server config for negotiation during a handshake.
type Algorithms struct {
    KeyExchanges []string
    Ciphers      []string
    MACs         []string
    HostKeys     []string
    PublicKeys   []string
    Compressions []string
}

// SupportedAlgorithms returns algorithms currently implemented by this package,
// excluding those with security issues, which are returned by
// InsecureAlgorithms. The algorithms listed here are in preference order.
func SupportedAlgorithms() Algorithms {
    return Algorithms{
        Ciphers:      supportedCiphers,
        MACs:         supportedMACs,
        KeyExchanges: supportedKexAlgos,
        HostKeys:     supportedHostKeyAlgos,
        PublicKeys:   supportedPubKeyAuthAlgos,
        Compressions: supportedCompressions,
    }
}

// InsecureAlgorithms returns algorithms currently implemented by this package
// and which have security issues.
func InsecureAlgorithms() Algorithms {
    return Algorithms{
        KeyExchanges: []string{InsecureKeyExchangeDH14SHA1, InsecureKeyExchangeDH1SHA1,
            InsecureKeyExchangeDHGEXSHA1},
        Ciphers: []string{
            InsecureCipherAES128CBC,
            InsecureCipherTripleDESCBC,
            InsecureCipherRC4256, InsecureCipherRC4128, InsecureCipherRC4,
        },
        MACs:         []string{InsecureHMACSHA196, InsecureHMACSHA1},
        HostKeys:     []string{InsecureCertAlgoRSAv01, InsecureCertAlgoDSAv01, InsecureKeyAlgoRSA, InsecureKeyAlgoDSA},
        PublicKeys:   []string{InsecureKeyAlgoRSA, InsecureKeyAlgoDSA},
        Compressions: nil,
    }
}
hanwen commented 11 months ago

it makes no sense to introduce variables/constants that are already deprecated on introduction. If we do this, we don't need to have the constants without Insecure prefix.

drakkan commented 11 months ago

it makes no sense to introduce variables/constants that are already deprecated on introduction. If we do this, we don't need to have the constants without Insecure prefix.

New constants introduced for insecure algorithms are prefixed with Insecure (e.g. InsecureHMACSHA1). CertAlgoRSAv01, CertAlgoDSAv01, KeyAlgoRSA, KeyAlgoDSA were already exported before this patch so I proposed to deprecate them and add the non-deprecated variant with the Insecure prefix. Sorry if this is not clear in my previous comment

hanwen commented 11 months ago

Sorry if this is not clear in my previous comment

sorry on my side: I didn't read your comment in detail. What you propose SGTM.

FiloSottile commented 11 months ago

I would drop Algorithms.Compressions and CompressionNone.

The names of the Algorithms fields should probably match the field names where they'd be used. Let's call HostKeys HostKeyAlgorithms (after the ClientConfig field) and PublicKeys PublicKeyAuthAlgorithms (after the ServerConfig field in #61244). This also helps making it clear they are algorithms (so they include rsa-sha2-256) instead of key types.

Unfortunately, it looks like ClientConfig.HostKeyAlgorithms includes certificate algorithms, while ServerConfig.PublicKeyAuthAlgorithms includes only underlying algorithms. This is awful, but it seems to match the on-the-wire semantics, so not something we can change. Do we also make the two Algorithms fields have different semantics, or do we make an Algorithms field that can't be used as a Config field?

Finally, we can't deprecate KeyAlgoRSA because while it is insecure as an algorithm name it is not insecure as key type name. (This was probably my fault for merging SigType and KeyType, but it's done now.)

hanwen commented 11 months ago

I would drop Algorithms.Compressions and CompressionNone.

sgtm. We can add it later once we support more than "None"

rsc commented 11 months ago

@drakkan can you please post an updated API?

drakkan commented 11 months ago

Thanks to all, here is the updated API

const (
        ...
        // Deprecated: this algorithm is insecure.
        CertAlgoDSAv01         = InsecureCertAlgoDSAv01
        InsecureCertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
        ...
)

const (
        ...
        // Deprecated: this algorithm is insecure.
       KeyAlgoDSA         = InsecureKeyAlgoDSA
       InsecureKeyAlgoDSA = "ssh-dss"
       ....
)

// Implemented ciphers algorithms.
const (
    CipherAES128GCM            = "aes128-gcm@openssh.com"
    CipherAES256GCM            = "aes256-gcm@openssh.com"
    CipherChacha20Poly1305     = "chacha20-poly1305@openssh.com"
    CipherAES128CTR            = "aes128-ctr"
    CipherAES192CTR            = "aes192-ctr"
    CipherAES256CTR            = "aes256-ctr"
    InsecureCipherAES128CBC    = "aes128-cbc"
    InsecureCipherTripleDESCBC = "3des-cbc"
    InsecureCipherRC4          = "arcfour"
    InsecureCipherRC4128       = "arcfour128"
    InsecureCipherRC4256       = "arcfour256"
)

// Implemented key exchanges algorithms.
const (
    InsecureKeyExchangeDH1SHA1   = "diffie-hellman-group1-sha1"
    InsecureKeyExchangeDH14SHA1  = "diffie-hellman-group14-sha1"
    KeyExchangeDH14SHA256        = "diffie-hellman-group14-sha256"
    KeyExchangeDH16SHA512        = "diffie-hellman-group16-sha512"
    KeyExchangeECDH256           = "ecdh-sha2-nistp256"
    KeyExchangeECDH384           = "ecdh-sha2-nistp384"
    KeyExchangeECDH521           = "ecdh-sha2-nistp521"
    KeyExchangeCurve25519SHA256  = "curve25519-sha256"
    InsecureKeyExchangeDHGEXSHA1 = "diffie-hellman-group-exchange-sha1"
    KeyExchangeDHGEXSHA256       = "diffie-hellman-group-exchange-sha256"

    // An alias for KeyExchangeCurve25519SHA256. This kex ID will be added if
    // KeyExchangeCurve25519SHA256 is requested for backward compatibility with
    // OpenSSH versions up to 7.2.
    keyExchangeCurve25519SHA256LibSSH = "curve25519-sha256@libssh.org"
)

// Implemented message authentication code (MAC) algorithms. SHA-1 based MACs are
// no longer considered secure.
const (
    HMACSHA256ETM      = "hmac-sha2-256-etm@openssh.com"
    HMACSHA512ETM      = "hmac-sha2-512-etm@openssh.com"
    HMACSHA256         = "hmac-sha2-256"
    HMACSHA512         = "hmac-sha2-512"
    InsecureHMACSHA1   = "hmac-sha1"
    InsecureHMACSHA196 = "hmac-sha1-96"
)

// Algorithms defines a set of algorithms that can be configured in the client
// or server config for negotiation during a handshake.
type Algorithms struct {
    KeyExchanges            []string
    Ciphers                 []string
    MACs                    []string
    HostKeyAlgorithms       []string
    PublicKeyAuthAlgorithms []string
}

// SupportedAlgorithms returns algorithms currently implemented by this package,
// excluding those with security issues, which are returned by
// InsecureAlgorithms. The algorithms listed here are in preference order.
func SupportedAlgorithms() Algorithms {
    return Algorithms{
        Ciphers:                 supportedCiphers,
        MACs:                    supportedMACs,
        KeyExchanges:            supportedKexAlgos,
        HostKeyAlgorithms:       supportedHostKeyAlgos,
        PublicKeyAuthAlgorithms: supportedPubKeyAuthAlgos,
    }
}

// InsecureAlgorithms returns algorithms currently implemented by this package
// and which have security issues.
func InsecureAlgorithms() Algorithms {
    return Algorithms{
        KeyExchanges: []string{InsecureKeyExchangeDH14SHA1, InsecureKeyExchangeDH1SHA1,
            InsecureKeyExchangeDHGEXSHA1},
        Ciphers: []string{
            InsecureCipherAES128CBC,
            InsecureCipherTripleDESCBC,
            InsecureCipherRC4256, InsecureCipherRC4128, InsecureCipherRC4,
        },
        MACs:                    []string{InsecureHMACSHA196, InsecureHMACSHA1},
        HostKeyAlgorithms:       []string{KeyAlgoRSA, InsecureKeyAlgoDSA, CertAlgoRSAv01, InsecureCertAlgoDSAv01},
        PublicKeyAuthAlgorithms: []string{KeyAlgoRSA, InsecureKeyAlgoDSA},
    }
}

HostKeyAlgorithms will include certificate algorithms while PublicKeyAuthAlgorithms will include only underlying algorithms.

drakkan commented 11 months ago

Update proposal, removed the redundat Algorithms from struct fields

const (
        ...
        // Deprecated: this algorithm is insecure.
        CertAlgoDSAv01         = InsecureCertAlgoDSAv01
        InsecureCertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
        ...
)

const (
        ...
        // Deprecated: this algorithm is insecure.
       KeyAlgoDSA         = InsecureKeyAlgoDSA
       InsecureKeyAlgoDSA = "ssh-dss"
       ....
)

// Implemented ciphers algorithms.
const (
    CipherAES128GCM            = "aes128-gcm@openssh.com"
    CipherAES256GCM            = "aes256-gcm@openssh.com"
    CipherChacha20Poly1305     = "chacha20-poly1305@openssh.com"
    CipherAES128CTR            = "aes128-ctr"
    CipherAES192CTR            = "aes192-ctr"
    CipherAES256CTR            = "aes256-ctr"
    InsecureCipherAES128CBC    = "aes128-cbc"
    InsecureCipherTripleDESCBC = "3des-cbc"
    InsecureCipherRC4          = "arcfour"
    InsecureCipherRC4128       = "arcfour128"
    InsecureCipherRC4256       = "arcfour256"
)

// Implemented key exchanges algorithms.
const (
    InsecureKeyExchangeDH1SHA1   = "diffie-hellman-group1-sha1"
    InsecureKeyExchangeDH14SHA1  = "diffie-hellman-group14-sha1"
    KeyExchangeDH14SHA256        = "diffie-hellman-group14-sha256"
    KeyExchangeDH16SHA512        = "diffie-hellman-group16-sha512"
    KeyExchangeECDH256           = "ecdh-sha2-nistp256"
    KeyExchangeECDH384           = "ecdh-sha2-nistp384"
    KeyExchangeECDH521           = "ecdh-sha2-nistp521"
    KeyExchangeCurve25519SHA256  = "curve25519-sha256"
    InsecureKeyExchangeDHGEXSHA1 = "diffie-hellman-group-exchange-sha1"
    KeyExchangeDHGEXSHA256       = "diffie-hellman-group-exchange-sha256"

    // An alias for KeyExchangeCurve25519SHA256. This kex ID will be added if
    // KeyExchangeCurve25519SHA256 is requested for backward compatibility with
    // OpenSSH versions up to 7.2.
    keyExchangeCurve25519SHA256LibSSH = "curve25519-sha256@libssh.org"
)

// Implemented message authentication code (MAC) algorithms. SHA-1 based MACs are
// no longer considered secure.
const (
    HMACSHA256ETM      = "hmac-sha2-256-etm@openssh.com"
    HMACSHA512ETM      = "hmac-sha2-512-etm@openssh.com"
    HMACSHA256         = "hmac-sha2-256"
    HMACSHA512         = "hmac-sha2-512"
    InsecureHMACSHA1   = "hmac-sha1"
    InsecureHMACSHA196 = "hmac-sha1-96"
)

// Algorithms defines a set of algorithms that can be configured in the client
// or server config for negotiation during a handshake.
type Algorithms struct {
    KeyExchanges            []string
    Ciphers                 []string
    MACs                    []string
    HostKeys       []string
    PublicKeyAuths []string
}

// SupportedAlgorithms returns algorithms currently implemented by this package,
// excluding those with security issues, which are returned by
// InsecureAlgorithms. The algorithms listed here are in preference order.
func SupportedAlgorithms() Algorithms {
    return Algorithms{
        Ciphers:                 supportedCiphers,
        MACs:                    supportedMACs,
        KeyExchanges:            supportedKexAlgos,
        HostKeys:       supportedHostKeyAlgos,
        PublicKeyAuths: supportedPubKeyAuthAlgos,
    }
}

// InsecureAlgorithms returns algorithms currently implemented by this package
// and which have security issues.
func InsecureAlgorithms() Algorithms {
    return Algorithms{
        KeyExchanges: []string{InsecureKeyExchangeDH14SHA1, InsecureKeyExchangeDH1SHA1,
            InsecureKeyExchangeDHGEXSHA1},
        Ciphers: []string{
            InsecureCipherAES128CBC,
            InsecureCipherTripleDESCBC,
            InsecureCipherRC4256, InsecureCipherRC4128, InsecureCipherRC4,
        },
        MACs:                    []string{InsecureHMACSHA196, InsecureHMACSHA1},
        HostKeys:       []string{KeyAlgoRSA, InsecureKeyAlgoDSA, CertAlgoRSAv01, InsecureCertAlgoDSAv01},
        PublicKeyAuths: []string{KeyAlgoRSA, InsecureKeyAlgoDSA},
    }
}
rsc commented 10 months ago

Have all remaining concerns about this proposal been addressed?

The proposal details are in https://github.com/golang/go/issues/61537#issuecomment-1814851454.

rsc commented 10 months ago

Based on the discussion above, this proposal seems like a likely accept. — rsc for the proposal review group

The proposal details are in https://github.com/golang/go/issues/61537#issuecomment-1814851454.

rsc commented 9 months ago

No change in consensus, so accepted. 🎉 This issue now tracks the work of implementing the proposal. — rsc for the proposal review group

The proposal details are in https://github.com/golang/go/issues/61537#issuecomment-1814851454.