diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 11 | ||||
-rw-r--r-- | diff-pem2html.go | 144 | ||||
-rw-r--r-- | diff.go | 173 |
4 files changed, 192 insertions, 137 deletions
@@ -7,4 +7,5 @@ NET-* crtsh-pem2html tls-getcerts tls-pem2html +diff-pem2html diff @@ -9,18 +9,15 @@ all: diff.html crtsh.pem: crtsh-getcerts config-domains.txt NET-crtsh ./crtsh-getcerts $$(cat config-domains.txt) > $@ -crtsh.html: %.html: %.pem crtsh-pem2html - ./crtsh-pem2html < $< > $@ - tls.pem: tls-getcerts config-sockets.txt NET-tls ./tls-getcerts $$(cat config-sockets.txt) > $@ -tls.html: %.html: %.pem tls-pem2html - ./tls-pem2html < $< > $@ - -diff.html: diff tls.pem crtsh.pem +diff.pem: diff tls.pem crtsh.pem ./diff tls.pem crtsh.pem > $@ +tls.html crtsh.html diff.html: %.html: %.pem %-pem2html + ./$*-pem2html < $< > $@ + NET-%: date > $@ diff --git a/diff-pem2html.go b/diff-pem2html.go new file mode 100644 index 0000000..8f3b1e9 --- /dev/null +++ b/diff-pem2html.go @@ -0,0 +1,144 @@ +package main + +import ( + "crypto/x509" + "encoding/pem" + "fmt" + "html/template" + "io/ioutil" + "os" +) + +func handleErr(err error, str string, a ...interface{}) { + a = append([]interface{}{err}, a...) + if err != nil { + fmt.Fprintf(os.Stderr, str, a...) + os.Exit(1) + } +} + +func handleBool(ok bool, str string, a ...interface{}) { + if !ok { + fmt.Fprintf(os.Stderr, str, a...) + os.Exit(1) + } +} + +var tmpl = template.Must(template.New("2html").Parse(`<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>CT log accuracy</title> + <style> + html { + height: 100%; + } + body { + font-size: 10px; + font-family: monospace; + height: 100%; + margin: 0; + display: flex; + align-items: center; + } + body > * { + margin: auto; + } + .diff-del, .diff-del a { color: red; } + .diff-add, .diff-add a { color: green; } + .diff-dat, .diff-dat a { color: blue; } + .diff-ctx, .diff-ctx a { color: black; } + tr:hover a { + background-color: #AAAAF3; + } + td a { + text-decoration: none; + padding: 0.1em 0.25em; + display: block; + width: 100%; + height: 100%; + color: black; + } + </style> + <script src="sorttable.js"></script> + <base target="_parent" /> +</head> +<body> +<table> + <tr class="diff-del"><td colspan=4>--- tls.pem</td></tr> + <tr class="diff-add"><td colspan=4>+++ crtsh.pem</td></tr> + <tr class="diff-dat"><td colspan=4>@@ -1,{{.nTLS}} +1,{{.nCrtSh}} @@</td></tr> +{{range $cert := .certs}} + <tr class={{$cert.Class}}> + <td><a href="{{$cert.Url}}">{{if eq $cert.Pfix " "}} {{else}}{{$cert.Pfix}}{{end}}</a></td> + <td><a href="{{$cert.Url}}">{{$cert.X509.Subject.CommonName}}</a></td> + <td><a href="{{$cert.Url}}">{{$cert.X509.NotBefore.Local.Format "2006-01-02 15:04:05"}}</a></td> + <td><a href="{{$cert.Url}}">{{$cert.X509.NotAfter.Local.Format "2006-01-02 15:04:05"}}</a></td> + </tr> +{{end}} +</table> +</body> +</html> +`)) + +type Cert struct { + Url string + action string + X509 *x509.Certificate +} + +func (cert Cert) Pfix() string { + return map[string]string { + "add": "+", + "del": "-", + "ctx": " ", + }[cert.action] +} + +func (cert Cert) Class() string { + return "diff-" + cert.action +} + +func main() { + data, err := ioutil.ReadAll(os.Stdin) + handleErr(err, "Error reading stdin: %v\n") + + var certs []Cert + a := 0 + b := 0 + for len(data) > 0 { + var certPem *pem.Block + certPem, data = pem.Decode(data) + + var ok bool + var cert Cert + + cert.Url, ok = certPem.Headers["X-Crt-Sh-Url"] + handleBool(ok, "Did not get X-Crt-Sh-Url\n") + + cert.action, ok = certPem.Headers["X-Diff-Action"] + handleBool(ok, "Did not get X-Diff-Action\n") + switch cert.action { + case "add": + b++ + case "del": + a++ + case "ctx": + a++ + b++ + default: + handleBool(false, "Unknown X-Diff-Action: %q\n", cert.action) + } + + cert.X509, err = x509.ParseCertificate(certPem.Bytes) + handleErr(err, "Error parsing cert: %v\n") + + certs = append(certs, cert) + } + + handleErr(tmpl.Execute(os.Stdout, map[string]interface{}{ + "certs":certs, + "nTLS": a, + "nCrtSh": b, + }), "Could not execute template: %v\n") +} @@ -4,8 +4,8 @@ import ( "crypto/x509" "encoding/pem" "fmt" + "io" "io/ioutil" - "html/template" "os" "sort" ) @@ -18,7 +18,24 @@ func handleErr(err error, str string, a ...interface{}) { } } -func readTLS(filename string) (map[string]*x509.Certificate, error) { +type Cert struct { + Url string + X509 *x509.Certificate +} + +func (cert Cert) WriteTo(w io.Writer, action string) error { + block := pem.Block{ + Type: "CERTIFICATE", + Headers: map[string]string{ + "X-Crt-Sh-Url": cert.Url, + "X-Diff-Action": action, + }, + Bytes: cert.X509.Raw, + } + return pem.Encode(w, &block) +} + +func readTLS(filename string) (map[string]Cert, error) { file, err := os.Open(filename) if err != nil { return nil, err @@ -28,7 +45,7 @@ func readTLS(filename string) (map[string]*x509.Certificate, error) { return nil, err } - ret := make(map[string]*x509.Certificate) + ret := make(map[string]Cert) for len(data) > 0 { var certPem *pem.Block certPem, data = pem.Decode(data) @@ -36,12 +53,15 @@ func readTLS(filename string) (map[string]*x509.Certificate, error) { if err != nil { return nil, err } - ret[certX509.Subject.CommonName] = certX509 + ret[certX509.Subject.CommonName] = Cert{ + Url: fmt.Sprintf("https://crt.sh/?serial=%036x", certX509.SerialNumber), + X509: certX509, + } } return ret, nil } -func readCrtSh(filename string, hosts []string) (map[string]*x509.Certificate, error) { +func readCrtSh(filename string, hosts []string) (map[string]Cert, error) { file, err := os.Open(filename) if err != nil { return nil, err @@ -51,7 +71,7 @@ func readCrtSh(filename string, hosts []string) (map[string]*x509.Certificate, e return nil, err } - ret := make(map[string]*x509.Certificate) + ret := make(map[string]Cert) for len(data) > 0 { var certPem *pem.Block certPem, data = pem.Decode(data) @@ -61,8 +81,11 @@ func readCrtSh(filename string, hosts []string) (map[string]*x509.Certificate, e } for _, host := range hosts { if certX509.VerifyHostname(host) == nil { - if old, haveold := ret[host]; !haveold || certX509.NotBefore.After(old.NotBefore) { - ret[host] = certX509 + if old, haveold := ret[host]; !haveold || certX509.NotBefore.After(old.X509.NotBefore) { + ret[host] = Cert{ + Url: certPem.Headers["X-Crt-Sh-Url"], + X509: certX509, + } } } } @@ -70,7 +93,7 @@ func readCrtSh(filename string, hosts []string) (map[string]*x509.Certificate, e return ret, nil } -func keys(m map[string]*x509.Certificate) []string { +func keys(m map[string]Cert) []string { ret := make([]string, len(m)) i := 0 for k := range m { @@ -79,113 +102,6 @@ func keys(m map[string]*x509.Certificate) []string { } sort.Strings(ret) return ret -} - -func fmtCert(cert *x509.Certificate) string { - return fmt.Sprintf("%s\t%s\t%s", - cert.Subject.CommonName, - cert.NotBefore.Format("2006-01-02 15:04:05 MST(-07)"), - cert.NotAfter.Format("2006-01-02 15:04:05 MST(-07)")) -} - -var tmpl = template.Must(template.New("2html"). - Funcs(template.FuncMap{ - "class": class, - "link": link, - "join": join, - "isNil": isNil, - }).Parse(`<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"> - <title>CT log accuracy</title> - <style> - html { - height: 100%; - } - body { - font-size: 10px; - font-family: monospace; - height: 100%; - margin: 0; - display: flex; - align-items: center; - } - body > * { - margin: auto; - } - .diff-del, .diff-del a { color: red; } - .diff-add, .diff-add a { color: green; } - .diff-dat, .diff-dat a { color: blue; } - .diff-ctx, .diff-ctx a { color: black; } - tr:hover a { - background-color: #AAAAF3; - } - td a { - text-decoration: none; - padding: 0.1em 0.25em; - display: block; - width: 100%; - height: 100%; - color: black; - } - </style> - <script src="sorttable.js"></script> - <base target="_parent" /> -</head> -<body> -<table> - <tr class="diff-del"><td colspan=4>--- {{.fileTLS}}</td></tr> - <tr class="diff-add"><td colspan=4>+++ {{.fileCrtSh}}</td></tr> - <tr class="diff-dat"><td colspan=4>@@ -1,{{len .certsTLS}} +1,{{len .certsCrtSh}} @@</td></tr> -{{define "Row"}} - <tr class={{.pfix | class}}> - <td><a href="{{.cert | link}}">{{if eq .pfix " "}} {{else}}{{.pfix}}{{end}}</a></td> - <td><a href="{{.cert | link}}">{{.cert.Subject.CommonName}}</a></td> - <td><a href="{{.cert | link}}">{{.cert.NotBefore.Local.Format "2006-01-02 15:04:05"}}</a></td> - <td><a href="{{.cert | link}}">{{.cert.NotAfter.Local.Format "2006-01-02 15:04:05"}}</a></td> - </tr> -{{end}} -{{range $cert := .certs}} -{{if isNil $cert.CrtSh}} -{{template "Row" join "-" $cert.TLS}} -{{else if $cert.TLS.Equal $cert.CrtSh | not}} -{{template "Row" join "-" $cert.TLS}} -{{template "Row" join "+" $cert.CrtSh}} -{{else}} -{{template "Row" join " " $cert.TLS}} -{{end}} -{{end}} -</table> -</body> -</html> -`)) - -type Cert struct { - TLS, CrtSh *x509.Certificate -} - -func link(cert *x509.Certificate) string { - return fmt.Sprintf("https://crt.sh/?serial=%036x", cert.SerialNumber) -} - -func isNil(cert *x509.Certificate) bool { - return cert == nil -} - -func class(pfix string) string { - return map[string]string { - "+": "diff-add", - "-": "diff-del", - " ": "diff-ctx", - }[pfix] -} - -func join(pfix string, cert *x509.Certificate) map[string]interface{} { - return map[string]interface{} { - "pfix": pfix, - "cert": cert, - } } func main() { @@ -198,20 +114,17 @@ func main() { certsCrtSh, err := readCrtSh(os.Args[2], hostsTLS) handleErr(err, "Could load crt.sh file: %v\n") - certs := make([]Cert, len(certsTLS)) - i := 0 for _, host := range hostsTLS { - var cert Cert - cert.TLS = certsTLS[host] - cert.CrtSh = certsCrtSh[host] - certs[i] = cert - i++ + certTLS := certsTLS[host] + certCrtSh, haveCrtSh := certsCrtSh[host] + + if !haveCrtSh { + handleErr(certTLS.WriteTo(os.Stdout, "del"), "Could not encode PEM: %v\n") + } else if ! certTLS.X509.Equal(certCrtSh.X509) { + handleErr(certTLS.WriteTo(os.Stdout, "del"), "Could not encode PEM: %v\n") + handleErr(certCrtSh.WriteTo(os.Stdout, "add"), "Could not encode PEM: %v\n") + } else { + handleErr(certCrtSh.WriteTo(os.Stdout, "ctx"), "Could not encode PEM: %v\n") + } } - handleErr(tmpl.Execute(os.Stdout, map[string]interface{}{ - "certs": certs, - "certsTLS": certsTLS, - "certsCrtSh": certsCrtSh, - "fileTLS": os.Args[1], - "fileCrtSh": os.Args[2], - }), "Could not execute template: %v\n") } |