]> Sergey Matveev's repositories - godlighty.git/blob - tls.go
No CSS
[godlighty.git] / tls.go
1 /*
2 godlighty -- highly-customizable HTTP, HTTP/2, HTTPS server
3 Copyright (C) 2021-2023 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package godlighty
19
20 import (
21         "crypto/tls"
22         "crypto/x509"
23         "encoding/pem"
24         "errors"
25         "fmt"
26         "log"
27         "os"
28 )
29
30 var (
31         NextProtos = []string{"h2", "http/1.1"}
32
33         HostToECDSACertificate map[string]*tls.Certificate
34         HostECDSAClientAuth    map[string]*x509.CertPool
35
36         HostToEdDSACertificate map[string]*tls.Certificate
37         HostEdDSAClientAuth    map[string]*x509.CertPool
38
39         HostToGOSTCertificate map[string]*tls.Certificate
40         HostGOSTClientAuth    map[string]*x509.CertPool
41 )
42
43 func CHIHasTLS13(chi *tls.ClientHelloInfo) bool {
44         for _, v := range chi.SupportedVersions {
45                 if v == tls.VersionTLS13 {
46                         return true
47                 }
48         }
49         return false
50 }
51
52 func CHIHasEdDSA(chi *tls.ClientHelloInfo) bool {
53         if !CHIHasTLS13(chi) {
54                 return false
55         }
56         for _, ss := range chi.SignatureSchemes {
57                 if ss == tls.Ed25519 {
58                         return true
59                 }
60         }
61         return false
62 }
63
64 func GetCertificate(chi *tls.ClientHelloInfo) (*tls.Certificate, error) {
65         if CHIHasGOST(chi) {
66                 if cert := HostToGOSTCertificate[chi.ServerName]; cert != nil {
67                         return cert, nil
68                 }
69         }
70         if CHIHasEdDSA(chi) {
71                 if cert := HostToEdDSACertificate[chi.ServerName]; cert != nil {
72                         return cert, nil
73                 }
74         }
75         cert := HostToECDSACertificate[chi.ServerName]
76         if cert == nil {
77                 return nil, errors.New("no certificate found")
78         }
79         return cert, nil
80 }
81
82 func GetConfigForClient(chi *tls.ClientHelloInfo) (*tls.Config, error) {
83         var pool *x509.CertPool
84         if CHIHasGOST(chi) {
85                 pool = HostGOSTClientAuth[chi.ServerName]
86         }
87         if pool == nil && CHIHasEdDSA(chi) {
88                 pool = HostEdDSAClientAuth[chi.ServerName]
89         }
90         if pool == nil {
91                 pool = HostECDSAClientAuth[chi.ServerName]
92         }
93         if pool == nil {
94                 return nil, nil
95         }
96         return &tls.Config{
97                 GetCertificate: GetCertificate,
98                 NextProtos:     NextProtos,
99                 ClientCAs:      pool,
100                 ClientAuth:     tls.RequireAndVerifyClientCert,
101         }, nil
102 }
103
104 func loadCertificates(
105         host string,
106         cfg *TLSCfg,
107         hostToCertificate *map[string]*tls.Certificate,
108         hostClientAuth *map[string]*x509.CertPool,
109 ) {
110         if cfg == nil {
111                 return
112         }
113         cert, err := tls.LoadX509KeyPair(cfg.Cert, cfg.Key)
114         if err != nil {
115                 log.Fatalln(err)
116         }
117         if cfg.CACert != "" {
118                 data, err := os.ReadFile(cfg.CACert)
119                 if err != nil {
120                         log.Fatalln(err)
121                 }
122                 block, _ := pem.Decode(data)
123                 if block == nil {
124                         log.Fatalln(fmt.Errorf("no PEM found: %s", cfg.CACert))
125                 }
126                 if block.Type != "CERTIFICATE" {
127                         log.Fatalln(fmt.Errorf("non CERTIFICATE: %s", cfg.CACert))
128                 }
129                 cert.Certificate = append(cert.Certificate, block.Bytes)
130         }
131         (*hostToCertificate)[host] = &cert
132         pool := x509.NewCertPool()
133         for _, p := range cfg.ClientCAs {
134                 data, err := os.ReadFile(p)
135                 if err != nil {
136                         log.Fatalln(err)
137                 }
138                 var block *pem.Block
139                 for len(data) > 0 {
140                         block, data = pem.Decode(data)
141                         if block == nil {
142                                 log.Fatalln("can not decode PEM:", p)
143                         }
144                         if block.Type != "CERTIFICATE" {
145                                 continue
146                         }
147                         ca, err := x509.ParseCertificate(block.Bytes)
148                         if err != nil {
149                                 log.Fatalln(err)
150                         }
151                         pool.AddCert(ca)
152                         (*hostClientAuth)[host] = pool
153                 }
154         }
155 }
156
157 func LoadCertificates() {
158         HostToECDSACertificate = make(map[string]*tls.Certificate, len(Hosts))
159         HostECDSAClientAuth = make(map[string]*x509.CertPool)
160         HostToEdDSACertificate = make(map[string]*tls.Certificate, len(Hosts))
161         HostEdDSAClientAuth = make(map[string]*x509.CertPool)
162         HostToGOSTCertificate = make(map[string]*tls.Certificate, len(Hosts))
163         HostGOSTClientAuth = make(map[string]*x509.CertPool)
164         for host, cfg := range Hosts {
165                 loadCertificates(host, cfg.ECDSATLS, &HostToECDSACertificate, &HostECDSAClientAuth)
166                 loadCertificates(host, cfg.EdDSATLS, &HostToEdDSACertificate, &HostEdDSAClientAuth)
167                 loadCertificates(host, cfg.GOSTTLS, &HostToGOSTCertificate, &HostGOSTClientAuth)
168         }
169 }
170
171 func NewTLSConfig() *tls.Config {
172         return &tls.Config{
173                 MinVersion:         tls.VersionTLS12,
174                 NextProtos:         NextProtos,
175                 GetCertificate:     GetCertificate,
176                 GetConfigForClient: GetConfigForClient,
177         }
178 }