1package tlsutil
2
3import (
4	"crypto/tls"
5	"crypto/x509"
6	"errors"
7	"fmt"
8
9	"github.com/hashicorp/vault/sdk/helper/strutil"
10)
11
12var ErrInvalidCertParams = errors.New("invalid certificate parameters")
13
14// TLSLookup maps the tls_min_version configuration to the internal value
15var TLSLookup = map[string]uint16{
16	"tls10": tls.VersionTLS10,
17	"tls11": tls.VersionTLS11,
18	"tls12": tls.VersionTLS12,
19}
20
21// cipherMap maps the cipher suite names to the internal cipher suite code.
22var cipherMap = map[string]uint16{
23	"TLS_RSA_WITH_RC4_128_SHA":                tls.TLS_RSA_WITH_RC4_128_SHA,
24	"TLS_RSA_WITH_3DES_EDE_CBC_SHA":           tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
25	"TLS_RSA_WITH_AES_128_CBC_SHA":            tls.TLS_RSA_WITH_AES_128_CBC_SHA,
26	"TLS_RSA_WITH_AES_256_CBC_SHA":            tls.TLS_RSA_WITH_AES_256_CBC_SHA,
27	"TLS_RSA_WITH_AES_128_CBC_SHA256":         tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
28	"TLS_RSA_WITH_AES_128_GCM_SHA256":         tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
29	"TLS_RSA_WITH_AES_256_GCM_SHA384":         tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
30	"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA":        tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
31	"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA":    tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
32	"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA":    tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
33	"TLS_ECDHE_RSA_WITH_RC4_128_SHA":          tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
34	"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA":     tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
35	"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA":      tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
36	"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA":      tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
37	"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
38	"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256":   tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
39	"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256":   tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
40	"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
41	"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384":   tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
42	"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
43	"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":    tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
44	"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":  tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
45}
46
47// ParseCiphers parse ciphersuites from the comma-separated string into recognized slice
48func ParseCiphers(cipherStr string) ([]uint16, error) {
49	suites := []uint16{}
50	ciphers := strutil.ParseStringSlice(cipherStr, ",")
51	for _, cipher := range ciphers {
52		if v, ok := cipherMap[cipher]; ok {
53			suites = append(suites, v)
54		} else {
55			return suites, fmt.Errorf("unsupported cipher %q", cipher)
56		}
57	}
58
59	return suites, nil
60}
61
62// GetCipherName returns the name of a given cipher suite code or an error if the
63// given cipher is unsupported.
64func GetCipherName(cipher uint16) (string, error) {
65	for cipherStr, cipherCode := range cipherMap {
66		if cipherCode == cipher {
67			return cipherStr, nil
68		}
69	}
70	return "", fmt.Errorf("unsupported cipher %d", cipher)
71}
72
73func ClientTLSConfig(caCert []byte, clientCert []byte, clientKey []byte) (*tls.Config, error) {
74	var tlsConfig *tls.Config
75	var pool *x509.CertPool
76
77	switch {
78	case len(caCert) != 0:
79		// Valid
80	case len(clientCert) != 0 && len(clientKey) != 0:
81		// Valid
82	default:
83		return nil, ErrInvalidCertParams
84	}
85
86	if len(caCert) != 0 {
87		pool = x509.NewCertPool()
88		pool.AppendCertsFromPEM(caCert)
89	}
90
91	tlsConfig = &tls.Config{
92		RootCAs:    pool,
93		ClientAuth: tls.RequireAndVerifyClientCert,
94		MinVersion: tls.VersionTLS12,
95	}
96
97	var cert tls.Certificate
98	var err error
99	if len(clientCert) != 0 && len(clientKey) != 0 {
100		cert, err = tls.X509KeyPair(clientCert, clientKey)
101		if err != nil {
102			return nil, err
103		}
104		tlsConfig.Certificates = []tls.Certificate{cert}
105	}
106	tlsConfig.BuildNameToCertificate()
107
108	return tlsConfig, nil
109}
110