]> Sergey Matveev's repositories - godlighty.git/blob - tls.go
b2ad433d6ddc0bb3d16c2c0b391b4ae0fadfa8cb
[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         var cert *tls.Certificate
71         if len(HostToECDSACertificate) == 0 {
72                 cert = HostToEdDSACertificate[chi.ServerName]
73         } else {
74                 if CHIHasEdDSA(chi) {
75                         if cert := HostToEdDSACertificate[chi.ServerName]; cert != nil {
76                                 return cert, nil
77                         }
78                 }
79                 cert = HostToECDSACertificate[chi.ServerName]
80         }
81         if cert == nil {
82                 return nil, errors.New("no certificate found")
83         }
84         return cert, nil
85 }
86
87 func GetConfigForClient(chi *tls.ClientHelloInfo) (*tls.Config, error) {
88         var pool *x509.CertPool
89         if CHIHasGOST(chi) {
90                 pool = HostGOSTClientAuth[chi.ServerName]
91         }
92         if pool == nil && (CHIHasEdDSA(chi) || len(HostECDSAClientAuth) == 0) {
93                 pool = HostEdDSAClientAuth[chi.ServerName]
94         }
95         if pool == nil {
96                 pool = HostECDSAClientAuth[chi.ServerName]
97         }
98         if pool == nil {
99                 return nil, nil
100         }
101         return &tls.Config{
102                 GetCertificate: GetCertificate,
103                 NextProtos:     NextProtos,
104                 ClientCAs:      pool,
105                 ClientAuth:     tls.RequireAndVerifyClientCert,
106         }, nil
107 }
108
109 func loadCertificates(
110         host string,
111         cfg *TLSCfg,
112         hostToCertificate *map[string]*tls.Certificate,
113         hostClientAuth *map[string]*x509.CertPool,
114 ) {
115         if cfg == nil {
116                 return
117         }
118         cert, err := tls.LoadX509KeyPair(cfg.Cert, cfg.Key)
119         if err != nil {
120                 log.Fatalln(err)
121         }
122         if cfg.CACert != "" {
123                 data, err := os.ReadFile(cfg.CACert)
124                 if err != nil {
125                         log.Fatalln(err)
126                 }
127                 block, _ := pem.Decode(data)
128                 if block == nil {
129                         log.Fatalln(fmt.Errorf("no PEM found: %s", cfg.CACert))
130                 }
131                 if block.Type != "CERTIFICATE" {
132                         log.Fatalln(fmt.Errorf("non CERTIFICATE: %s", cfg.CACert))
133                 }
134                 cert.Certificate = append(cert.Certificate, block.Bytes)
135         }
136         (*hostToCertificate)[host] = &cert
137         pool := x509.NewCertPool()
138         for _, p := range cfg.ClientCAs {
139                 data, err := os.ReadFile(p)
140                 if err != nil {
141                         log.Fatalln(err)
142                 }
143                 var block *pem.Block
144                 for len(data) > 0 {
145                         block, data = pem.Decode(data)
146                         if block == nil {
147                                 log.Fatalln("can not decode PEM:", p)
148                         }
149                         if block.Type != "CERTIFICATE" {
150                                 continue
151                         }
152                         ca, err := x509.ParseCertificate(block.Bytes)
153                         if err != nil {
154                                 log.Fatalln(err)
155                         }
156                         pool.AddCert(ca)
157                         (*hostClientAuth)[host] = pool
158                 }
159         }
160 }
161
162 func LoadCertificates() {
163         HostToECDSACertificate = make(map[string]*tls.Certificate, len(Hosts))
164         HostECDSAClientAuth = make(map[string]*x509.CertPool)
165         HostToEdDSACertificate = make(map[string]*tls.Certificate, len(Hosts))
166         HostEdDSAClientAuth = make(map[string]*x509.CertPool)
167         HostToGOSTCertificate = make(map[string]*tls.Certificate, len(Hosts))
168         HostGOSTClientAuth = make(map[string]*x509.CertPool)
169         for host, cfg := range Hosts {
170                 loadCertificates(host, cfg.ECDSATLS, &HostToECDSACertificate, &HostECDSAClientAuth)
171                 loadCertificates(host, cfg.EdDSATLS, &HostToEdDSACertificate, &HostEdDSAClientAuth)
172                 loadCertificates(host, cfg.GOSTTLS, &HostToGOSTCertificate, &HostGOSTClientAuth)
173         }
174 }
175
176 func NewTLSConfig() *tls.Config {
177         return &tls.Config{
178                 MinVersion:         tls.VersionTLS12,
179                 NextProtos:         NextProtos,
180                 GetCertificate:     GetCertificate,
181                 GetConfigForClient: GetConfigForClient,
182         }
183 }