/* tofuproxy -- flexible HTTP/HTTPS proxy, TLS terminator, X.509 TOFU manager, WARC/geminispace browser Copyright (C) 2021-2023 Sergey Matveev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package tofuproxy import ( "crypto/sha256" "crypto/sha512" "crypto/x509" "encoding/hex" "fmt" "log" "github.com/miekg/dns" ) var DNSSrv string func DANE(addr string, cert *x509.Certificate) (bool, bool) { if DNSSrv == "" { return false, false } host, port, err := SplitHostPort(addr) if err != nil { log.Printf("can not split host+port: %s: %+v\n", addr, err) return false, false } if port == "" { port = "443" } m := new(dns.Msg) m.SetQuestion(dns.Fqdn(fmt.Sprintf("_%s._tcp.%s", port, host)), dns.TypeTLSA) msg, err := dns.Exchange(m, DNSSrv) if err != nil { log.Printf("DNS: %+v\n", err) return false, false } if msg.MsgHdr.Rcode != dns.RcodeSuccess { return false, false } exists := false for _, answer := range msg.Answer { tlsa, ok := answer.(*dns.TLSA) if !ok { continue } if tlsa.Usage != 3 { // Non EE continue } exists = true var toMatch []byte switch tlsa.Selector { case 0: toMatch = cert.Raw case 1: toMatch = cert.RawSubjectPublicKeyInfo } var hsh []byte switch tlsa.MatchingType { case 0: hsh = toMatch case 1: our := sha256.Sum256(toMatch) hsh = our[:] case 2: our := sha512.Sum512(toMatch) hsh = our[:] } if tlsa.Certificate == hex.EncodeToString(hsh) { return true, true } } return exists, false }