package tlsconfig import ( "crypto/tls" "fmt" "sort" "strings" ) type Config struct { Ciphers []string `toml:"ciphers"` MinVersion string `toml:"min-version"` MaxVersion string `toml:"max-version"` } func NewConfig() Config { return Config{} } func (c Config) Validate() error { _, err := c.Parse() return err } func (c Config) Parse() (out *tls.Config, err error) { if len(c.Ciphers) > 0 { if out == nil { out = new(tls.Config) } for _, name := range c.Ciphers { cipher, ok := ciphersMap[strings.ToUpper(name)] if !ok { return nil, unknownCipher(name) } out.CipherSuites = append(out.CipherSuites, cipher) } } if c.MinVersion != "" { if out == nil { out = new(tls.Config) } version, ok := versionsMap[strings.ToUpper(c.MinVersion)] if !ok { return nil, unknownVersion(c.MinVersion) } out.MinVersion = version } if c.MaxVersion != "" { if out == nil { out = new(tls.Config) } version, ok := versionsMap[strings.ToUpper(c.MaxVersion)] if !ok { return nil, unknownVersion(c.MaxVersion) } out.MaxVersion = version } return out, nil } var ciphersMap = map[string]uint16{ "TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, // TLS 1.3 cipher suites. "TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256, "TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384, "TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256, // TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator // that the client is doing version fallback. See RFC 7507. "TLS_FALLBACK_SCSV": tls.TLS_FALLBACK_SCSV, } func unknownCipher(name string) error { available := make([]string, 0, len(ciphersMap)) for name := range ciphersMap { available = append(available, name) } sort.Strings(available) return fmt.Errorf("unknown cipher suite: %q. available ciphers: %s", name, strings.Join(available, ", ")) } var versionsMap = map[string]uint16{ "SSL3.0": tls.VersionSSL30, "TLS1.0": tls.VersionTLS10, "1.0": tls.VersionTLS10, "TLS1.1": tls.VersionTLS11, "1.1": tls.VersionTLS11, "TLS1.2": tls.VersionTLS12, "1.2": tls.VersionTLS12, "TLS1.3": tls.VersionTLS13, "1.3": tls.VersionTLS13, } func unknownVersion(name string) error { available := make([]string, 0, len(versionsMap)) for name := range versionsMap { // skip the ones that just begin with a number. they may be confusing // due to the duplication, and just help if the user specifies without // the TLS part. if name[0] == '1' { continue } available = append(available, name) } sort.Strings(available) return fmt.Errorf("unknown tls version: %q. available versions: %s", name, strings.Join(available, ", ")) }