Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ jobs:
- name: Update Mozilla Included CA Certificate List
run: go generate .

- name: Run checks of Mozilla Included CA Certificate List
run: go test -v ./...

- name: Commit changes
id: commit_changes
uses: EndBug/add-and-commit@v9
Expand Down
118 changes: 118 additions & 0 deletions embedded/rootcerts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package embedded_test

import (
"crypto/x509"
"encoding/pem"
"testing"
"time"

"github.com/breml/rootcerts/embedded"
)

func parsePEM(pemCerts []byte) (certs []*x509.Certificate, err error) {
for len(pemCerts) > 0 {
var block *pem.Block

block, pemCerts = pem.Decode(pemCerts)
if block == nil {
break
}

if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
continue
}

cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}

certs = append(certs, cert)
}
return
}

func checkRootCertsPEM(t *testing.T, pemCerts []byte, whenFail time.Time, whenWarn time.Time) (ok bool) {
now := time.Now()
t.Logf("Checking certificate validity on %s...", whenFail)
certs, err := parsePEM(pemCerts)
if err != nil {
t.Error(err)
return false
}

roots := x509.NewCertPool()
for _, cert := range certs {
roots.AddCert(cert)
}

var minExpires time.Time
var minExpiresName string
ok = true
for _, cert := range certs {
name := cert.Subject.CommonName
if name == "" {
name = cert.Subject.String() + " (⚠️ missing CommonName)"
if name == "" {
name = cert.Issuer.String()
}
}

if !cert.IsCA {
t.Errorf("❌ %s: not a certificate authority", name)
}

const keyUsageExpected = x509.KeyUsageCertSign | x509.KeyUsageCRLSign | x509.KeyUsageDigitalSignature
if (cert.KeyUsage &^ keyUsageExpected) != 0 {
t.Logf("⚠️ %s: unexpected key usage %#x (expecting %#x, see constants at https://pkg.go.dev/crypto/x509#KeyUsage)", name, cert.KeyUsage, keyUsageExpected)
}

if minExpires.IsZero() || cert.NotAfter.Before(minExpires) {
minExpires = cert.NotAfter
minExpiresName = name
}

// Check that the certificate is valid now
if cert.NotBefore.After(now) {
t.Errorf("❌ %s: fails NotBefore check: %s", name, cert.NotBefore)
continue
}

// ... and that it will still be valid later
if cert.NotAfter.Before(whenFail) {
t.Errorf("❌ %s: fails NotAfter check: %s", name, cert.NotAfter)
continue
}

if cert.NotAfter.Before(whenWarn) {
t.Logf("⚠️ %s: fails NotAfter check: %s", name, cert.NotAfter)
}

_, err := cert.Verify(x509.VerifyOptions{
Roots: roots,
CurrentTime: whenFail,
})
if err != nil {
t.Errorf("❌ %s: %s", name, err)
ok = false
continue
}

t.Logf("✅ %s (expires: %s)", name, cert.NotAfter)
}

if ok {
t.Log("Success.")
t.Logf("MinExpire: %s (Certificate: %s)", minExpires, minExpiresName)
}

return
}

func TestCerts(t *testing.T) {
// Check that certificates will still be valid in 1 month, warn if invalid in 3 months
checkRootCertsPEM(t, []byte(embedded.MozillaCACertificatesPEM()), time.Now().AddDate(0, 1, 0), time.Now().AddDate(0, 3, 0))

// Should fail
// checkRootCertsPEM(t, []byte(embedded.MozillaCACertificatesPEM()), time.Now().AddDate(20, 0, 0), time.Now().AddDate(30, 0, 0))
}
Loading