summaryrefslogtreecommitdiff
path: root/modules/caddytls
diff options
context:
space:
mode:
authorMatthew Holt <mholt@users.noreply.github.com>2019-05-29 23:11:46 -0600
committerMatthew Holt <mholt@users.noreply.github.com>2019-05-29 23:11:46 -0600
commit34399332354b5cbc742200ef11aa33f199ba6755 (patch)
treec35f022730e8a83ddddf3c99ebc9efc2bd5304d9 /modules/caddytls
parent1b6b422c638532d49e697242d9fcf1aa0c3fdc53 (diff)
Implement session ticket keys; default STEK module with rotation
Diffstat (limited to 'modules/caddytls')
-rw-r--r--modules/caddytls/connpolicy.go14
-rw-r--r--modules/caddytls/sessiontickets.go214
-rw-r--r--modules/caddytls/standardstek/stek.go112
-rw-r--r--modules/caddytls/tls.go109
-rw-r--r--modules/caddytls/values.go102
5 files changed, 452 insertions, 99 deletions
diff --git a/modules/caddytls/connpolicy.go b/modules/caddytls/connpolicy.go
index 3241d3c..006afe9 100644
--- a/modules/caddytls/connpolicy.go
+++ b/modules/caddytls/connpolicy.go
@@ -133,9 +133,21 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy2.Context) error {
},
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS13,
- // TODO: Session ticket key rotation (use Storage)
}
+ // session tickets support
+ cfg.SessionTicketsDisabled = tlsApp.SessionTickets.Disabled
+
+ // session ticket key rotation
+ tlsApp.SessionTickets.register(cfg)
+ ctx.OnCancel(func() {
+ // do cleanup when the context is cancelled because,
+ // though unlikely, it is possible that a context
+ // needing a TLS server config could exist for less
+ // than the lifetime of the whole app
+ tlsApp.SessionTickets.unregister(cfg)
+ })
+
// add all the cipher suites in order, without duplicates
cipherSuitesAdded := make(map[uint16]struct{})
for _, csName := range p.CipherSuites {
diff --git a/modules/caddytls/sessiontickets.go b/modules/caddytls/sessiontickets.go
new file mode 100644
index 0000000..22c9a2f
--- /dev/null
+++ b/modules/caddytls/sessiontickets.go
@@ -0,0 +1,214 @@
+package caddytls
+
+import (
+ "crypto/rand"
+ "crypto/tls"
+ "encoding/json"
+ "fmt"
+ "io"
+ "sync"
+ "time"
+
+ "bitbucket.org/lightcodelabs/caddy2"
+)
+
+// SessionTicketService configures and manages TLS session tickets.
+type SessionTicketService struct {
+ KeySource json.RawMessage `json:"key_source,omitempty"`
+ RotationInterval caddy2.Duration `json:"rotation_interval,omitempty"`
+ MaxKeys int `json:"max_keys,omitempty"`
+ DisableRotation bool `json:"disable_rotation,omitempty"`
+ Disabled bool `json:"disabled,omitempty"`
+
+ keySource STEKProvider
+ configs map[*tls.Config]struct{}
+ stopChan chan struct{}
+ currentKeys [][32]byte
+ mu *sync.Mutex
+}
+
+func (s *SessionTicketService) provision(ctx caddy2.Context) error {
+ s.configs = make(map[*tls.Config]struct{})
+ s.mu = new(sync.Mutex)
+
+ // establish sane defaults
+ if s.RotationInterval == 0 {
+ s.RotationInterval = caddy2.Duration(defaultSTEKRotationInterval)
+ }
+ if s.MaxKeys <= 0 {
+ s.MaxKeys = defaultMaxSTEKs
+ }
+ if s.KeySource == nil {
+ s.KeySource = json.RawMessage(`{"provider":"standard"}`)
+ }
+
+ // load the STEK module, which will provide keys
+ val, err := ctx.LoadModuleInline("provider", "tls.stek", s.KeySource)
+ if err != nil {
+ return fmt.Errorf("loading TLS session ticket ephemeral keys provider module: %s", err)
+ }
+ s.keySource = val.(STEKProvider)
+ s.KeySource = nil // allow GC to deallocate - TODO: Does this help?
+
+ // if session tickets or just rotation are
+ // disabled, no need to start service
+ if s.Disabled || s.DisableRotation {
+ return nil
+ }
+
+ // start the STEK module; this ensures we have
+ // a starting key before any config needs one
+ return s.start()
+}
+
+// start loads the starting STEKs and spawns a goroutine
+// which loops to rotate the STEKs, which continues until
+// stop() is called. If start() was already called, this
+// is a no-op.
+func (s *SessionTicketService) start() error {
+ if s.stopChan != nil {
+ return nil
+ }
+ s.stopChan = make(chan struct{})
+
+ // initializing the key source gives us our
+ // initial key(s) to start with; if successful,
+ // we need to be sure to call Next() so that
+ // the key source can know when it is done
+ initialKeys, err := s.keySource.Initialize(s)
+ if err != nil {
+ return fmt.Errorf("setting STEK module configuration: %v", err)
+ }
+
+ s.mu.Lock()
+ s.currentKeys = initialKeys
+ s.mu.Unlock()
+
+ // keep the keys rotated
+ go s.stayUpdated()
+
+ return nil
+}
+
+// stayUpdated is a blocking function which rotates
+// the keys whenever new ones are sent. It reads
+// from keysChan until s.stop() is called.
+func (s *SessionTicketService) stayUpdated() {
+ // this call is essential when Initialize()
+ // returns without error, because the stop
+ // channel is the only way the key source
+ // will know when to clean up
+ keysChan := s.keySource.Next(s.stopChan)
+
+ for {
+ select {
+ case newKeys := <-keysChan:
+ s.mu.Lock()
+ s.currentKeys = newKeys
+ configs := s.configs
+ s.mu.Unlock()
+ for cfg := range configs {
+ cfg.SetSessionTicketKeys(newKeys)
+ }
+ case <-s.stopChan:
+ return
+ }
+ }
+}
+
+// stop terminates the key rotation goroutine.
+func (s *SessionTicketService) stop() {
+ if s.stopChan != nil {
+ close(s.stopChan)
+ }
+}
+
+// register sets the session ticket keys on cfg
+// and keeps them updated. Any values registered
+// must be unregistered, or they will not be
+// garbage-collected. s.start() must have been
+// called first. If session tickets are disabled
+// or if ticket key rotation is disabled, this
+// function is a no-op.
+func (s *SessionTicketService) register(cfg *tls.Config) {
+ if s.Disabled || s.DisableRotation {
+ return
+ }
+ s.mu.Lock()
+ cfg.SetSessionTicketKeys(s.currentKeys)
+ s.configs[cfg] = struct{}{}
+ s.mu.Unlock()
+}
+
+// unregister stops session key management on cfg and
+// removes the internal stored reference to cfg. If
+// session tickets are disabled or if ticket key rotation
+// is disabled, this function is a no-op.
+func (s *SessionTicketService) unregister(cfg *tls.Config) {
+ if s.Disabled || s.DisableRotation {
+ return
+ }
+ s.mu.Lock()
+ delete(s.configs, cfg)
+ s.mu.Unlock()
+}
+
+// RotateSTEKs rotates the keys in keys by producing a new key and eliding
+// the oldest one. The new slice of keys is returned.
+func (s SessionTicketService) RotateSTEKs(keys [][32]byte) ([][32]byte, error) {
+ // produce a new key
+ newKey, err := s.generateSTEK()
+ if err != nil {
+ return nil, fmt.Errorf("generating STEK: %v", err)
+ }
+
+ // we need to prepend this new key to the list of
+ // keys so that it is preferred, but we need to be
+ // careful that we do not grow the slice larger
+ // than MaxKeys, otherwise we'll be storing one
+ // more key in memory than we expect; so be sure
+ // that the slice does not grow beyond the limit
+ // even for a brief period of time, since there's
+ // no guarantee when that extra allocation will
+ // be overwritten; this is why we first trim the
+ // length to one less the max, THEN prepend the
+ // new key
+ if len(keys) >= s.MaxKeys {
+ keys[len(keys)-1] = [32]byte{} // zero-out memory of oldest key
+ keys = keys[:s.MaxKeys-1] // trim length of slice
+ }
+ keys = append([][32]byte{newKey}, keys...) // prepend new key
+
+ return keys, nil
+}
+
+// generateSTEK generates key material suitable for use as a
+// session ticket ephemeral key.
+func (s *SessionTicketService) generateSTEK() ([32]byte, error) {
+ var newTicketKey [32]byte
+ _, err := io.ReadFull(rand.Reader, newTicketKey[:])
+ return newTicketKey, err
+}
+
+// STEKProvider is a type that can provide session ticket ephemeral
+// keys (STEKs).
+type STEKProvider interface {
+ // Initialize provides the STEK configuration to the STEK
+ // module so that it can obtain and manage keys accordingly.
+ // It returns the initial key(s) to use. Implementations can
+ // rely on Next() being called if Initialize() returns
+ // without error, so that it may know when it is done.
+ Initialize(config *SessionTicketService) ([][32]byte, error)
+
+ // Next returns the channel through which the next session
+ // ticket keys will be transmitted until doneChan is closed.
+ // Keys should be sent on keysChan as they are updated.
+ // When doneChan is closed, any resources allocated in
+ // Initialize() must be cleaned up.
+ Next(doneChan <-chan struct{}) (keysChan <-chan [][32]byte)
+}
+
+const (
+ defaultSTEKRotationInterval = 12 * time.Hour
+ defaultMaxSTEKs = 4
+)
diff --git a/modules/caddytls/standardstek/stek.go b/modules/caddytls/standardstek/stek.go
new file mode 100644
index 0000000..46ac786
--- /dev/null
+++ b/modules/caddytls/standardstek/stek.go
@@ -0,0 +1,112 @@
+package standardstek
+
+import (
+ "log"
+ "sync"
+ "time"
+
+ "bitbucket.org/lightcodelabs/caddy2"
+ "bitbucket.org/lightcodelabs/caddy2/modules/caddytls"
+)
+
+func init() {
+ caddy2.RegisterModule(caddy2.Module{
+ Name: "tls.stek.standard",
+ New: func() interface{} { return new(standardSTEKProvider) },
+ })
+}
+
+type standardSTEKProvider struct {
+ stekConfig *caddytls.SessionTicketService
+ timer *time.Timer
+}
+
+// Initialize sets the configuration for s and returns the starting keys.
+func (s *standardSTEKProvider) Initialize(config *caddytls.SessionTicketService) ([][32]byte, error) {
+ // keep a reference to the config, we'll need when rotating keys
+ s.stekConfig = config
+
+ itvl := time.Duration(s.stekConfig.RotationInterval)
+
+ mutex.Lock()
+ defer mutex.Unlock()
+
+ // if this is our first rotation or we are overdue
+ // for one, perform a rotation immediately; otherwise,
+ // we assume that the keys are non-empty and fresh
+ since := time.Since(lastRotation)
+ if lastRotation.IsZero() || since > itvl {
+ var err error
+ keys, err = s.stekConfig.RotateSTEKs(keys)
+ if err != nil {
+ return nil, err
+ }
+ since = 0 // since this is overdue or is the first rotation, use full interval
+ lastRotation = time.Now()
+ }
+
+ // create timer for the remaining time on the interval;
+ // this timer is cleaned up only when Next() returns
+ s.timer = time.NewTimer(itvl - since)
+
+ return keys, nil
+}
+
+// Next returns a channel which transmits the latest session ticket keys.
+func (s *standardSTEKProvider) Next(doneChan <-chan struct{}) <-chan [][32]byte {
+ keysChan := make(chan [][32]byte)
+ go s.rotate(doneChan, keysChan)
+ return keysChan
+}
+
+// rotate rotates keys on a regular basis, sending each updated set of
+// keys down keysChan, until doneChan is closed.
+func (s *standardSTEKProvider) rotate(doneChan <-chan struct{}, keysChan chan<- [][32]byte) {
+ for {
+ select {
+ case now := <-s.timer.C:
+ // copy the slice header to avoid races
+ mutex.RLock()
+ keysCopy := keys
+ mutex.RUnlock()
+
+ // generate a new key, rotating old ones
+ var err error
+ keysCopy, err = s.stekConfig.RotateSTEKs(keysCopy)
+ if err != nil {
+ // TODO: improve this handling
+ log.Printf("[ERROR] Generating STEK: %v", err)
+ continue
+ }
+
+ // replace keys slice with updated value and
+ // record the timestamp of rotation
+ mutex.Lock()
+ keys = keysCopy
+ lastRotation = now
+ mutex.Unlock()
+
+ // send the updated keys to the service
+ keysChan <- keysCopy
+
+ // timer channel is already drained, so reset directly (see godoc)
+ s.timer.Reset(time.Duration(s.stekConfig.RotationInterval))
+
+ case <-doneChan:
+ // again, see godocs for why timer is stopped this way
+ if !s.timer.Stop() {
+ <-s.timer.C
+ }
+ return
+ }
+ }
+}
+
+var (
+ lastRotation time.Time
+ keys [][32]byte
+ mutex sync.RWMutex // protects keys and lastRotation
+)
+
+// Interface guard
+var _ caddytls.STEKProvider = (*standardSTEKProvider)(nil)
diff --git a/modules/caddytls/tls.go b/modules/caddytls/tls.go
index 29ca88e..6a9c97e 100644
--- a/modules/caddytls/tls.go
+++ b/modules/caddytls/tls.go
@@ -2,14 +2,12 @@ package caddytls
import (
"crypto/tls"
- "crypto/x509"
"encoding/json"
"fmt"
"net/http"
"bitbucket.org/lightcodelabs/caddy2"
"github.com/go-acme/lego/challenge"
- "github.com/klauspost/cpuid"
"github.com/mholt/certmagic"
)
@@ -22,8 +20,9 @@ func init() {
// TLS represents a process-wide TLS configuration.
type TLS struct {
- Certificates map[string]json.RawMessage `json:"certificates"`
- Automation AutomationConfig `json:"automation"`
+ Certificates map[string]json.RawMessage `json:"certificates,omitempty"`
+ Automation AutomationConfig `json:"automation,omitempty"`
+ SessionTickets SessionTicketService `json:"session_tickets,omitempty"`
certificateLoaders []CertificateLoader
certCache *certmagic.Cache
@@ -44,6 +43,7 @@ func (t *TLS) Provision(ctx caddy2.Context) error {
},
})
+ // automation/management policies
for i, ap := range t.Automation.Policies {
val, err := ctx.LoadModuleInline("module", "tls.management", ap.Management)
if err != nil {
@@ -65,6 +65,12 @@ func (t *TLS) Provision(ctx caddy2.Context) error {
t.certificateLoaders = append(t.certificateLoaders, val.(CertificateLoader))
}
+ // session ticket ephemeral keys (STEK) service and provider
+ err := t.SessionTickets.provision(ctx)
+ if err != nil {
+ return fmt.Errorf("provisioning session tickets configuration: %v", err)
+ }
+
return nil
}
@@ -110,6 +116,7 @@ func (t *TLS) Stop() error {
// TODO: ensure locks are cleaned up too... maybe in certmagic though
t.certCache.Stop()
}
+ t.SessionTickets.stop()
return nil
}
@@ -230,98 +237,4 @@ type ManagerMaker interface {
newManager(interactive bool) (certmagic.Manager, error)
}
-// supportedCipherSuites is the unordered map of cipher suite
-// string names to their definition in crypto/tls.
-// TODO: might not be needed much longer, see:
-// https://github.com/golang/go/issues/30325
-var supportedCipherSuites = map[string]uint16{
- "ECDHE_ECDSA_AES256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
- "ECDHE_RSA_AES256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
- "ECDHE_ECDSA_AES128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
- "ECDHE_RSA_AES128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
- "ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
- "ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
- "ECDHE_RSA_AES256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
- "ECDHE_RSA_AES128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
- "ECDHE_ECDSA_AES256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
- "ECDHE_ECDSA_AES128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
- "RSA_AES256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
- "RSA_AES128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
- "ECDHE_RSA_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
- "RSA_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
-}
-
-// defaultCipherSuites is the ordered list of all the cipher
-// suites we want to support by default, assuming AES-NI
-// (hardware acceleration for AES).
-var defaultCipherSuitesWithAESNI = []uint16{
- 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,
-}
-
-// defaultCipherSuites is the ordered list of all the cipher
-// suites we want to support by default, assuming lack of
-// AES-NI (NO hardware acceleration for AES).
-var defaultCipherSuitesWithoutAESNI = []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,
-}
-
-// getOptimalDefaultCipherSuites returns an appropriate cipher
-// suite to use depending on the hardware support for AES.
-//
-// See https://github.com/mholt/caddy/issues/1674
-func getOptimalDefaultCipherSuites() []uint16 {
- if cpuid.CPU.AesNi() {
- return defaultCipherSuitesWithAESNI
- }
- return defaultCipherSuitesWithoutAESNI
-}
-
-// supportedCurves is the unordered map of supported curves.
-// https://golang.org/pkg/crypto/tls/#CurveID
-var supportedCurves = map[string]tls.CurveID{
- "X25519": tls.X25519,
- "P256": tls.CurveP256,
- "P384": tls.CurveP384,
- "P521": tls.CurveP521,
-}
-
-// defaultCurves is the list of only the curves we want to use
-// by default, in descending order of preference.
-//
-// This list should only include curves which are fast by design
-// (e.g. X25519) and those for which an optimized assembly
-// implementation exists (e.g. P256). The latter ones can be
-// found here:
-// https://github.com/golang/go/tree/master/src/crypto/elliptic
-var defaultCurves = []tls.CurveID{
- tls.X25519,
- tls.CurveP256,
-}
-
-// supportedProtocols is a map of supported protocols.
-// HTTP/2 only supports TLS 1.2 and higher.
-var supportedProtocols = map[string]uint16{
- "tls1.0": tls.VersionTLS10,
- "tls1.1": tls.VersionTLS11,
- "tls1.2": tls.VersionTLS12,
- "tls1.3": tls.VersionTLS13,
-}
-
-// publicKeyAlgorithms is the map of supported public key algorithms.
-var publicKeyAlgorithms = map[string]x509.PublicKeyAlgorithm{
- "rsa": x509.RSA,
- "dsa": x509.DSA,
- "ecdsa": x509.ECDSA,
-}
-
const automateKey = "automate"
diff --git a/modules/caddytls/values.go b/modules/caddytls/values.go
new file mode 100644
index 0000000..0559dc6
--- /dev/null
+++ b/modules/caddytls/values.go
@@ -0,0 +1,102 @@
+package caddytls
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+
+ "github.com/klauspost/cpuid"
+)
+
+// supportedCipherSuites is the unordered map of cipher suite
+// string names to their definition in crypto/tls.
+// TODO: might not be needed much longer, see:
+// https://github.com/golang/go/issues/30325
+var supportedCipherSuites = map[string]uint16{
+ "ECDHE_ECDSA_AES256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+ "ECDHE_RSA_AES256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ "ECDHE_ECDSA_AES128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ "ECDHE_RSA_AES128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ "ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
+ "ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ "ECDHE_RSA_AES256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ "ECDHE_RSA_AES128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ "ECDHE_ECDSA_AES256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+ "ECDHE_ECDSA_AES128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+ "RSA_AES256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
+ "RSA_AES128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
+ "ECDHE_RSA_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ "RSA_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+}
+
+// defaultCipherSuites is the ordered list of all the cipher
+// suites we want to support by default, assuming AES-NI
+// (hardware acceleration for AES).
+var defaultCipherSuitesWithAESNI = []uint16{
+ 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,
+}
+
+// defaultCipherSuites is the ordered list of all the cipher
+// suites we want to support by default, assuming lack of
+// AES-NI (NO hardware acceleration for AES).
+var defaultCipherSuitesWithoutAESNI = []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,
+}
+
+// getOptimalDefaultCipherSuites returns an appropriate cipher
+// suite to use depending on the hardware support for AES.
+//
+// See https://github.com/mholt/caddy/issues/1674
+func getOptimalDefaultCipherSuites() []uint16 {
+ if cpuid.CPU.AesNi() {
+ return defaultCipherSuitesWithAESNI
+ }
+ return defaultCipherSuitesWithoutAESNI
+}
+
+// supportedCurves is the unordered map of supported curves.
+// https://golang.org/pkg/crypto/tls/#CurveID
+var supportedCurves = map[string]tls.CurveID{
+ "X25519": tls.X25519,
+ "P256": tls.CurveP256,
+ "P384": tls.CurveP384,
+ "P521": tls.CurveP521,
+}
+
+// defaultCurves is the list of only the curves we want to use
+// by default, in descending order of preference.
+//
+// This list should only include curves which are fast by design
+// (e.g. X25519) and those for which an optimized assembly
+// implementation exists (e.g. P256). The latter ones can be
+// found here:
+// https://github.com/golang/go/tree/master/src/crypto/elliptic
+var defaultCurves = []tls.CurveID{
+ tls.X25519,
+ tls.CurveP256,
+}
+
+// supportedProtocols is a map of supported protocols.
+// HTTP/2 only supports TLS 1.2 and higher.
+var supportedProtocols = map[string]uint16{
+ "tls1.0": tls.VersionTLS10,
+ "tls1.1": tls.VersionTLS11,
+ "tls1.2": tls.VersionTLS12,
+ "tls1.3": tls.VersionTLS13,
+}
+
+// publicKeyAlgorithms is the map of supported public key algorithms.
+var publicKeyAlgorithms = map[string]x509.PublicKeyAlgorithm{
+ "rsa": x509.RSA,
+ "dsa": x509.DSA,
+ "ecdsa": x509.ECDSA,
+}