mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-16 21:56:40 -05:00
Implement session ticket keys; default STEK module with rotation
This commit is contained in:
parent
1b6b422c63
commit
3439933235
6 changed files with 453 additions and 99 deletions
|
@ -13,6 +13,7 @@ import (
|
|||
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/reverseproxy"
|
||||
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddyhttp/rewrite"
|
||||
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddytls"
|
||||
_ "bitbucket.org/lightcodelabs/caddy2/modules/caddytls/standardstek"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
214
modules/caddytls/sessiontickets.go
Normal file
214
modules/caddytls/sessiontickets.go
Normal file
|
@ -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
|
||||
)
|
112
modules/caddytls/standardstek/stek.go
Normal file
112
modules/caddytls/standardstek/stek.go
Normal file
|
@ -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)
|
|
@ -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"
|
||||
|
|
102
modules/caddytls/values.go
Normal file
102
modules/caddytls/values.go
Normal file
|
@ -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,
|
||||
}
|
Loading…
Reference in a new issue