From b0cf3f0d2dd574cf14ec9b97ffd0fb3d878ce565 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Wed, 17 May 2017 12:45:17 -0400 Subject: [PATCH] tls: Prefer ChaCha20 if AES-NI instruction set is unavailable (#1675) Fixes #1674 --- caddytls/config.go | 34 ++++++++++++++++++++++++++++++++-- caddytls/config_test.go | 20 +++++++++++++++++++- caddytls/setup_test.go | 16 +--------------- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/caddytls/config.go b/caddytls/config.go index 40cc4bea..49b2e9c7 100644 --- a/caddytls/config.go +++ b/caddytls/config.go @@ -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{ diff --git a/caddytls/config_test.go b/caddytls/config_test.go index 5fccb1ba..c4e11ba6 100644 --- a/caddytls/config_test.go +++ b/caddytls/config_test.go @@ -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 { diff --git a/caddytls/setup_test.go b/caddytls/setup_test.go index ed18bb0d..b609fde7 100644 --- a/caddytls/setup_test.go +++ b/caddytls/setup_test.go @@ -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) {