package main
import (
"crypto/x509"
"encoding/pem"
"fmt"
"html/template"
"io/ioutil"
"os"
"sort"
"time"
)
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...)
}
}
var tmpl = template.Must(template.New("pem2html").
Funcs(template.FuncMap{
"red": red,
"green": green,
}).Parse(`
CT log
`))
var now = time.Now()
type interpolation struct {
ta, tb time.Time
ba, bb byte
}
func (i interpolation) interpolate(tc time.Time) byte {
db := i.tb.Sub(i.ta)
dc := tc.Sub(i.ta)
pct := float64(dc) / float64(db)
if pct < 0 {
pct = 0
} else if pct > 1 {
pct = 1
}
sb := int16(i.bb) - int16(i.ba)
sc := int16(pct * float64(sb))
return byte(int16(i.ba) + sc)
}
var daysago = interpolation{
ta: now.AddDate(0, 0, -30),
tb: now,
ba: 0xF3,
bb: 0x00,
}
var daysuntil = interpolation{
ta: now,
tb: now.AddDate(0, 0, 30),
ba: 0x00,
bb: 0xF3,
}
func green(t time.Time) string {
b := daysago.interpolate(t)
return fmt.Sprintf("#%02X%02X%02X", b, 0xF3, b)
}
func red(t time.Time) string {
b := daysuntil.interpolate(t)
return fmt.Sprintf("#%02X%02X%02X", 0xF3, b, b)
}
type Cert struct {
Url string
Updated time.Time
X509 *x509.Certificate
}
type Certs []Cert
// Len is the number of elements in the collection.
func (l Certs) Len() int {
return len(l)
}
// Less reports whether the element with
// index i should sort before the element with index j.
func (l Certs) Less(i, j int) bool {
return l[i].Updated.UTC().After(l[j].Updated.UTC())
}
// Swap swaps the elements with indexes i and j.
func (l Certs) Swap(i, j int) {
tmp := l[i]
l[i] = l[j]
l[j] = tmp
}
func main() {
data, err := ioutil.ReadAll(os.Stdin)
handleErr(err, "Error reading stdin: %v\n")
var certs Certs
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")
str, ok := certPem.Headers["X-Crt-Sh-Updated"]
handleBool(ok, "Did not get X-Crt-Sh-Updated\n")
cert.Updated, err = time.Parse("2006-01-02T15:04:05Z", str)
handleErr(err, "Could not parse updated time")
cert.X509, err = x509.ParseCertificate(certPem.Bytes)
handleErr(err, "Error parsing cert: %v\n")
certs = append(certs, cert)
}
sort.Sort(certs)
handleErr(tmpl.Execute(os.Stdout, map[string]interface{}{"certs":certs, "now": now}), "Could not execute template: %v\n")
}