From 22b364593e83b70cd0d414fb8dd29115c2486ca4 Mon Sep 17 00:00:00 2001 From: Nicolas JUHEL Date: Tue, 3 Dec 2024 11:17:11 +0100 Subject: [PATCH] Package certificates: - rework package to allow convert config to model and retrive config from model - add sub package to manage cipher, curves, auth client, tls version, certificates, root ca... - add some small test (can be expande to having more coverage) - optimize some code Package httpcli: - update code following change in certificates Package httpserver: - update code following change in certificates Package Config/Components: - update code following change in certificates Package FTPClient: - update code following change in certificates Package Nats: - update code following change in certificates --- certificates/auth/encode.go | 89 +++++ certificates/auth/format.go | 55 +++ certificates/auth/interface.go | 99 ++++++ certificates/auth/models.go | 62 ++++ certificates/authClient.go | 86 +++++ certificates/ca/encode.go | 165 +++++++++ certificates/ca/format.go | 61 ++++ certificates/ca/interface.go | 78 +++++ certificates/ca/models.go | 69 ++++ certificates/cert.go | 84 +++++ certificates/certificates_config_test.go | 214 ++++++++++++ certificates/certificates_suite_test.go | 75 ++++ certificates/certs/config.go | 165 +++++++++ certificates/certs/encode.go | 244 +++++++++++++ certificates/certs/format.go | 86 +++++ certificates/certs/interface.go | 76 ++++ certificates/certs/models.go | 75 ++++ certificates/cipher/encode.go | 89 +++++ certificates/cipher/format.go | 100 ++++++ certificates/cipher/interface.go | 199 +++++++++++ certificates/cipher/models.go | 62 ++++ certificates/ciphers.go | 52 +++ certificates/config.go | 175 +++++----- certificates/curves.go | 52 +++ certificates/curves/encode.go | 89 +++++ certificates/curves/format.go | 98 ++++++ certificates/curves/interface.go | 120 +++++++ certificates/curves/models.go | 62 ++++ certificates/interface.go | 102 ++++-- certificates/model.go | 421 ++++++++--------------- certificates/rootca.go | 79 +++++ certificates/tlsversion/encode.go | 39 +-- certificates/tlsversion/format.go | 43 +-- certificates/tlsversion/interface.go | 41 +-- certificates/tlsversion/models.go | 39 +-- certificates/tools.go | 133 ++----- config/components/smtp/client.go | 2 +- config/components/tls/client.go | 6 +- ftpclient/config.go | 9 +- ftpclient/interface.go | 41 ++- ftpclient/model.go | 44 ++- httpcli/dns-mapper/transport.go | 7 +- httpserver/config.go | 22 +- httpserver/pool/config.go | 4 +- nats/client.go | 9 +- nats/config.go | 71 ++-- nats/server.go | 27 +- 47 files changed, 3313 insertions(+), 707 deletions(-) create mode 100644 certificates/auth/encode.go create mode 100644 certificates/auth/format.go create mode 100644 certificates/auth/interface.go create mode 100644 certificates/auth/models.go create mode 100644 certificates/authClient.go create mode 100644 certificates/ca/encode.go create mode 100644 certificates/ca/format.go create mode 100644 certificates/ca/interface.go create mode 100644 certificates/ca/models.go create mode 100644 certificates/cert.go create mode 100644 certificates/certificates_config_test.go create mode 100644 certificates/certificates_suite_test.go create mode 100644 certificates/certs/config.go create mode 100644 certificates/certs/encode.go create mode 100644 certificates/certs/format.go create mode 100644 certificates/certs/interface.go create mode 100644 certificates/certs/models.go create mode 100644 certificates/cipher/encode.go create mode 100644 certificates/cipher/format.go create mode 100644 certificates/cipher/interface.go create mode 100644 certificates/cipher/models.go create mode 100644 certificates/ciphers.go create mode 100644 certificates/curves.go create mode 100644 certificates/curves/encode.go create mode 100644 certificates/curves/format.go create mode 100644 certificates/curves/interface.go create mode 100644 certificates/curves/models.go create mode 100644 certificates/rootca.go diff --git a/certificates/auth/encode.go b/certificates/auth/encode.go new file mode 100644 index 0000000..3f14b5d --- /dev/null +++ b/certificates/auth/encode.go @@ -0,0 +1,89 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package auth + +import ( + "fmt" + + "gopkg.in/yaml.v3" +) + +func (a *ClientAuth) unmarshall(val []byte) error { + *a = parseBytes(val) + return nil +} + +func (a ClientAuth) MarshalJSON() ([]byte, error) { + t := a.String() + b := make([]byte, 0, len(t)+2) + b = append(b, '"') + b = append(b, []byte(t)...) + b = append(b, '"') + return b, nil +} + +func (a *ClientAuth) UnmarshalJSON(bytes []byte) error { + return a.unmarshall(bytes) +} + +func (a ClientAuth) MarshalYAML() (interface{}, error) { + return []byte(a.String()), nil +} + +func (a *ClientAuth) UnmarshalYAML(value *yaml.Node) error { + return a.unmarshall([]byte(value.Value)) +} + +func (a ClientAuth) MarshalTOML() ([]byte, error) { + return []byte(a.String()), nil +} + +func (a *ClientAuth) UnmarshalTOML(i interface{}) error { + if p, k := i.([]byte); k { + return a.unmarshall(p) + } + if p, k := i.(string); k { + return a.unmarshall([]byte(p)) + } + return fmt.Errorf("tls client Auth: value not in valid format") +} + +func (a ClientAuth) MarshalText() ([]byte, error) { + return []byte(a.String()), nil +} + +func (a *ClientAuth) UnmarshalText(bytes []byte) error { + return a.unmarshall(bytes) +} + +func (a ClientAuth) MarshalCBOR() ([]byte, error) { + return []byte(a.String()), nil +} + +func (a *ClientAuth) UnmarshalCBOR(bytes []byte) error { + return a.unmarshall(bytes) +} diff --git a/certificates/auth/format.go b/certificates/auth/format.go new file mode 100644 index 0000000..3f38d69 --- /dev/null +++ b/certificates/auth/format.go @@ -0,0 +1,55 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package auth + +import ( + "crypto/tls" + "strings" +) + +func (a ClientAuth) String() string { + switch a { + case RequireAndVerifyClientCert: + return strict + " " + require + " " + verify + case VerifyClientCertIfGiven: + return verify + case RequireAnyClientCert: + return require + case RequestClientCert: + return request + default: + return none + } +} + +func (a ClientAuth) Code() string { + return strings.ToLower(a.String()) +} + +func (a ClientAuth) TLS() tls.ClientAuthType { + return tls.ClientAuthType(a) +} diff --git a/certificates/auth/interface.go b/certificates/auth/interface.go new file mode 100644 index 0000000..9481234 --- /dev/null +++ b/certificates/auth/interface.go @@ -0,0 +1,99 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package auth + +import ( + "crypto/tls" + "strings" +) + +const ( + strict = "strict" + require = "require" + verify = "verify" + request = "request" + none = "none" +) + +type ClientAuth tls.ClientAuthType + +const ( + NoClientCert = ClientAuth(tls.NoClientCert) + RequestClientCert = ClientAuth(tls.RequestClientCert) + RequireAnyClientCert = ClientAuth(tls.RequireAnyClientCert) + VerifyClientCertIfGiven = ClientAuth(tls.VerifyClientCertIfGiven) + RequireAndVerifyClientCert = ClientAuth(tls.RequireAndVerifyClientCert) +) + +func List() []ClientAuth { + return []ClientAuth{ + NoClientCert, + RequestClientCert, + RequireAnyClientCert, + VerifyClientCertIfGiven, + RequireAndVerifyClientCert, + } +} + +func Parse(s string) ClientAuth { + s = strings.ToLower(s) + s = strings.Replace(s, "\"", "", -1) + s = strings.Replace(s, "'", "", -1) + s = strings.TrimSpace(s) + + switch { + case strings.Contains(s, strict) || (strings.Contains(s, require) && strings.Contains(s, verify)): + return RequireAndVerifyClientCert + case strings.Contains(s, verify): + return VerifyClientCertIfGiven + case strings.Contains(s, require) && !strings.Contains(s, verify): + return RequireAnyClientCert + case strings.Contains(s, request): + return RequestClientCert + default: + return NoClientCert + } +} + +func ParseInt(d int) ClientAuth { + switch tls.ClientAuthType(d) { + case tls.RequireAndVerifyClientCert: + return RequireAndVerifyClientCert + case tls.VerifyClientCertIfGiven: + return VerifyClientCertIfGiven + case tls.RequireAnyClientCert: + return RequireAnyClientCert + case tls.RequestClientCert: + return RequestClientCert + default: + return NoClientCert + } +} + +func parseBytes(p []byte) ClientAuth { + return Parse(string(p)) +} diff --git a/certificates/auth/models.go b/certificates/auth/models.go new file mode 100644 index 0000000..117c16a --- /dev/null +++ b/certificates/auth/models.go @@ -0,0 +1,62 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package auth + +import ( + "reflect" + + libmap "github.com/mitchellh/mapstructure" +) + +func ViperDecoderHook() libmap.DecodeHookFuncType { + return func(from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) { + var ( + z = ClientAuth(0) + t string + k bool + ) + + // Check if the data type matches the expected one + if from.Kind() != reflect.String { + return data, nil + } else if t, k = data.(string); !k { + return data, nil + } + + // Check if the target type matches the expected one + if to != reflect.TypeOf(z) { + return data, nil + } + + // Format/decode/parse the data and return the new value + if e := z.unmarshall([]byte(t)); e != nil { + return nil, e + } else { + return z, nil + } + } +} diff --git a/certificates/authClient.go b/certificates/authClient.go new file mode 100644 index 0000000..5a221ff --- /dev/null +++ b/certificates/authClient.go @@ -0,0 +1,86 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package certificates + +import ( + "crypto/x509" + + tlsaut "github.com/nabbar/golib/certificates/auth" + tlscas "github.com/nabbar/golib/certificates/ca" +) + +func (o *config) SetClientAuth(a tlsaut.ClientAuth) { + o.clientAuth = a +} + +func (o *config) GetClientCA() []tlscas.Cert { + var res = make([]tlscas.Cert, 0) + + for _, c := range o.clientCA { + res = append(res, c) + } + + return res +} + +func (o *config) GetClientCAPool() *x509.CertPool { + var res = x509.NewCertPool() + + for _, ca := range o.clientCA { + ca.AppendPool(res) + } + + return res +} + +func (o *config) AddClientCAString(ca string) bool { + if ca != "" { + if c, e := tlscas.Parse(ca); e == nil { + o.clientCA = append(o.clientCA, c) + return true + } + } + + return false +} + +func (o *config) AddClientCAFile(pemFile string) error { + var fct = func(p []byte) error { + if c, e := tlscas.ParseByte(p); e != nil { + return e + } else { + o.clientCA = append(o.clientCA, c) + return nil + } + } + + if e := checkFile(fct, pemFile); e != nil { + return e + } else { + return nil + } +} diff --git a/certificates/ca/encode.go b/certificates/ca/encode.go new file mode 100644 index 0000000..584df50 --- /dev/null +++ b/certificates/ca/encode.go @@ -0,0 +1,165 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package ca + +import ( + "bytes" + "crypto/x509" + "encoding/json" + "encoding/pem" + "os" + + "github.com/fxamacker/cbor/v2" + "github.com/pelletier/go-toml/v2" + "gopkg.in/yaml.v3" +) + +func isFile(path string) bool { + f, e := os.Stat(path) + if e != nil { + return false + } + return !f.IsDir() +} + +func (o *mod) unMarshall(p []byte) error { + if len(p) < 1 { + return ErrInvalidPairCertificate + } + + p = bytes.TrimSpace(p) + + // remove \n\r + p = bytes.Trim(p, "\n") + p = bytes.Trim(p, "\r") + + // do again if \r\n + p = bytes.Trim(p, "\n") + p = bytes.Trim(p, "\r") + + p = bytes.TrimSpace(p) + v := make([]*x509.Certificate, 0) + + for { + block, rest := pem.Decode(p) + if block == nil { + break + } + if block.Type == "CERTIFICATE" { + if c, e := x509.ParseCertificate(block.Bytes); e == nil { + v = append(v, c) + } + } else if fs := string(block.Bytes); isFile(fs) { + if b, e := os.ReadFile(fs); e == nil { + if crt, err := x509.ParseCertificate(b); err == nil { + v = append(v, crt) + } + } + } + + p = rest + } + + o.c = v + return nil +} + +func (o *mod) MarshalText() (text []byte, err error) { + if s, e := o.Chain(); e != nil { + return nil, e + } else { + return []byte(s), nil + } +} + +func (o *mod) UnmarshalText(text []byte) error { + return o.unMarshall(text) +} + +func (o *mod) MarshalBinary() (data []byte, err error) { + return o.MarshalCBOR() +} + +func (o *mod) UnmarshalBinary(data []byte) error { + return o.UnmarshalCBOR(data) +} + +func (o *mod) MarshalJSON() ([]byte, error) { + if s, e := o.Chain(); e != nil { + return nil, e + } else { + return json.Marshal(s) + } +} + +func (o *mod) UnmarshalJSON(bytes []byte) error { + return o.unMarshall(bytes) +} + +func (o *mod) MarshalYAML() (interface{}, error) { + if s, e := o.Chain(); e != nil { + return nil, e + } else { + return yaml.Marshal(s) + } +} + +func (o *mod) UnmarshalYAML(value *yaml.Node) error { + return o.unMarshall([]byte(value.Value)) +} + +func (o *mod) MarshalTOML() ([]byte, error) { + if s, e := o.Chain(); e != nil { + return nil, e + } else { + return toml.Marshal(s) + } +} + +func (o *mod) UnmarshalTOML(i interface{}) error { + if p, k := i.([]byte); k { + return o.unMarshall(p) + } + + if s, k := i.(string); k { + return o.unMarshall([]byte(s)) + } + + return ErrInvalidCertificate +} + +func (o *mod) MarshalCBOR() ([]byte, error) { + if s, e := o.Chain(); e != nil { + return nil, e + } else { + return cbor.Marshal(s) + } +} + +func (o *mod) UnmarshalCBOR(bytes []byte) error { + return o.unMarshall(bytes) +} diff --git a/certificates/ca/format.go b/certificates/ca/format.go new file mode 100644 index 0000000..5888217 --- /dev/null +++ b/certificates/ca/format.go @@ -0,0 +1,61 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package ca + +import ( + "bytes" + "crypto/x509" + "encoding/pem" +) + +func (o *mod) String() string { + s, _ := o.Chain() + return s +} + +func (o *mod) Chain() (string, error) { + var buf = bytes.NewBuffer(make([]byte, 0)) + + for _, c := range o.c { + if c == nil { + continue + } else if e := pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: c.Raw}); e != nil { + return "", e + } + } + + return buf.String(), nil +} + +func (o *mod) AppendPool(p *x509.CertPool) { + for _, c := range o.c { + if c == nil { + continue + } + p.AddCert(c) + } +} diff --git a/certificates/ca/interface.go b/certificates/ca/interface.go new file mode 100644 index 0000000..8b4d8a6 --- /dev/null +++ b/certificates/ca/interface.go @@ -0,0 +1,78 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package ca + +import ( + "crypto/x509" + "encoding" + "encoding/json" + "errors" + "fmt" + + "github.com/fxamacker/cbor/v2" + "github.com/pelletier/go-toml" + "gopkg.in/yaml.v3" +) + +var ( + ErrInvalidPairCertificate = errors.New("invalid pair certificate") + ErrInvalidCertificate = errors.New("invalid certificate") +) + +type Cert interface { + encoding.TextMarshaler + encoding.TextUnmarshaler + encoding.BinaryMarshaler + encoding.BinaryUnmarshaler + json.Marshaler + json.Unmarshaler + yaml.Marshaler + yaml.Unmarshaler + toml.Marshaler + toml.Unmarshaler + cbor.Marshaler + cbor.Unmarshaler + fmt.Stringer + + AppendPool(p *x509.CertPool) +} + +func Parse(str string) (Cert, error) { + return ParseByte([]byte(str)) +} + +func ParseByte(p []byte) (Cert, error) { + c := &mod{ + c: make([]*x509.Certificate, 0), + } + + if e := c.unMarshall(p); e != nil { + return nil, e + } + + return c, nil +} diff --git a/certificates/ca/models.go b/certificates/ca/models.go new file mode 100644 index 0000000..eb09850 --- /dev/null +++ b/certificates/ca/models.go @@ -0,0 +1,69 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package ca + +import ( + "crypto/x509" + "reflect" + + libmap "github.com/mitchellh/mapstructure" +) + +type mod struct { + c []*x509.Certificate +} + +func ViperDecoderHook() libmap.DecodeHookFuncType { + return func(from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) { + var ( + z = &mod{ + c: make([]*x509.Certificate, 0), + } + t string + k bool + ) + + // Check if the data type matches the expected one + if from.Kind() != reflect.String { + return data, nil + } else if t, k = data.(string); !k { + return data, nil + } + + // Check if the target type matches the expected one + if to != reflect.TypeOf(z) { + return data, nil + } + + // Format/decode/parse the data and return the new value + if e := z.unMarshall([]byte(t)); e != nil { + return nil, e + } else { + return z, nil + } + } +} diff --git a/certificates/cert.go b/certificates/cert.go new file mode 100644 index 0000000..adbf6c4 --- /dev/null +++ b/certificates/cert.go @@ -0,0 +1,84 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package certificates + +import ( + "crypto/tls" + + tlscrt "github.com/nabbar/golib/certificates/certs" +) + +func (o *config) LenCertificatePair() int { + return len(o.cert) +} + +func (o *config) CleanCertificatePair() { + o.cert = make([]tlscrt.Cert, 0) +} + +func (o *config) GetCertificatePair() []tls.Certificate { + var res = make([]tls.Certificate, 0) + + for _, c := range o.cert { + res = append(res, c.TLS()) + } + + return res +} + +func (o *config) AddCertificatePairString(key, crt string) error { + if c, e := tlscrt.ParsePair(key, crt); e != nil { + return e + } else { + o.cert = append(o.cert, c) + return nil + } +} + +func (o *config) AddCertificatePairFile(keyFile, crtFile string) error { + var ( + key = make([]byte, 0) + pub = make([]byte, 0) + fct = func(p []byte) error { + if len(key) < 1 { + copy(key, p) + } else { + copy(pub, p) + } + return nil + } + ) + + if e := checkFile(fct, keyFile, crtFile); e != nil { + return e + } else if c, e := tlscrt.ParsePair(string(key), string(pub)); e != nil { + return e + } else { + o.cert = append(o.cert, c) + return nil + } +} diff --git a/certificates/certificates_config_test.go b/certificates/certificates_config_test.go new file mode 100644 index 0000000..9ef3e84 --- /dev/null +++ b/certificates/certificates_config_test.go @@ -0,0 +1,214 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package certificates_test + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/json" + "encoding/pem" + "math/big" + "net" + "os" + "time" + + libtls "github.com/nabbar/golib/certificates" + tlscrt "github.com/nabbar/golib/certificates/certs" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func genCertififcate() ([]byte, []byte) { + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + Expect(err).ToNot(HaveOccurred()) + Expect(priv).ToNot(BeNil()) + + keyUsage := x509.KeyUsageDigitalSignature + notBefore := time.Now() + notAfter := notBefore.Add(time.Hour * 24 * 365) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + Expect(err).ToNot(HaveOccurred()) + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + KeyUsage: keyUsage, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + template.DNSNames = append(template.DNSNames, "example.com") + template.DNSNames = append(template.DNSNames, "localhost") + + if ip := net.ParseIP("127.0.0.1"); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + Expect(err).ToNot(HaveOccurred()) + + bufPub := bytes.NewBuffer(make([]byte, 0)) + err = pem.Encode(bufPub, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + Expect(err).ToNot(HaveOccurred()) + + bufKey := bytes.NewBuffer(make([]byte, 0)) + privBytes, err := x509.MarshalPKCS8PrivateKey(priv) + Expect(err).ToNot(HaveOccurred()) + + err = pem.Encode(bufKey, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}) + Expect(err).ToNot(HaveOccurred()) + + return bufPub.Bytes(), bufKey.Bytes() +} + +func writeGenCert(pub, key string) { + p, k := genCertififcate() + + f, e := os.Create(pub) + Expect(e).ToNot(HaveOccurred()) + _, e = f.Write(p) + Expect(e).ToNot(HaveOccurred()) + Expect(f.Close()).ToNot(HaveOccurred()) + + f, e = os.Create(key) + Expect(e).ToNot(HaveOccurred()) + _, e = f.Write(k) + Expect(e).ToNot(HaveOccurred()) + Expect(f.Close()).ToNot(HaveOccurred()) +} + +func getConfigFile() []byte { + writeGenCert(pubFile, keyFile) + + str := &tlscrt.ConfigPair{ + Key: keyFile, + Pub: pubFile, + } + + p, e := json.Marshal(str) + Expect(e).ToNot(HaveOccurred()) + + var oldConfig string = `{ + "authClient": "none", + "certs": [` + string(p) + `], + "cipherList": ["RSA_AES_128_GCM_SHA256", "RSA_AES_256_GCM_SHA384", "ECDHE_RSA_AES_128_GCM_SHA256", "ECDHE_ECDSA_AES_128_GCM_SHA256", "ECDHE_RSA_AES_256_GCM_SHA384", "ECDHE_ECDSA_AES_256_GCM_SHA384", "ECDHE_RSA_CHACHA20_POLY1305_SHA256", "ECDHE_ECDSA_CHACHA20_POLY1305_SHA256", "AES_128_GCM_SHA256", "AES_256_GCM_SHA384", "CHACHA20_POLY1305_SHA256"], + "clientCA": [], + "curveList": ["X25519", "P256", "P384", "P521"], + "dynamicSizingDisable": false, + "inheritDefault": false, + "rootCA": [], + "sessionTicketDisable": false, + "versionMax": "1.3", + "versionMin": "1.2" + }` + + return []byte(oldConfig) +} + +func getConfigChain() []byte { + pub, key := genCertififcate() + str := "\n" + string(pub) + string(key) + buf, err := json.Marshal(str) + Expect(err).ToNot(HaveOccurred()) + + var oldConfig string = `{ + "authClient": "none", + "certs": [` + string(buf) + `], + "cipherList": ["RSA_AES_128_GCM_SHA256", "RSA_AES_256_GCM_SHA384", "ECDHE_RSA_AES_128_GCM_SHA256", "ECDHE_ECDSA_AES_128_GCM_SHA256", "ECDHE_RSA_AES_256_GCM_SHA384", "ECDHE_ECDSA_AES_256_GCM_SHA384", "ECDHE_RSA_CHACHA20_POLY1305_SHA256", "ECDHE_ECDSA_CHACHA20_POLY1305_SHA256", "AES_128_GCM_SHA256", "AES_256_GCM_SHA384", "CHACHA20_POLY1305_SHA256"], + "clientCA": [], + "curveList": ["X25519", "P256", "P384", "P521"], + "dynamicSizingDisable": false, + "inheritDefault": false, + "rootCA": [], + "sessionTicketDisable": false, + "versionMax": "1.3", + "versionMin": "1.2" + }` + + return []byte(oldConfig) +} + +var _ = Describe("certificates test", func() { + + Context("Using a config json", func() { + It("must success to new Config when certificates are paste as file mode", func() { + cfg := libtls.Config{} + + p := getConfigFile() + Expect(len(p)).To(BeNumerically(">", 0)) + + e := json.Unmarshal(p, &cfg) + Expect(e).ToNot(HaveOccurred()) + + cnf := cfg.New() + Expect(cnf).ToNot(BeNil()) + Expect(len(cnf.GetCertificatePair())).To(Equal(1)) + + cfgtls := cnf.TLS("localhost") + Expect(cfgtls).ToNot(BeNil()) + Expect(len(cfgtls.Certificates)).To(Equal(1)) + Expect(len(cfgtls.CipherSuites)).To(BeNumerically(">", 0)) + Expect(len(cfgtls.CurvePreferences)).To(BeNumerically(">", 0)) + + p, e = json.Marshal(cnf.Config()) + Expect(e).ToNot(HaveOccurred()) + Expect(len(p)).To(BeNumerically(">", 0)) + }) + It("must success to new Config when certificates are paste as chain mode", func() { + cfg := libtls.Config{} + + p := getConfigChain() + Expect(len(p)).To(BeNumerically(">", 0)) + + e := json.Unmarshal(p, &cfg) + Expect(e).ToNot(HaveOccurred()) + + cnf := cfg.New() + Expect(cnf).ToNot(BeNil()) + Expect(len(cnf.GetCertificatePair())).To(Equal(1)) + + cfgtls := cnf.TLS("localhost") + Expect(cfgtls).ToNot(BeNil()) + Expect(len(cfgtls.Certificates)).To(Equal(1)) + Expect(len(cfgtls.CipherSuites)).To(BeNumerically(">", 0)) + Expect(len(cfgtls.CurvePreferences)).To(BeNumerically(">", 0)) + + p, e = json.Marshal(cnf) + Expect(e).ToNot(HaveOccurred()) + Expect(len(p)).To(BeNumerically(">", 0)) + }) + }) +}) diff --git a/certificates/certificates_suite_test.go b/certificates/certificates_suite_test.go new file mode 100644 index 0000000..8c383d4 --- /dev/null +++ b/certificates/certificates_suite_test.go @@ -0,0 +1,75 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package certificates_test + +import ( + "os" + "path/filepath" + "reflect" + "strings" + "testing" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +/* + Using https://onsi.github.io/ginkgo/ + Running with $> ginkgo -cover . +*/ + +type EmptyStruct struct{} + +var ( + keyFile string + pubFile string +) + +// TestGolibEncodingAESHelper tests the Golib AES Encoding Helper function. +func TestGolibArchiveHelper(t *testing.T) { + time.Sleep(500 * time.Millisecond) // Adding delay for better testing synchronization + RegisterFailHandler(Fail) // Registering fail handler for better test failure reporting + RunSpecs(t, "Certificates Helper Suite") // Running the test suite for Encoding AES Helper +} + +var _ = BeforeSuite(func() { + keyFile = filepath.Join(os.Getenv("GOPATH"), "src", strings.Replace(reflect.TypeOf(EmptyStruct{}).PkgPath(), "_test", "", -1), "test_ed25519.key") + pubFile = filepath.Join(os.Getenv("GOPATH"), "src", strings.Replace(reflect.TypeOf(EmptyStruct{}).PkgPath(), "_test", "", -1), "test_ed25519.pub") +}) + +var _ = AfterSuite(func() { + if keyFile != "" { + if _, e := os.Stat(keyFile); e == nil { + Expect(os.Remove(keyFile)).ToNot(HaveOccurred()) + } + } + if pubFile != "" { + if _, e := os.Stat(pubFile); e == nil { + Expect(os.Remove(pubFile)).ToNot(HaveOccurred()) + } + } +}) diff --git a/certificates/certs/config.go b/certificates/certs/config.go new file mode 100644 index 0000000..fba6409 --- /dev/null +++ b/certificates/certs/config.go @@ -0,0 +1,165 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package certs + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "errors" + "os" + "strings" +) + +var ( + ErrInvalidPairCertificate = errors.New("invalid pair certificate") + ErrInvalidCertificate = errors.New("invalid certificate") + ErrInvalidPrivateKey = errors.New("invalid private key") +) + +func cleanPem(s string) string { + s = strings.TrimSpace(s) + + // remove \n\r + s = strings.Trim(s, "\n") + s = strings.Trim(s, "\r") + + // do again if \r\n + s = strings.Trim(s, "\n") + s = strings.Trim(s, "\r") + + return strings.TrimSpace(s) +} + +type Config interface { + Cert() (*tls.Certificate, error) +} + +type ConfigPair struct { + Key string `mapstructure:"key" json:"key" yaml:"key" toml:"key"` + Pub string `mapstructure:"pub" json:"pub" yaml:"pub" toml:"pub"` +} + +func (c *ConfigPair) Cert() (*tls.Certificate, error) { + c.Key = cleanPem(c.Key) + c.Pub = cleanPem(c.Pub) + + if c == nil { + return nil, ErrInvalidPairCertificate + } else if len(c.Key) < 1 || len(c.Pub) < 1 { + return nil, ErrInvalidPairCertificate + } + + if _, e := os.Stat(c.Key); e == nil { + if b, e := os.ReadFile(c.Key); e == nil { + c.Key = cleanPem(string(b)) + } + } + + if _, e := os.Stat(c.Pub); e == nil { + if b, e := os.ReadFile(c.Pub); e == nil { + c.Pub = cleanPem(string(b)) + } + } + + if crt, err := tls.X509KeyPair([]byte(c.Pub), []byte(c.Key)); err != nil { + return nil, err + } else { + return &crt, nil + } +} + +type ConfigChain string + +func (c *ConfigChain) Cert() (*tls.Certificate, error) { + var ( + err error + crt tls.Certificate + ) + + if c == nil { + return nil, ErrInvalidPairCertificate + } else if len(*c) < 1 { + return nil, ErrInvalidPairCertificate + } + + s := string(*c) + + if _, e := os.Stat(s); e == nil { + if b, e := os.ReadFile(s); e == nil { + s = cleanPem(string(b)) + } + } + + p := []byte(cleanPem(s)) + + for { + block, rest := pem.Decode(p) + if block == nil { + break + } + if block.Type == "CERTIFICATE" { + crt.Certificate = append(crt.Certificate, block.Bytes) + } else { + crt.PrivateKey, err = c.getPrivateKey(block.Bytes) + if err != nil { + return nil, err + } + } + + p = rest + } + + if len(crt.Certificate) == 0 { + return nil, ErrInvalidCertificate + } else if crt.PrivateKey == nil { + return nil, ErrInvalidCertificate + } + + return &crt, nil +} + +func (c *ConfigChain) getPrivateKey(der []byte) (crypto.PrivateKey, error) { + if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { + return key, nil + } + if key, err := x509.ParsePKCS8PrivateKey(der); err == nil { + switch k := key.(type) { + case *rsa.PrivateKey, *ecdsa.PrivateKey: + return k, nil + default: + return nil, ErrInvalidPrivateKey + } + } + if key, err := x509.ParseECPrivateKey(der); err == nil { + return key, nil + } + return nil, ErrInvalidPrivateKey +} diff --git a/certificates/certs/encode.go b/certificates/certs/encode.go new file mode 100644 index 0000000..dc4ddce --- /dev/null +++ b/certificates/certs/encode.go @@ -0,0 +1,244 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package certs + +import ( + "crypto/tls" + "encoding/json" + + "github.com/fxamacker/cbor/v2" + "github.com/pelletier/go-toml/v2" + "gopkg.in/yaml.v3" +) + +func (o *Certif) unMarshall(p []byte) error { + if o.UnmarshalJSON(p) == nil { + return nil + } else if o.UnmarshalYAML(&yaml.Node{Value: string(p)}) != nil { + return nil + } else if o.UnmarshalTOML(p) != nil { + return nil + } else if o.UnmarshalCBOR(p) != nil { + return nil + } else if o.UnmarshalText(p) != nil { + return nil + } + + return ErrInvalidCertificate +} + +func (o *Certif) MarshalText() (text []byte, err error) { + return []byte(o.String()), err +} + +func (o *Certif) UnmarshalText(text []byte) error { + var ( + chn = ConfigChain(text) + crt *tls.Certificate + err error + ) + + if crt, err = chn.Cert(); err != nil { + return err + } else if crt == nil || len(crt.Certificate) == 0 { + return ErrInvalidPairCertificate + } else { + o.c = *crt + return nil + } +} + +func (o *Certif) MarshalBinary() (data []byte, err error) { + return o.MarshalCBOR() +} + +func (o *Certif) UnmarshalBinary(data []byte) error { + return o.UnmarshalCBOR(data) +} + +func (o *Certif) MarshalJSON() ([]byte, error) { + t := o.String() + return json.Marshal(t) +} + +func (o *Certif) UnmarshalJSON(bytes []byte) error { + var ( + cfg ConfigPair + chn ConfigChain + crt *tls.Certificate + err error + ) + + if err = json.Unmarshal(bytes, &cfg); err == nil { + if crt, err = cfg.Cert(); err != nil { + return err + } else if crt == nil || len(crt.Certificate) == 0 { + return ErrInvalidPairCertificate + } else { + o.c = *crt + return nil + } + } else if err = json.Unmarshal(bytes, &chn); err == nil { + if crt, err = chn.Cert(); err != nil { + return err + } else if crt == nil || len(crt.Certificate) == 0 { + return ErrInvalidPairCertificate + } else { + o.c = *crt + return nil + } + } + + return ErrInvalidCertificate +} + +func (o *Certif) MarshalYAML() (interface{}, error) { + t := o.String() + return yaml.Marshal(t) +} + +func (o *Certif) UnmarshalYAML(value *yaml.Node) error { + var ( + src = []byte(value.Value) + cfg ConfigPair + chn ConfigChain + crt *tls.Certificate + err error + ) + + if err = yaml.Unmarshal(src, &cfg); err == nil { + if crt, err = cfg.Cert(); err != nil { + return err + } else if crt == nil || len(crt.Certificate) == 0 { + return ErrInvalidPairCertificate + } else { + o.c = *crt + return nil + } + } else if err = yaml.Unmarshal(src, &chn); err == nil { + if crt, err = chn.Cert(); err != nil { + return err + } else if crt == nil || len(crt.Certificate) == 0 { + return ErrInvalidPairCertificate + } else { + o.c = *crt + return nil + } + } + + return ErrInvalidCertificate +} + +func (o *Certif) MarshalTOML() ([]byte, error) { + t := o.String() + return toml.Marshal(t) +} + +func (o *Certif) UnmarshalTOML(i interface{}) error { + var ( + p []byte + s string + k bool + ) + + if p, k = i.([]byte); !k { + if s, k = i.(string); k { + p = []byte(s) + } else { + return ErrInvalidCertificate + } + } + + if len(p) < 1 { + return ErrInvalidCertificate + } + + var ( + cfg ConfigPair + chn ConfigChain + crt *tls.Certificate + err error + ) + + if err = toml.Unmarshal(p, &cfg); err == nil { + if crt, err = cfg.Cert(); err != nil { + return err + } else if crt == nil || len(crt.Certificate) == 0 { + return ErrInvalidPairCertificate + } else { + o.c = *crt + return nil + } + } else if err = toml.Unmarshal(p, &chn); err == nil { + if crt, err = chn.Cert(); err != nil { + return err + } else if crt == nil || len(crt.Certificate) == 0 { + return ErrInvalidPairCertificate + } else { + o.c = *crt + return nil + } + } + + return ErrInvalidCertificate +} + +func (o *Certif) MarshalCBOR() ([]byte, error) { + t := o.String() + return cbor.Marshal(t) +} + +func (o *Certif) UnmarshalCBOR(bytes []byte) error { + var ( + cfg ConfigPair + chn ConfigChain + crt *tls.Certificate + err error + ) + + if err = cbor.Unmarshal(bytes, &cfg); err == nil { + if crt, err = cfg.Cert(); err != nil { + return err + } else if crt == nil || len(crt.Certificate) == 0 { + return ErrInvalidPairCertificate + } else { + o.c = *crt + return nil + } + } else if err = cbor.Unmarshal(bytes, &chn); err == nil { + if crt, err = chn.Cert(); err != nil { + return err + } else if crt == nil || len(crt.Certificate) == 0 { + return ErrInvalidPairCertificate + } else { + o.c = *crt + return nil + } + } + + return ErrInvalidCertificate +} diff --git a/certificates/certs/format.go b/certificates/certs/format.go new file mode 100644 index 0000000..031acb3 --- /dev/null +++ b/certificates/certs/format.go @@ -0,0 +1,86 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package certs + +import ( + "bytes" + "crypto/tls" + "crypto/x509" + "encoding/pem" +) + +func (o *Certif) String() string { + str, _ := o.Chain() + return str +} + +func (o *Certif) Pair() (pub string, key string, err error) { + var ( + bufPub = bytes.NewBuffer(make([]byte, 0)) + bufKey = bytes.NewBuffer(make([]byte, 0)) + ) + + for _, certDER := range o.c.Certificate { + block := &pem.Block{ + Type: "CERTIFICATE", + Bytes: certDER, + } + if err = pem.Encode(bufPub, block); err != nil { + return "", "", err + } + } + + // Afficher la clé privée si disponible + if o.c.PrivateKey != nil { + var p []byte + if p, err = x509.MarshalPKCS8PrivateKey(o.c.PrivateKey); err != nil { + return "", "", err + } else { + privateKeyBlock := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: p, + } + if err = pem.Encode(bufKey, privateKeyBlock); err != nil { + return "", "", err + } + } + } + + return bufPub.String(), bufKey.String(), nil +} + +func (o *Certif) Chain() (string, error) { + if pub, key, err := o.Pair(); err != nil { + return "", err + } else { + return pub + key, nil + } +} + +func (o *Certif) TLS() tls.Certificate { + return o.c +} diff --git a/certificates/certs/interface.go b/certificates/certs/interface.go new file mode 100644 index 0000000..d887e37 --- /dev/null +++ b/certificates/certs/interface.go @@ -0,0 +1,76 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package certs + +import ( + "crypto/tls" + "encoding" + "encoding/json" + "fmt" + + "github.com/fxamacker/cbor/v2" + "github.com/pelletier/go-toml" + "gopkg.in/yaml.v3" +) + +type Cert interface { + encoding.TextMarshaler + encoding.TextUnmarshaler + encoding.BinaryMarshaler + encoding.BinaryUnmarshaler + json.Marshaler + json.Unmarshaler + yaml.Marshaler + yaml.Unmarshaler + toml.Marshaler + toml.Unmarshaler + cbor.Marshaler + cbor.Unmarshaler + fmt.Stringer + + TLS() tls.Certificate + Model() Certif +} + +func Parse(chain string) (Cert, error) { + c := ConfigChain(chain) + return parseCert(&c) +} + +func ParsePair(key, pub string) (Cert, error) { + return parseCert(&ConfigPair{Key: key, Pub: pub}) +} + +func parseCert(cfg Config) (Cert, error) { + if c, e := cfg.Cert(); e != nil { + return nil, e + } else if c == nil { + return nil, ErrInvalidPairCertificate + } else { + return &Certif{c: *c}, nil + } +} diff --git a/certificates/certs/models.go b/certificates/certs/models.go new file mode 100644 index 0000000..357f65a --- /dev/null +++ b/certificates/certs/models.go @@ -0,0 +1,75 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package certs + +import ( + "crypto/tls" + "reflect" + + libmap "github.com/mitchellh/mapstructure" +) + +type Certif struct { + c tls.Certificate +} + +func (o *Certif) Cert() Cert { + return o +} + +func (o *Certif) Model() Certif { + return *o +} + +func ViperDecoderHook() libmap.DecodeHookFuncType { + return func(from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) { + var ( + z = &Certif{c: tls.Certificate{}} + t string + k bool + ) + + // Check if the data type matches the expected one + if from.Kind() != reflect.String { + return data, nil + } else if t, k = data.(string); !k { + return data, nil + } + + // Check if the target type matches the expected one + if to != reflect.TypeOf(z) { + return data, nil + } + + // Format/decode/parse the data and return the new value + if e := z.unMarshall([]byte(t)); e != nil { + return nil, e + } else { + return z, nil + } + } +} diff --git a/certificates/cipher/encode.go b/certificates/cipher/encode.go new file mode 100644 index 0000000..c4c0cf1 --- /dev/null +++ b/certificates/cipher/encode.go @@ -0,0 +1,89 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package curves + +import ( + "fmt" + + "gopkg.in/yaml.v3" +) + +func (v *Cipher) unmarshall(val []byte) error { + *v = parseBytes(val) + return nil +} + +func (v Cipher) MarshalJSON() ([]byte, error) { + t := v.String() + b := make([]byte, 0, len(t)+2) + b = append(b, '"') + b = append(b, []byte(t)...) + b = append(b, '"') + return b, nil +} + +func (v *Cipher) UnmarshalJSON(bytes []byte) error { + return v.unmarshall(bytes) +} + +func (v Cipher) MarshalYAML() (interface{}, error) { + return []byte(v.String()), nil +} + +func (v *Cipher) UnmarshalYAML(value *yaml.Node) error { + return v.unmarshall([]byte(value.Value)) +} + +func (v Cipher) MarshalTOML() ([]byte, error) { + return []byte(v.String()), nil +} + +func (v *Cipher) UnmarshalTOML(i interface{}) error { + if p, k := i.([]byte); k { + return v.unmarshall(p) + } + if p, k := i.(string); k { + return v.unmarshall([]byte(p)) + } + return fmt.Errorf("cipher: value not in valid format") +} + +func (v Cipher) MarshalText() ([]byte, error) { + return []byte(v.String()), nil +} + +func (v *Cipher) UnmarshalText(bytes []byte) error { + return v.unmarshall(bytes) +} + +func (v Cipher) MarshalCBOR() ([]byte, error) { + return []byte(v.String()), nil +} + +func (v *Cipher) UnmarshalCBOR(bytes []byte) error { + return v.unmarshall(bytes) +} diff --git a/certificates/cipher/format.go b/certificates/cipher/format.go new file mode 100644 index 0000000..a13dac8 --- /dev/null +++ b/certificates/cipher/format.go @@ -0,0 +1,100 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package curves + +import ( + "strings" +) + +func (v Cipher) String() string { + return strings.Join(v.Code(), "_") +} + +func (v Cipher) Code() []string { + switch v { + case TLS_RSA_WITH_AES_128_GCM_SHA256: + return []string{"rsa", "aes", "128", "gcm", "sha256"} + case TLS_RSA_WITH_AES_256_GCM_SHA384: + return []string{"rsa", "aes", "256", "gcm", "sha384"} + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + return []string{"ecdhe", "rsa", "aes", "128", "gcm", "sha256"} + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + return []string{"ecdhe", "ecdsa", "aes", "128", "gcm", "sha256"} + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + return []string{"ecdhe", "rsa", "aes", "256", "gcm", "sha384"} + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + return []string{"ecdhe", "ecdsa", "aes", "256", "gcm", "sha384"} + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + return []string{"ecdhe", "rsa", "chacha20", "poly1305", "sha256"} + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + return []string{"ecdhe", "ecdsa", "chacha20", "poly1305", "sha256"} + case TLS_AES_128_GCM_SHA256: + return []string{"aes", "128", "gcm", "sha256"} + case TLS_AES_256_GCM_SHA384: + return []string{"aes", "256", "gcm", "sha384"} + case TLS_CHACHA20_POLY1305_SHA256: + return []string{"chacha20", "poly1305", "sha256"} + default: + return []string{} + } +} + +func (v Cipher) Cipher() uint16 { + return uint16(v) +} + +func (v Cipher) TLS() uint16 { + return v.Cipher() +} + +func (v Cipher) Uint16() uint16 { + return v.Cipher() +} + +func (v Cipher) Uint() uint { + return uint(v.Cipher()) +} + +func (v Cipher) Uint32() uint32 { + return uint32(v.Cipher()) +} + +func (v Cipher) Uint64() uint64 { + return uint64(v.Cipher()) +} + +func (v Cipher) Int() int { + return int(v.Cipher()) +} + +func (v Cipher) Int32() int32 { + return int32(v.Cipher()) +} + +func (v Cipher) Int64() int64 { + return int64(v.Cipher()) +} diff --git a/certificates/cipher/interface.go b/certificates/cipher/interface.go new file mode 100644 index 0000000..584ab31 --- /dev/null +++ b/certificates/cipher/interface.go @@ -0,0 +1,199 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package curves + +import ( + "crypto/tls" + "math" + "slices" + "strings" +) + +type Cipher uint16 + +const ( + Unknown Cipher = Cipher(0) + + // TLS 1.0 - 1.2 cipher suites. + TLS_RSA_WITH_AES_128_GCM_SHA256 = Cipher(tls.TLS_RSA_WITH_AES_128_GCM_SHA256) + TLS_RSA_WITH_AES_256_GCM_SHA384 = Cipher(tls.TLS_RSA_WITH_AES_256_GCM_SHA384) + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = Cipher(tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = Cipher(tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = Cipher(tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = Cipher(tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = Cipher(tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = Cipher(tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) + + // TLS 1.3 cipher suites. + TLS_AES_128_GCM_SHA256 = Cipher(tls.TLS_AES_128_GCM_SHA256) + TLS_AES_256_GCM_SHA384 = Cipher(tls.TLS_AES_256_GCM_SHA384) + TLS_CHACHA20_POLY1305_SHA256 = Cipher(tls.TLS_CHACHA20_POLY1305_SHA256) +) + +func List() []Cipher { + return []Cipher{ + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_CHACHA20_POLY1305_SHA256, + } +} + +func ListString() []string { + var res = make([]string, 0) + for _, c := range List() { + res = append(res, c.String()) + } + return res +} + +func Parse(s string) Cipher { + s = strings.ToLower(s) + s = strings.Replace(s, "\"", "", -1) + s = strings.Replace(s, "'", "", -1) + s = strings.Replace(s, "tls", "", -1) + s = strings.Replace(s, ".", "_", -1) + s = strings.Replace(s, "-", "_", -1) + s = strings.Replace(s, " ", "_", -1) + s = strings.TrimSpace(s) + + p := strings.Split(s, "_") + + switch { + case containString(p, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256.Code()): + return TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + case containString(p, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.Code()): + return TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + case containString(p, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384.Code()): + return TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + case containString(p, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384.Code()): + return TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + case containString(p, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256.Code()): + return TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + case containString(p, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256.Code()): + return TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + case containString(p, TLS_CHACHA20_POLY1305_SHA256.Code()): + return TLS_CHACHA20_POLY1305_SHA256 + case containString(p, TLS_RSA_WITH_AES_128_GCM_SHA256.Code()): + return TLS_RSA_WITH_AES_128_GCM_SHA256 + case containString(p, TLS_RSA_WITH_AES_256_GCM_SHA384.Code()): + return TLS_RSA_WITH_AES_256_GCM_SHA384 + case containString(p, TLS_AES_128_GCM_SHA256.Code()): + return TLS_AES_128_GCM_SHA256 + case containString(p, TLS_AES_256_GCM_SHA384.Code()): + return TLS_AES_256_GCM_SHA384 + default: + return Unknown + } +} + +func containString[S ~[]string](s S, v S) bool { + keys := []string{ + "chacha20", + "poly1305", + "ecdhe", + "rsa", + "ecdsa", + "aes", + "128", + "256", + "sha256", + "sha384", + "gcm", + } + + for _, k := range keys { + if !keyContainString(s, v, k) { + return false + } + } + + return true +} + +func keyContainString[S ~[]string](s S, v S, k string) bool { + if slices.Contains(s, k) && !slices.Contains(v, k) { + return false + } else if !slices.Contains(s, k) && slices.Contains(v, k) { + return false + } + + return true +} + +func ParseInt(d int) Cipher { + if d > math.MaxUint16 { + d = math.MaxUint16 + } else if d < 1 { + d = 0 + } + + switch uint16(d) { + case tls.TLS_RSA_WITH_AES_128_GCM_SHA256: + return TLS_RSA_WITH_AES_128_GCM_SHA256 + case tls.TLS_RSA_WITH_AES_256_GCM_SHA384: + return TLS_RSA_WITH_AES_256_GCM_SHA384 + case tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + return TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + case tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + return TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + case tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + return TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + case tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + return TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + case tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + return TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + case tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + return TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + case tls.TLS_AES_128_GCM_SHA256: + return TLS_AES_128_GCM_SHA256 + case tls.TLS_AES_256_GCM_SHA384: + return TLS_AES_256_GCM_SHA384 + case tls.TLS_CHACHA20_POLY1305_SHA256: + return TLS_CHACHA20_POLY1305_SHA256 + default: + return Unknown + } +} + +func Check(cipher uint16) bool { + if c := ParseInt(int(cipher)); c == Unknown { + return false + } + return true +} + +func parseBytes(p []byte) Cipher { + return Parse(string(p)) +} diff --git a/certificates/cipher/models.go b/certificates/cipher/models.go new file mode 100644 index 0000000..efc3cf5 --- /dev/null +++ b/certificates/cipher/models.go @@ -0,0 +1,62 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package curves + +import ( + "reflect" + + libmap "github.com/mitchellh/mapstructure" +) + +func ViperDecoderHook() libmap.DecodeHookFuncType { + return func(from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) { + var ( + z = Cipher(0) + t string + k bool + ) + + // Check if the data type matches the expected one + if from.Kind() != reflect.String { + return data, nil + } else if t, k = data.(string); !k { + return data, nil + } + + // Check if the target type matches the expected one + if to != reflect.TypeOf(z) { + return data, nil + } + + // Format/decode/parse the data and return the new value + if e := z.unmarshall([]byte(t)); e != nil { + return nil, e + } else { + return z, nil + } + } +} diff --git a/certificates/ciphers.go b/certificates/ciphers.go new file mode 100644 index 0000000..fd44641 --- /dev/null +++ b/certificates/ciphers.go @@ -0,0 +1,52 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package certificates + +import tlscpr "github.com/nabbar/golib/certificates/cipher" + +func (o *config) SetCipherList(c []tlscpr.Cipher) { + o.cipherList = make([]tlscpr.Cipher, 0) + o.AddCiphers(c...) +} + +func (o *config) AddCiphers(c ...tlscpr.Cipher) { + for _, i := range c { + o.cipherList = append(o.cipherList, i) + } +} + +func (o *config) GetCiphers() []tlscpr.Cipher { + var res = make([]tlscpr.Cipher, 0) + + for _, i := range o.cipherList { + if tlscpr.Check(i.Uint16()) { + res = append(res, i) + } + } + + return res +} diff --git a/certificates/config.go b/certificates/config.go index 25cf069..c7f2811 100644 --- a/certificates/config.go +++ b/certificates/config.go @@ -30,29 +30,27 @@ import ( "fmt" libval "github.com/go-playground/validator/v10" + tlsaut "github.com/nabbar/golib/certificates/auth" + tlscas "github.com/nabbar/golib/certificates/ca" + tlscrt "github.com/nabbar/golib/certificates/certs" + tlscpr "github.com/nabbar/golib/certificates/cipher" + tlscrv "github.com/nabbar/golib/certificates/curves" + tlsvrs "github.com/nabbar/golib/certificates/tlsversion" liberr "github.com/nabbar/golib/errors" ) -type Certif struct { - Key string `mapstructure:"key" json:"key" yaml:"key" toml:"key"` - Pem string `mapstructure:"pem" json:"pem" yaml:"pem" toml:"pem"` -} - type Config struct { - CurveList []string `mapstructure:"curveList" json:"curveList" yaml:"curveList" toml:"curveList"` - CipherList []string `mapstructure:"cipherList" json:"cipherList" yaml:"cipherList" toml:"cipherList"` - RootCAString []string `mapstructure:"rootCA" json:"rootCA" yaml:"rootCA" toml:"rootCA"` - RootCAFile []string `mapstructure:"rootCAFiles" json:"rootCAFiles" yaml:"rootCAFiles" toml:"rootCAFiles"` - ClientCAString []string `mapstructure:"clientCA" json:"clientCA" yaml:"clientCA" toml:"clientCA"` - ClientCAFiles []string `mapstructure:"clientCAFiles" json:"clientCAFiles" yaml:"clientCAFiles" toml:"clientCAFiles"` - CertPairString []Certif `mapstructure:"certPair" json:"certPair" yaml:"certPair" toml:"certPair"` - CertPairFile []Certif `mapstructure:"certPairFiles" json:"certPairFiles" yaml:"certPairFiles" toml:"certPairFiles"` - VersionMin string `mapstructure:"versionMin" json:"versionMin" yaml:"versionMin" toml:"versionMin"` - VersionMax string `mapstructure:"versionMax" json:"versionMax" yaml:"versionMax" toml:"versionMax"` - AuthClient string `mapstructure:"authClient" json:"authClient" yaml:"authClient" toml:"authClient"` - InheritDefault bool `mapstructure:"inheritDefault" json:"inheritDefault" yaml:"inheritDefault" toml:"inheritDefault"` - DynamicSizingDisable bool `mapstructure:"dynamicSizingDisable" json:"dynamicSizingDisable" yaml:"dynamicSizingDisable" toml:"dynamicSizingDisable"` - SessionTicketDisable bool `mapstructure:"sessionTicketDisable" json:"sessionTicketDisable" yaml:"sessionTicketDisable" toml:"sessionTicketDisable"` + CurveList []tlscrv.Curves `mapstructure:"curveList" json:"curveList" yaml:"curveList" toml:"curveList"` + CipherList []tlscpr.Cipher `mapstructure:"cipherList" json:"cipherList" yaml:"cipherList" toml:"cipherList"` + RootCA []tlscas.Cert `mapstructure:"rootCA" json:"rootCA" yaml:"rootCA" toml:"rootCA"` + ClientCA []tlscas.Cert `mapstructure:"clientCA" json:"clientCA" yaml:"clientCA" toml:"clientCA"` + Certs []tlscrt.Certif `mapstructure:"certs" json:"certs" yaml:"certs" toml:"certs"` + VersionMin tlsvrs.Version `mapstructure:"versionMin" json:"versionMin" yaml:"versionMin" toml:"versionMin"` + VersionMax tlsvrs.Version `mapstructure:"versionMax" json:"versionMax" yaml:"versionMax" toml:"versionMax"` + AuthClient tlsaut.ClientAuth `mapstructure:"authClient" json:"authClient" yaml:"authClient" toml:"authClient"` + InheritDefault bool `mapstructure:"inheritDefault" json:"inheritDefault" yaml:"inheritDefault" toml:"inheritDefault"` + DynamicSizingDisable bool `mapstructure:"dynamicSizingDisable" json:"dynamicSizingDisable" yaml:"dynamicSizingDisable" toml:"dynamicSizingDisable"` + SessionTicketDisable bool `mapstructure:"sessionTicketDisable" json:"sessionTicketDisable" yaml:"sessionTicketDisable" toml:"sessionTicketDisable"` } func (c *Config) Validate() liberr.Error { @@ -76,7 +74,7 @@ func (c *Config) Validate() liberr.Error { return nil } -func (c *Config) New() (TLSConfig, liberr.Error) { +func (c *Config) New() TLSConfig { if c.InheritDefault { return c.NewFrom(Default) } else { @@ -85,117 +83,114 @@ func (c *Config) New() (TLSConfig, liberr.Error) { } // nolint #gocognit -func (c *Config) NewFrom(cfg TLSConfig) (TLSConfig, liberr.Error) { - var t *config +func (c *Config) NewFrom(cfg TLSConfig) TLSConfig { + var t *Config if cfg != nil { - t = asStruct(cfg.Clone()) + t = cfg.Config() } if t == nil { - t = asStruct(New()) - t.caRoot = SystemRootCA() + t = &Config{} } - if c.VersionMin != "" { - t.tlsMinVersion = StringToTlsVersion(c.VersionMin) + if c.VersionMin != tlsvrs.VersionUnknown { + t.VersionMin = c.VersionMin } - if c.VersionMax != "" { - t.tlsMaxVersion = StringToTlsVersion(c.VersionMax) + if c.VersionMax != tlsvrs.VersionUnknown { + t.VersionMax = c.VersionMax } if c.DynamicSizingDisable { - t.dynSizingDisabled = true + t.DynamicSizingDisable = true } if c.SessionTicketDisable { - t.ticketSessionDisabled = true + t.SessionTicketDisable = true } - if c.AuthClient != "" { - t.clientAuth = StringToClientAuth(c.AuthClient) + if c.AuthClient != tlsaut.NoClientCert { + t.AuthClient = c.AuthClient } if len(c.CipherList) > 0 { for _, a := range c.CipherList { - if len(a) < 1 { - continue + if tlscpr.Check(a.Uint16()) { + t.CipherList = append(t.CipherList, a) } - t.cipherList = append(t.cipherList, StringToCipherKey(a)) } } if len(c.CurveList) > 0 { for _, a := range c.CurveList { - if len(a) < 1 { - continue - } - t.curveList = append(t.curveList, StringToCurveID(a)) - } - } - - if len(c.RootCAString) > 0 { - for _, s := range c.RootCAString { - if len(s) < 1 { - continue - } - t.AddRootCAString(s) - } - } - - if len(c.RootCAFile) > 0 { - for _, f := range c.RootCAFile { - if len(f) < 1 { - continue - } - if e := t.AddRootCAFile(f); e != nil { - return nil, e + if tlscrv.Check(a.Uint16()) { + t.CurveList = append(t.CurveList, a) } } } - if len(c.ClientCAString) > 0 { - for _, s := range c.ClientCAString { - if len(s) < 1 { - continue - } - t.AddClientCAString(s) + if len(c.RootCA) > 0 { + for _, s := range c.RootCA { + t.RootCA = append(t.RootCA, s) } } - if len(c.ClientCAFiles) > 0 { - for _, f := range c.ClientCAFiles { - if len(f) < 1 { - continue - } - if e := t.AddClientCAFile(f); e != nil { - return nil, e - } + if len(c.ClientCA) > 0 { + for _, s := range c.ClientCA { + t.ClientCA = append(t.ClientCA, s) } } - if len(c.CertPairString) > 0 { - for _, s := range c.CertPairString { - if len(s.Key) < 1 || len(s.Pem) < 1 { - continue - } - if e := t.AddCertificatePairString(s.Key, s.Pem); e != nil { - return nil, e - } + if len(c.Certs) > 0 { + for _, s := range c.Certs { + t.Certs = append(t.Certs, s) } } - if len(c.CertPairFile) > 0 { - for _, f := range c.CertPairFile { - if len(f.Key) < 1 || len(f.Pem) < 1 { - continue - } - if e := t.AddCertificatePairFile(f.Key, f.Pem); e != nil { - return nil, e - } + res := &config{ + rand: nil, + cert: make([]tlscrt.Cert, 0), + cipherList: make([]tlscpr.Cipher, 0), + curveList: make([]tlscrv.Curves, 0), + caRoot: make([]tlscas.Cert, 0), + clientAuth: t.AuthClient, + clientCA: make([]tlscas.Cert, 0), + tlsMinVersion: t.VersionMin, + tlsMaxVersion: t.VersionMax, + dynSizingDisabled: t.DynamicSizingDisable, + ticketSessionDisabled: t.SessionTicketDisable, + } + + if len(t.Certs) > 0 { + for _, s := range t.Certs { + res.cert = append(res.cert, s.Cert()) } } - return t, nil + if len(t.CipherList) > 0 { + for _, s := range t.CipherList { + res.cipherList = append(res.cipherList, s) + } + } + + if len(t.CurveList) > 0 { + for _, s := range t.CurveList { + res.curveList = append(res.curveList, s) + } + } + + if len(t.RootCA) > 0 { + for _, s := range t.RootCA { + res.caRoot = append(res.caRoot, s) + } + } + + if len(t.ClientCA) > 0 { + for _, s := range t.ClientCA { + res.clientCA = append(res.clientCA, s) + } + } + + return res } diff --git a/certificates/curves.go b/certificates/curves.go new file mode 100644 index 0000000..9122b26 --- /dev/null +++ b/certificates/curves.go @@ -0,0 +1,52 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package certificates + +import tlscrv "github.com/nabbar/golib/certificates/curves" + +func (o *config) SetCurveList(c []tlscrv.Curves) { + o.curveList = make([]tlscrv.Curves, 0) + o.AddCurves(c...) +} + +func (o *config) AddCurves(c ...tlscrv.Curves) { + for _, i := range c { + o.curveList = append(o.curveList, i) + } +} + +func (o *config) GetCurves() []tlscrv.Curves { + var res = make([]tlscrv.Curves, 0) + + for _, i := range o.curveList { + if tlscrv.Check(i.Uint16()) { + res = append(res, i) + } + } + + return res +} diff --git a/certificates/curves/encode.go b/certificates/curves/encode.go new file mode 100644 index 0000000..7212d4d --- /dev/null +++ b/certificates/curves/encode.go @@ -0,0 +1,89 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package curves + +import ( + "fmt" + + "gopkg.in/yaml.v3" +) + +func (v *Curves) unmarshall(val []byte) error { + *v = parseBytes(val) + return nil +} + +func (v Curves) MarshalJSON() ([]byte, error) { + t := v.String() + b := make([]byte, 0, len(t)+2) + b = append(b, '"') + b = append(b, []byte(t)...) + b = append(b, '"') + return b, nil +} + +func (v *Curves) UnmarshalJSON(bytes []byte) error { + return v.unmarshall(bytes) +} + +func (v Curves) MarshalYAML() (interface{}, error) { + return []byte(v.String()), nil +} + +func (v *Curves) UnmarshalYAML(value *yaml.Node) error { + return v.unmarshall([]byte(value.Value)) +} + +func (v Curves) MarshalTOML() ([]byte, error) { + return []byte(v.String()), nil +} + +func (v *Curves) UnmarshalTOML(i interface{}) error { + if p, k := i.([]byte); k { + return v.unmarshall(p) + } + if p, k := i.(string); k { + return v.unmarshall([]byte(p)) + } + return fmt.Errorf("size: value not in valid format") +} + +func (v Curves) MarshalText() ([]byte, error) { + return []byte(v.String()), nil +} + +func (v *Curves) UnmarshalText(bytes []byte) error { + return v.unmarshall(bytes) +} + +func (v Curves) MarshalCBOR() ([]byte, error) { + return []byte(v.String()), nil +} + +func (v *Curves) UnmarshalCBOR(bytes []byte) error { + return v.unmarshall(bytes) +} diff --git a/certificates/curves/format.go b/certificates/curves/format.go new file mode 100644 index 0000000..079a7af --- /dev/null +++ b/certificates/curves/format.go @@ -0,0 +1,98 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package curves + +import ( + "crypto/tls" + "strings" +) + +func (v Curves) String() string { + switch v { + case X25519: + return "X25519" + case P256: + return "P256" + case P384: + return "P384" + case P521: + return "P521" + default: + return "" + } +} + +func (v Curves) Code() string { + return strings.ToLower(v.String()) +} + +func (v Curves) CurveID() tls.CurveID { + switch v { + case X25519: + return tls.X25519 + case P256: + return tls.CurveP256 + case P384: + return tls.CurveP384 + case P521: + return tls.CurveP521 + default: + return 0 + } +} + +func (v Curves) TLS() tls.CurveID { + return v.CurveID() +} + +func (v Curves) Uint16() uint16 { + return uint16(v.CurveID()) +} + +func (v Curves) Uint() uint { + return uint(v.CurveID()) +} + +func (v Curves) Uint32() uint32 { + return uint32(v.CurveID()) +} + +func (v Curves) Uint64() uint64 { + return uint64(v.CurveID()) +} + +func (v Curves) Int() int { + return int(v.CurveID()) +} + +func (v Curves) Int32() int32 { + return int32(v.CurveID()) +} + +func (v Curves) Int64() int64 { + return int64(v.CurveID()) +} diff --git a/certificates/curves/interface.go b/certificates/curves/interface.go new file mode 100644 index 0000000..b0b670a --- /dev/null +++ b/certificates/curves/interface.go @@ -0,0 +1,120 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package curves + +import ( + "crypto/tls" + "math" + "strings" +) + +type Curves uint16 + +const ( + Unknown Curves = iota + X25519 = Curves(tls.X25519) + P256 = Curves(tls.CurveP256) + P384 = Curves(tls.CurveP384) + P521 = Curves(tls.CurveP521) +) + +func List() []Curves { + return []Curves{ + X25519, + P256, + P384, + P521, + } +} + +func ListString() []string { + var res = make([]string, 0) + for _, c := range List() { + res = append(res, c.String()) + } + return res +} + +func Parse(s string) Curves { + s = strings.ToLower(s) + s = strings.Replace(s, "\"", "", -1) + s = strings.Replace(s, "'", "", -1) + s = strings.Replace(s, "x", "", -1) + s = strings.Replace(s, "X", "", -1) + s = strings.Replace(s, "p", "", -1) + s = strings.Replace(s, "P", "", -1) + s = strings.Replace(s, ".", "", -1) + s = strings.Replace(s, "-", "", -1) + s = strings.Replace(s, "_", "", -1) + s = strings.Replace(s, " ", "", -1) + s = strings.TrimSpace(s) + + switch { + case strings.EqualFold(s, "25519"): + return X25519 + case strings.EqualFold(s, "256"): + return P256 + case strings.EqualFold(s, "384"): + return P384 + case strings.EqualFold(s, "521"): + return P521 + default: + return Unknown + } +} + +func ParseInt(d int) Curves { + if d > math.MaxUint16 { + d = math.MaxUint16 + } else if d < 1 { + d = 0 + } + + switch tls.CurveID(d) { + case tls.X25519: + return X25519 + case tls.CurveP256: + return P256 + case tls.CurveP384: + return P384 + case tls.CurveP521: + return P521 + default: + return Unknown + } +} + +func Check(curves uint16) bool { + if c := ParseInt(int(curves)); c == Unknown { + return false + } + return true +} + +func parseBytes(p []byte) Curves { + return Parse(string(p)) +} diff --git a/certificates/curves/models.go b/certificates/curves/models.go new file mode 100644 index 0000000..3a29a6b --- /dev/null +++ b/certificates/curves/models.go @@ -0,0 +1,62 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package curves + +import ( + "reflect" + + libmap "github.com/mitchellh/mapstructure" +) + +func ViperDecoderHook() libmap.DecodeHookFuncType { + return func(from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) { + var ( + z = Curves(0) + t string + k bool + ) + + // Check if the data type matches the expected one + if from.Kind() != reflect.String { + return data, nil + } else if t, k = data.(string); !k { + return data, nil + } + + // Check if the target type matches the expected one + if to != reflect.TypeOf(z) { + return data, nil + } + + // Format/decode/parse the data and return the new value + if e := z.unmarshall([]byte(t)); e != nil { + return nil, e + } else { + return z, nil + } + } +} diff --git a/certificates/interface.go b/certificates/interface.go index 5869db5..2d00949 100644 --- a/certificates/interface.go +++ b/certificates/interface.go @@ -29,9 +29,15 @@ package certificates import ( "crypto/tls" "crypto/x509" + "io" "net/http" - liberr "github.com/nabbar/golib/errors" + tlsaut "github.com/nabbar/golib/certificates/auth" + tlscas "github.com/nabbar/golib/certificates/ca" + tlscrt "github.com/nabbar/golib/certificates/certs" + tlscpr "github.com/nabbar/golib/certificates/cipher" + tlscrv "github.com/nabbar/golib/certificates/curves" + tlsvrs "github.com/nabbar/golib/certificates/tlsversion" ) type FctHttpClient func(def TLSConfig, servername string) *http.Client @@ -39,50 +45,62 @@ type FctTLSDefault func() TLSConfig type FctRootCA func() []string type TLSConfig interface { + RegisterRand(rand io.Reader) + AddRootCAString(rootCA string) bool - AddRootCAFile(pemFile string) liberr.Error - GetRootCA() *x509.CertPool + AddRootCAFile(pemFile string) error + GetRootCA() []tlscas.Cert + GetRootCAPool() *x509.CertPool AddClientCAString(ca string) bool - AddClientCAFile(pemFile string) liberr.Error - GetClientCA() *x509.CertPool + AddClientCAFile(pemFile string) error + GetClientCA() []tlscas.Cert + GetClientCAPool() *x509.CertPool + SetClientAuth(a tlsaut.ClientAuth) - AddCertificatePairString(key, crt string) liberr.Error - AddCertificatePairFile(keyFile, crtFile string) liberr.Error + AddCertificatePairString(key, crt string) error + AddCertificatePairFile(keyFile, crtFile string) error LenCertificatePair() int CleanCertificatePair() GetCertificatePair() []tls.Certificate - SetVersionMin(vers uint16) - SetVersionMax(vers uint16) - SetClientAuth(cAuth tls.ClientAuthType) - SetCipherList(cipher []uint16) - SetCurveList(curves []tls.CurveID) + SetVersionMin(v tlsvrs.Version) + GetVersionMin() tlsvrs.Version + SetVersionMax(v tlsvrs.Version) + GetVersionMax() tlsvrs.Version + + SetCipherList(c []tlscpr.Cipher) + AddCiphers(c ...tlscpr.Cipher) + GetCiphers() []tlscpr.Cipher + + SetCurveList(c []tlscrv.Curves) + AddCurves(c ...tlscrv.Curves) + GetCurves() []tlscrv.Curves + SetDynamicSizingDisabled(flag bool) SetSessionTicketDisabled(flag bool) Clone() TLSConfig + TLS(serverName string) *tls.Config TlsConfig(serverName string) *tls.Config + Config() *Config } var Default = New() func New() TLSConfig { return &config{ - caRoot: nil, - cert: nil, - - tlsMinVersion: 0, - tlsMaxVersion: 0, - - cipherList: nil, - curveList: nil, - + rand: nil, + cert: make([]tlscrt.Cert, 0), + cipherList: make([]tlscpr.Cipher, 0), + curveList: make([]tlscrv.Curves, 0), + caRoot: make([]tlscas.Cert, 0), + clientAuth: tlsaut.NoClientCert, + clientCA: make([]tlscas.Cert, 0), + tlsMinVersion: tlsvrs.VersionUnknown, + tlsMaxVersion: tlsvrs.VersionUnknown, dynSizingDisabled: false, ticketSessionDisabled: false, - - clientAuth: 0, - clientCA: nil, } } @@ -92,7 +110,7 @@ func AddRootCAContents(rootContent string) bool { } // Deprecated: use local config and no more globals default config. -func AddRootCAFile(rootFile string) liberr.Error { +func AddRootCAFile(rootFile string) error { return Default.AddRootCAFile(rootFile) } @@ -102,17 +120,17 @@ func AddCACertificateContents(caContent string) bool { } // Deprecated: use local config and no more globals default config. -func AddCACertificateFile(caFile string) liberr.Error { +func AddCACertificateFile(caFile string) error { return Default.AddClientCAFile(caFile) } // Deprecated: use local config and no more globals default config. -func AddCertificatePairString(key, crt string) liberr.Error { +func AddCertificatePairString(key, crt string) error { return Default.AddCertificatePairString(key, crt) } // Deprecated: use local config and no more globals default config. -func AddCertificatePairFile(keyFile, crtFile string) liberr.Error { +func AddCertificatePairFile(keyFile, crtFile string) error { return Default.AddCertificatePairFile(keyFile, crtFile) } @@ -137,37 +155,47 @@ func AppendCertificates(cert []tls.Certificate) []tls.Certificate { // Deprecated: use local config and no more globals default config. func GetRootCA() *x509.CertPool { - return Default.GetRootCA() + return Default.GetRootCAPool() } // Deprecated: use local config and no more globals default config. func GetClientCA() *x509.CertPool { - return Default.GetClientCA() + return Default.GetClientCAPool() } // Deprecated: use local config and no more globals default config. func SetVersionMin(vers uint16) { - Default.SetVersionMin(vers) + Default.SetVersionMin(tlsvrs.ParseInt(int(vers))) } // Deprecated: use local config and no more globals default config. func SetVersionMax(vers uint16) { - Default.SetVersionMax(vers) + Default.SetVersionMax(tlsvrs.ParseInt(int(vers))) } // Deprecated: use local config and no more globals default config. func SetClientAuth(auth string) { - Default.SetClientAuth(StringToClientAuth(auth)) + Default.SetClientAuth(tlsaut.Parse(auth)) } // Deprecated: use local config and no more globals default config. func SetCipherList(cipher []uint16) { - Default.SetCipherList(cipher) + Default.SetCipherList(make([]tlscpr.Cipher, 0)) + + for _, i := range cipher { + c := tlscpr.ParseInt(int(i)) + Default.AddCiphers(c) + } } // Deprecated: use local config and no more globals default config. func SetCurve(curves []tls.CurveID) { - Default.SetCurveList(curves) + Default.SetCurveList(make([]tlscrv.Curves, 0)) + + for _, i := range curves { + c := tlscrv.ParseInt(int(i)) + Default.AddCurves(c) + } } // Deprecated: use local config and no more globals default config. @@ -191,11 +219,11 @@ func GetTlsConfigCertificates() *tls.Config { } // Deprecated: use local config and no more globals default config. -func AddCertificateContents(keyContents, certContents string) liberr.Error { +func AddCertificateContents(keyContents, certContents string) error { return Default.AddCertificatePairString(keyContents, certContents) } // Deprecated: use local config and no more globals default config. -func AddCertificateFile(keyFile, certFile string) liberr.Error { +func AddCertificateFile(keyFile, certFile string) error { return Default.AddCertificatePairFile(keyFile, certFile) } diff --git a/certificates/model.go b/certificates/model.go index c6edfa3..b1ce0ed 100644 --- a/certificates/model.go +++ b/certificates/model.go @@ -27,329 +27,212 @@ package certificates import ( - "bytes" "crypto/tls" "crypto/x509" - "io/ioutil" - "os" - "strings" + "io" - liberr "github.com/nabbar/golib/errors" + tlsaut "github.com/nabbar/golib/certificates/auth" + tlscas "github.com/nabbar/golib/certificates/ca" + tlscrt "github.com/nabbar/golib/certificates/certs" + tlscpr "github.com/nabbar/golib/certificates/cipher" + tlscrv "github.com/nabbar/golib/certificates/curves" + tlsvrs "github.com/nabbar/golib/certificates/tlsversion" ) type config struct { - cert []tls.Certificate - cipherList []uint16 - curveList []tls.CurveID - caRoot *x509.CertPool - clientAuth tls.ClientAuthType - clientCA *x509.CertPool - tlsMinVersion uint16 - tlsMaxVersion uint16 + rand io.Reader + cert []tlscrt.Cert + cipherList []tlscpr.Cipher + curveList []tlscrv.Curves + caRoot []tlscas.Cert + clientAuth tlsaut.ClientAuth + clientCA []tlscas.Cert + tlsMinVersion tlsvrs.Version + tlsMaxVersion tlsvrs.Version dynSizingDisabled bool ticketSessionDisabled bool } -func (c *config) checkFile(pemFiles ...string) liberr.Error { - for _, f := range pemFiles { - if f == "" { - return ErrorParamEmpty.Error(nil) - } +func (o *config) RegisterRand(rand io.Reader) { + o.rand = rand +} - if _, e := os.Stat(f); e != nil { - return ErrorFileStat.Error(e) - } +func (o *config) SetVersionMin(v tlsvrs.Version) { + o.tlsMinVersion = v +} - /* #nosec */ - b, e := ioutil.ReadFile(f) - if e != nil { - return ErrorFileRead.Error(e) - } +func (o *config) GetVersionMin() tlsvrs.Version { + return o.tlsMinVersion +} - b = bytes.Trim(b, "\n") - b = bytes.Trim(b, "\r") - b = bytes.TrimSpace(b) +func (o *config) SetVersionMax(v tlsvrs.Version) { + o.tlsMaxVersion = v +} - if len(b) < 1 { - return ErrorFileEmpty.Error(nil) +func (o *config) GetVersionMax() tlsvrs.Version { + return o.tlsMaxVersion +} + +func (o *config) SetDynamicSizingDisabled(flag bool) { + o.dynSizingDisabled = flag +} + +func (o *config) SetSessionTicketDisabled(flag bool) { + o.ticketSessionDisabled = flag +} + +func (o *config) Clone() TLSConfig { + cfg := &config{ + rand: o.rand, + cert: make([]tlscrt.Cert, 0), + cipherList: make([]tlscpr.Cipher, 0), + curveList: make([]tlscrv.Curves, 0), + caRoot: make([]tlscas.Cert, 0), + clientAuth: o.clientAuth, + clientCA: make([]tlscas.Cert, 0), + tlsMinVersion: o.tlsMinVersion, + tlsMaxVersion: o.tlsMaxVersion, + dynSizingDisabled: o.dynSizingDisabled, + ticketSessionDisabled: o.ticketSessionDisabled, + } + + if len(o.cert) > 0 { + for _, c := range o.cert { + cfg.cert = append(cfg.cert, c) } } - return nil + if len(o.cipherList) > 0 { + for _, c := range o.cipherList { + if tlscpr.Check(c.Uint16()) { + cfg.cipherList = append(cfg.cipherList, c) + } + } + } + + if len(o.curveList) > 0 { + for _, c := range o.curveList { + if tlscpr.Check(c.Uint16()) { + cfg.curveList = append(cfg.curveList, c) + } + } + } + + if len(o.caRoot) > 0 { + for _, c := range o.caRoot { + cfg.caRoot = append(cfg.caRoot, c) + } + } + + if len(o.clientCA) > 0 { + for _, c := range o.clientCA { + cfg.clientCA = append(cfg.clientCA, c) + } + } + + return cfg } -func (c *config) AddRootCAString(rootCA string) bool { - if c.caRoot == nil { - c.caRoot = SystemRootCA() - } - - if rootCA != "" { - return c.caRoot.AppendCertsFromPEM([]byte(rootCA)) - } - - return false +func (o *config) TlsConfig(serverName string) *tls.Config { + return o.TLS(serverName) } -func (c *config) AddRootCAFile(pemFile string) liberr.Error { - if e := c.checkFile(pemFile); e != nil { - return e - } - - if c.caRoot == nil { - c.caRoot = SystemRootCA() - } - - //nolint #nosec - /* #nosec */ - b, _ := ioutil.ReadFile(pemFile) - - if c.caRoot.AppendCertsFromPEM(b) { - return nil - } - - return ErrorCertAppend.Error(nil) -} - -func (c *config) AddClientCAString(ca string) bool { - if c.clientCA == nil { - c.clientCA = x509.NewCertPool() - } - - if ca != "" { - return c.clientCA.AppendCertsFromPEM([]byte(ca)) - } - - return false -} - -func (c *config) AddClientCAFile(pemFile string) liberr.Error { - if e := c.checkFile(pemFile); e != nil { - return e - } - - if c.clientCA == nil { - c.clientCA = x509.NewCertPool() - } - - //nolint #nosec - /* #nosec */ - b, _ := ioutil.ReadFile(pemFile) - - if c.clientCA.AppendCertsFromPEM(b) { - return nil - } - - return ErrorCertAppend.Error(nil) -} - -func (c *config) AddCertificatePairString(key, crt string) liberr.Error { - if len(c.cert) == 0 { - c.cert = make([]tls.Certificate, 0) - } - - key = strings.Trim(key, "\n") - crt = strings.Trim(crt, "\n") - - key = strings.Trim(key, "\r") - crt = strings.Trim(crt, "\r") - - key = strings.TrimSpace(key) - crt = strings.TrimSpace(crt) - - if len(key) < 1 || len(crt) < 1 { - return ErrorParamEmpty.Error(nil) - } - - p, err := tls.X509KeyPair([]byte(crt), []byte(key)) - if err != nil { - return ErrorCertKeyPairParse.Error(err) - } - - c.cert = append(c.cert, p) - return nil -} - -func (c *config) AddCertificatePairFile(keyFile, crtFile string) liberr.Error { - if e := c.checkFile(keyFile, crtFile); e != nil { - return e - } - - if len(c.cert) == 0 { - c.cert = make([]tls.Certificate, 0) - } - - if p, e := tls.LoadX509KeyPair(crtFile, keyFile); e != nil { - return ErrorCertKeyPairLoad.Error(e) - } else { - c.cert = append(c.cert, p) - return nil - } -} - -func (c *config) TlsConfig(serverName string) *tls.Config { +func (o *config) TLS(serverName string) *tls.Config { /* #nosec */ cnf := &tls.Config{ - InsecureSkipVerify: false, + Rand: nil, + Certificates: make([]tls.Certificate, 0), + RootCAs: SystemRootCA(), + ServerName: "", + ClientAuth: tls.NoClientCert, + ClientCAs: x509.NewCertPool(), + InsecureSkipVerify: false, + CipherSuites: make([]uint16, 0), + SessionTicketsDisabled: false, + MinVersion: 0, + MaxVersion: 0, + CurvePreferences: make([]tls.CurveID, 0), + DynamicRecordSizingDisabled: false, + Renegotiation: tls.RenegotiateNever, } if serverName != "" { cnf.ServerName = serverName } - if c.ticketSessionDisabled { + if o.ticketSessionDisabled { cnf.SessionTicketsDisabled = true } - if c.dynSizingDisabled { + if o.dynSizingDisabled { cnf.DynamicRecordSizingDisabled = true } - if c.tlsMinVersion != 0 { - cnf.MinVersion = c.tlsMinVersion + if o.tlsMinVersion != tlsvrs.VersionUnknown { + cnf.MinVersion = o.tlsMinVersion.TLS() } - if c.tlsMaxVersion != 0 { - cnf.MaxVersion = c.tlsMaxVersion + if o.tlsMaxVersion != tlsvrs.VersionUnknown { + cnf.MaxVersion = o.tlsMaxVersion.TLS() } - if len(c.cipherList) > 0 { - cnf.PreferServerCipherSuites = true - cnf.CipherSuites = c.cipherList + if len(o.cipherList) > 0 { + for _, c := range o.cipherList { + if c != tlscpr.Unknown { + cnf.CipherSuites = append(cnf.CipherSuites, c.TLS()) + } + } } - if len(c.curveList) > 0 { - cnf.CurvePreferences = c.curveList + if len(o.curveList) > 0 { + for _, c := range o.curveList { + if c != tlscrv.Unknown { + cnf.CurvePreferences = append(cnf.CurvePreferences, c.TLS()) + } + } } - if c.caRoot != nil { - cnf.RootCAs = c.caRoot + if o.caRoot != nil { + for _, c := range o.caRoot { + c.AppendPool(cnf.RootCAs) + } } - if len(c.cert) > 0 { - cnf.Certificates = c.cert + if len(o.cert) > 0 { + for _, c := range o.cert { + cnf.Certificates = append(cnf.Certificates, c.TLS()) + } } - if c.clientAuth != tls.NoClientCert { - cnf.ClientAuth = c.clientAuth - if c.clientCA != nil { - cnf.ClientCAs = c.clientCA + if o.clientAuth != tlsaut.NoClientCert { + cnf.ClientAuth = o.clientAuth.TLS() + if len(o.clientCA) > 0 { + for _, c := range o.clientCA { + c.AppendPool(cnf.ClientCAs) + } } } return cnf } -func (c *config) cloneCipherList() []uint16 { - if c.cipherList == nil { - return nil +func (o *config) Config() *Config { + var crt = make([]tlscrt.Certif, 0) + for _, c := range o.cert { + crt = append(crt, c.Model()) } - return append(make([]uint16, 0), c.cipherList...) -} - -func (c *config) cloneCurveList() []tls.CurveID { - if c.curveList == nil { - return nil - } - - return append(make([]tls.CurveID, 0), c.curveList...) -} - -func (c *config) cloneCertificates() []tls.Certificate { - if c.cert == nil { - return nil - } - - return append(make([]tls.Certificate, 0), c.cert...) -} - -func (c *config) cloneRootCA() *x509.CertPool { - if c.caRoot == nil { - return nil - } - - list := *c.caRoot - - return &list -} - -func (c *config) cloneClientCA() *x509.CertPool { - if c.clientCA == nil { - return nil - } - - list := *c.clientCA - - return &list -} - -func (c *config) Clone() TLSConfig { - return &config{ - caRoot: c.cloneRootCA(), - cert: c.cloneCertificates(), - tlsMinVersion: c.tlsMinVersion, - tlsMaxVersion: c.tlsMaxVersion, - cipherList: c.cloneCipherList(), - curveList: c.cloneCurveList(), - dynSizingDisabled: c.dynSizingDisabled, - ticketSessionDisabled: c.ticketSessionDisabled, - clientAuth: c.clientAuth, - clientCA: c.cloneClientCA(), + return &Config{ + CurveList: o.curveList, + CipherList: o.cipherList, + RootCA: o.caRoot, + ClientCA: o.clientCA, + Certs: crt, + VersionMin: o.tlsMinVersion, + VersionMax: o.tlsMaxVersion, + AuthClient: o.clientAuth, + InheritDefault: false, + DynamicSizingDisable: o.dynSizingDisabled, + SessionTicketDisable: o.ticketSessionDisabled, } } - -func asStruct(cfg TLSConfig) *config { - if c, ok := cfg.(*config); ok { - return c - } - - return nil -} - -func (c *config) GetRootCA() *x509.CertPool { - return c.caRoot -} - -func (c *config) GetClientCA() *x509.CertPool { - return c.clientCA -} - -func (c *config) LenCertificatePair() int { - return len(c.cert) -} - -func (c *config) CleanCertificatePair() { - c.cert = make([]tls.Certificate, 0) -} - -func (c *config) GetCertificatePair() []tls.Certificate { - return c.cert -} - -func (c *config) SetVersionMin(vers uint16) { - c.tlsMinVersion = vers -} - -func (c *config) SetVersionMax(vers uint16) { - c.tlsMaxVersion = vers -} - -func (c *config) SetClientAuth(cAuth tls.ClientAuthType) { - c.clientAuth = cAuth -} - -func (c *config) SetCipherList(cipher []uint16) { - c.cipherList = cipher -} - -func (c *config) SetCurveList(curves []tls.CurveID) { - c.curveList = curves -} - -func (c *config) SetDynamicSizingDisabled(flag bool) { - c.dynSizingDisabled = flag -} - -func (c *config) SetSessionTicketDisabled(flag bool) { - c.ticketSessionDisabled = flag -} diff --git a/certificates/rootca.go b/certificates/rootca.go new file mode 100644 index 0000000..8870885 --- /dev/null +++ b/certificates/rootca.go @@ -0,0 +1,79 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package certificates + +import ( + "crypto/x509" + + tlscas "github.com/nabbar/golib/certificates/ca" +) + +func (o *config) GetRootCA() []tlscas.Cert { + var res = make([]tlscas.Cert, 0) + + for _, c := range o.caRoot { + res = append(res, c) + } + + return res +} + +func (o *config) GetRootCAPool() *x509.CertPool { + var res = x509.NewCertPool() + for _, ca := range o.caRoot { + ca.AppendPool(res) + } + return res +} + +func (o *config) AddRootCAString(rootCA string) bool { + if rootCA != "" { + if c, e := tlscas.Parse(rootCA); e == nil { + o.caRoot = append(o.caRoot, c) + return true + } + } + + return false +} + +func (o *config) AddRootCAFile(pemFile string) error { + var fct = func(p []byte) error { + if c, e := tlscas.ParseByte(p); e != nil { + return e + } else { + o.caRoot = append(o.caRoot, c) + return nil + } + } + + if e := checkFile(fct, pemFile); e != nil { + return e + } + + return nil +} diff --git a/certificates/tlsversion/encode.go b/certificates/tlsversion/encode.go index 2219fb6..ec31669 100644 --- a/certificates/tlsversion/encode.go +++ b/certificates/tlsversion/encode.go @@ -1,29 +1,28 @@ -/*********************************************************************************************************************** +/* + * MIT License * - * MIT License + * Copyright (c) 2020 Nicolas JUHEL * - * Copyright (c) 2022 Nicolas JUHEL + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. * * - **********************************************************************************************************************/ + */ package tlsversion diff --git a/certificates/tlsversion/format.go b/certificates/tlsversion/format.go index bbc2612..f82f452 100644 --- a/certificates/tlsversion/format.go +++ b/certificates/tlsversion/format.go @@ -1,29 +1,28 @@ -/*********************************************************************************************************************** +/* + * MIT License * - * MIT License + * Copyright (c) 2020 Nicolas JUHEL * - * Copyright (c) 2022 Nicolas JUHEL + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. * * - **********************************************************************************************************************/ + */ package tlsversion @@ -53,6 +52,10 @@ func (v Version) Code() string { return s } +func (v Version) TLS() uint16 { + return v.Uint16() +} + func (v Version) Uint16() uint16 { switch v { case VersionTLS10: diff --git a/certificates/tlsversion/interface.go b/certificates/tlsversion/interface.go index 8fe8e90..7265c1d 100644 --- a/certificates/tlsversion/interface.go +++ b/certificates/tlsversion/interface.go @@ -1,29 +1,28 @@ -/*********************************************************************************************************************** +/* + * MIT License * - * MIT License + * Copyright (c) 2020 Nicolas JUHEL * - * Copyright (c) 2022 Nicolas JUHEL + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. * * - **********************************************************************************************************************/ + */ package tlsversion @@ -60,6 +59,8 @@ func ListHigh() []Version { func Parse(s string) Version { s = strings.ToLower(s) + s = strings.Replace(s, "\"", "", -1) + s = strings.Replace(s, "'", "", -1) s = strings.Replace(s, "tls", "", -1) s = strings.Replace(s, "ssl", "", -1) s = strings.Replace(s, ".", "", -1) diff --git a/certificates/tlsversion/models.go b/certificates/tlsversion/models.go index 83e3da9..3d896ad 100644 --- a/certificates/tlsversion/models.go +++ b/certificates/tlsversion/models.go @@ -1,29 +1,28 @@ -/*********************************************************************************************************************** +/* + * MIT License * - * MIT License + * Copyright (c) 2020 Nicolas JUHEL * - * Copyright (c) 2022 Nicolas JUHEL + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. * * - **********************************************************************************************************************/ + */ package tlsversion diff --git a/certificates/tools.go b/certificates/tools.go index a6cff9d..efe4ff2 100644 --- a/certificates/tools.go +++ b/certificates/tools.go @@ -27,22 +27,12 @@ package certificates import ( - "crypto/tls" + "bytes" "crypto/x509" + "os" "runtime" - "strings" ) -// Deprecated: use StringToCipherKey. -func GetCipherKey(cipher string) uint16 { - return StringToCipherKey(cipher) -} - -// Deprecated: use StringToCurveID. -func GetCurveID(curveRef string) tls.CurveID { - return StringToCurveID(curveRef) -} - func SystemRootCA() *x509.CertPool { if runtime.GOOS == "windows" { return x509.NewCertPool() @@ -53,105 +43,36 @@ func SystemRootCA() *x509.CertPool { } } -func StringToTlsVersion(tlsVersStr string) uint16 { - tlsVersStr = strings.ToLower(tlsVersStr) - tlsVersStr = strings.Replace(tlsVersStr, "TLS", "", -1) - tlsVersStr = strings.TrimSpace(tlsVersStr) +func checkFile(fct func(p []byte) error, pemFiles ...string) error { + for _, f := range pemFiles { + if f == "" { + return ErrorParamEmpty.Error(nil) + } - switch tlsVersStr { - case "1", "1.0": - return tls.VersionTLS10 - case "1.1": - return tls.VersionTLS11 - case "1.2": - return tls.VersionTLS12 - case "1.3": - return tls.VersionTLS13 - default: - return tls.VersionTLS12 - } -} + if _, e := os.Stat(f); e != nil { + return e + } -func StringToClientAuth(auth string) tls.ClientAuthType { - switch strings.ToLower(auth) { - case "request": - return tls.RequestClientCert - case "require": - return tls.RequireAnyClientCert - case "verify": - return tls.VerifyClientCertIfGiven - case "strict": - return tls.RequireAndVerifyClientCert - default: - return tls.NoClientCert - } -} + /* #nosec */ + b, e := os.ReadFile(f) + if e != nil { + return e + } -func StringToCipherKey(cipher string) uint16 { - cipher = strings.ToLower(cipher) - cipher = strings.ReplaceAll(cipher, " ", "") - cipher = strings.ReplaceAll(cipher, "_", "") - cipher = strings.ReplaceAll(cipher, "-", "") + b = bytes.Trim(b, "\n") + b = bytes.Trim(b, "\r") + b = bytes.TrimSpace(b) - rsa := strings.Contains(cipher, "rsa") - dhe := strings.Contains(cipher, "ecdhe") - dsa := strings.Contains(cipher, "ecdsa") - gcm := strings.Contains(cipher, "gcm") - aes1 := strings.Contains(cipher, "aes128") - aes2 := strings.Contains(cipher, "aes256") - cha := strings.Contains(cipher, "chacha20") - poly := strings.Contains(cipher, "poly1305") - - switch { - // TLS v1.0 - v1.2 - case !dhe && rsa && aes1 && !gcm: - return tls.TLS_RSA_WITH_AES_128_CBC_SHA256 - case !dhe && rsa && aes1 && gcm: - return tls.TLS_RSA_WITH_AES_128_GCM_SHA256 - case !dhe && rsa && aes2 && gcm: - return tls.TLS_RSA_WITH_AES_256_GCM_SHA384 - case dhe && dsa && aes1 && !gcm: - return tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 - case dhe && rsa && aes1 && !gcm: - return tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - case dhe && rsa && aes1 && gcm: - return tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - case dhe && dsa && aes1 && gcm: - return tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - case dhe && rsa && aes2 && gcm: - return tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - case dhe && dsa && aes2 && gcm: - return tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - case rsa && (cha || poly): - return tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - case dsa && (cha || poly): - return tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 - - // TLS v1.3 - case aes1 && gcm: - return tls.TLS_AES_128_GCM_SHA256 - case aes2 && gcm: - return tls.TLS_AES_256_GCM_SHA384 - case cha || poly: - return tls.TLS_CHACHA20_POLY1305_SHA256 + if len(b) < 1 { + return ErrorFileEmpty.Error(nil) + } else if fct == nil { + continue + } + if e = fct(b); e != nil { + return e + } } - return 0 -} - -func StringToCurveID(curveRef string) tls.CurveID { - curveRef = strings.ToLower(curveRef) - - if strings.Contains(curveRef, "p2") { - return tls.CurveP256 - } else if strings.Contains(curveRef, "p3") { - return tls.CurveP384 - } else if strings.Contains(curveRef, "p5") { - return tls.CurveP521 - } else if strings.Contains(curveRef, "x25") { - return tls.X25519 - } - - return 0 + return nil } diff --git a/config/components/smtp/client.go b/config/components/smtp/client.go index 3da0952..ccb8fa2 100644 --- a/config/components/smtp/client.go +++ b/config/components/smtp/client.go @@ -146,7 +146,7 @@ func (o *componentSmtp) _GetTLS() libtls.TLSConfig { } func (o *componentSmtp) _GetTLSConfig(cfg libtls.Config) *tls.Config { - if i, e := cfg.NewFrom(o._GetTLS()); e != nil { + if i := cfg.NewFrom(o._GetTLS()); i == nil { // #nosec return &tls.Config{} } else { diff --git a/config/components/tls/client.go b/config/components/tls/client.go index 8778b5e..f8842d9 100644 --- a/config/components/tls/client.go +++ b/config/components/tls/client.go @@ -27,6 +27,8 @@ package tls import ( + "fmt" + libtls "github.com/nabbar/golib/certificates" cfgtps "github.com/nabbar/golib/config/types" libver "github.com/nabbar/golib/version" @@ -159,8 +161,8 @@ func (o *componentTls) _runCli() error { if cfg, err = o._getConfig(); err != nil { return prt.Error(err) - } else if tls, err = cfg.New(); err != nil { - return prt.Error(err) + } else if tls = cfg.New(); tls == nil { + return prt.Error(fmt.Errorf("cannot use tls config for new instance")) } else if o.f != nil { for _, s := range o.f() { tls.AddRootCAString(s) diff --git a/ftpclient/config.go b/ftpclient/config.go index a91d2d5..5eee1ad 100644 --- a/ftpclient/config.go +++ b/ftpclient/config.go @@ -34,7 +34,6 @@ import ( libval "github.com/go-playground/validator/v10" libftp "github.com/jlaffaye/ftp" libtls "github.com/nabbar/golib/certificates" - liberr "github.com/nabbar/golib/errors" ) type ConfigTimeZone struct { @@ -81,7 +80,7 @@ type Config struct { } // Validate allow checking if the config' struct is valid with the awaiting model -func (c *Config) Validate() liberr.Error { +func (c *Config) Validate() error { var e = ErrorValidatorError.Error(nil) if err := libval.New().Struct(c); err != nil { @@ -110,11 +109,11 @@ func (c *Config) RegisterDefaultTLS(fct func() libtls.TLSConfig) { c.ftls = fct } -func (c *Config) New() (*libftp.ServerConn, liberr.Error) { +func (c *Config) New() (*libftp.ServerConn, error) { var opt = make([]libftp.DialOption, 0) - if tls, err := c.TLS.NewFrom(c.ftls()); err != nil { - return nil, err + if tls := c.TLS.NewFrom(c.ftls()); tls == nil { + return nil, fmt.Errorf("no tls configured") } else if c.ForceTLS { opt = append(opt, libftp.DialWithExplicitTLS(tls.TlsConfig(""))) } else { diff --git a/ftpclient/interface.go b/ftpclient/interface.go index 8413b20..1ab383c 100644 --- a/ftpclient/interface.go +++ b/ftpclient/interface.go @@ -33,46 +33,45 @@ import ( "time" libftp "github.com/jlaffaye/ftp" - liberr "github.com/nabbar/golib/errors" ) type FTPClient interface { // Connect establish the connection to server with the given configuration registered. - Connect() liberr.Error + Connect() error // Check try to retrieve a valid connection to the server and send an NOOP command to check the connection. - Check() liberr.Error + Check() error // Close send the QUID command to the server if the connection is valid (cf Check). Close() // NameList issues an NLST FTP command. - NameList(path string) ([]string, liberr.Error) + NameList(path string) ([]string, error) // List issues a LIST FTP command. - List(path string) ([]*libftp.Entry, liberr.Error) + List(path string) ([]*libftp.Entry, error) // ChangeDir issues a CWD FTP command, which changes the current directory to the specified path. - ChangeDir(path string) liberr.Error + ChangeDir(path string) error // CurrentDir issues a PWD FTP command, which Returns the path of the current directory. - CurrentDir() (string, liberr.Error) + CurrentDir() (string, error) // FileSize issues a SIZE FTP command, which Returns the size of the file. - FileSize(path string) (int64, liberr.Error) + FileSize(path string) (int64, error) // GetTime issues the MDTM FTP command to obtain the file modification time. // It returns a UTC time. - GetTime(path string) (time.Time, liberr.Error) + GetTime(path string) (time.Time, error) // SetTime issues the MFMT FTP command to set the file modification time. // Also it can use a non-standard form of the MDTM command supported by the VsFtpd server instead of MFMT for the same purpose. // See "mdtm_write" in https://security.appspot.com/vsftpd/vsftpd_conf.html - SetTime(path string, t time.Time) liberr.Error + SetTime(path string, t time.Time) error // Retr issues a RETR FTP command to fetch the specified file from the remote FTP server. // The returned ReadCloser must be closed to cleanup the FTP data connection. - Retr(path string) (*libftp.Response, liberr.Error) + Retr(path string) (*libftp.Response, error) // RetrFrom issues a RETR FTP command to fetch the specified file from the remote FTP server, // the server will not send the offset first bytes of the file. @@ -82,38 +81,38 @@ type FTPClient interface { // Stor issues a STOR FTP command to store a file to the remote FTP server. // Stor creates the specified file with the content of the io.Reader. // Hint: io.Pipe() can be used if an io.Writer is required. - Stor(path string, r io.Reader) liberr.Error + Stor(path string, r io.Reader) error // StorFrom issues a STOR FTP command to store a file to the remote FTP server. // Stor creates the specified file with the content of the io.Reader, writing on the server will start at the given file offset. // Hint: io.Pipe() can be used if an io.Writer is required. - StorFrom(path string, r io.Reader, offset uint64) liberr.Error + StorFrom(path string, r io.Reader, offset uint64) error // Append issues a APPE FTP command to store a file to the remote FTP server. // If a file already exists with the given path, then the content of the io.Reader is appended. // Otherwise, a new file is created with that content. Hint: io.Pipe() can be used if an io.Writer is required. - Append(path string, r io.Reader) liberr.Error + Append(path string, r io.Reader) error // Rename renames a file on the remote FTP server. - Rename(from, to string) liberr.Error + Rename(from, to string) error // Delete issues a DELE FTP command to delete the specified file from the remote FTP server. - Delete(path string) liberr.Error + Delete(path string) error // RemoveDirRecur deletes a non-empty folder recursively using RemoveDir and Delete. - RemoveDirRecur(path string) liberr.Error + RemoveDirRecur(path string) error // MakeDir issues a MKD FTP command to create the specified directory on the remote FTP server. - MakeDir(path string) liberr.Error + MakeDir(path string) error // RemoveDir issues a RMD FTP command to remove the specified directory from the remote FTP server. - RemoveDir(path string) liberr.Error + RemoveDir(path string) error //Walk prepares the internal walk function so that the caller can begin traversing the directory. - Walk(root string) (*libftp.Walker, liberr.Error) + Walk(root string) (*libftp.Walker, error) } -func New(cfg *Config) (FTPClient, liberr.Error) { +func New(cfg *Config) (FTPClient, error) { c := &ftpClient{ m: sync.Mutex{}, cfg: new(atomic.Value), diff --git a/ftpclient/model.go b/ftpclient/model.go index 10ad39e..0c99d64 100644 --- a/ftpclient/model.go +++ b/ftpclient/model.go @@ -34,7 +34,6 @@ import ( "time" libftp "github.com/jlaffaye/ftp" - liberr "github.com/nabbar/golib/errors" ) type ftpClient struct { @@ -96,10 +95,9 @@ func (f *ftpClient) setClient(cli *libftp.ServerConn) { f.cli.Store(cli) } -func (f *ftpClient) Connect() liberr.Error { +func (f *ftpClient) Connect() error { var ( e error - err liberr.Error cfg *Config cli *libftp.ServerConn ) @@ -116,8 +114,8 @@ func (f *ftpClient) Connect() liberr.Error { return ErrorNotInitialized.Error(nil) } - if cli, err = cfg.New(); err != nil { - return err + if cli, e = cfg.New(); e != nil { + return e } if e = cli.NoOp(); e != nil { @@ -128,7 +126,7 @@ func (f *ftpClient) Connect() liberr.Error { return nil } -func (f *ftpClient) Check() liberr.Error { +func (f *ftpClient) Check() error { var cli *libftp.ServerConn if cli = f.getClient(); cli == nil { @@ -153,7 +151,7 @@ func (f *ftpClient) Close() { } } -func (f *ftpClient) NameList(path string) ([]string, liberr.Error) { +func (f *ftpClient) NameList(path string) ([]string, error) { if err := f.Check(); err != nil { return nil, err } @@ -165,7 +163,7 @@ func (f *ftpClient) NameList(path string) ([]string, liberr.Error) { } } -func (f *ftpClient) List(path string) ([]*libftp.Entry, liberr.Error) { +func (f *ftpClient) List(path string) ([]*libftp.Entry, error) { if err := f.Check(); err != nil { return nil, err } @@ -177,7 +175,7 @@ func (f *ftpClient) List(path string) ([]*libftp.Entry, liberr.Error) { } } -func (f *ftpClient) ChangeDir(path string) liberr.Error { +func (f *ftpClient) ChangeDir(path string) error { if err := f.Check(); err != nil { return err } @@ -189,7 +187,7 @@ func (f *ftpClient) ChangeDir(path string) liberr.Error { } } -func (f *ftpClient) CurrentDir() (string, liberr.Error) { +func (f *ftpClient) CurrentDir() (string, error) { if err := f.Check(); err != nil { return "", err } @@ -201,7 +199,7 @@ func (f *ftpClient) CurrentDir() (string, liberr.Error) { } } -func (f *ftpClient) FileSize(path string) (int64, liberr.Error) { +func (f *ftpClient) FileSize(path string) (int64, error) { if err := f.Check(); err != nil { return 0, err } @@ -213,7 +211,7 @@ func (f *ftpClient) FileSize(path string) (int64, liberr.Error) { } } -func (f *ftpClient) GetTime(path string) (time.Time, liberr.Error) { +func (f *ftpClient) GetTime(path string) (time.Time, error) { if err := f.Check(); err != nil { return time.Time{}, err } @@ -225,7 +223,7 @@ func (f *ftpClient) GetTime(path string) (time.Time, liberr.Error) { } } -func (f *ftpClient) SetTime(path string, t time.Time) liberr.Error { +func (f *ftpClient) SetTime(path string, t time.Time) error { if err := f.Check(); err != nil { return err } @@ -237,7 +235,7 @@ func (f *ftpClient) SetTime(path string, t time.Time) liberr.Error { } } -func (f *ftpClient) Retr(path string) (*libftp.Response, liberr.Error) { +func (f *ftpClient) Retr(path string) (*libftp.Response, error) { if err := f.Check(); err != nil { return nil, err } @@ -261,7 +259,7 @@ func (f *ftpClient) RetrFrom(path string, offset uint64) (*libftp.Response, erro } } -func (f *ftpClient) Stor(path string, r io.Reader) liberr.Error { +func (f *ftpClient) Stor(path string, r io.Reader) error { if err := f.Check(); err != nil { return err } @@ -273,7 +271,7 @@ func (f *ftpClient) Stor(path string, r io.Reader) liberr.Error { } } -func (f *ftpClient) StorFrom(path string, r io.Reader, offset uint64) liberr.Error { +func (f *ftpClient) StorFrom(path string, r io.Reader, offset uint64) error { if err := f.Check(); err != nil { return err } @@ -285,7 +283,7 @@ func (f *ftpClient) StorFrom(path string, r io.Reader, offset uint64) liberr.Err } } -func (f *ftpClient) Append(path string, r io.Reader) liberr.Error { +func (f *ftpClient) Append(path string, r io.Reader) error { if err := f.Check(); err != nil { return err } @@ -297,7 +295,7 @@ func (f *ftpClient) Append(path string, r io.Reader) liberr.Error { } } -func (f *ftpClient) Rename(from, to string) liberr.Error { +func (f *ftpClient) Rename(from, to string) error { if err := f.Check(); err != nil { return err } @@ -309,7 +307,7 @@ func (f *ftpClient) Rename(from, to string) liberr.Error { } } -func (f *ftpClient) Delete(path string) liberr.Error { +func (f *ftpClient) Delete(path string) error { if err := f.Check(); err != nil { return err } @@ -321,7 +319,7 @@ func (f *ftpClient) Delete(path string) liberr.Error { } } -func (f *ftpClient) RemoveDirRecur(path string) liberr.Error { +func (f *ftpClient) RemoveDirRecur(path string) error { if err := f.Check(); err != nil { return err } @@ -333,7 +331,7 @@ func (f *ftpClient) RemoveDirRecur(path string) liberr.Error { } } -func (f *ftpClient) MakeDir(path string) liberr.Error { +func (f *ftpClient) MakeDir(path string) error { if err := f.Check(); err != nil { return err } @@ -345,7 +343,7 @@ func (f *ftpClient) MakeDir(path string) liberr.Error { } } -func (f *ftpClient) RemoveDir(path string) liberr.Error { +func (f *ftpClient) RemoveDir(path string) error { if err := f.Check(); err != nil { return err } @@ -357,7 +355,7 @@ func (f *ftpClient) RemoveDir(path string) liberr.Error { } } -func (f *ftpClient) Walk(root string) (*libftp.Walker, liberr.Error) { +func (f *ftpClient) Walk(root string) (*libftp.Walker, error) { if err := f.Check(); err != nil { return nil, err } diff --git a/httpcli/dns-mapper/transport.go b/httpcli/dns-mapper/transport.go index 268c2a7..65d996b 100644 --- a/httpcli/dns-mapper/transport.go +++ b/httpcli/dns-mapper/transport.go @@ -75,16 +75,13 @@ func (o *dmp) Transport(cfg TransportConfig) *http.Transport { prx = http.ProxyURL(cfg.Proxy) } - var ( - err error - ssl libtls.TLSConfig - ) + var ssl libtls.TLSConfig if cfg.TLSConfig == nil { ssl = libtls.New() ssl.SetVersionMin(tls.VersionTLS12) ssl.SetVersionMax(tls.VersionTLS13) - } else if ssl, err = cfg.TLSConfig.New(); err != nil { + } else if ssl = cfg.TLSConfig.New(); ssl == nil { ssl = libtls.New() ssl.SetVersionMin(tls.VersionTLS12) ssl.SetVersionMax(tls.VersionTLS13) diff --git a/httpserver/config.go b/httpserver/config.go index 5bb80e4..403bd46 100644 --- a/httpserver/config.go +++ b/httpserver/config.go @@ -37,7 +37,6 @@ import ( libtls "github.com/nabbar/golib/certificates" libctx "github.com/nabbar/golib/context" libdur "github.com/nabbar/golib/duration" - liberr "github.com/nabbar/golib/errors" srvtps "github.com/nabbar/golib/httpserver/types" liblog "github.com/nabbar/golib/logger" logcfg "github.com/nabbar/golib/logger/config" @@ -210,12 +209,9 @@ func (c *Config) Clone() Config { TLS: libtls.Config{ CurveList: c.TLS.CurveList, CipherList: c.TLS.CipherList, - RootCAString: c.TLS.RootCAString, - RootCAFile: c.TLS.RootCAFile, - ClientCAString: c.TLS.ClientCAString, - ClientCAFiles: c.TLS.ClientCAFiles, - CertPairString: c.TLS.CertPairString, - CertPairFile: c.TLS.CertPairFile, + RootCA: c.TLS.RootCA, + ClientCA: c.TLS.ClientCA, + Certs: c.TLS.Certs, VersionMin: c.TLS.VersionMin, VersionMax: c.TLS.VersionMax, AuthClient: c.TLS.AuthClient, @@ -239,17 +235,21 @@ func (c *Config) SetContext(f libctx.FuncContext) { c.getParentContext = f } -func (c *Config) GetTLS() (libtls.TLSConfig, liberr.Error) { +func (c *Config) GetTLS() (libtls.TLSConfig, error) { var def libtls.TLSConfig if c.TLS.InheritDefault && c.getTLSDefault != nil { def = c.getTLSDefault() } - return c.TLS.NewFrom(def) + if cfg := c.TLS.NewFrom(def); cfg != nil { + return cfg, nil + } + + return nil, fmt.Errorf("no tls configuration found") } -func (c *Config) CheckTLS() (libtls.TLSConfig, liberr.Error) { +func (c *Config) CheckTLS() (libtls.TLSConfig, error) { if ssl, err := c.GetTLS(); err != nil { return nil, err } else if ssl == nil || ssl.LenCertificatePair() < 1 { @@ -337,7 +337,7 @@ func (c *Config) GetHandlerKey() string { return c.HandlerKey } -func (c *Config) Validate() liberr.Error { +func (c *Config) Validate() error { err := ErrorServerValidate.Error(nil) if er := libval.New().Struct(c); er != nil { diff --git a/httpserver/pool/config.go b/httpserver/pool/config.go index 980a28f..26fc123 100644 --- a/httpserver/pool/config.go +++ b/httpserver/pool/config.go @@ -91,11 +91,11 @@ func (p Config) Walk(fct FuncWalkConfig) { } } -func (p Config) Validate() liberr.Error { +func (p Config) Validate() error { var e = ErrorPoolValidate.Error(nil) p.Walk(func(cfg libhtp.Config) bool { - var err liberr.Error + var err error if err = cfg.Validate(); err != nil { e.Add(err) diff --git a/nats/client.go b/nats/client.go index bb4845c..8c9875e 100644 --- a/nats/client.go +++ b/nats/client.go @@ -33,7 +33,6 @@ import ( libval "github.com/go-playground/validator/v10" libtls "github.com/nabbar/golib/certificates" - liberr "github.com/nabbar/golib/errors" natcli "github.com/nats-io/nats.go" ) @@ -147,7 +146,7 @@ type Client struct { TLSConfig libtls.Config } -func (c Client) Validate() liberr.Error { +func (c Client) Validate() error { err := ErrorConfigValidation.Error(nil) if er := libval.New().Struct(c); er != nil { @@ -168,7 +167,7 @@ func (c Client) Validate() liberr.Error { return nil } -func (c Client) NewClient(defTls libtls.TLSConfig) (*natcli.Conn, liberr.Error) { +func (c Client) NewClient(defTls libtls.TLSConfig) (*natcli.Conn, error) { opts := natcli.GetDefaultOptions() if c.Url != "" { @@ -273,8 +272,8 @@ func (c Client) NewClient(defTls libtls.TLSConfig) (*natcli.Conn, liberr.Error) } if c.Secure { - if t, e := c.TLSConfig.NewFrom(defTls); e != nil { - return nil, e + if t := c.TLSConfig.NewFrom(defTls); t == nil { + return nil, fmt.Errorf("no valid tls configuration") } else { opts.TLSConfig = t.TlsConfig("") } diff --git a/nats/config.go b/nats/config.go index 1fe3245..3b373af 100644 --- a/nats/config.go +++ b/nats/config.go @@ -38,7 +38,6 @@ import ( libval "github.com/go-playground/validator/v10" libtls "github.com/nabbar/golib/certificates" - liberr "github.com/nabbar/golib/errors" libiot "github.com/nabbar/golib/ioutils" liblog "github.com/nabbar/golib/logger" loglvl "github.com/nabbar/golib/logger/level" @@ -63,7 +62,7 @@ type Config struct { Customs *ConfigCustom `mapstructure:"-" json:"-" yaml:"-" toml:"-"` } -func (c Config) Validate() liberr.Error { +func (c Config) Validate() error { err := ErrorConfigValidation.Error(nil) if er := libval.New().Struct(c); er != nil { @@ -84,7 +83,7 @@ func (c Config) Validate() liberr.Error { return nil } -func (c Config) LogConfigJson() liberr.Error { +func (c Config) LogConfigJson() error { if c.Logs.LogFile == "" { return nil } @@ -129,7 +128,7 @@ func (c Config) LogConfigJson() liberr.Error { return nil } -func (c Config) NatsOption(defaultTls libtls.TLSConfig, log liblog.Logger) (*natsrv.Options, liberr.Error) { +func (c Config) NatsOption(defaultTls libtls.TLSConfig, log liblog.Logger) (*natsrv.Options, error) { cfg := &natsrv.Options{ CheckConfig: false, } @@ -187,7 +186,7 @@ func (c Config) NatsOption(defaultTls libtls.TLSConfig, log liblog.Logger) (*nat return cfg, nil } -func (c *ConfigCustom) makeOpt(cfg *natsrv.Options, defTls libtls.TLSConfig) liberr.Error { +func (c *ConfigCustom) makeOpt(cfg *natsrv.Options, defTls libtls.TLSConfig) error { if cfg == nil { return ErrorParamsInvalid.Error(nil) } @@ -209,8 +208,8 @@ func (c *ConfigCustom) makeOpt(cfg *natsrv.Options, defTls libtls.TLSConfig) lib } if c.AccountResolverTLS { - if t, e := c.AccountResolverTLSConfig.NewFrom(defTls); e != nil { - return e + if t := c.AccountResolverTLSConfig.NewFrom(defTls); t == nil { + return fmt.Errorf("no valid tls config") } else { cfg.AccountResolverTLSConfig = t.TlsConfig("") } @@ -221,7 +220,7 @@ func (c *ConfigCustom) makeOpt(cfg *natsrv.Options, defTls libtls.TLSConfig) lib return nil } -func (c ConfigAuth) makeOpt(cfg *natsrv.Options) liberr.Error { +func (c ConfigAuth) makeOpt(cfg *natsrv.Options) error { if cfg == nil { return ErrorParamsInvalid.Error(nil) } @@ -285,7 +284,7 @@ func (c ConfigAuth) makeOpt(cfg *natsrv.Options) liberr.Error { return nil } -func (c ConfigNkey) makeOpt(auth ConfigAuth, cfg *natsrv.Options) (*natsrv.NkeyUser, liberr.Error) { +func (c ConfigNkey) makeOpt(auth ConfigAuth, cfg *natsrv.Options) (*natsrv.NkeyUser, error) { if cfg == nil { return nil, ErrorParamsInvalid.Error(nil) } @@ -342,7 +341,7 @@ func (c ConfigNkey) makeOpt(auth ConfigAuth, cfg *natsrv.Options) (*natsrv.NkeyU }, nil } -func (c ConfigUser) makeOpt(auth ConfigAuth, cfg *natsrv.Options) (*natsrv.User, liberr.Error) { +func (c ConfigUser) makeOpt(auth ConfigAuth, cfg *natsrv.Options) (*natsrv.User, error) { if cfg == nil { return nil, ErrorParamsInvalid.Error(nil) } @@ -473,7 +472,7 @@ func (c ConfigPermissionResponse) makeOpt() *natsrv.ResponsePermission { return res } -func (c ConfigLogger) makeOpt(log liblog.Logger, cfg *natsrv.Options) liberr.Error { +func (c ConfigLogger) makeOpt(log liblog.Logger, cfg *natsrv.Options) error { if cfg == nil { return ErrorParamsInvalid.Error(nil) } @@ -540,7 +539,7 @@ func (c ConfigLogger) makeOpt(log liblog.Logger, cfg *natsrv.Options) liberr.Err return nil } -func (c ConfigLimits) makeOpt(cfg *natsrv.Options) liberr.Error { +func (c ConfigLimits) makeOpt(cfg *natsrv.Options) error { if cfg == nil { return ErrorParamsInvalid.Error(nil) } @@ -604,7 +603,7 @@ func (c ConfigLimits) makeOpt(cfg *natsrv.Options) liberr.Error { return nil } -func (c ConfigSrv) makeOpt(cfg *natsrv.Options, defTls libtls.TLSConfig) liberr.Error { +func (c ConfigSrv) makeOpt(cfg *natsrv.Options, defTls libtls.TLSConfig) error { if cfg == nil { return ErrorParamsInvalid.Error(nil) } @@ -733,8 +732,8 @@ func (c ConfigSrv) makeOpt(cfg *natsrv.Options, defTls libtls.TLSConfig) liberr. if c.TLS { cfg.TLS = true - if t, e := c.TLSConfig.NewFrom(defTls); e != nil { - return e + if t := c.TLSConfig.NewFrom(defTls); t == nil { + return fmt.Errorf("no valid tls config") } else { cfg.TLSConfig = t.TlsConfig("") } @@ -757,7 +756,7 @@ func (c ConfigSrv) makeOpt(cfg *natsrv.Options, defTls libtls.TLSConfig) liberr. return nil } -func (c ConfigCluster) makeOpt(defTls libtls.TLSConfig) (natsrv.ClusterOpts, liberr.Error) { +func (c ConfigCluster) makeOpt(defTls libtls.TLSConfig) (natsrv.ClusterOpts, error) { cfg := natsrv.ClusterOpts{ Name: c.Name, Host: c.Host, @@ -786,8 +785,8 @@ func (c ConfigCluster) makeOpt(defTls libtls.TLSConfig) (natsrv.ClusterOpts, lib } if c.TLS { - if t, e := c.TLSConfig.NewFrom(defTls); e != nil { - return cfg, e + if t := c.TLSConfig.NewFrom(defTls); t == nil { + return cfg, fmt.Errorf("no valid tls config") } else { cfg.TLSConfig = t.TlsConfig("") } @@ -803,7 +802,7 @@ func (c ConfigCluster) makeOpt(defTls libtls.TLSConfig) (natsrv.ClusterOpts, lib return cfg, nil } -func (c ConfigGateway) makeOpt(defTls libtls.TLSConfig) (natsrv.GatewayOpts, liberr.Error) { +func (c ConfigGateway) makeOpt(defTls libtls.TLSConfig) (natsrv.GatewayOpts, error) { cfg := natsrv.GatewayOpts{ Name: c.Name, Host: c.Host, @@ -826,8 +825,8 @@ func (c ConfigGateway) makeOpt(defTls libtls.TLSConfig) (natsrv.GatewayOpts, lib } if c.TLS { - if t, e := c.TLSConfig.NewFrom(defTls); e != nil { - return cfg, e + if t := c.TLSConfig.NewFrom(defTls); t == nil { + return cfg, fmt.Errorf("no valid tls config") } else { cfg.TLSConfig = t.TlsConfig("") } @@ -850,7 +849,7 @@ func (c ConfigGateway) makeOpt(defTls libtls.TLSConfig) (natsrv.GatewayOpts, lib return cfg, nil } -func (c ConfigGatewayRemote) makeOpt(defTls libtls.TLSConfig) (*natsrv.RemoteGatewayOpts, liberr.Error) { +func (c ConfigGatewayRemote) makeOpt(defTls libtls.TLSConfig) (*natsrv.RemoteGatewayOpts, error) { res := &natsrv.RemoteGatewayOpts{ Name: "", TLSConfig: nil, @@ -863,8 +862,8 @@ func (c ConfigGatewayRemote) makeOpt(defTls libtls.TLSConfig) (*natsrv.RemoteGat } if c.TLS { - if t, e := c.TLSConfig.NewFrom(defTls); e != nil { - return nil, e + if t := c.TLSConfig.NewFrom(defTls); t == nil { + return nil, fmt.Errorf("no valid tls config") } else { res.TLSConfig = t.TlsConfig("") } @@ -891,7 +890,7 @@ func (c ConfigGatewayRemote) makeOpt(defTls libtls.TLSConfig) (*natsrv.RemoteGat return res, nil } -func (c ConfigLeaf) makeOpt(cfg *natsrv.Options, auth ConfigAuth, defTls libtls.TLSConfig) (natsrv.LeafNodeOpts, liberr.Error) { +func (c ConfigLeaf) makeOpt(cfg *natsrv.Options, auth ConfigAuth, defTls libtls.TLSConfig) (natsrv.LeafNodeOpts, error) { res := natsrv.LeafNodeOpts{ Host: c.Host, Port: c.Port, @@ -924,8 +923,8 @@ func (c ConfigLeaf) makeOpt(cfg *natsrv.Options, auth ConfigAuth, defTls libtls. } if c.TLS { - if t, e := c.TLSConfig.NewFrom(defTls); e != nil { - return res, e + if t := c.TLSConfig.NewFrom(defTls); t == nil { + return res, fmt.Errorf("no valid tls config") } else { res.TLSConfig = t.TlsConfig("") } @@ -951,7 +950,7 @@ func (c ConfigLeaf) makeOpt(cfg *natsrv.Options, auth ConfigAuth, defTls libtls. return res, nil } -func (c ConfigLeafRemote) makeOpt(defTls libtls.TLSConfig) (*natsrv.RemoteLeafOpts, liberr.Error) { +func (c ConfigLeafRemote) makeOpt(defTls libtls.TLSConfig) (*natsrv.RemoteLeafOpts, error) { res := &natsrv.RemoteLeafOpts{ LocalAccount: c.LocalAccount, URLs: make([]*url.URL, 0), @@ -974,8 +973,8 @@ func (c ConfigLeafRemote) makeOpt(defTls libtls.TLSConfig) (*natsrv.RemoteLeafOp if c.TLS { res.TLS = true - if t, e := c.TLSConfig.NewFrom(defTls); e != nil { - return nil, e + if t := c.TLSConfig.NewFrom(defTls); t == nil { + return nil, fmt.Errorf("no valid tls config") } else { res.TLSConfig = t.TlsConfig("") } @@ -1009,7 +1008,7 @@ func (c ConfigLeafRemote) makeOpt(defTls libtls.TLSConfig) (*natsrv.RemoteLeafOp return res, nil } -func (c ConfigWebsocket) makeOpt(defTls libtls.TLSConfig) (natsrv.WebsocketOpts, liberr.Error) { +func (c ConfigWebsocket) makeOpt(defTls libtls.TLSConfig) (natsrv.WebsocketOpts, error) { cfg := natsrv.WebsocketOpts{ Host: c.Host, Port: c.Port, @@ -1044,8 +1043,8 @@ func (c ConfigWebsocket) makeOpt(defTls libtls.TLSConfig) (natsrv.WebsocketOpts, if !c.NoTLS { cfg.NoTLS = false - if t, e := c.TLSConfig.NewFrom(defTls); e != nil { - return cfg, e + if t := c.TLSConfig.NewFrom(defTls); t == nil { + return cfg, fmt.Errorf("no valid tls config") } else { cfg.TLSConfig = t.TlsConfig("") } @@ -1063,7 +1062,7 @@ func (c ConfigWebsocket) makeOpt(defTls libtls.TLSConfig) (natsrv.WebsocketOpts, return cfg, nil } -func (c ConfigMQTT) makeOpt(defTls libtls.TLSConfig) (natsrv.MQTTOpts, liberr.Error) { +func (c ConfigMQTT) makeOpt(defTls libtls.TLSConfig) (natsrv.MQTTOpts, error) { cfg := natsrv.MQTTOpts{ Host: c.Host, Port: c.Port, @@ -1084,8 +1083,8 @@ func (c ConfigMQTT) makeOpt(defTls libtls.TLSConfig) (natsrv.MQTTOpts, liberr.Er } if !c.TLS { - if t, e := c.TLSConfig.NewFrom(defTls); e != nil { - return cfg, e + if t := c.TLSConfig.NewFrom(defTls); t == nil { + return cfg, fmt.Errorf("no valid tls config") } else { cfg.TLSConfig = t.TlsConfig("") } diff --git a/nats/server.go b/nats/server.go index 8d2fa58..a7ed1db 100644 --- a/nats/server.go +++ b/nats/server.go @@ -37,7 +37,6 @@ import ( libtls "github.com/nabbar/golib/certificates" libctx "github.com/nabbar/golib/context" - liberr "github.com/nabbar/golib/errors" montps "github.com/nabbar/golib/monitor/types" libver "github.com/nabbar/golib/version" natsrv "github.com/nats-io/nats-server/v2/server" @@ -50,8 +49,8 @@ const ( ) type Server interface { - Listen(ctx context.Context) liberr.Error - Restart(ctx context.Context) liberr.Error + Listen(ctx context.Context) error + Restart(ctx context.Context) error Shutdown() GetOptions() *natsrv.Options @@ -62,9 +61,9 @@ type Server interface { IsReadyTimeout(parent context.Context, dur time.Duration) bool WaitReady(ctx context.Context, tick time.Duration) - ClientAdvertise(ctx context.Context, tick time.Duration, defTls libtls.TLSConfig, opt Client) (cli *natcli.Conn, err liberr.Error) - ClientCluster(ctx context.Context, tick time.Duration, defTls libtls.TLSConfig, opt Client) (cli *natcli.Conn, err liberr.Error) - ClientServer(ctx context.Context, tick time.Duration, defTls libtls.TLSConfig, opt Client) (cli *natcli.Conn, err liberr.Error) + ClientAdvertise(ctx context.Context, tick time.Duration, defTls libtls.TLSConfig, opt Client) (cli *natcli.Conn, err error) + ClientCluster(ctx context.Context, tick time.Duration, defTls libtls.TLSConfig, opt Client) (cli *natcli.Conn, err error) + ClientServer(ctx context.Context, tick time.Duration, defTls libtls.TLSConfig, opt Client) (cli *natcli.Conn, err error) Monitor(ctx libctx.FuncContext, vrs libver.Version) (montps.Monitor, error) } @@ -89,11 +88,11 @@ type server struct { o *atomic.Value s *atomic.Value r *atomic.Value - e liberr.Error + e error m sync.Mutex } -func (s *server) Listen(ctx context.Context) liberr.Error { +func (s *server) Listen(ctx context.Context) error { if s.IsRunning() || s.IsReady() { s.Shutdown() } @@ -123,7 +122,7 @@ func (s *server) Listen(ctx context.Context) liberr.Error { return nil } -func (s *server) Restart(ctx context.Context) liberr.Error { +func (s *server) Restart(ctx context.Context) error { return s.Listen(ctx) } @@ -212,7 +211,7 @@ func (s *server) WaitReady(ctx context.Context, tick time.Duration) { } } -func (s *server) ClientAdvertise(ctx context.Context, tick time.Duration, defTls libtls.TLSConfig, opt Client) (cli *natcli.Conn, err liberr.Error) { +func (s *server) ClientAdvertise(ctx context.Context, tick time.Duration, defTls libtls.TLSConfig, opt Client) (cli *natcli.Conn, err error) { if o := s.GetOptions(); o != nil && o.ClientAdvertise != "" { opt.Url = s._FormatAddress(o.ClientAdvertise) } else { @@ -222,7 +221,7 @@ func (s *server) ClientAdvertise(ctx context.Context, tick time.Duration, defTls return opt.NewClient(defTls) } -func (s *server) ClientCluster(ctx context.Context, tick time.Duration, defTls libtls.TLSConfig, opt Client) (cli *natcli.Conn, err liberr.Error) { +func (s *server) ClientCluster(ctx context.Context, tick time.Duration, defTls libtls.TLSConfig, opt Client) (cli *natcli.Conn, err error) { s.WaitReady(ctx, tick) if srv := s._GetServer(); srv != nil { @@ -236,7 +235,7 @@ func (s *server) ClientCluster(ctx context.Context, tick time.Duration, defTls l return opt.NewClient(defTls) } -func (s *server) ClientServer(ctx context.Context, tick time.Duration, defTls libtls.TLSConfig, opt Client) (cli *natcli.Conn, err liberr.Error) { +func (s *server) ClientServer(ctx context.Context, tick time.Duration, defTls libtls.TLSConfig, opt Client) (cli *natcli.Conn, err error) { var o *natsrv.Options if o = s.GetOptions(); o == nil { @@ -290,14 +289,14 @@ func (s *server) _SetServer(srv *natsrv.Server) { s.s.Store(srv) } -func (s *server) _GetError() liberr.Error { +func (s *server) _GetError() error { s.m.Lock() defer s.m.Unlock() return s.e } -func (s *server) _SetError(err liberr.Error) { +func (s *server) _SetError(err error) { s.m.Lock() defer s.m.Unlock()