checker-tls/tlsenum/ciphers.go
Pierre-Olivier Mercier a9f37c79cf Add tlsenum package and add version/cipher enumeration into the checker
tlsenum package probes a remote endpoint with one ClientHello
per (version, cipher) pair via utls, so the checker can report the
exact set the server accepts rather than only the suite Go's stdlib
happens to negotiate. Probe accepts an Upgrader callback so STARTTLS
dialects plug in without tlsenum learning about them; the checker
bridges its existing dialect registry through upgraderFor.
2026-04-29 13:35:29 +07:00

103 lines
4.5 KiB
Go

package tlsenum
// CipherSuite pairs an IANA TLS cipher suite ID with its standard name.
//
// The catalog below intentionally covers the "real-world" set: modern AEAD
// suites used by TLS 1.2/1.3, plus a long tail of legacy CBC/RC4/3DES/EXPORT
// suites we want to *detect* on remote servers (so we can flag them), even
// though Go's stdlib refuses to negotiate them. utls lets us put any 16-bit
// value in the offered list, so the server's accept/reject decision is the
// source of truth.
type CipherSuite struct {
ID uint16
Name string
// TLS13 is true for the five TLS 1.3 AEAD suites; those must only be
// offered with TLS 1.3 ClientHellos.
TLS13 bool
}
// TLS13Ciphers are the AEAD suites defined for TLS 1.3 (RFC 8446 §B.4).
var TLS13Ciphers = []CipherSuite{
{0x1301, "TLS_AES_128_GCM_SHA256", true},
{0x1302, "TLS_AES_256_GCM_SHA384", true},
{0x1303, "TLS_CHACHA20_POLY1305_SHA256", true},
{0x1304, "TLS_AES_128_CCM_SHA256", true},
{0x1305, "TLS_AES_128_CCM_8_SHA256", true},
}
// LegacyCiphers covers TLS 1.0/1.1/1.2 (and SSLv3) suites. Not exhaustive of
// the IANA registry, but it includes everything any modern audit cares about:
// ECDHE/DHE/RSA/PSK kex, AES-GCM/CCM/CBC, ChaCha20, 3DES, RC4, NULL, EXPORT,
// anonymous, and a handful of GOST/CAMELLIA/ARIA entries seen in the wild.
var LegacyCiphers = []CipherSuite{
// ECDHE-ECDSA
{0xC02B, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", false},
{0xC02C, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", false},
{0xCCA9, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", false},
{0xC023, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", false},
{0xC024, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", false},
{0xC009, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", false},
{0xC00A, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", false},
{0xC008, "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", false},
{0xC007, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", false},
{0xC006, "TLS_ECDHE_ECDSA_WITH_NULL_SHA", false},
// ECDHE-RSA
{0xC02F, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", false},
{0xC030, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", false},
{0xCCA8, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", false},
{0xC027, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", false},
{0xC028, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", false},
{0xC013, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", false},
{0xC014, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", false},
{0xC012, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", false},
{0xC011, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", false},
{0xC010, "TLS_ECDHE_RSA_WITH_NULL_SHA", false},
// DHE-RSA
{0x009E, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", false},
{0x009F, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", false},
{0xCCAA, "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", false},
{0x0067, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", false},
{0x006B, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", false},
{0x0033, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", false},
{0x0039, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", false},
{0x0016, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", false},
// Plain RSA
{0x009C, "TLS_RSA_WITH_AES_128_GCM_SHA256", false},
{0x009D, "TLS_RSA_WITH_AES_256_GCM_SHA384", false},
{0x003C, "TLS_RSA_WITH_AES_128_CBC_SHA256", false},
{0x003D, "TLS_RSA_WITH_AES_256_CBC_SHA256", false},
{0x002F, "TLS_RSA_WITH_AES_128_CBC_SHA", false},
{0x0035, "TLS_RSA_WITH_AES_256_CBC_SHA", false},
{0x000A, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", false},
{0x0005, "TLS_RSA_WITH_RC4_128_SHA", false},
{0x0004, "TLS_RSA_WITH_RC4_128_MD5", false},
{0x003B, "TLS_RSA_WITH_NULL_SHA256", false},
{0x0002, "TLS_RSA_WITH_NULL_SHA", false},
{0x0001, "TLS_RSA_WITH_NULL_MD5", false},
// Anonymous (broken by design — flag if seen)
{0x006D, "TLS_DH_anon_WITH_AES_256_CBC_SHA256", false},
{0x0034, "TLS_DH_anon_WITH_AES_128_CBC_SHA", false},
{0x003A, "TLS_DH_anon_WITH_AES_256_CBC_SHA", false},
{0xC018, "TLS_ECDH_anon_WITH_AES_128_CBC_SHA", false},
{0xC019, "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", false},
// EXPORT (40-bit, illegal since ~2000 — flag if seen)
{0x0008, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA", false},
{0x0014, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", false},
{0x0017, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5", false},
{0x0019, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA", false},
{0x0003, "TLS_RSA_EXPORT_WITH_RC4_40_MD5", false},
{0x0006, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5", false},
}
// AllCiphers concatenates legacy and TLS 1.3 cipher suites.
func AllCiphers() []CipherSuite {
out := make([]CipherSuite, 0, len(LegacyCiphers)+len(TLS13Ciphers))
out = append(out, LegacyCiphers...)
out = append(out, TLS13Ciphers...)
return out
}