aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicola Murino <nicola.murino@gmail.com>2025-05-04 14:40:10 +0200
committerGopher Robot <gobot@golang.org>2025-05-15 10:43:30 -0700
commitac58737d259962f38068acb1bcd4a51850ec4951 (patch)
treeb142269653a486b4d644cdd18acb16832437552b
parent9dbbcf002b5cbae3b65a93e406438711bef52e53 (diff)
downloadgo-x-crypto-ac58737d259962f38068acb1bcd4a51850ec4951.tar.xz
ssh: export supported algorithms
Fixes golang/go#61537 Change-Id: If3478121e3ae445391e3faeceeb889d75e9e3214 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/531935 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Nicola Murino <nicola.murino@gmail.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Michael Knyszek <mknyszek@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org>
-rw-r--r--ssh/agent/client.go22
-rw-r--r--ssh/agent/server.go4
-rw-r--r--ssh/certs.go41
-rw-r--r--ssh/certs_test.go2
-rw-r--r--ssh/cipher.go24
-rw-r--r--ssh/cipher_test.go22
-rw-r--r--ssh/client_auth_test.go5
-rw-r--r--ssh/client_test.go10
-rw-r--r--ssh/common.go337
-rw-r--r--ssh/example_test.go110
-rw-r--r--ssh/handshake.go2
-rw-r--r--ssh/handshake_test.go4
-rw-r--r--ssh/kex.go40
-rw-r--r--ssh/keys.go25
-rw-r--r--ssh/mac.go12
-rw-r--r--ssh/mlkem.go10
-rw-r--r--ssh/server.go4
-rw-r--r--ssh/server_test.go2
-rw-r--r--ssh/test/recording_client_test.go4
-rw-r--r--ssh/test/recording_server_test.go2
-rw-r--r--ssh/test/recording_test.go2
-rw-r--r--ssh/test/session_test.go4
-rw-r--r--ssh/transport.go7
23 files changed, 482 insertions, 213 deletions
diff --git a/ssh/agent/client.go b/ssh/agent/client.go
index 106708d..37525e1 100644
--- a/ssh/agent/client.go
+++ b/ssh/agent/client.go
@@ -555,7 +555,7 @@ func (c *client) insertKey(s interface{}, comment string, constraints []byte) er
})
case *dsa.PrivateKey:
req = ssh.Marshal(dsaKeyMsg{
- Type: ssh.KeyAlgoDSA,
+ Type: ssh.InsecureKeyAlgoDSA,
P: k.P,
Q: k.Q,
G: k.G,
@@ -803,16 +803,16 @@ var _ ssh.AlgorithmSigner = &agentKeyringSigner{}
//
// This map must be kept in sync with the one in certs.go.
var certKeyAlgoNames = map[string]string{
- ssh.CertAlgoRSAv01: ssh.KeyAlgoRSA,
- ssh.CertAlgoRSASHA256v01: ssh.KeyAlgoRSASHA256,
- ssh.CertAlgoRSASHA512v01: ssh.KeyAlgoRSASHA512,
- ssh.CertAlgoDSAv01: ssh.KeyAlgoDSA,
- ssh.CertAlgoECDSA256v01: ssh.KeyAlgoECDSA256,
- ssh.CertAlgoECDSA384v01: ssh.KeyAlgoECDSA384,
- ssh.CertAlgoECDSA521v01: ssh.KeyAlgoECDSA521,
- ssh.CertAlgoSKECDSA256v01: ssh.KeyAlgoSKECDSA256,
- ssh.CertAlgoED25519v01: ssh.KeyAlgoED25519,
- ssh.CertAlgoSKED25519v01: ssh.KeyAlgoSKED25519,
+ ssh.CertAlgoRSAv01: ssh.KeyAlgoRSA,
+ ssh.CertAlgoRSASHA256v01: ssh.KeyAlgoRSASHA256,
+ ssh.CertAlgoRSASHA512v01: ssh.KeyAlgoRSASHA512,
+ ssh.InsecureCertAlgoDSAv01: ssh.InsecureKeyAlgoDSA,
+ ssh.CertAlgoECDSA256v01: ssh.KeyAlgoECDSA256,
+ ssh.CertAlgoECDSA384v01: ssh.KeyAlgoECDSA384,
+ ssh.CertAlgoECDSA521v01: ssh.KeyAlgoECDSA521,
+ ssh.CertAlgoSKECDSA256v01: ssh.KeyAlgoSKECDSA256,
+ ssh.CertAlgoED25519v01: ssh.KeyAlgoED25519,
+ ssh.CertAlgoSKED25519v01: ssh.KeyAlgoSKED25519,
}
// underlyingAlgo returns the signature algorithm associated with algo (which is
diff --git a/ssh/agent/server.go b/ssh/agent/server.go
index e35ca7c..88ce4da 100644
--- a/ssh/agent/server.go
+++ b/ssh/agent/server.go
@@ -506,7 +506,7 @@ func (s *server) insertIdentity(req []byte) error {
switch record.Type {
case ssh.KeyAlgoRSA:
addedKey, err = parseRSAKey(req)
- case ssh.KeyAlgoDSA:
+ case ssh.InsecureKeyAlgoDSA:
addedKey, err = parseDSAKey(req)
case ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521:
addedKey, err = parseECDSAKey(req)
@@ -514,7 +514,7 @@ func (s *server) insertIdentity(req []byte) error {
addedKey, err = parseEd25519Key(req)
case ssh.CertAlgoRSAv01:
addedKey, err = parseRSACert(req)
- case ssh.CertAlgoDSAv01:
+ case ssh.InsecureCertAlgoDSAv01:
addedKey, err = parseDSACert(req)
case ssh.CertAlgoECDSA256v01, ssh.CertAlgoECDSA384v01, ssh.CertAlgoECDSA521v01:
addedKey, err = parseECDSACert(req)
diff --git a/ssh/certs.go b/ssh/certs.go
index 27d0e14..a3dc629 100644
--- a/ssh/certs.go
+++ b/ssh/certs.go
@@ -20,14 +20,19 @@ import (
// returned by MultiAlgorithmSigner and don't appear in the Signature.Format
// field.
const (
- CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
- CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
- CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
- CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
- CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
- CertAlgoSKECDSA256v01 = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
- CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com"
- CertAlgoSKED25519v01 = "sk-ssh-ed25519-cert-v01@openssh.com"
+ CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
+ // Deprecated: DSA is only supported at insecure key sizes, and was removed
+ // from major implementations.
+ CertAlgoDSAv01 = InsecureCertAlgoDSAv01
+ // Deprecated: DSA is only supported at insecure key sizes, and was removed
+ // from major implementations.
+ InsecureCertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
+ CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
+ CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
+ CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
+ CertAlgoSKECDSA256v01 = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
+ CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com"
+ CertAlgoSKED25519v01 = "sk-ssh-ed25519-cert-v01@openssh.com"
// CertAlgoRSASHA256v01 and CertAlgoRSASHA512v01 can't appear as a
// Certificate.Type (or PublicKey.Type), but only in
@@ -485,16 +490,16 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
//
// This map must be kept in sync with the one in agent/client.go.
var certKeyAlgoNames = map[string]string{
- CertAlgoRSAv01: KeyAlgoRSA,
- CertAlgoRSASHA256v01: KeyAlgoRSASHA256,
- CertAlgoRSASHA512v01: KeyAlgoRSASHA512,
- CertAlgoDSAv01: KeyAlgoDSA,
- CertAlgoECDSA256v01: KeyAlgoECDSA256,
- CertAlgoECDSA384v01: KeyAlgoECDSA384,
- CertAlgoECDSA521v01: KeyAlgoECDSA521,
- CertAlgoSKECDSA256v01: KeyAlgoSKECDSA256,
- CertAlgoED25519v01: KeyAlgoED25519,
- CertAlgoSKED25519v01: KeyAlgoSKED25519,
+ CertAlgoRSAv01: KeyAlgoRSA,
+ CertAlgoRSASHA256v01: KeyAlgoRSASHA256,
+ CertAlgoRSASHA512v01: KeyAlgoRSASHA512,
+ InsecureCertAlgoDSAv01: InsecureKeyAlgoDSA,
+ CertAlgoECDSA256v01: KeyAlgoECDSA256,
+ CertAlgoECDSA384v01: KeyAlgoECDSA384,
+ CertAlgoECDSA521v01: KeyAlgoECDSA521,
+ CertAlgoSKECDSA256v01: KeyAlgoSKECDSA256,
+ CertAlgoED25519v01: KeyAlgoED25519,
+ CertAlgoSKED25519v01: KeyAlgoSKED25519,
}
// underlyingAlgo returns the signature algorithm associated with algo (which is
diff --git a/ssh/certs_test.go b/ssh/certs_test.go
index 6208bb3..1a4b499 100644
--- a/ssh/certs_test.go
+++ b/ssh/certs_test.go
@@ -297,7 +297,7 @@ func TestCertTypes(t *testing.T) {
{"legacyRSASigner", &legacyRSASigner{testSigners["rsa"]}, KeyAlgoRSA},
{"multiAlgoRSASignerSHA256", multiAlgoSignerSHA256, KeyAlgoRSASHA256},
{"multiAlgoRSASignerSHA512", multiAlgoSignerSHA512, KeyAlgoRSASHA512},
- {CertAlgoDSAv01, testSigners["dsa"], ""},
+ {InsecureCertAlgoDSAv01, testSigners["dsa"], ""},
}
k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
diff --git a/ssh/cipher.go b/ssh/cipher.go
index 741e984..94ba8d9 100644
--- a/ssh/cipher.go
+++ b/ssh/cipher.go
@@ -98,36 +98,36 @@ func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream,
var cipherModes = map[string]*cipherMode{
// Ciphers from RFC 4344, which introduced many CTR-based ciphers. Algorithms
// are defined in the order specified in the RFC.
- "aes128-ctr": {16, aes.BlockSize, streamCipherMode(0, newAESCTR)},
- "aes192-ctr": {24, aes.BlockSize, streamCipherMode(0, newAESCTR)},
- "aes256-ctr": {32, aes.BlockSize, streamCipherMode(0, newAESCTR)},
+ CipherAES128CTR: {16, aes.BlockSize, streamCipherMode(0, newAESCTR)},
+ CipherAES192CTR: {24, aes.BlockSize, streamCipherMode(0, newAESCTR)},
+ CipherAES256CTR: {32, aes.BlockSize, streamCipherMode(0, newAESCTR)},
// Ciphers from RFC 4345, which introduces security-improved arcfour ciphers.
// They are defined in the order specified in the RFC.
- "arcfour128": {16, 0, streamCipherMode(1536, newRC4)},
- "arcfour256": {32, 0, streamCipherMode(1536, newRC4)},
+ InsecureCipherRC4128: {16, 0, streamCipherMode(1536, newRC4)},
+ InsecureCipherRC4256: {32, 0, streamCipherMode(1536, newRC4)},
// Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol.
// Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and
// RC4) has problems with weak keys, and should be used with caution."
// RFC 4345 introduces improved versions of Arcfour.
- "arcfour": {16, 0, streamCipherMode(0, newRC4)},
+ InsecureCipherRC4: {16, 0, streamCipherMode(0, newRC4)},
// AEAD ciphers
- gcm128CipherID: {16, 12, newGCMCipher},
- gcm256CipherID: {32, 12, newGCMCipher},
- chacha20Poly1305ID: {64, 0, newChaCha20Cipher},
+ CipherAES128GCM: {16, 12, newGCMCipher},
+ CipherAES256GCM: {32, 12, newGCMCipher},
+ CipherChaCha20Poly1305: {64, 0, newChaCha20Cipher},
// CBC mode is insecure and so is not included in the default config.
// (See https://www.ieee-security.org/TC/SP2013/papers/4977a526.pdf). If absolutely
// needed, it's possible to specify a custom Config to enable it.
// You should expect that an active attacker can recover plaintext if
// you do.
- aes128cbcID: {16, aes.BlockSize, newAESCBCCipher},
+ InsecureCipherAES128CBC: {16, aes.BlockSize, newAESCBCCipher},
// 3des-cbc is insecure and is not included in the default
// config.
- tripledescbcID: {24, des.BlockSize, newTripleDESCBCCipher},
+ InsecureCipherTripleDESCBC: {24, des.BlockSize, newTripleDESCBCCipher},
}
// prefixLen is the length of the packet prefix that contains the packet length
@@ -635,8 +635,6 @@ func (c *cbcCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader
return nil
}
-const chacha20Poly1305ID = "chacha20-poly1305@openssh.com"
-
// chacha20Poly1305Cipher implements the chacha20-poly1305@openssh.com
// AEAD, which is described here:
//
diff --git a/ssh/cipher_test.go b/ssh/cipher_test.go
index f1be0d6..feaf42c 100644
--- a/ssh/cipher_test.go
+++ b/ssh/cipher_test.go
@@ -22,7 +22,7 @@ func TestDefaultCiphersExist(t *testing.T) {
t.Errorf("supported cipher %q is unknown", cipherAlgo)
}
}
- for _, cipherAlgo := range preferredCiphers {
+ for _, cipherAlgo := range defaultCiphers {
if _, ok := cipherModes[cipherAlgo]; !ok {
t.Errorf("preferred cipher %q is unknown", cipherAlgo)
}
@@ -30,8 +30,8 @@ func TestDefaultCiphersExist(t *testing.T) {
}
func TestPacketCiphers(t *testing.T) {
- defaultMac := "hmac-sha2-256"
- defaultCipher := "aes128-ctr"
+ defaultMac := HMACSHA256
+ defaultCipher := CipherAES128CTR
for cipher := range cipherModes {
t.Run("cipher="+cipher,
func(t *testing.T) { testPacketCipher(t, cipher, defaultMac) })
@@ -47,7 +47,7 @@ func testPacketCipher(t *testing.T, cipher, mac string) {
algs := directionAlgorithms{
Cipher: cipher,
MAC: mac,
- Compression: "none",
+ Compression: compressionNone,
}
client, err := newPacketCipher(clientKeys, algs, kr)
if err != nil {
@@ -78,9 +78,9 @@ func testPacketCipher(t *testing.T, cipher, mac string) {
func TestCBCOracleCounterMeasure(t *testing.T) {
kr := &kexResult{Hash: crypto.SHA1}
algs := directionAlgorithms{
- Cipher: aes128cbcID,
- MAC: "hmac-sha1",
- Compression: "none",
+ Cipher: InsecureCipherAES128CBC,
+ MAC: HMACSHA1,
+ Compression: compressionNone,
}
client, err := newPacketCipher(clientKeys, algs, kr)
if err != nil {
@@ -141,7 +141,7 @@ func TestCVE202143565(t *testing.T) {
constructPacket func(packetCipher) io.Reader
}{
{
- cipher: gcm128CipherID,
+ cipher: CipherAES128GCM,
constructPacket: func(client packetCipher) io.Reader {
internalCipher := client.(*gcmCipher)
b := &bytes.Buffer{}
@@ -159,7 +159,7 @@ func TestCVE202143565(t *testing.T) {
},
},
{
- cipher: chacha20Poly1305ID,
+ cipher: CipherChaCha20Poly1305,
constructPacket: func(client packetCipher) io.Reader {
internalCipher := client.(*chacha20Poly1305Cipher)
b := &bytes.Buffer{}
@@ -201,13 +201,13 @@ func TestCVE202143565(t *testing.T) {
}
for _, tc := range tests {
- mac := "hmac-sha2-256"
+ mac := HMACSHA256
kr := &kexResult{Hash: crypto.SHA1}
algs := directionAlgorithms{
Cipher: tc.cipher,
MAC: mac,
- Compression: "none",
+ Compression: compressionNone,
}
client, err := newPacketCipher(clientKeys, algs, kr)
if err != nil {
diff --git a/ssh/client_auth_test.go b/ssh/client_auth_test.go
index ec27133..8d1767c 100644
--- a/ssh/client_auth_test.go
+++ b/ssh/client_auth_test.go
@@ -324,6 +324,9 @@ func TestMethodInvalidAlgorithm(t *testing.T) {
}
func TestClientHMAC(t *testing.T) {
+ supportedAlgos := SupportedAlgorithms()
+ insecureAlgos := InsecureAlgorithms()
+ supportedMACs := append(supportedAlgos.MACs, insecureAlgos.MACs...)
for _, mac := range supportedMACs {
config := &ClientConfig{
User: "testuser",
@@ -349,7 +352,7 @@ func TestClientUnsupportedCipher(t *testing.T) {
PublicKeys(),
},
Config: Config{
- Ciphers: []string{"aes128-cbc"}, // not currently supported
+ Ciphers: []string{"unsupported-cipher"}, // not currently supported
},
}
if err := tryAuth(t, config); err == nil {
diff --git a/ssh/client_test.go b/ssh/client_test.go
index 2621f0e..d583900 100644
--- a/ssh/client_test.go
+++ b/ssh/client_test.go
@@ -292,7 +292,7 @@ func TestUnsupportedAlgorithm(t *testing.T) {
{
"unsupported and supported KEXs",
Config{
- KeyExchanges: []string{"unsupported", kexAlgoCurve25519SHA256},
+ KeyExchanges: []string{"unsupported", KeyExchangeCurve25519},
},
"",
},
@@ -306,7 +306,7 @@ func TestUnsupportedAlgorithm(t *testing.T) {
{
"unsupported and supported ciphers",
Config{
- Ciphers: []string{"unsupported", chacha20Poly1305ID},
+ Ciphers: []string{"unsupported", CipherChaCha20Poly1305},
},
"",
},
@@ -315,16 +315,16 @@ func TestUnsupportedAlgorithm(t *testing.T) {
Config{
MACs: []string{"unsupported"},
// MAC is used for non AAED ciphers.
- Ciphers: []string{"aes256-ctr"},
+ Ciphers: []string{CipherAES256CTR},
},
"no common algorithm",
},
{
"unsupported and supported MACs",
Config{
- MACs: []string{"unsupported", "hmac-sha2-256-etm@openssh.com"},
+ MACs: []string{"unsupported", HMACSHA256ETM},
// MAC is used for non AAED ciphers.
- Ciphers: []string{"aes256-ctr"},
+ Ciphers: []string{CipherAES256CTR},
},
"",
},
diff --git a/ssh/common.go b/ssh/common.go
index 7e9c2cb..2b11e22 100644
--- a/ssh/common.go
+++ b/ssh/common.go
@@ -10,6 +10,7 @@ import (
"fmt"
"io"
"math"
+ "slices"
"sync"
_ "crypto/sha1"
@@ -24,69 +25,257 @@ const (
serviceSSH = "ssh-connection"
)
-// 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,
-}
+// The ciphers currently or previously implemented by this library, to use in
+// [Config.Ciphers]. For a list, see the [Algorithms.Ciphers] returned by
+// [SupportedAlgorithms] or [InsecureAlgorithms].
+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"
+)
-// preferredCiphers specifies the default preference for ciphers.
-var preferredCiphers = []string{
- "aes128-gcm@openssh.com", gcm256CipherID,
- chacha20Poly1305ID,
- "aes128-ctr", "aes192-ctr", "aes256-ctr",
-}
+// The key exchanges currently or previously implemented by this library, to use
+// in [Config.KeyExchanges]. For a list, see the
+// [Algorithms.KeyExchanges] returned by [SupportedAlgorithms] or
+// [InsecureAlgorithms].
+const (
+ InsecureKeyExchangeDH1SHA1 = "diffie-hellman-group1-sha1"
+ InsecureKeyExchangeDH14SHA1 = "diffie-hellman-group14-sha1"
+ KeyExchangeDH14SHA256 = "diffie-hellman-group14-sha256"
+ KeyExchangeDH16SHA512 = "diffie-hellman-group16-sha512"
+ KeyExchangeECDHP256 = "ecdh-sha2-nistp256"
+ KeyExchangeECDHP384 = "ecdh-sha2-nistp384"
+ KeyExchangeECDHP521 = "ecdh-sha2-nistp521"
+ KeyExchangeCurve25519 = "curve25519-sha256"
+ InsecureKeyExchangeDHGEXSHA1 = "diffie-hellman-group-exchange-sha1"
+ KeyExchangeDHGEXSHA256 = "diffie-hellman-group-exchange-sha256"
+ // KeyExchangeMLKEM768X25519 is supported from Go 1.24.
+ KeyExchangeMLKEM768X25519 = "mlkem768x25519-sha256"
-// supportedKexAlgos specifies the supported key-exchange algorithms in
-// preference order.
-var supportedKexAlgos = []string{
- kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
- // P384 and P521 are not constant-time yet, but since we don't
- // reuse ephemeral keys, using them for ECDH should be OK.
- kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
- kexAlgoDH14SHA256, kexAlgoDH16SHA512, kexAlgoDH14SHA1,
- kexAlgoDH1SHA1,
-}
+ // An alias for KeyExchangeCurve25519SHA256.
+ keyExchangeCurve25519LibSSH = "curve25519-sha256@libssh.org"
+)
-// serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden
-// for the server half.
-var serverForbiddenKexAlgos = map[string]struct{}{
- kexAlgoDHGEXSHA1: {}, // server half implementation is only minimal to satisfy the automated tests
- kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests
-}
+// The message authentication code (MAC) currently or previously implemented by
+// this library, to use in [Config.MACs]. For a list, see the
+// [Algorithms.MACs] returned by [SupportedAlgorithms] or
+// [InsecureAlgorithms].
+const (
+ HMACSHA256ETM = "hmac-sha2-256-etm@openssh.com"
+ HMACSHA512ETM = "hmac-sha2-512-etm@openssh.com"
+ HMACSHA256 = "hmac-sha2-256"
+ HMACSHA512 = "hmac-sha2-512"
+ HMACSHA1 = "hmac-sha1"
+ InsecureHMACSHA196 = "hmac-sha1-96"
+)
-// preferredKexAlgos specifies the default preference for key-exchange
-// algorithms in preference order. The diffie-hellman-group16-sha512 algorithm
-// is disabled by default because it is a bit slower than the others.
-var preferredKexAlgos = []string{
- kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
- kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
- kexAlgoDH14SHA256, kexAlgoDH14SHA1,
-}
+var (
+ // supportedKexAlgos specifies key-exchange algorithms implemented by this
+ // package in preference order, excluding those with security issues.
+ supportedKexAlgos = []string{
+ KeyExchangeCurve25519,
+ keyExchangeCurve25519LibSSH,
+ KeyExchangeECDHP256,
+ KeyExchangeECDHP384,
+ KeyExchangeECDHP521,
+ KeyExchangeDH14SHA256,
+ KeyExchangeDH16SHA512,
+ KeyExchangeDHGEXSHA256,
+ }
+ // defaultKexAlgos specifies the default preference for key-exchange
+ // algorithms in preference order.
+ defaultKexAlgos = []string{
+ KeyExchangeCurve25519,
+ keyExchangeCurve25519LibSSH,
+ KeyExchangeECDHP256,
+ KeyExchangeECDHP384,
+ KeyExchangeECDHP521,
+ KeyExchangeDH14SHA256,
+ InsecureKeyExchangeDH14SHA1,
+ }
+ // insecureKexAlgos specifies key-exchange algorithms implemented by this
+ // package and which have security issues.
+ insecureKexAlgos = []string{
+ InsecureKeyExchangeDH14SHA1,
+ InsecureKeyExchangeDH1SHA1,
+ InsecureKeyExchangeDHGEXSHA1,
+ }
+ // supportedCiphers specifies cipher algorithms implemented by this package
+ // in preference order, excluding those with security issues.
+ supportedCiphers = []string{
+ CipherAES128GCM,
+ CipherAES256GCM,
+ CipherChaCha20Poly1305,
+ CipherAES128CTR,
+ CipherAES192CTR,
+ CipherAES256CTR,
+ }
+ // defaultCiphers specifies the default preference for ciphers algorithms
+ // in preference order.
+ defaultCiphers = supportedCiphers
+ // insecureCiphers specifies cipher algorithms implemented by this
+ // package and which have security issues.
+ insecureCiphers = []string{
+ InsecureCipherAES128CBC,
+ InsecureCipherTripleDESCBC,
+ InsecureCipherRC4256,
+ InsecureCipherRC4128,
+ InsecureCipherRC4,
+ }
+ // supportedMACs specifies MAC algorithms implemented by this package in
+ // preference order, excluding those with security issues.
+ supportedMACs = []string{
+ HMACSHA256ETM,
+ HMACSHA512ETM,
+ HMACSHA256,
+ HMACSHA512,
+ HMACSHA1,
+ }
+ // defaultMACs specifies the default preference for MAC algorithms in
+ // preference order.
+ defaultMACs = []string{
+ HMACSHA256ETM,
+ HMACSHA512ETM,
+ HMACSHA256,
+ HMACSHA512,
+ HMACSHA1,
+ InsecureHMACSHA196,
+ }
+ // insecureMACs specifies MAC algorithms implemented by this
+ // package and which have security issues.
+ insecureMACs = []string{
+ InsecureHMACSHA196,
+ }
+ // supportedHostKeyAlgos specifies the supported host-key algorithms (i.e.
+ // methods of authenticating servers) implemented by this package in
+ // preference order, excluding those with security issues.
+ supportedHostKeyAlgos = []string{
+ CertAlgoRSASHA256v01,
+ CertAlgoRSASHA512v01,
+ CertAlgoECDSA256v01,
+ CertAlgoECDSA384v01,
+ CertAlgoECDSA521v01,
+ CertAlgoED25519v01,
+ KeyAlgoRSASHA256,
+ KeyAlgoRSASHA512,
+ KeyAlgoECDSA256,
+ KeyAlgoECDSA384,
+ KeyAlgoECDSA521,
+ KeyAlgoED25519,
+ }
+ // defaultHostKeyAlgos specifies the default preference for host-key
+ // algorithms in preference order.
+ defaultHostKeyAlgos = []string{
+ CertAlgoRSASHA256v01,
+ CertAlgoRSASHA512v01,
+ CertAlgoRSAv01,
+ InsecureCertAlgoDSAv01,
+ CertAlgoECDSA256v01,
+ CertAlgoECDSA384v01,
+ CertAlgoECDSA521v01,
+ CertAlgoED25519v01,
+ KeyAlgoECDSA256,
+ KeyAlgoECDSA384,
+ KeyAlgoECDSA521,
+ KeyAlgoRSASHA256,
+ KeyAlgoRSASHA512,
+ KeyAlgoRSA,
+ InsecureKeyAlgoDSA,
+ KeyAlgoED25519,
+ }
+ // insecureHostKeyAlgos specifies host-key algorithms implemented by this
+ // package and which have security issues.
+ insecureHostKeyAlgos = []string{
+ KeyAlgoRSA,
+ InsecureKeyAlgoDSA,
+ CertAlgoRSAv01,
+ InsecureCertAlgoDSAv01,
+ }
+ // supportedPubKeyAuthAlgos specifies the supported client public key
+ // authentication algorithms. Note that this doesn't include certificate
+ // types since those use the underlying algorithm. Order is irrelevant.
+ supportedPubKeyAuthAlgos = []string{
+ KeyAlgoED25519,
+ KeyAlgoSKED25519,
+ KeyAlgoSKECDSA256,
+ KeyAlgoECDSA256,
+ KeyAlgoECDSA384,
+ KeyAlgoECDSA521,
+ KeyAlgoRSASHA256,
+ KeyAlgoRSASHA512,
+ }
-// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
-// of authenticating servers) in preference order.
-var supportedHostKeyAlgos = []string{
- CertAlgoRSASHA256v01, CertAlgoRSASHA512v01,
- CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
- CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
+ // defaultPubKeyAuthAlgos specifies the preferred client public key
+ // authentication algorithms. This list is sent to the client if it supports
+ // the server-sig-algs extension. Order is irrelevant.
+ defaultPubKeyAuthAlgos = []string{
+ KeyAlgoED25519,
+ KeyAlgoSKED25519,
+ KeyAlgoSKECDSA256,
+ KeyAlgoECDSA256,
+ KeyAlgoECDSA384,
+ KeyAlgoECDSA521,
+ KeyAlgoRSASHA256,
+ KeyAlgoRSASHA512,
+ KeyAlgoRSA,
+ InsecureKeyAlgoDSA,
+ }
+ // insecurePubKeyAuthAlgos specifies client public key authentication
+ // algorithms implemented by this package and which have security issues.
+ insecurePubKeyAuthAlgos = []string{
+ KeyAlgoRSA,
+ InsecureKeyAlgoDSA,
+ }
+)
- KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
- KeyAlgoRSASHA256, KeyAlgoRSASHA512,
- KeyAlgoRSA, KeyAlgoDSA,
+// 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
+}
- KeyAlgoED25519,
+// 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: slices.Clone(supportedCiphers),
+ MACs: slices.Clone(supportedMACs),
+ KeyExchanges: slices.Clone(supportedKexAlgos),
+ HostKeys: slices.Clone(supportedHostKeyAlgos),
+ PublicKeyAuths: slices.Clone(supportedPubKeyAuthAlgos),
+ }
}
-// supportedMACs specifies a default set of MAC algorithms in preference order.
-// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
-// because they have reached the end of their useful life.
-var supportedMACs = []string{
- "hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com", "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", "hmac-sha1-96",
+// InsecureAlgorithms returns algorithms currently implemented by this package
+// and which have security issues.
+func InsecureAlgorithms() Algorithms {
+ return Algorithms{
+ KeyExchanges: slices.Clone(insecureKexAlgos),
+ Ciphers: slices.Clone(insecureCiphers),
+ MACs: slices.Clone(insecureMACs),
+ HostKeys: slices.Clone(insecureHostKeyAlgos),
+ PublicKeyAuths: slices.Clone(insecurePubKeyAuthAlgos),
+ }
+}
+
+// serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden
+// for the server half.
+var serverForbiddenKexAlgos = map[string]struct{}{
+ InsecureKeyExchangeDHGEXSHA1: {}, // server half implementation is only minimal to satisfy the automated tests
+ KeyExchangeDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests
}
var supportedCompressions = []string{compressionNone}
@@ -94,13 +283,13 @@ var supportedCompressions = []string{compressionNone}
// hashFuncs keeps the mapping of supported signature algorithms to their
// respective hashes needed for signing and verification.
var hashFuncs = map[string]crypto.Hash{
- KeyAlgoRSA: crypto.SHA1,
- KeyAlgoRSASHA256: crypto.SHA256,
- KeyAlgoRSASHA512: crypto.SHA512,
- KeyAlgoDSA: crypto.SHA1,
- KeyAlgoECDSA256: crypto.SHA256,
- KeyAlgoECDSA384: crypto.SHA384,
- KeyAlgoECDSA521: crypto.SHA512,
+ KeyAlgoRSA: crypto.SHA1,
+ KeyAlgoRSASHA256: crypto.SHA256,
+ KeyAlgoRSASHA512: crypto.SHA512,
+ InsecureKeyAlgoDSA: crypto.SHA1,
+ KeyAlgoECDSA256: crypto.SHA256,
+ KeyAlgoECDSA384: crypto.SHA384,
+ KeyAlgoECDSA521: crypto.SHA512,
// KeyAlgoED25519 doesn't pre-hash.
KeyAlgoSKECDSA256: crypto.SHA256,
KeyAlgoSKED25519: crypto.SHA256,
@@ -135,18 +324,6 @@ func isRSACert(algo string) bool {
return isRSA(algo)
}
-// supportedPubKeyAuthAlgos specifies the supported client public key
-// authentication algorithms. Note that this doesn't include certificate types
-// since those use the underlying algorithm. This list is sent to the client if
-// it supports the server-sig-algs extension. Order is irrelevant.
-var supportedPubKeyAuthAlgos = []string{
- KeyAlgoED25519,
- KeyAlgoSKED25519, KeyAlgoSKECDSA256,
- KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
- KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA,
- KeyAlgoDSA,
-}
-
// unexpectedMessageError results when the SSH message that we received didn't
// match what we wanted.
func unexpectedMessageError(expected, got uint8) error {
@@ -182,7 +359,7 @@ func (a *directionAlgorithms) rekeyBytes() int64 {
// 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is
// 128.
switch a.Cipher {
- case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcm128CipherID, gcm256CipherID, aes128cbcID:
+ case CipherAES128CTR, CipherAES192CTR, CipherAES256CTR, CipherAES128GCM, CipherAES256GCM, InsecureCipherAES128CBC:
return 16 * (1 << 32)
}
@@ -192,9 +369,9 @@ func (a *directionAlgorithms) rekeyBytes() int64 {
}
var aeadCiphers = map[string]bool{
- gcm128CipherID: true,
- gcm256CipherID: true,
- chacha20Poly1305ID: true,
+ CipherAES128GCM: true,
+ CipherAES256GCM: true,
+ CipherChaCha20Poly1305: true,
}
type algorithms struct {
@@ -297,7 +474,7 @@ func (c *Config) SetDefaults() {
c.Rand = rand.Reader
}
if c.Ciphers == nil {
- c.Ciphers = preferredCiphers
+ c.Ciphers = defaultCiphers
}
var ciphers []string
for _, c := range c.Ciphers {
@@ -309,7 +486,7 @@ func (c *Config) SetDefaults() {
c.Ciphers = ciphers
if c.KeyExchanges == nil {
- c.KeyExchanges = preferredKexAlgos
+ c.KeyExchanges = defaultKexAlgos
}
var kexs []string
for _, k := range c.KeyExchanges {
@@ -321,7 +498,7 @@ func (c *Config) SetDefaults() {
c.KeyExchanges = kexs
if c.MACs == nil {
- c.MACs = supportedMACs
+ c.MACs = defaultMACs
}
var macs []string
for _, m := range c.MACs {
diff --git a/ssh/example_test.go b/ssh/example_test.go
index 97b3b6a..d6bad3e 100644
--- a/ssh/example_test.go
+++ b/ssh/example_test.go
@@ -154,6 +154,75 @@ func ExampleNewServerConn() {
}
}
+func ExampleServerConfig() {
+ // Minimal ServerConfig with SHA-1 algoritms disabled and supporting only
+ // public key authentication.
+
+ // The algorithms returned by ssh.SupportedAlgorithms() are different from
+ // the default ones and do not include algorithms that are considered
+ // insecure, such as those using SHA-1, returned by
+ // ssh.InsecureAlgorithms().
+ algorithms := ssh.SupportedAlgorithms()
+
+ // Public key authentication is done by comparing
+ // the public key of a received connection
+ // with the entries in the authorized_keys file.
+ authorizedKeysBytes, err := os.ReadFile("authorized_keys")
+ if err != nil {
+ log.Fatalf("Failed to load authorized_keys, err: %v", err)
+ }
+
+ authorizedKeysMap := map[string]bool{}
+ for len(authorizedKeysBytes) > 0 {
+ pubKey, _, _, rest, err := ssh.ParseAuthorizedKey(authorizedKeysBytes)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ authorizedKeysMap[string(pubKey.Marshal())] = true
+ authorizedKeysBytes = rest
+ }
+
+ config := &ssh.ServerConfig{
+ Config: ssh.Config{
+ KeyExchanges: algorithms.KeyExchanges,
+ Ciphers: algorithms.Ciphers,
+ MACs: algorithms.MACs,
+ },
+ // The configured PublicKeyAuthAlgorithms do not contain SHA-1 based
+ // signature formats, so if the client attempts to use them the
+ // authentication will fail before calling the defined callback.
+ PublicKeyAuthAlgorithms: algorithms.PublicKeyAuths,
+ PublicKeyCallback: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
+ if authorizedKeysMap[string(pubKey.Marshal())] {
+ return &ssh.Permissions{
+ // Record the public key used for authentication.
+ Extensions: map[string]string{
+ "pubkey-fp": ssh.FingerprintSHA256(pubKey),
+ },
+ }, nil
+ }
+ return nil, fmt.Errorf("unknown public key for %q", c.User())
+ },
+ }
+
+ privateBytes, err := os.ReadFile("id_rsa")
+ if err != nil {
+ log.Fatal("Failed to load private key: ", err)
+ }
+
+ private, err := ssh.ParsePrivateKey(privateBytes)
+ if err != nil {
+ log.Fatal("Failed to parse private key: ", err)
+ }
+ // Restrict host key algorithms to disable ssh-rsa.
+ signer, err := ssh.NewSignerWithAlgorithms(private.(ssh.AlgorithmSigner), []string{ssh.KeyAlgoRSASHA256, ssh.KeyAlgoRSASHA512})
+ if err != nil {
+ log.Fatal("Failed to create private key with restricted algorithms: ", err)
+ }
+ config.AddHostKey(signer)
+}
+
func ExampleServerConfig_AddHostKey() {
// Minimal ServerConfig supporting only password authentication.
config := &ssh.ServerConfig{
@@ -262,6 +331,47 @@ func ExampleDial() {
fmt.Println(b.String())
}
+func ExampleClientConfig() {
+ var hostKey ssh.PublicKey
+ key, err := os.ReadFile("/home/user/.ssh/id_rsa")
+ if err != nil {
+ log.Fatalf("unable to read private key: %v", err)
+ }
+
+ // Create the Signer for this private key.
+ signer, err := ssh.ParsePrivateKey(key)
+ if err != nil {
+ log.Fatalf("unable to parse private key: %v", err)
+ }
+
+ // Minimal ClientConfig with SHA-1 algoritms disabled.
+ // The algorithms returned by ssh.SupportedAlgorithms() are different from
+ // the default ones and do not include algorithms that are considered
+ // insecure, such as those using SHA-1, returned by
+ // ssh.InsecureAlgorithms().
+ algorithms := ssh.SupportedAlgorithms()
+ config := &ssh.ClientConfig{
+ Config: ssh.Config{
+ KeyExchanges: algorithms.KeyExchanges,
+ Ciphers: algorithms.Ciphers,
+ MACs: algorithms.MACs,
+ },
+ User: "username",
+ Auth: []ssh.AuthMethod{
+ ssh.PublicKeys(signer),
+ },
+ HostKeyCallback: ssh.FixedHostKey(hostKey),
+ // We should check that hostKey algorithm is inlcuded in
+ // algorithms.HostKeys.
+ HostKeyAlgorithms: algorithms.HostKeys,
+ }
+ client, err := ssh.Dial("tcp", "yourserver.com:22", config)
+ if err != nil {
+ log.Fatal("Failed to dial: ", err)
+ }
+ defer client.Close()
+}
+
func ExamplePublicKeys() {
var hostKey ssh.PublicKey
// A public key may be used to authenticate against the remote
diff --git a/ssh/handshake.go b/ssh/handshake.go
index b6bf546..9d3222d 100644
--- a/ssh/handshake.go
+++ b/ssh/handshake.go
@@ -164,7 +164,7 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt
if config.HostKeyAlgorithms != nil {
t.hostKeyAlgorithms = config.HostKeyAlgorithms
} else {
- t.hostKeyAlgorithms = supportedHostKeyAlgos
+ t.hostKeyAlgorithms = defaultHostKeyAlgos
}
go t.readLoop()
go t.kexLoop()
diff --git a/ssh/handshake_test.go b/ssh/handshake_test.go
index 019e47f..c0663c2 100644
--- a/ssh/handshake_test.go
+++ b/ssh/handshake_test.go
@@ -762,7 +762,7 @@ func TestHandshakePendingPacketsError(t *testing.T) {
func TestHandshakeRekeyDefault(t *testing.T) {
clientConf := &ClientConfig{
Config: Config{
- Ciphers: []string{"aes128-ctr"},
+ Ciphers: []string{CipherAES128CTR},
},
HostKeyCallback: InsecureIgnoreHostKey(),
}
@@ -788,7 +788,7 @@ func TestHandshakeRekeyDefault(t *testing.T) {
}
func TestHandshakeAEADCipherNoMAC(t *testing.T) {
- for _, cipher := range []string{chacha20Poly1305ID, gcm128CipherID} {
+ for _, cipher := range []string{CipherChaCha20Poly1305, CipherAES128GCM} {
checker := &syncChecker{
called: make(chan int, 1),
}
diff --git a/ssh/kex.go b/ssh/kex.go
index 8a05f79..184544a 100644
--- a/ssh/kex.go
+++ b/ssh/kex.go
@@ -19,24 +19,6 @@ import (
"golang.org/x/crypto/curve25519"
)
-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"
- kexAlgoCurve25519SHA256LibSSH = "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.
- kexAlgoDHGEXSHA1 = "diffie-hellman-group-exchange-sha1"
- kexAlgoDHGEXSHA256 = "diffie-hellman-group-exchange-sha256"
-)
-
// kexResult captures the outcome of a key exchange.
type kexResult struct {
// Session hash. See also RFC 4253, section 8.
@@ -405,7 +387,7 @@ func init() {
// This is the group called diffie-hellman-group1-sha1 in
// RFC 4253 and Oakley Group 2 in RFC 2409.
p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
- kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{
+ kexAlgoMap[InsecureKeyExchangeDH1SHA1] = &dhGroup{
g: new(big.Int).SetInt64(2),
p: p,
pMinus1: new(big.Int).Sub(p, bigOne),
@@ -422,11 +404,11 @@ func init() {
pMinus1: new(big.Int).Sub(p, bigOne),
}
- kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{
+ kexAlgoMap[InsecureKeyExchangeDH14SHA1] = &dhGroup{
g: group14.g, p: group14.p, pMinus1: group14.pMinus1,
hashFunc: crypto.SHA1,
}
- kexAlgoMap[kexAlgoDH14SHA256] = &dhGroup{
+ kexAlgoMap[KeyExchangeDH14SHA256] = &dhGroup{
g: group14.g, p: group14.p, pMinus1: group14.pMinus1,
hashFunc: crypto.SHA256,
}
@@ -435,20 +417,20 @@ func init() {
// 8268 and Oakley Group 16 in RFC 3526.
p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF", 16)
- kexAlgoMap[kexAlgoDH16SHA512] = &dhGroup{
+ kexAlgoMap[KeyExchangeDH16SHA512] = &dhGroup{
g: new(big.Int).SetInt64(2),
p: p,
pMinus1: new(big.Int).Sub(p, bigOne),
hashFunc: crypto.SHA512,
}
- kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}
- kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
- kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
- kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{}
- kexAlgoMap[kexAlgoCurve25519SHA256LibSSH] = &curve25519sha256{}
- kexAlgoMap[kexAlgoDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1}
- kexAlgoMap[kexAlgoDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256}
+ kexAlgoMap[KeyExchangeECDHP521] = &ecdh{elliptic.P521()}
+ kexAlgoMap[KeyExchangeECDHP384] = &ecdh{elliptic.P384()}
+ kexAlgoMap[KeyExchangeECDHP256] = &ecdh{elliptic.P256()}
+ kexAlgoMap[KeyExchangeCurve25519] = &curve25519sha256{}
+ kexAlgoMap[keyExchangeCurve25519LibSSH] = &curve25519sha256{}
+ kexAlgoMap[InsecureKeyExchangeDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1}
+ kexAlgoMap[KeyExchangeDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256}
}
// curve25519sha256 implements the curve25519-sha256 (formerly known as
diff --git a/ssh/keys.go b/ssh/keys.go
index 98e6706..566e09d 100644
--- a/ssh/keys.go
+++ b/ssh/keys.go
@@ -36,14 +36,19 @@ import (
// ClientConfig.HostKeyAlgorithms, Signature.Format, or as AlgorithmSigner
// arguments.
const (
- KeyAlgoRSA = "ssh-rsa"
- KeyAlgoDSA = "ssh-dss"
- KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
- KeyAlgoSKECDSA256 = "sk-ecdsa-sha2-nistp256@openssh.com"
- KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
- KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
- KeyAlgoED25519 = "ssh-ed25519"
- KeyAlgoSKED25519 = "sk-ssh-ed25519@openssh.com"
+ KeyAlgoRSA = "ssh-rsa"
+ // Deprecated: DSA is only supported at insecure key sizes, and was removed
+ // from major implementations.
+ KeyAlgoDSA = InsecureKeyAlgoDSA
+ // Deprecated: DSA is only supported at insecure key sizes, and was removed
+ // from major implementations.
+ InsecureKeyAlgoDSA = "ssh-dss"
+ KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
+ KeyAlgoSKECDSA256 = "sk-ecdsa-sha2-nistp256@openssh.com"
+ KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
+ KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
+ KeyAlgoED25519 = "ssh-ed25519"
+ KeyAlgoSKED25519 = "sk-ssh-ed25519@openssh.com"
// KeyAlgoRSASHA256 and KeyAlgoRSASHA512 are only public key algorithms, not
// public key formats, so they can't appear as a PublicKey.Type. The
@@ -67,7 +72,7 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err
switch algo {
case KeyAlgoRSA:
return parseRSA(in)
- case KeyAlgoDSA:
+ case InsecureKeyAlgoDSA:
return parseDSA(in)
case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
return parseECDSA(in)
@@ -77,7 +82,7 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err
return parseED25519(in)
case KeyAlgoSKED25519:
return parseSKEd25519(in)
- case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01:
+ case CertAlgoRSAv01, InsecureCertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01:
cert, err := parseCert(in, certKeyAlgoNames[algo])
if err != nil {
return nil, nil, err
diff --git a/ssh/mac.go b/ssh/mac.go
index 06a1b27..de2639d 100644
--- a/ssh/mac.go
+++ b/ssh/mac.go
@@ -47,22 +47,22 @@ func (t truncatingMAC) Size() int {
func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() }
var macModes = map[string]*macMode{
- "hmac-sha2-512-etm@openssh.com": {64, true, func(key []byte) hash.Hash {
+ HMACSHA512ETM: {64, true, func(key []byte) hash.Hash {
return hmac.New(sha512.New, key)
}},
- "hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash {
+ HMACSHA256ETM: {32, true, func(key []byte) hash.Hash {
return hmac.New(sha256.New, key)
}},
- "hmac-sha2-512": {64, false, func(key []byte) hash.Hash {
+ HMACSHA512: {64, false, func(key []byte) hash.Hash {
return hmac.New(sha512.New, key)
}},
- "hmac-sha2-256": {32, false, func(key []byte) hash.Hash {
+ HMACSHA256: {32, false, func(key []byte) hash.Hash {
return hmac.New(sha256.New, key)
}},
- "hmac-sha1": {20, false, func(key []byte) hash.Hash {
+ HMACSHA1: {20, false, func(key []byte) hash.Hash {
return hmac.New(sha1.New, key)
}},
- "hmac-sha1-96": {20, false, func(key []byte) hash.Hash {
+ InsecureHMACSHA196: {20, false, func(key []byte) hash.Hash {
return truncatingMAC{12, hmac.New(sha1.New, key)}
}},
}
diff --git a/ssh/mlkem.go b/ssh/mlkem.go
index 40681dd..657e107 100644
--- a/ssh/mlkem.go
+++ b/ssh/mlkem.go
@@ -19,19 +19,15 @@ import (
"golang.org/x/crypto/curve25519"
)
-const (
- kexAlgoMLKEM768xCurve25519SHA256 = "mlkem768x25519-sha256"
-)
-
func init() {
// After Go 1.24rc1 mlkem swapped the order of return values of Encapsulate.
// See #70950.
if runtime.Version() == "go1.24rc1" {
return
}
- supportedKexAlgos = slices.Insert(supportedKexAlgos, 0, kexAlgoMLKEM768xCurve25519SHA256)
- preferredKexAlgos = slices.Insert(preferredKexAlgos, 0, kexAlgoMLKEM768xCurve25519SHA256)
- kexAlgoMap[kexAlgoMLKEM768xCurve25519SHA256] = &mlkem768WithCurve25519sha256{}
+ supportedKexAlgos = slices.Insert(supportedKexAlgos, 0, KeyExchangeMLKEM768X25519)
+ defaultKexAlgos = slices.Insert(defaultKexAlgos, 0, KeyExchangeMLKEM768X25519)
+ kexAlgoMap[KeyExchangeMLKEM768X25519] = &mlkem768WithCurve25519sha256{}
}
// mlkem768WithCurve25519sha256 implements the hybrid ML-KEM768 with
diff --git a/ssh/server.go b/ssh/server.go
index 1839ddc..99af665 100644
--- a/ssh/server.go
+++ b/ssh/server.go
@@ -243,10 +243,10 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha
fullConf.MaxAuthTries = 6
}
if len(fullConf.PublicKeyAuthAlgorithms) == 0 {
- fullConf.PublicKeyAuthAlgorithms = supportedPubKeyAuthAlgos
+ fullConf.PublicKeyAuthAlgorithms = defaultPubKeyAuthAlgos
} else {
for _, algo := range fullConf.PublicKeyAuthAlgorithms {
- if !contains(supportedPubKeyAuthAlgos, algo) {
+ if !contains(SupportedAlgorithms().PublicKeyAuths, algo) && !contains(InsecureAlgorithms().PublicKeyAuths, algo) {
c.Close()
return nil, nil, nil, fmt.Errorf("ssh: unsupported public key authentication algorithm %s", algo)
}
diff --git a/ssh/server_test.go b/ssh/server_test.go
index c2b24f4..5bd18db 100644
--- a/ssh/server_test.go
+++ b/ssh/server_test.go
@@ -212,7 +212,7 @@ func TestNewServerConnValidationErrors(t *testing.T) {
serverConf = &ServerConfig{
Config: Config{
- KeyExchanges: []string{kexAlgoDHGEXSHA256},
+ KeyExchanges: []string{KeyExchangeDHGEXSHA256},
},
}
c = &markerConn{}
diff --git a/ssh/test/recording_client_test.go b/ssh/test/recording_client_test.go
index a312003..5c7830f 100644
--- a/ssh/test/recording_client_test.go
+++ b/ssh/test/recording_client_test.go
@@ -236,7 +236,7 @@ func recordingsClientConfig() *ssh.ClientConfig {
config := clientConfig()
config.SetDefaults()
// Remove ML-KEM since it only works with Go 1.24.
- if config.KeyExchanges[0] == "mlkem768x25519-sha256" {
+ if config.KeyExchanges[0] == ssh.KeyExchangeMLKEM768X25519 {
config.KeyExchanges = config.KeyExchanges[1:]
}
config.Auth = []ssh.AuthMethod{
@@ -352,7 +352,7 @@ func TestHostKeyCheck(t *testing.T) {
// change the keys.
hostDB.keys[ssh.KeyAlgoRSA][25]++
- hostDB.keys[ssh.KeyAlgoDSA][25]++
+ hostDB.keys[ssh.InsecureKeyAlgoDSA][25]++
hostDB.keys[ssh.KeyAlgoECDSA256][25]++
test := clientTest{
diff --git a/ssh/test/recording_server_test.go b/ssh/test/recording_server_test.go
index 6a17040..fe99198 100644
--- a/ssh/test/recording_server_test.go
+++ b/ssh/test/recording_server_test.go
@@ -219,7 +219,7 @@ func recordingsServerConfig() *ssh.ServerConfig {
config.SetDefaults()
// Remove ML-KEM since it only works with Go 1.24.
config.SetDefaults()
- if config.KeyExchanges[0] == "mlkem768x25519-sha256" {
+ if config.KeyExchanges[0] == ssh.KeyExchangeMLKEM768X25519 {
config.KeyExchanges = config.KeyExchanges[1:]
}
config.AddHostKey(testSigners["rsa"])
diff --git a/ssh/test/recording_test.go b/ssh/test/recording_test.go
index 4f8bb2a..4356345 100644
--- a/ssh/test/recording_test.go
+++ b/ssh/test/recording_test.go
@@ -128,7 +128,7 @@ func clientConfig() *ssh.ClientConfig {
HostKeyCallback: hostKeyDB().Check,
HostKeyAlgorithms: []string{ // by default, don't allow certs as this affects the hostKeyDB checker
ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521,
- ssh.KeyAlgoRSA, ssh.KeyAlgoDSA,
+ ssh.KeyAlgoRSA, ssh.InsecureKeyAlgoDSA,
ssh.KeyAlgoED25519,
},
}
diff --git a/ssh/test/session_test.go b/ssh/test/session_test.go
index ae1e698..fda8d8c 100644
--- a/ssh/test/session_test.go
+++ b/ssh/test/session_test.go
@@ -224,8 +224,8 @@ func testOneCipher(t *testing.T, cipher string, cipherOrder []string) {
}
var deprecatedCiphers = []string{
- "aes128-cbc", "3des-cbc",
- "arcfour128", "arcfour256",
+ ssh.InsecureCipherAES128CBC, ssh.InsecureCipherTripleDESCBC,
+ ssh.InsecureCipherRC4128, ssh.InsecureCipherRC4256,
}
func TestCiphers(t *testing.T) {
diff --git a/ssh/transport.go b/ssh/transport.go
index 0424d2d..9e51085 100644
--- a/ssh/transport.go
+++ b/ssh/transport.go
@@ -16,13 +16,6 @@ import (
// wire. No message decoding is done, to minimize the impact on timing.
const debugTransport = false
-const (
- gcm128CipherID = "aes128-gcm@openssh.com"
- gcm256CipherID = "aes256-gcm@openssh.com"
- aes128cbcID = "aes128-cbc"
- tripledescbcID = "3des-cbc"
-)
-
// packetConn represents a transport that implements packet based
// operations.
type packetConn interface {