From 4943ae58d8790345daf0883da7ceea9fb3108c30 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Mon, 10 Aug 2020 15:29:18 -0700 Subject: [PATCH] Move TLSOption, TLSVersion, CipherSuites and ASN1DN to certificates. --- api/api.go | 3 +- api/api_test.go | 13 ++- api/sign.go | 12 +-- authority/config.go | 24 +++-- authority/config_test.go | 26 +++--- authority/tls.go | 5 +- authority/tls_options.go | 157 +++++++++++++++++++++++++++++++ authority/tls_options_test.go | 169 ++++++++++++++++++++++++++++++++++ authority/tls_test.go | 13 ++- ca/ca_test.go | 4 +- go.mod | 2 +- pki/pki.go | 11 +-- 12 files changed, 384 insertions(+), 55 deletions(-) create mode 100644 authority/tls_options.go create mode 100644 authority/tls_options_test.go diff --git a/api/api.go b/api/api.go index be735d69..0cabb416 100644 --- a/api/api.go +++ b/api/api.go @@ -23,7 +23,6 @@ import ( "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/logging" - "github.com/smallstep/cli/crypto/tlsutil" ) // Authority is the interface implemented by a CA authority. @@ -32,7 +31,7 @@ type Authority interface { // context specifies the Authorize[Sign|Revoke|etc.] method. Authorize(ctx context.Context, ott string) ([]provisioner.SignOption, error) AuthorizeSign(ott string) ([]provisioner.SignOption, error) - GetTLSOptions() *tlsutil.TLSOptions + GetTLSOptions() *authority.TLSOptions Root(shasum string) (*x509.Certificate, error) Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) Renew(peer *x509.Certificate) ([]*x509.Certificate, error) diff --git a/api/api_test.go b/api/api_test.go index 7df021cc..31d45f5d 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -32,7 +32,6 @@ import ( "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/logging" "github.com/smallstep/certificates/templates" - "github.com/smallstep/cli/crypto/tlsutil" "github.com/smallstep/cli/jose" "golang.org/x/crypto/ssh" ) @@ -547,7 +546,7 @@ type mockAuthority struct { ret1, ret2 interface{} err error authorizeSign func(ott string) ([]provisioner.SignOption, error) - getTLSOptions func() *tlsutil.TLSOptions + getTLSOptions func() *authority.TLSOptions root func(shasum string) (*x509.Certificate, error) sign func(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) renew func(cert *x509.Certificate) ([]*x509.Certificate, error) @@ -584,11 +583,11 @@ func (m *mockAuthority) AuthorizeSign(ott string) ([]provisioner.SignOption, err return m.ret1.([]provisioner.SignOption), m.err } -func (m *mockAuthority) GetTLSOptions() *tlsutil.TLSOptions { +func (m *mockAuthority) GetTLSOptions() *authority.TLSOptions { if m.getTLSOptions != nil { return m.getTLSOptions() } - return m.ret1.(*tlsutil.TLSOptions) + return m.ret1.(*authority.TLSOptions) } func (m *mockAuthority) Root(shasum string) (*x509.Certificate, error) { @@ -881,7 +880,7 @@ func Test_caHandler_Sign(t *testing.T) { authorizeSign: func(ott string) ([]provisioner.SignOption, error) { return tt.certAttrOpts, tt.autherr }, - getTLSOptions: func() *tlsutil.TLSOptions { + getTLSOptions: func() *authority.TLSOptions { return nil }, }).(*caHandler) @@ -932,7 +931,7 @@ func Test_caHandler_Renew(t *testing.T) { t.Run(tt.name, func(t *testing.T) { h := New(&mockAuthority{ ret1: tt.cert, ret2: tt.root, err: tt.err, - getTLSOptions: func() *tlsutil.TLSOptions { + getTLSOptions: func() *authority.TLSOptions { return nil }, }).(*caHandler) @@ -993,7 +992,7 @@ func Test_caHandler_Rekey(t *testing.T) { t.Run(tt.name, func(t *testing.T) { h := New(&mockAuthority{ ret1: tt.cert, ret2: tt.root, err: tt.err, - getTLSOptions: func() *tlsutil.TLSOptions { + getTLSOptions: func() *authority.TLSOptions { return nil }, }).(*caHandler) diff --git a/api/sign.go b/api/sign.go index 90a9aa99..a834b520 100644 --- a/api/sign.go +++ b/api/sign.go @@ -5,9 +5,9 @@ import ( "encoding/json" "net/http" + "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" - "github.com/smallstep/cli/crypto/tlsutil" ) // SignRequest is the request body for a certificate signature request. @@ -37,11 +37,11 @@ func (s *SignRequest) Validate() error { // SignResponse is the response object of the certificate signature request. type SignResponse struct { - ServerPEM Certificate `json:"crt"` - CaPEM Certificate `json:"ca"` - CertChainPEM []Certificate `json:"certChain"` - TLSOptions *tlsutil.TLSOptions `json:"tlsOptions,omitempty"` - TLS *tls.ConnectionState `json:"-"` + ServerPEM Certificate `json:"crt"` + CaPEM Certificate `json:"ca"` + CertChainPEM []Certificate `json:"certChain"` + TLSOptions *authority.TLSOptions `json:"tlsOptions,omitempty"` + TLS *tls.ConnectionState `json:"-"` } // Sign is an HTTP handler that reads a certificate request and an diff --git a/authority/config.go b/authority/config.go index a26d19ad..1d49f9a1 100644 --- a/authority/config.go +++ b/authority/config.go @@ -12,15 +12,13 @@ import ( "github.com/smallstep/certificates/db" kms "github.com/smallstep/certificates/kms/apiv1" "github.com/smallstep/certificates/templates" - "github.com/smallstep/cli/crypto/tlsutil" - "github.com/smallstep/cli/crypto/x509util" ) var ( // DefaultTLSOptions represents the default TLS version as well as the cipher // suites used in the TLS certificates. - DefaultTLSOptions = tlsutil.TLSOptions{ - CipherSuites: x509util.CipherSuites{ + DefaultTLSOptions = TLSOptions{ + CipherSuites: CipherSuites{ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", @@ -61,15 +59,27 @@ type Config struct { DB *db.Config `json:"db,omitempty"` Monitoring json.RawMessage `json:"monitoring,omitempty"` AuthorityConfig *AuthConfig `json:"authority,omitempty"` - TLS *tlsutil.TLSOptions `json:"tls,omitempty"` + TLS *TLSOptions `json:"tls,omitempty"` Password string `json:"password,omitempty"` Templates *templates.Templates `json:"templates,omitempty"` } +// ASN1DN contains ASN1.DN attributes that are used in Subject and Issuer +// x509 Certificate blocks. +type ASN1DN struct { + Country string `json:"country,omitempty" step:"country"` + Organization string `json:"organization,omitempty" step:"organization"` + OrganizationalUnit string `json:"organizationalUnit,omitempty" step:"organizationalUnit"` + Locality string `json:"locality,omitempty" step:"locality"` + Province string `json:"province,omitempty" step:"province"` + StreetAddress string `json:"streetAddress,omitempty" step:"streetAddress"` + CommonName string `json:"commonName,omitempty" step:"commonName"` +} + // AuthConfig represents the configuration options for the authority. type AuthConfig struct { Provisioners provisioner.List `json:"provisioners"` - Template *x509util.ASN1DN `json:"template,omitempty"` + Template *ASN1DN `json:"template,omitempty"` Claims *provisioner.Claims `json:"claims,omitempty"` DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` Backdate *provisioner.Duration `json:"backdate,omitempty"` @@ -82,7 +92,7 @@ func (c *AuthConfig) init() { c.Provisioners = provisioner.List{} } if c.Template == nil { - c.Template = &x509util.ASN1DN{} + c.Template = &ASN1DN{} } if c.Backdate == nil { c.Backdate = &provisioner.Duration{ diff --git a/authority/config_test.go b/authority/config_test.go index d049d47e..22bfd6c8 100644 --- a/authority/config_test.go +++ b/authority/config_test.go @@ -7,8 +7,6 @@ import ( "github.com/pkg/errors" "github.com/smallstep/assert" "github.com/smallstep/certificates/authority/provisioner" - "github.com/smallstep/cli/crypto/tlsutil" - "github.com/smallstep/cli/crypto/x509util" stepJOSE "github.com/smallstep/cli/jose" ) @@ -35,7 +33,7 @@ func TestConfigValidate(t *testing.T) { type ConfigValidateTest struct { config *Config err error - tls tlsutil.TLSOptions + tls TLSOptions } tests := map[string]func(*testing.T) ConfigValidateTest{ "empty-address": func(t *testing.T) ConfigValidateTest { @@ -141,7 +139,7 @@ func TestConfigValidate(t *testing.T) { DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, - TLS: &tlsutil.TLSOptions{}, + TLS: &TLSOptions{}, }, tls: DefaultTLSOptions, } @@ -156,8 +154,8 @@ func TestConfigValidate(t *testing.T) { DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, - TLS: &tlsutil.TLSOptions{ - CipherSuites: x509util.CipherSuites{ + TLS: &TLSOptions{ + CipherSuites: CipherSuites{ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", }, MinVersion: 1.0, @@ -165,8 +163,8 @@ func TestConfigValidate(t *testing.T) { Renegotiation: true, }, }, - tls: tlsutil.TLSOptions{ - CipherSuites: x509util.CipherSuites{ + tls: TLSOptions{ + CipherSuites: CipherSuites{ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", }, MinVersion: 1.0, @@ -185,8 +183,8 @@ func TestConfigValidate(t *testing.T) { DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, - TLS: &tlsutil.TLSOptions{ - CipherSuites: x509util.CipherSuites{ + TLS: &TLSOptions{ + CipherSuites: CipherSuites{ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", }, MinVersion: 1.2, @@ -217,7 +215,7 @@ func TestConfigValidate(t *testing.T) { } func TestAuthConfigValidate(t *testing.T) { - asn1dn := x509util.ASN1DN{ + asn1dn := ASN1DN{ Country: "Tazmania", Organization: "Acme Co", Locality: "Landscapes", @@ -245,7 +243,7 @@ func TestAuthConfigValidate(t *testing.T) { type AuthConfigValidateTest struct { ac *AuthConfig - asn1dn x509util.ASN1DN + asn1dn ASN1DN err error } tests := map[string]func(*testing.T) AuthConfigValidateTest{ @@ -258,7 +256,7 @@ func TestAuthConfigValidate(t *testing.T) { "ok-empty-provisioners": func(t *testing.T) AuthConfigValidateTest { return AuthConfigValidateTest{ ac: &AuthConfig{}, - asn1dn: x509util.ASN1DN{}, + asn1dn: ASN1DN{}, } }, "ok-empty-asn1dn-template": func(t *testing.T) AuthConfigValidateTest { @@ -266,7 +264,7 @@ func TestAuthConfigValidate(t *testing.T) { ac: &AuthConfig{ Provisioners: p, }, - asn1dn: x509util.ASN1DN{}, + asn1dn: ASN1DN{}, } }, "ok-custom-asn1dn": func(t *testing.T) AuthConfigValidateTest { diff --git a/authority/tls.go b/authority/tls.go index baaac270..bff60649 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -17,21 +17,20 @@ import ( "github.com/smallstep/certificates/db" "github.com/smallstep/certificates/errs" "github.com/smallstep/cli/crypto/pemutil" - "github.com/smallstep/cli/crypto/tlsutil" x509legacy "github.com/smallstep/cli/crypto/x509util" "github.com/smallstep/cli/jose" "go.step.sm/crypto/x509util" ) // GetTLSOptions returns the tls options configured. -func (a *Authority) GetTLSOptions() *tlsutil.TLSOptions { +func (a *Authority) GetTLSOptions() *TLSOptions { return a.config.TLS } var oidAuthorityKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 35} var oidSubjectKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 14} -func withDefaultASN1DN(def *x509legacy.ASN1DN) provisioner.CertificateModifierFunc { +func withDefaultASN1DN(def *ASN1DN) provisioner.CertificateModifierFunc { return func(crt *x509.Certificate, opts provisioner.SignOptions) error { if def == nil { return errors.New("default ASN1DN template cannot be nil") diff --git a/authority/tls_options.go b/authority/tls_options.go new file mode 100644 index 00000000..3edde605 --- /dev/null +++ b/authority/tls_options.go @@ -0,0 +1,157 @@ +package authority + +import ( + "crypto/tls" + "fmt" + + "github.com/pkg/errors" +) + +var ( + // DefaultTLSMinVersion default minimum version of TLS. + DefaultTLSMinVersion = TLSVersion(1.2) + // DefaultTLSMaxVersion default maximum version of TLS. + DefaultTLSMaxVersion = TLSVersion(1.3) + // DefaultTLSRenegotiation default TLS connection renegotiation policy. + DefaultTLSRenegotiation = false // Never regnegotiate. + // DefaultTLSCipherSuites specifies default step ciphersuite(s). + DefaultTLSCipherSuites = CipherSuites{ + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + } + // ApprovedTLSCipherSuites smallstep approved ciphersuites. + ApprovedTLSCipherSuites = CipherSuites{ + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", + } +) + +// TLSVersion represents a TLS version number. +type TLSVersion float64 + +// Validate implements models.Validator and checks that a cipher suite is +// valid. +func (v TLSVersion) Validate() error { + if _, ok := tlsVersions[v]; ok { + return nil + } + return errors.Errorf("%f is not a valid tls version", v) +} + +// Value returns the Go constant for the TLSVersion. +func (v TLSVersion) Value() uint16 { + return tlsVersions[v] +} + +// String returns the Go constant for the TLSVersion. +func (v TLSVersion) String() string { + k := v.Value() + switch k { + case tls.VersionTLS10: + return "1.0" + case tls.VersionTLS11: + return "1.1" + case tls.VersionTLS12: + return "1.2" + case tls.VersionTLS13: + return "1.3" + default: + return fmt.Sprintf("unexpected value: %f", v) + } +} + +// tlsVersions has the list of supported tls version. +var tlsVersions = map[TLSVersion]uint16{ + // Defaults to TLS 1.3 + 0: tls.VersionTLS13, + // Options + 1.0: tls.VersionTLS10, + 1.1: tls.VersionTLS11, + 1.2: tls.VersionTLS12, + 1.3: tls.VersionTLS13, +} + +// CipherSuites represents an array of string codes representing the cipher +// suites. +type CipherSuites []string + +// Validate implements models.Validator and checks that a cipher suite is +// valid. +func (c CipherSuites) Validate() error { + for _, s := range c { + if _, ok := cipherSuites[s]; !ok { + return errors.Errorf("%s is not a valid cipher suite", s) + } + } + return nil +} + +// Value returns an []uint16 for the cipher suites. +func (c CipherSuites) Value() []uint16 { + values := make([]uint16, len(c)) + for i, s := range c { + values[i] = cipherSuites[s] + } + return values +} + +// cipherSuites has the list of supported cipher suites. +var cipherSuites = map[string]uint16{ + "TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA, + "TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, + "TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, + "TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, + "TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256, + "TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256, + "TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, +} + +// TLSOptions represents the TLS options that can be specified on *tls.Config +// types to configure HTTPS servers and clients. +type TLSOptions struct { + CipherSuites CipherSuites `json:"cipherSuites"` + MinVersion TLSVersion `json:"minVersion"` + MaxVersion TLSVersion `json:"maxVersion"` + Renegotiation bool `json:"renegotiation"` +} + +// TLSConfig returns the tls.Config equivalent of the TLSOptions. +func (t *TLSOptions) TLSConfig() *tls.Config { + var rs tls.RenegotiationSupport + if t.Renegotiation { + rs = tls.RenegotiateFreelyAsClient + } else { + rs = tls.RenegotiateNever + } + + return &tls.Config{ + CipherSuites: t.CipherSuites.Value(), + MinVersion: t.MinVersion.Value(), + MaxVersion: t.MaxVersion.Value(), + Renegotiation: rs, + } +} diff --git a/authority/tls_options_test.go b/authority/tls_options_test.go new file mode 100644 index 00000000..96c58c5d --- /dev/null +++ b/authority/tls_options_test.go @@ -0,0 +1,169 @@ +package authority + +import ( + "crypto/tls" + "reflect" + "testing" +) + +func TestTLSVersion_Validate(t *testing.T) { + tests := []struct { + name string + v TLSVersion + wantErr bool + }{ + {"default", TLSVersion(0), false}, + {"1.0", TLSVersion(1.0), false}, + {"1.1", TLSVersion(1.1), false}, + {"1.2", TLSVersion(1.2), false}, + {"1.3", TLSVersion(1.3), false}, + {"0.99", TLSVersion(0.99), true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.v.Validate(); (err != nil) != tt.wantErr { + t.Errorf("TLSVersion.Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestTLSVersion_String(t *testing.T) { + tests := []struct { + name string + v TLSVersion + want string + }{ + {"default", TLSVersion(0), "1.3"}, + {"1.0", TLSVersion(1.0), "1.0"}, + {"1.1", TLSVersion(1.1), "1.1"}, + {"1.2", TLSVersion(1.2), "1.2"}, + {"1.3", TLSVersion(1.3), "1.3"}, + {"0.99", TLSVersion(0.99), "unexpected value: 0.990000"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.v.String(); got != tt.want { + t.Errorf("TLSVersion.String() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCipherSuites_Validate(t *testing.T) { + tests := []struct { + name string + c CipherSuites + wantErr bool + }{ + {"TLS_RSA_WITH_RC4_128_SHA", CipherSuites{"TLS_RSA_WITH_RC4_128_SHA"}, false}, + {"TLS_RSA_WITH_3DES_EDE_CBC_SHA", CipherSuites{"TLS_RSA_WITH_3DES_EDE_CBC_SHA"}, false}, + {"TLS_RSA_WITH_AES_128_CBC_SHA", CipherSuites{"TLS_RSA_WITH_AES_128_CBC_SHA"}, false}, + {"TLS_RSA_WITH_AES_256_CBC_SHA", CipherSuites{"TLS_RSA_WITH_AES_256_CBC_SHA"}, false}, + {"TLS_RSA_WITH_AES_128_CBC_SHA256", CipherSuites{"TLS_RSA_WITH_AES_128_CBC_SHA256"}, false}, + {"TLS_RSA_WITH_AES_128_GCM_SHA256", CipherSuites{"TLS_RSA_WITH_AES_128_GCM_SHA256"}, false}, + {"TLS_RSA_WITH_AES_256_GCM_SHA384", CipherSuites{"TLS_RSA_WITH_AES_256_GCM_SHA384"}, false}, + {"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", CipherSuites{"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA"}, false}, + {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", CipherSuites{"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"}, false}, + {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", CipherSuites{"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"}, false}, + {"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", CipherSuites{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"}, false}, + {"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", CipherSuites{"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"}, false}, + {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", CipherSuites{"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"}, false}, + {"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", CipherSuites{"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305"}, false}, + {"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", CipherSuites{"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"}, false}, + {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", CipherSuites{"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"}, false}, + {"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", CipherSuites{"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"}, false}, + {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", CipherSuites{"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"}, false}, + {"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", CipherSuites{"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"}, false}, + {"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", CipherSuites{"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"}, false}, + {"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", CipherSuites{"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"}, false}, + {"multiple", CipherSuites{"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"}, false}, + {"fail", CipherSuites{"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", "TLS_BAD_CIPHERSUITE"}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.c.Validate(); (err != nil) != tt.wantErr { + t.Errorf("CipherSuites.Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestCipherSuites_Value(t *testing.T) { + tests := []struct { + name string + c CipherSuites + want []uint16 + }{ + {"TLS_RSA_WITH_RC4_128_SHA", CipherSuites{"TLS_RSA_WITH_RC4_128_SHA"}, []uint16{tls.TLS_RSA_WITH_RC4_128_SHA}}, + {"TLS_RSA_WITH_3DES_EDE_CBC_SHA", CipherSuites{"TLS_RSA_WITH_3DES_EDE_CBC_SHA"}, []uint16{tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA}}, + {"TLS_RSA_WITH_AES_128_CBC_SHA", CipherSuites{"TLS_RSA_WITH_AES_128_CBC_SHA"}, []uint16{tls.TLS_RSA_WITH_AES_128_CBC_SHA}}, + {"TLS_RSA_WITH_AES_256_CBC_SHA", CipherSuites{"TLS_RSA_WITH_AES_256_CBC_SHA"}, []uint16{tls.TLS_RSA_WITH_AES_256_CBC_SHA}}, + {"TLS_RSA_WITH_AES_128_CBC_SHA256", CipherSuites{"TLS_RSA_WITH_AES_128_CBC_SHA256"}, []uint16{tls.TLS_RSA_WITH_AES_128_CBC_SHA256}}, + {"TLS_RSA_WITH_AES_128_GCM_SHA256", CipherSuites{"TLS_RSA_WITH_AES_128_GCM_SHA256"}, []uint16{tls.TLS_RSA_WITH_AES_128_GCM_SHA256}}, + {"TLS_RSA_WITH_AES_256_GCM_SHA384", CipherSuites{"TLS_RSA_WITH_AES_256_GCM_SHA384"}, []uint16{tls.TLS_RSA_WITH_AES_256_GCM_SHA384}}, + {"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", CipherSuites{"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA"}, []uint16{tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA}}, + {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", CipherSuites{"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"}, []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}}, + {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", CipherSuites{"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"}, []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}}, + {"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", CipherSuites{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"}, []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}}, + {"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", CipherSuites{"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"}, []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}}, + {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", CipherSuites{"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"}, []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384}}, + {"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", CipherSuites{"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305"}, []uint16{tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305}}, + {"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", CipherSuites{"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"}, []uint16{tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA}}, + {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", CipherSuites{"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"}, []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}}, + {"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", CipherSuites{"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"}, []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}}, + {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", CipherSuites{"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"}, []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256}}, + {"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", CipherSuites{"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"}, []uint16{tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}}, + {"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", CipherSuites{"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"}, []uint16{tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384}}, + {"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", CipherSuites{"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"}, []uint16{tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305}}, + {"multiple", CipherSuites{"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"}, []uint16{tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}}, + {"fail", CipherSuites{"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", "TLS_BAD_CIPHERSUITE"}, []uint16{tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 0}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.c.Value(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CipherSuites.Value() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestTLSOptions_TLSConfig(t *testing.T) { + type fields struct { + CipherSuites CipherSuites + MinVersion TLSVersion + MaxVersion TLSVersion + Renegotiation bool + } + tests := []struct { + name string + fields fields + want *tls.Config + }{ + {"default", fields{DefaultTLSCipherSuites, DefaultTLSMinVersion, DefaultTLSMaxVersion, DefaultTLSRenegotiation}, &tls.Config{ + CipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + MinVersion: tls.VersionTLS12, + MaxVersion: tls.VersionTLS13, + Renegotiation: tls.RenegotiateNever, + }}, + {"renegotation", fields{DefaultTLSCipherSuites, DefaultTLSMinVersion, DefaultTLSMaxVersion, true}, &tls.Config{ + CipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + MinVersion: tls.VersionTLS12, + MaxVersion: tls.VersionTLS13, + Renegotiation: tls.RenegotiateFreelyAsClient, + }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := &TLSOptions{ + CipherSuites: tt.fields.CipherSuites, + MinVersion: tt.fields.MinVersion, + MaxVersion: tt.fields.MaxVersion, + Renegotiation: tt.fields.Renegotiation, + } + if got := o.TLSConfig(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("TLSOptions.TLSConfig() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/authority/tls_test.go b/authority/tls_test.go index 41347f17..47bc964d 100644 --- a/authority/tls_test.go +++ b/authority/tls_test.go @@ -22,7 +22,6 @@ import ( "github.com/smallstep/certificates/errs" "github.com/smallstep/cli/crypto/keys" "github.com/smallstep/cli/crypto/pemutil" - "github.com/smallstep/cli/crypto/tlsutil" "github.com/smallstep/cli/crypto/x509util" "github.com/smallstep/cli/jose" "gopkg.in/square/go-jose.v2/jwt" @@ -122,7 +121,7 @@ func TestAuthority_Sign(t *testing.T) { a := testAuthority(t) assert.FatalError(t, err) - a.config.AuthorityConfig.Template = &x509util.ASN1DN{ + a.config.AuthorityConfig.Template = &ASN1DN{ Country: "Tazmania", Organization: "Acme Co", Locality: "Landscapes", @@ -478,7 +477,7 @@ func TestAuthority_Renew(t *testing.T) { assert.FatalError(t, err) a := testAuthority(t) - a.config.AuthorityConfig.Template = &x509util.ASN1DN{ + a.config.AuthorityConfig.Template = &ASN1DN{ Country: "Tazmania", Organization: "Acme Co", Locality: "Landscapes", @@ -705,7 +704,7 @@ func TestAuthority_Rekey(t *testing.T) { assert.FatalError(t, err) a := testAuthority(t) - a.config.AuthorityConfig.Template = &x509util.ASN1DN{ + a.config.AuthorityConfig.Template = &ASN1DN{ Country: "Tazmania", Organization: "Acme Co", Locality: "Landscapes", @@ -946,7 +945,7 @@ func TestAuthority_Rekey(t *testing.T) { func TestAuthority_GetTLSOptions(t *testing.T) { type renewTest struct { auth *Authority - opts *tlsutil.TLSOptions + opts *TLSOptions } tests := map[string]func() (*renewTest, error){ "default": func() (*renewTest, error) { @@ -955,8 +954,8 @@ func TestAuthority_GetTLSOptions(t *testing.T) { }, "non-default": func() (*renewTest, error) { a := testAuthority(t) - a.config.TLS = &tlsutil.TLSOptions{ - CipherSuites: x509util.CipherSuites{ + a.config.TLS = &TLSOptions{ + CipherSuites: CipherSuites{ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", }, diff --git a/ca/ca_test.go b/ca/ca_test.go index 08e40728..43733a82 100644 --- a/ca/ca_test.go +++ b/ca/ca_test.go @@ -79,7 +79,7 @@ func TestCASign(t *testing.T) { pub, priv, err := keys.GenerateDefaultKeyPair() assert.FatalError(t, err) - asn1dn := &x509util.ASN1DN{ + asn1dn := &authority.ASN1DN{ Country: "Tazmania", Organization: "Acme Co", Locality: "Landscapes", @@ -558,7 +558,7 @@ func TestCARenew(t *testing.T) { pub, _, err := keys.GenerateDefaultKeyPair() assert.FatalError(t, err) - asn1dn := &x509util.ASN1DN{ + asn1dn := &authority.ASN1DN{ Country: "Tazmania", Organization: "Acme Co", Locality: "Landscapes", diff --git a/go.mod b/go.mod index c163eaa3..757cc8fc 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/smallstep/certificates -go 1.13 +go 1.14 require ( cloud.google.com/go v0.51.0 diff --git a/pki/pki.go b/pki/pki.go index d52f247e..db0ef1ac 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -23,7 +23,6 @@ import ( "github.com/smallstep/cli/config" "github.com/smallstep/cli/crypto/keys" "github.com/smallstep/cli/crypto/pemutil" - "github.com/smallstep/cli/crypto/tlsutil" "github.com/smallstep/cli/crypto/x509util" "github.com/smallstep/cli/errs" "github.com/smallstep/cli/jose" @@ -432,11 +431,11 @@ func (p *PKI) GenerateConfig(opt ...Option) (*authority.Config, error) { DisableIssuedAtCheck: false, Provisioners: provisioner.List{prov}, }, - TLS: &tlsutil.TLSOptions{ - MinVersion: x509util.DefaultTLSMinVersion, - MaxVersion: x509util.DefaultTLSMaxVersion, - Renegotiation: x509util.DefaultTLSRenegotiation, - CipherSuites: x509util.DefaultTLSCipherSuites, + TLS: &authority.TLSOptions{ + MinVersion: authority.DefaultTLSMinVersion, + MaxVersion: authority.DefaultTLSMaxVersion, + Renegotiation: authority.DefaultTLSRenegotiation, + CipherSuites: authority.DefaultTLSCipherSuites, }, Templates: p.getTemplates(), }