github.com/Axway/agent-sdk@v1.1.101/pkg/config/tlsconfig.go (about)

     1  package config
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"errors"
     7  	"fmt"
     8  	"io/ioutil"
     9  
    10  	log "github.com/Axway/agent-sdk/pkg/util/log"
    11  
    12  	"github.com/Axway/agent-sdk/pkg/util/exception"
    13  )
    14  
    15  // TLSCipherSuite - defined type
    16  type TLSCipherSuite uint16
    17  
    18  // Taken from https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
    19  var tlsCipherSuites = map[string]TLSCipherSuite{
    20  	// ECDHE-ECDSA
    21  	"ECDHE-ECDSA-AES-128-CBC-SHA":    TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA),
    22  	"ECDHE-ECDSA-AES-128-CBC-SHA256": TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256),
    23  	"ECDHE-ECDSA-AES-128-GCM-SHA256": TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
    24  	"ECDHE-ECDSA-AES-256-CBC-SHA":    TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA),
    25  	"ECDHE-ECDSA-AES-256-GCM-SHA384": TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
    26  	"ECDHE-ECDSA-CHACHA20-POLY1305":  TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305),
    27  	"ECDHE-ECDSA-RC4-128-SHA":        TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA),
    28  
    29  	// ECDHE-RSA
    30  	"ECDHE-RSA-3DES-CBC3-SHA":      TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA),
    31  	"ECDHE-RSA-AES-128-CBC-SHA":    TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA),
    32  	"ECDHE-RSA-AES-128-CBC-SHA256": TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256),
    33  	"ECDHE-RSA-AES-128-GCM-SHA256": TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),
    34  	"ECDHE-RSA-AES-256-CBC-SHA":    TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA),
    35  	"ECDHE-RSA-AES-256-GCM-SHA384": TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
    36  	"ECDHE-RSA-CHACHA20-POLY1305":  TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305),
    37  	"ECDHE-RSA-RC4-128-SHA":        TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA),
    38  
    39  	// RSA-X
    40  	"RSA-RC4-128-SHA":   TLSCipherSuite(tls.TLS_RSA_WITH_RC4_128_SHA),
    41  	"RSA-3DES-CBC3-SHA": TLSCipherSuite(tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA),
    42  
    43  	// RSA-AES
    44  	"RSA-AES-128-CBC-SHA":    TLSCipherSuite(tls.TLS_RSA_WITH_AES_128_CBC_SHA),
    45  	"RSA-AES-128-CBC-SHA256": TLSCipherSuite(tls.TLS_RSA_WITH_AES_128_CBC_SHA256),
    46  	"RSA-AES-128-GCM-SHA256": TLSCipherSuite(tls.TLS_RSA_WITH_AES_128_GCM_SHA256),
    47  	"RSA-AES-256-CBC-SHA":    TLSCipherSuite(tls.TLS_RSA_WITH_AES_256_CBC_SHA),
    48  	"RSA-AES-256-GCM-SHA384": TLSCipherSuite(tls.TLS_RSA_WITH_AES_256_GCM_SHA384),
    49  
    50  	// TLS 1.3
    51  	"TLS-AES-128-GCM-SHA256":       TLSCipherSuite(tls.TLS_AES_128_GCM_SHA256),
    52  	"TLS-AES-256-GCM-SHA384":       TLSCipherSuite(tls.TLS_AES_256_GCM_SHA384),
    53  	"TLS-CHACHA20-POLY1305-SHA256": TLSCipherSuite(tls.TLS_CHACHA20_POLY1305_SHA256),
    54  }
    55  
    56  // TLSDefaultCipherSuitesStringSlice - list of suites to use by default
    57  func TLSDefaultCipherSuitesStringSlice() []string {
    58  	suites := TLSDefaultCipherSuites
    59  
    60  	var values []string
    61  	for _, v := range suites {
    62  		values = append(values, v.String())
    63  	}
    64  
    65  	return values
    66  }
    67  
    68  // TLSDefaultCipherSuites - list of suites to use by default
    69  var TLSDefaultCipherSuites = []TLSCipherSuite{
    70  	TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
    71  	TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
    72  	TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305),
    73  	TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305),
    74  	TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
    75  	TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),
    76  	TLSCipherSuite(tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256),
    77  	TLSCipherSuite(tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256),
    78  }
    79  
    80  var tlsCipherSuitesInverse = make(map[TLSCipherSuite]string, len(tlsCipherSuites))
    81  
    82  // Unpack - transforms the string into a constant.
    83  func (cs *TLSCipherSuite) Unpack(s string) error {
    84  	if s == "" {
    85  		return nil
    86  	}
    87  	suite, found := tlsCipherSuites[s]
    88  	if !found {
    89  		return fmt.Errorf("invalid tls cipher suite '%v'", s)
    90  	}
    91  
    92  	*cs = suite
    93  	return nil
    94  }
    95  
    96  func (cs *TLSCipherSuite) String() string {
    97  	if s, found := tlsCipherSuitesInverse[*cs]; found {
    98  		return s
    99  	}
   100  	return "unknown"
   101  }
   102  
   103  func cipherAsValue(cs string) TLSCipherSuite {
   104  	if s, ok := tlsCipherSuites[cs]; ok {
   105  		return s
   106  	}
   107  	return TLSCipherSuite(0) // return bogus value
   108  }
   109  
   110  // TLSVersion - define type for version
   111  type TLSVersion uint16
   112  
   113  // Define all the possible TLS version.
   114  var tlsVersions = map[string]TLSVersion{
   115  	"TLS1.0": tls.VersionTLS10,
   116  	"TLS1.1": tls.VersionTLS11,
   117  	"TLS1.2": tls.VersionTLS12,
   118  	"TLS1.3": tls.VersionTLS13,
   119  }
   120  
   121  var tlsVersionsInverse = make(map[TLSVersion]string, len(tlsVersions))
   122  
   123  // Unpack transforms the string into a constant.
   124  func (v *TLSVersion) Unpack(s string) error {
   125  	if s == "" {
   126  		return nil
   127  	}
   128  	version, found := tlsVersions[s]
   129  	if !found {
   130  		return fmt.Errorf("invalid tls version '%v'", s)
   131  	}
   132  
   133  	*v = version
   134  	return nil
   135  }
   136  
   137  // TLSDefaultMinVersionString - get the default min version string
   138  func TLSDefaultMinVersionString() string {
   139  	return tlsVersionsInverse[TLSDefaultMinVersion]
   140  }
   141  
   142  // TLSDefaultMinVersion - get the default min version
   143  var TLSDefaultMinVersion TLSVersion = tls.VersionTLS12
   144  
   145  // TLSVersionAsValue - get the version value
   146  func TLSVersionAsValue(cs string) TLSVersion {
   147  	// value of 0 means to use the default. Leave it alone.
   148  	if cs == "0" {
   149  		return TLSVersion(0)
   150  	}
   151  	if s, ok := tlsVersions[cs]; ok {
   152  		return s
   153  	}
   154  	return TLSVersion(1) // return a bogus value for validation checking
   155  }
   156  
   157  // Init creates a inverse representation of the values mapping.
   158  func init() {
   159  	for cipherName, i := range tlsCipherSuites {
   160  		tlsCipherSuitesInverse[i] = cipherName
   161  	}
   162  	for versionName, i := range tlsVersions {
   163  		tlsVersionsInverse[i] = versionName
   164  	}
   165  }
   166  
   167  // TLSConfig - interface
   168  type TLSConfig interface {
   169  	GetNextProtos() []string
   170  	IsInsecureSkipVerify() bool
   171  	GetCipherSuites() []TLSCipherSuite
   172  	GetMinVersion() TLSVersion
   173  	GetMaxVersion() TLSVersion
   174  	BuildTLSConfig() *tls.Config
   175  }
   176  
   177  // TLSConfiguration - A Config structure is used to configure a TLS client or server.
   178  // After one has been passed to a TLS function it must not be modified. A Config may be reused;
   179  // the tls package will also not modify it.
   180  type TLSConfiguration struct {
   181  	IConfigValidator
   182  	// NextProtos is a list of supported application level protocols, in order of preference.
   183  	NextProtos []string `config:"nextProtos,replace" json:"nextProtos,omitempty"`
   184  
   185  	// InsecureSkipVerify controls whether a client verifies the server's certificate chain and host name.
   186  	// If InsecureSkipVerify is true, TLS accepts any certificate presented by the server and any host
   187  	// name in that certificate. In this mode, TLS is susceptible to man-in-the-middle attacks.
   188  	// This should be used only for testing.
   189  	InsecureSkipVerify bool `config:"insecureSkipVerify" json:"insecureSkipVerify,omitempty"`
   190  
   191  	// CipherSuites is a list of supported cipher suites for TLS versions up to TLS 1.2. If CipherSuites
   192  	// is nil, a default list of secure cipher suites is used, with a preference order based on hardware
   193  	// performance. The default cipher suites might change over Go versions. Note that TLS 1.3
   194  	// ciphersuites are not configurable.
   195  	CipherSuites []TLSCipherSuite `config:"cipherSuites,replace" json:"cipherSuites,omitempty"`
   196  
   197  	// MinVersion contains the minimum SSL/TLS version that is acceptable. If zero, then TLS 1.0 is taken as the minimum.
   198  	MinVersion TLSVersion `config:"minVersion" json:"minVersion,omitempty"`
   199  
   200  	// MaxVersion contains the maximum SSL/TLS version that is acceptable. If zero, then the maximum
   201  	// version supported by this package is used, which is currently TLS 1.3.
   202  	MaxVersion TLSVersion `config:"maxVersion" json:"maxVersion,omitempty"`
   203  
   204  	// RootCertificate
   205  	RootCertificatePath string `config:"rootCACertPath" json:"rootCACertPath,omitempty"`
   206  
   207  	// Client Certificate Path
   208  	ClientCertificatePath string `config:"clientCertPath" json:"clientCertPath,omitempty"`
   209  
   210  	// Client Key Path
   211  	ClientKeyPath string `config:"clientKeyPath" json:"clientKeyPath,omitempty"`
   212  }
   213  
   214  // NewTLSConfig - build default config
   215  func NewTLSConfig() TLSConfig {
   216  	return &TLSConfiguration{
   217  		InsecureSkipVerify: false,
   218  		NextProtos:         []string{},
   219  		CipherSuites:       TLSDefaultCipherSuites,
   220  		MinVersion:         TLSDefaultMinVersion,
   221  		MaxVersion:         0,
   222  	}
   223  }
   224  
   225  // BuildTLSConfig takes the TLSConfiguration and transforms it into a `tls.Config`.
   226  func (c *TLSConfiguration) BuildTLSConfig() *tls.Config {
   227  	if c == nil {
   228  		// use default TLS settings, if config is empty.
   229  		return &tls.Config{}
   230  	}
   231  
   232  	ciphers := c.buildUintArrayFromSuites()
   233  	cfg := &tls.Config{
   234  		MinVersion:         uint16(c.MinVersion),
   235  		MaxVersion:         uint16(c.MaxVersion),
   236  		InsecureSkipVerify: c.InsecureSkipVerify,
   237  		CipherSuites:       ciphers,
   238  		NextProtos:         c.NextProtos,
   239  	}
   240  	if c.RootCertificatePath != "" {
   241  		caCert, err := ioutil.ReadFile(c.RootCertificatePath)
   242  		if err == nil { // config validated in ValidateCfg
   243  			caCertPool := x509.NewCertPool()
   244  			caCertPool.AppendCertsFromPEM(caCert)
   245  			cfg.RootCAs = caCertPool
   246  		}
   247  	}
   248  
   249  	if c.ClientCertificatePath != "" && c.ClientKeyPath != "" {
   250  		cert, err := tls.LoadX509KeyPair(c.ClientCertificatePath, c.ClientKeyPath)
   251  		if err == nil { // config validated in ValidateCfg
   252  			cfg.Certificates = []tls.Certificate{cert}
   253  		}
   254  	}
   255  
   256  	return cfg
   257  }
   258  
   259  // LoadFrom takes the `tls.Config` and transforms it into a TLSConfiguration.
   260  func (c *TLSConfiguration) LoadFrom(tlsCfg *tls.Config) {
   261  	if tlsCfg == nil {
   262  		c.InsecureSkipVerify = true
   263  		return
   264  	}
   265  
   266  	c.InsecureSkipVerify = tlsCfg.InsecureSkipVerify
   267  	c.MaxVersion = TLSVersion(tlsCfg.MaxVersion)
   268  	c.MinVersion = TLSVersion(tlsCfg.MinVersion)
   269  
   270  	c.CipherSuites = make([]TLSCipherSuite, 0)
   271  	for _, cipher := range tlsCfg.CipherSuites {
   272  		c.CipherSuites = append(c.CipherSuites, TLSCipherSuite(cipher))
   273  	}
   274  }
   275  
   276  // buildUintArrayFromSuites -
   277  func (c *TLSConfiguration) buildUintArrayFromSuites() []uint16 {
   278  	var ciphers []uint16
   279  	for _, suite := range c.CipherSuites {
   280  		ciphers = append(ciphers, uint16(suite))
   281  	}
   282  
   283  	return ciphers
   284  }
   285  
   286  // NewCipherArray - create an array of TLSCipherSuite
   287  func NewCipherArray(ciphers []string) []TLSCipherSuite {
   288  	if len(ciphers) == 0 {
   289  		return nil
   290  	}
   291  
   292  	var result []TLSCipherSuite
   293  	for _, v := range ciphers {
   294  		s := cipherAsValue(v)
   295  		if s == 0 {
   296  			log.Errorf("Invalid cipher suite value found: %v", v)
   297  		}
   298  		result = append(result, s)
   299  	}
   300  	return result
   301  }
   302  
   303  // GetNextProtos -
   304  func (c *TLSConfiguration) GetNextProtos() []string {
   305  	return c.NextProtos
   306  }
   307  
   308  // IsInsecureSkipVerify -
   309  func (c *TLSConfiguration) IsInsecureSkipVerify() bool {
   310  	return c.InsecureSkipVerify
   311  }
   312  
   313  // GetCipherSuites -
   314  func (c *TLSConfiguration) GetCipherSuites() []TLSCipherSuite {
   315  	return c.CipherSuites
   316  }
   317  
   318  // GetMinVersion -
   319  func (c *TLSConfiguration) GetMinVersion() TLSVersion {
   320  	return c.MinVersion
   321  }
   322  
   323  // GetMaxVersion -
   324  func (c *TLSConfiguration) GetMaxVersion() TLSVersion {
   325  	return c.MaxVersion
   326  }
   327  
   328  // ValidateCfg - Validates the config, implementing IConfigInterface
   329  func (c *TLSConfiguration) ValidateCfg() (err error) {
   330  	exception.Block{
   331  		Try: func() {
   332  			c.validateConfig()
   333  		},
   334  		Catch: func(e error) {
   335  			err = e
   336  		},
   337  	}.Do()
   338  
   339  	return
   340  }
   341  
   342  // Validate -
   343  func (c *TLSConfiguration) validateConfig() {
   344  	if c.MinVersion != 0 && !c.isValidMinVersion() {
   345  		exception.Throw(errors.New("ssl.minVersion not valid in config"))
   346  	}
   347  
   348  	if c.MaxVersion != 0 && !c.isValidMaxVersion() {
   349  		exception.Throw(errors.New("ssl.maxVersion not valid in config"))
   350  	}
   351  
   352  	if len(c.CipherSuites) != 0 && !c.isValidCiphers() {
   353  		exception.Throw(errors.New("ssl.cipherSuites not valid in config"))
   354  	}
   355  	if c.RootCertificatePath != "" {
   356  		_, err := ioutil.ReadFile(c.RootCertificatePath)
   357  		if err != nil {
   358  			exception.Throw(errors.New("ssl.rootCertificatePath not valid in config"))
   359  		}
   360  	}
   361  	if c.ClientCertificatePath != "" {
   362  		_, err := tls.LoadX509KeyPair(c.ClientCertificatePath, c.ClientKeyPath)
   363  		if err != nil {
   364  			exception.Throw(errors.New("ssl.clientCertificatePath or sss.clientKeyPath not valid in config"))
   365  		}
   366  	}
   367  }
   368  
   369  func (c *TLSConfiguration) isValidMinVersion() bool {
   370  	if c.MinVersion == 0 {
   371  		return true
   372  	}
   373  
   374  	_, ok := tlsVersionsInverse[c.MinVersion]
   375  	return ok
   376  }
   377  
   378  func (c *TLSConfiguration) isValidMaxVersion() bool {
   379  	if c.MaxVersion == 0 {
   380  		return true
   381  	}
   382  
   383  	_, ok := tlsVersionsInverse[c.MaxVersion]
   384  	return ok
   385  }
   386  
   387  func (c *TLSConfiguration) isValidCiphers() bool {
   388  	for _, v := range c.CipherSuites {
   389  		if _, ok := tlsCipherSuitesInverse[v]; !ok {
   390  			return false
   391  		}
   392  	}
   393  
   394  	return true
   395  }