From 4840ee95895e3c27b2963393d93b44b6efc0a7b2 Mon Sep 17 00:00:00 2001 From: JeremyRand Date: Wed, 8 Nov 2017 15:57:05 +0000 Subject: [PATCH] (WIP) tlshook: Support non-dehydrated certificates. --- tlshook/tlshook.go | 69 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/tlshook/tlshook.go b/tlshook/tlshook.go index a911f7b..39a78f9 100644 --- a/tlshook/tlshook.go +++ b/tlshook/tlshook.go @@ -43,7 +43,74 @@ func DomainValueHookTLS(qname string, ncv *ncdomain.Value) (err error) { } - // TODO: support non-dehydrated certificates + // For non-dehydrated certificates + // TODO: test this code. + // since this code has not been tested yet, it's disabled for safety reasons. + //if len(port.TLSA) > 0 { + if false { + + log.Info("Just saw a TLS port 443 capable domain request for ", qname, "!") + + for index, cert := range port.TLSA { + + // cert usage 3 is end-entity cert that need not pass CA-based validation + // cert selector 0 is a full certificate (not just public key) + // cert matching type 0 is exact match (not hashed) + if cert.Usage == 3 && cert.Selector == 0 && cert.MatchingType == 0 { + + log.Info("Certificate # ", index, " is usable with hex value ", cert.Certificate) + + origCertBytes, err:= hex.DecodeString(cert.Certificate) + if err != nil { + log.Info("Failed to decode hex string of TLSA certificate, ", err) + continue + } + + origCert, err := x509.ParseCertificate(origCertBytes) + if err != nil { + log.Info("Failed to parse TLSA certificate, ", err) + continue + } + + // TODO: look into being a bit more flexible with cert serial number, validity period, and subject serial number. + // The uniformity in those fields is due to compression rather than security concerns. + // So we could possibly pass those through in cases like this. + // Subject serial number is also there due to transparency concerns, so maybe don't allow customizing it. + + dehydrated, err := certdehydrate.DehydrateCert(origCert) + if err != nil { + log.Info("Failed to dehydrate TLSA certificate, ", err) + continue + } + + rehydrated, err := certdehydrate.RehydrateCert(dehydrated) + if err != nil { + log.Info("Failed to rehydrate TLSA certificate, ", err) + continue + } + + rehydratedDerBytes, err := certdehydrate.FillRehydratedCertTemplate(*rehydrated, qname) + if err != nil { + log.Info("Failed to fill rehydrated TLSA certificate, ", err) + continue + } + + if ! bytes.Equal(origCertBytes, rehydratedDerBytes) { + log.Info("TLSA certificate didn't conform to dehydration template; skipping certificate.") + continue + } + + // TODO: check return value + certinject.InjectCert(rehydratedDerBytes) + + } else { + + log.Info("Certificate # ", index, " is not usable because we cannot recover the full end-entity certificate from the TLSA record.") + + } + + } + } } }