]> Sergey Matveev's repositories - tofuproxy.git/blob - tls/dane.go
f2db78331b3d2f448002f8af2632fe0d834f7e62
[tofuproxy.git] / tls / dane.go
1 /*
2 tofuproxy -- flexible HTTP/HTTPS proxy, TLS terminator, X.509 TOFU
3              manager, WARC/geminispace browser
4 Copyright (C) 2021-2023 Sergey Matveev <stargrave@stargrave.org>
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, version 3 of the License.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package tofuproxy
20
21 import (
22         "crypto/sha256"
23         "crypto/sha512"
24         "crypto/x509"
25         "encoding/hex"
26         "fmt"
27         "log"
28
29         "github.com/miekg/dns"
30 )
31
32 var DNSSrv string
33
34 func DANE(addr string, cert *x509.Certificate) (bool, bool) {
35         if DNSSrv == "" {
36                 return false, false
37         }
38         host, port, err := SplitHostPort(addr)
39         if err != nil {
40                 log.Printf("can not split host+port: %s: %+v\n", addr, err)
41                 return false, false
42         }
43         if port == "" {
44                 port = "443"
45         }
46         m := new(dns.Msg)
47         m.SetQuestion(dns.Fqdn(fmt.Sprintf("_%s._tcp.%s", port, host)), dns.TypeTLSA)
48         msg, err := dns.Exchange(m, DNSSrv)
49         if err != nil {
50                 log.Printf("DNS: %+v\n", err)
51                 return false, false
52         }
53         if msg.MsgHdr.Rcode != dns.RcodeSuccess {
54                 return false, false
55         }
56         exists := false
57         for _, answer := range msg.Answer {
58                 tlsa, ok := answer.(*dns.TLSA)
59                 if !ok {
60                         continue
61                 }
62                 if tlsa.Usage != 3 {
63                         // Non EE
64                         continue
65                 }
66                 exists = true
67                 var toMatch []byte
68                 switch tlsa.Selector {
69                 case 0:
70                         toMatch = cert.Raw
71                 case 1:
72                         toMatch = cert.RawSubjectPublicKeyInfo
73                 }
74                 var hsh []byte
75                 switch tlsa.MatchingType {
76                 case 0:
77                         hsh = toMatch
78                 case 1:
79                         our := sha256.Sum256(toMatch)
80                         hsh = our[:]
81                 case 2:
82                         our := sha512.Sum512(toMatch)
83                         hsh = our[:]
84                 }
85                 if tlsa.Certificate == hex.EncodeToString(hsh) {
86                         return true, true
87                 }
88         }
89         return exists, false
90 }