Commit b0cf3f0d authored by Andrew Steinborn's avatar Andrew Steinborn Committed by Matt Holt

tls: Prefer ChaCha20 if AES-NI instruction set is unavailable (#1675)

Fixes #1674
parent 8d3f3369
......@@ -9,6 +9,7 @@ import (
"net/url"
"strings"
"github.com/codahale/aesnicheck"
"github.com/mholt/caddy"
"github.com/xenolf/lego/acme"
)
......@@ -294,7 +295,7 @@ func (c *Config) buildStandardTLSConfig() error {
// default cipher suites
if len(config.CipherSuites) == 0 {
config.CipherSuites = defaultCiphers
config.CipherSuites = getPreferredDefaultCiphers()
}
// for security, ensure TLS_FALLBACK_SCSV is always included first
......@@ -380,7 +381,7 @@ func RegisterConfigGetter(serverType string, fn ConfigGetter) {
func SetDefaultTLSParams(config *Config) {
// If no ciphers provided, use default list
if len(config.Ciphers) == 0 {
config.Ciphers = defaultCiphers
config.Ciphers = getPreferredDefaultCiphers()
}
// Not a cipher suite, but still important for mitigating protocol downgrade attacks
......@@ -464,6 +465,35 @@ var defaultCiphers = []uint16{
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
}
// List of ciphers we should prefer if native AESNI support is missing
var defaultCiphersNonAESNI = []uint16{
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
}
// getPreferredDefaultCiphers returns an appropriate cipher suite to use, depending on
// the hardware support available for AES-NI.
//
// See https://github.com/mholt/caddy/issues/1674
func getPreferredDefaultCiphers() []uint16 {
if aesnicheck.HasAESNI() {
return defaultCiphers
}
// Return a cipher suite that prefers ChaCha20
return defaultCiphersNonAESNI
}
// Map of supported curves
// https://golang.org/pkg/crypto/tls/#CurveID
var supportedCurvesMap = map[string]tls.CurveID{
......
......@@ -6,6 +6,8 @@ import (
"net/url"
"reflect"
"testing"
"github.com/codahale/aesnicheck"
)
func TestConvertTLSConfigProtocolVersions(t *testing.T) {
......@@ -60,10 +62,11 @@ func TestConvertTLSConfigCipherSuites(t *testing.T) {
{Enabled: true, Ciphers: nil},
}
defaultCiphersExpected := getPreferredDefaultCiphers()
expectedCiphers := [][]uint16{
{tls.TLS_FALLBACK_SCSV, 0xc02c, 0xc030},
{tls.TLS_FALLBACK_SCSV, 0xc012, 0xc030, 0xc00a},
append([]uint16{tls.TLS_FALLBACK_SCSV}, defaultCiphers...),
append([]uint16{tls.TLS_FALLBACK_SCSV}, defaultCiphersExpected...),
}
for i, config := range configs {
......@@ -79,6 +82,21 @@ func TestConvertTLSConfigCipherSuites(t *testing.T) {
}
}
func TestGetPreferredDefaultCiphers(t *testing.T) {
expectedCiphers := defaultCiphers
if !aesnicheck.HasAESNI() {
expectedCiphers = defaultCiphersNonAESNI
}
// Ensure ordering is correct and ciphers are what we expected.
result := getPreferredDefaultCiphers()
for i, actual := range result {
if actual != expectedCiphers[i] {
t.Errorf("Expected cipher in position %d to be %0x, got %0x", i, expectedCiphers[i], actual)
}
}
}
func TestStorageForNoURL(t *testing.T) {
c := &Config{}
if _, err := c.StorageFor(""); err == nil {
......
......@@ -58,21 +58,7 @@ func TestSetupParseBasic(t *testing.T) {
}
// Cipher checks
expectedCiphers := []uint16{
tls.TLS_FALLBACK_SCSV,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
}
expectedCiphers := append([]uint16{tls.TLS_FALLBACK_SCSV}, getPreferredDefaultCiphers()...)
// Ensure count is correct (plus one for TLS_FALLBACK_SCSV)
if len(cfg.Ciphers) != len(expectedCiphers) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment