boulder/cmd/cert-checker/main_test.go

249 lines
7.6 KiB
Go

// Copyright 2015 ISRG. All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"log"
"math/big"
mrand "math/rand"
"sync"
"testing"
"time"
"github.com/jmhodges/clock"
"golang.org/x/net/context"
"github.com/letsencrypt/boulder/core"
blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/policy"
"github.com/letsencrypt/boulder/sa"
"github.com/letsencrypt/boulder/sa/satest"
"github.com/letsencrypt/boulder/test"
"github.com/letsencrypt/boulder/test/vars"
)
var pa *policy.AuthorityImpl
func init() {
var err error
pa, err = policy.New(map[string]bool{})
if err != nil {
log.Fatal(err)
}
err = pa.SetHostnamePolicyFile("../../test/hostname-policy.json")
if err != nil {
log.Fatal(err)
}
}
func BenchmarkCheckCert(b *testing.B) {
saDbMap, err := sa.NewDbMap(vars.DBConnSA, 0)
if err != nil {
fmt.Println("Couldn't connect to database")
return
}
defer func() {
test.ResetSATestDatabase(b)()
}()
checker := newChecker(saDbMap, clock.Default(), pa, expectedValidityPeriod)
testKey, _ := rsa.GenerateKey(rand.Reader, 1024)
expiry := time.Now().AddDate(0, 0, 1)
serial := big.NewInt(1337)
rawCert := x509.Certificate{
Subject: pkix.Name{
CommonName: "example.com",
},
NotAfter: expiry,
DNSNames: []string{"example-a.com"},
SerialNumber: serial,
}
certDer, _ := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey)
cert := core.Certificate{
Serial: core.SerialToString(serial),
Digest: core.Fingerprint256(certDer),
DER: certDer,
Issued: time.Now(),
Expires: expiry,
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
checker.checkCert(cert)
}
}
func TestCheckCert(t *testing.T) {
saDbMap, err := sa.NewDbMap(vars.DBConnSA, 0)
test.AssertNotError(t, err, "Couldn't connect to database")
saCleanup := test.ResetSATestDatabase(t)
defer func() {
saCleanup()
}()
testKey, _ := rsa.GenerateKey(rand.Reader, 1024)
fc := clock.NewFake()
fc.Add(time.Hour * 24 * 90)
checker := newChecker(saDbMap, fc, pa, expectedValidityPeriod)
issued := checker.clock.Now().Add(-time.Hour * 24 * 45)
goodExpiry := issued.Add(expectedValidityPeriod)
serial := big.NewInt(1337)
// Problems
// Expiry period is too long
// Basic Constraints aren't set
// Wrong key usage (none)
rawCert := x509.Certificate{
Subject: pkix.Name{
CommonName: "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeexample.com",
},
NotBefore: issued,
NotAfter: goodExpiry.AddDate(0, 0, 1), // Period too long
DNSNames: []string{"example-a.com"},
SerialNumber: serial,
BasicConstraintsValid: false,
}
brokenCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey)
test.AssertNotError(t, err, "Couldn't create certificate")
// Problems
// Digest doesn't match
// Serial doesn't match
// Expiry doesn't match
// Issued doesn't match
cert := core.Certificate{
Serial: "8485f2687eba29ad455ae4e31c8679206fec",
DER: brokenCertDer,
Issued: issued.Add(12 * time.Hour),
Expires: goodExpiry.AddDate(0, 0, 2), // Expiration doesn't match
}
problems := checker.checkCert(cert)
problemsMap := map[string]int{
"Stored digest doesn't match certificate digest": 1,
"Stored serial doesn't match certificate serial": 1,
"Stored expiration doesn't match certificate NotAfter": 1,
"Certificate doesn't have basic constraints set": 1,
"Certificate has a validity period longer than 2160h0m0s": 1,
"Stored issuance date is outside of 6 hour window of certificate NotBefore": 1,
"Certificate has incorrect key usage extensions": 1,
"Certificate has common name >64 characters long (65)": 1,
}
for _, p := range problems {
_, ok := problemsMap[p]
if !ok {
t.Errorf("Found unexpected problem '%s'.", p)
}
delete(problemsMap, p)
}
for k := range problemsMap {
t.Errorf("Expected problem but didn't find it: '%s'.", k)
}
test.AssertEquals(t, len(problems), 8)
// Same settings as above, but the stored serial number in the DB is invalid.
cert.Serial = "not valid"
problems = checker.checkCert(cert)
foundInvalidSerialProblem := false
for _, p := range problems {
if p == "Stored serial is invalid" {
foundInvalidSerialProblem = true
}
}
test.Assert(t, foundInvalidSerialProblem, "Invalid certificate serial number in DB did not trigger problem.")
// Fix the problems
rawCert.Subject.CommonName = "example-a.com"
rawCert.NotAfter = goodExpiry
rawCert.BasicConstraintsValid = true
rawCert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
goodCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey)
test.AssertNotError(t, err, "Couldn't create certificate")
parsed, err := x509.ParseCertificate(goodCertDer)
test.AssertNotError(t, err, "Couldn't parse created certificate")
cert.Serial = core.SerialToString(serial)
cert.Digest = core.Fingerprint256(goodCertDer)
cert.DER = goodCertDer
cert.Expires = parsed.NotAfter
cert.Issued = parsed.NotBefore
problems = checker.checkCert(cert)
test.AssertEquals(t, len(problems), 0)
}
func TestGetAndProcessCerts(t *testing.T) {
saDbMap, err := sa.NewDbMap(vars.DBConnSA, 0)
test.AssertNotError(t, err, "Couldn't connect to database")
fc := clock.NewFake()
checker := newChecker(saDbMap, fc, pa, expectedValidityPeriod)
sa, err := sa.NewSQLStorageAuthority(saDbMap, fc, blog.NewMock())
test.AssertNotError(t, err, "Couldn't create SA to insert certificates")
saCleanUp := test.ResetSATestDatabase(t)
defer func() {
saCleanUp()
}()
testKey, _ := rsa.GenerateKey(rand.Reader, 1024)
// Problems
// Expiry period is too long
rawCert := x509.Certificate{
Subject: pkix.Name{
CommonName: "not-blacklisted.com",
},
BasicConstraintsValid: true,
DNSNames: []string{"not-blacklisted.com"},
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
}
reg := satest.CreateWorkingRegistration(t, sa)
test.AssertNotError(t, err, "Couldn't create registration")
for i := int64(0); i < 5; i++ {
rawCert.SerialNumber = big.NewInt(mrand.Int63())
certDER, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey)
test.AssertNotError(t, err, "Couldn't create certificate")
_, err = sa.AddCertificate(context.Background(), certDER, reg.ID)
test.AssertNotError(t, err, "Couldn't add certificate")
}
batchSize = 2
err = checker.getCerts(false)
test.AssertNotError(t, err, "Failed to retrieve certificates")
test.AssertEquals(t, len(checker.certs), 5)
wg := new(sync.WaitGroup)
wg.Add(1)
checker.processCerts(wg, false)
test.AssertEquals(t, checker.issuedReport.BadCerts, int64(5))
test.AssertEquals(t, len(checker.issuedReport.Entries), 5)
}
func TestSaveReport(t *testing.T) {
r := report{
begin: time.Time{},
end: time.Time{},
GoodCerts: 2,
BadCerts: 1,
Entries: map[string]reportEntry{
"020000000000004b475da49b91da5c17": {
Valid: true,
},
"020000000000004d1613e581432cba7e": {
Valid: true,
},
"020000000000004e402bc21035c6634a": {
Valid: false,
Problems: []string{"None really..."},
},
},
}
err := r.dump()
test.AssertNotError(t, err, "Failed to dump results")
}