From e18d36ebae30536c77c61cd5da123991e0ca1629 Mon Sep 17 00:00:00 2001
From: Rasmus Dahlberg <rasmus@rgdd.se>
Date: Sun, 31 Dec 2023 09:39:25 +0100
Subject: Add drafty prototype

---
 pkg/crtutil/crt_util.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 pkg/crtutil/crt_util.go

(limited to 'pkg/crtutil')

diff --git a/pkg/crtutil/crt_util.go b/pkg/crtutil/crt_util.go
new file mode 100644
index 0000000..11bcd7e
--- /dev/null
+++ b/pkg/crtutil/crt_util.go
@@ -0,0 +1,71 @@
+// Package crtutil provides utility functions for working with certificates.
+package crtutil
+
+import (
+	"crypto/sha256"
+	"encoding/pem"
+	"fmt"
+
+	ct "github.com/google/certificate-transparency-go"
+	"github.com/google/certificate-transparency-go/x509"
+)
+
+// CertificateChainFromPEM parses a certificate chain in PEM format.  At least
+// one certificate must be in the chain.  The first certificate must be a leaf,
+// whereas all other certificates must CA certificates (intermdiates/roots).
+func CertificateChainFromPEM(b []byte) ([]x509.Certificate, error) {
+	var chain []x509.Certificate
+
+	for {
+		block, rest := pem.Decode(b)
+		if block == nil {
+			break
+		}
+		crt, err := x509.ParseCertificate(block.Bytes)
+		if err != nil {
+			return nil, fmt.Errorf("parse certificate: %v", err)
+		}
+
+		chain = append(chain, *crt)
+		b = rest
+	}
+
+	if len(chain) == 0 {
+		return nil, fmt.Errorf("no certificates in the provided chain")
+	}
+	if chain[0].IsCA {
+		return nil, fmt.Errorf("leaf certificate has the CA bit set")
+	}
+	for _, crt := range chain[1:] {
+		if !crt.IsCA {
+			return nil, fmt.Errorf("non-leaf certificate without the CA bit set")
+		}
+	}
+
+	return chain, nil
+}
+
+// CertificateFromLogEntry parses the (pre-)certificate in a log entry
+func CertificateFromLogEntry(leafData, extraData []byte) (x509.Certificate, error) {
+	entry, err := ct.LogEntryFromLeaf(0, &ct.LeafEntry{LeafInput: leafData, ExtraData: extraData})
+	if err != nil {
+		return x509.Certificate{}, fmt.Errorf("parse leaf: %v", err)
+	}
+	if entry.Precert == nil && entry.X509Cert == nil {
+		return x509.Certificate{}, fmt.Errorf("neither precertificate nor certificate in leaf")
+	}
+	if entry.Precert != nil && entry.X509Cert != nil {
+		return x509.Certificate{}, fmt.Errorf("both certificate and precertificate in leaf")
+	}
+	if entry.Precert != nil {
+		return *entry.Precert.TBSCertificate, nil
+	}
+	return *entry.X509Cert, nil
+}
+
+// UniqueID derives a unique certificate ID.  The same value is derived
+// regardless of if the (pre-)certificate is logged multiple times.
+func UniqueID(crt x509.Certificate) string {
+	h := sha256.Sum256([]byte(crt.SerialNumber.String()))
+	return fmt.Sprintf("FIXME:%x", h[:]) // not a secure mapping
+}
-- 
cgit v1.2.3