From 16a92e8c1ea2a890d841019761be5c9f6b334f7a Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Wed, 6 Oct 2021 17:13:22 +0300 Subject: [PATCH] GOST certificates --- cfg.go | 1 + doc/index.texi | 9 +++- tls.go | 109 ++++++++++++++++++++++++++++++------------------- tls_gost.go | 15 +++++++ tls_nogost.go | 9 ++++ 5 files changed, 100 insertions(+), 43 deletions(-) create mode 100644 tls_gost.go create mode 100644 tls_nogost.go diff --git a/cfg.go b/cfg.go index 9bc1206..e4e9419 100644 --- a/cfg.go +++ b/cfg.go @@ -40,6 +40,7 @@ type Hook func(http.ResponseWriter, *http.Request) bool type HostCfg struct { Root string TLS *TLSCfg + GOSTTLS *TLSCfg DirList bool WebDAV bool Hooks []Hook diff --git a/doc/index.texi b/doc/index.texi index 41a3fd5..9265d5e 100644 --- a/doc/index.texi +++ b/doc/index.texi @@ -23,8 +23,13 @@ dependencies, producing single statically linked executable. Maximal reuse of native libraries capabilities. @item Modern, reliable, secure and fast TLS 1.3 implementation with -ChaCha20-Poly1305, session resumption and GOST cryptography (if built -with @url{http://www.gostls13.cypherpunks.ru/, gostls13}). SNI supported. +ChaCha20-Poly1305, session resumption and SNI. + +@item If built with @url{http://www.gostls13.cypherpunks.ru/, gostls13}, +then @url{http://www.gost.cypherpunks.ru/, GOST} TLS 1.3 cryptography +will be fully supported, with ability to use GOST-based X.509 +certificates if client announces its knowledge of GOST algorithms (with +the fallback to ordinary ECDSA ones). @item HTTP/1.1, @url{https://en.wikipedia.org/wiki/HTTP%2F2, HTTP/2} (only when negotiated during ALPN) and keepalives support. Graceful diff --git a/tls.go b/tls.go index 88f1dc3..e3f62a9 100644 --- a/tls.go +++ b/tls.go @@ -28,12 +28,21 @@ import ( ) var ( - NextProtos = []string{"h2", "http/1.1"} + NextProtos = []string{"h2", "http/1.1"} + HostToCertificate map[string]*tls.Certificate HostClientAuth map[string]*x509.CertPool + + HostToGOSTCertificate map[string]*tls.Certificate + HostGOSTClientAuth map[string]*x509.CertPool ) func GetCertificate(chi *tls.ClientHelloInfo) (*tls.Certificate, error) { + if CHIHasGOST(chi) { + if cert := HostToGOSTCertificate[chi.ServerName]; cert != nil { + return cert, nil + } + } cert := HostToCertificate[chi.ServerName] if cert == nil { return nil, errors.New("no certificate found") @@ -42,7 +51,13 @@ func GetCertificate(chi *tls.ClientHelloInfo) (*tls.Certificate, error) { } func GetConfigForClient(chi *tls.ClientHelloInfo) (*tls.Config, error) { - pool := HostClientAuth[chi.ServerName] + var pool *x509.CertPool + if CHIHasGOST(chi) { + pool = HostGOSTClientAuth[chi.ServerName] + } + if pool == nil { + pool = HostClientAuth[chi.ServerName] + } if pool == nil { return nil, nil } @@ -54,58 +69,70 @@ func GetConfigForClient(chi *tls.ClientHelloInfo) (*tls.Config, error) { }, nil } -func LoadCertificates() { - HostToCertificate = make(map[string]*tls.Certificate, len(Hosts)) - HostClientAuth = make(map[string]*x509.CertPool) - for host, cfg := range Hosts { - if cfg.TLS == nil { - continue +func loadCertificates( + host string, + cfg *TLSCfg, + hostToCertificate *map[string]*tls.Certificate, + hostClientAuth *map[string]*x509.CertPool, +) { + if cfg == nil { + return + } + cert, err := tls.LoadX509KeyPair(cfg.Cert, cfg.Key) + if err != nil { + log.Fatalln(err) + } + if cfg.CACert != "" { + data, err := ioutil.ReadFile(cfg.CACert) + if err != nil { + log.Fatalln(err) + } + block, _ := pem.Decode(data) + if block == nil { + log.Fatalln(fmt.Errorf("no PEM found: %s", cfg.CACert)) } - cert, err := tls.LoadX509KeyPair(cfg.TLS.Cert, cfg.TLS.Key) + if block.Type != "CERTIFICATE" { + log.Fatalln(fmt.Errorf("non CERTIFICATE: %s", cfg.CACert)) + } + cert.Certificate = append(cert.Certificate, block.Bytes) + } + (*hostToCertificate)[host] = &cert + pool := x509.NewCertPool() + for _, p := range cfg.ClientCAs { + data, err := ioutil.ReadFile(p) if err != nil { log.Fatalln(err) } - if cfg.TLS.CACert != "" { - data, err := ioutil.ReadFile(cfg.TLS.CACert) - if err != nil { - log.Fatalln(err) - } - block, _ := pem.Decode(data) + var block *pem.Block + for len(data) > 0 { + block, data = pem.Decode(data) if block == nil { - log.Fatalln(fmt.Errorf("no PEM found: %s", cfg.TLS.CACert)) + log.Fatalln("can not decode PEM:", p) } if block.Type != "CERTIFICATE" { - log.Fatalln(fmt.Errorf("non CERTIFICATE: %s", cfg.TLS.CACert)) + continue } - cert.Certificate = append(cert.Certificate, block.Bytes) - } - HostToCertificate[host] = &cert - pool := x509.NewCertPool() - for _, p := range cfg.TLS.ClientCAs { - data, err := ioutil.ReadFile(p) + ca, err := x509.ParseCertificate(block.Bytes) if err != nil { log.Fatalln(err) } - var block *pem.Block - for len(data) > 0 { - block, data = pem.Decode(data) - if block == nil { - log.Fatalln("can not decode PEM:", p) - } - if block.Type != "CERTIFICATE" { - continue - } - ca, err := x509.ParseCertificate(block.Bytes) - if err != nil { - log.Fatalln(err) - } - pool.AddCert(ca) - } - } - if len(pool.Subjects()) > 0 { - HostClientAuth[host] = pool + pool.AddCert(ca) } } + if len(pool.Subjects()) > 0 { + (*hostClientAuth)[host] = pool + } +} + +func LoadCertificates() { + HostToCertificate = make(map[string]*tls.Certificate, len(Hosts)) + HostClientAuth = make(map[string]*x509.CertPool) + HostToGOSTCertificate = make(map[string]*tls.Certificate, len(Hosts)) + HostGOSTClientAuth = make(map[string]*x509.CertPool) + for host, cfg := range Hosts { + loadCertificates(host, cfg.TLS, &HostToCertificate, &HostClientAuth) + loadCertificates(host, cfg.GOSTTLS, &HostToGOSTCertificate, &HostGOSTClientAuth) + } } func NewTLSConfig() *tls.Config { diff --git a/tls_gost.go b/tls_gost.go new file mode 100644 index 0000000..a4ab28d --- /dev/null +++ b/tls_gost.go @@ -0,0 +1,15 @@ +//go:build !nogostls13 + +package godlighty + +import "crypto/tls" + +func CHIHasGOST(chi *tls.ClientHelloInfo) bool { + for _, ss := range chi.SignatureSchemes { + switch ss { + case tls.GOSTR34102012256A, tls.GOSTR34102012256B, tls.GOSTR34102012256C, tls.GOSTR34102012256D, tls.GOSTR34102012512A, tls.GOSTR34102012512B, tls.GOSTR34102012512C: + return true + } + } + return false +} diff --git a/tls_nogost.go b/tls_nogost.go new file mode 100644 index 0000000..d753ba0 --- /dev/null +++ b/tls_nogost.go @@ -0,0 +1,9 @@ +//go:build nogostls13 + +package godlighty + +import "crypto/tls" + +func CHIHasGOST(chi *tls.ClientHelloInfo) bool { + return false +} -- 2.44.0