boulder/cmd/ocsp-updater/main_test.go

362 lines
12 KiB
Go

package main
import (
"crypto/x509"
"testing"
"time"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1"
"github.com/letsencrypt/boulder/cmd"
"github.com/letsencrypt/boulder/core"
"github.com/letsencrypt/boulder/mocks"
"github.com/letsencrypt/boulder/sa"
"github.com/letsencrypt/boulder/sa/satest"
"github.com/letsencrypt/boulder/test"
"github.com/letsencrypt/boulder/test/vars"
)
type mockCA struct{}
func (ca *mockCA) IssueCertificate(csr x509.CertificateRequest, regID int64) (core.Certificate, error) {
return core.Certificate{}, nil
}
func (ca *mockCA) GenerateOCSP(xferObj core.OCSPSigningRequest) (ocsp []byte, err error) {
ocsp = []byte{1, 2, 3}
return
}
type mockPub struct {
sa core.StorageAuthority
}
func (p *mockPub) SubmitToCT(_ []byte) error {
return p.sa.AddSCTReceipt(core.SignedCertificateTimestamp{
SCTVersion: 0,
LogID: "id",
Timestamp: 0,
Extensions: []byte{},
Signature: []byte{0},
CertificateSerial: "00",
})
}
var log = mocks.UseMockLog()
func setup(t *testing.T) (*OCSPUpdater, core.StorageAuthority, *gorp.DbMap, clock.FakeClock, func()) {
dbMap, err := sa.NewDbMap(vars.DBConnSA)
test.AssertNotError(t, err, "Failed to create dbMap")
fc := clock.NewFake()
fc.Add(1 * time.Hour)
sa, err := sa.NewSQLStorageAuthority(dbMap, fc)
test.AssertNotError(t, err, "Failed to create SA")
cleanUp := test.ResetSATestDatabase(t)
stats, _ := statsd.NewNoopClient(nil)
updater, err := newUpdater(
stats,
fc,
dbMap,
&mockCA{},
&mockPub{sa},
sa,
cmd.OCSPUpdaterConfig{
NewCertificateBatchSize: 1,
OldOCSPBatchSize: 1,
MissingSCTBatchSize: 1,
NewCertificateWindow: cmd.ConfigDuration{Duration: time.Second},
OldOCSPWindow: cmd.ConfigDuration{Duration: time.Second},
MissingSCTWindow: cmd.ConfigDuration{Duration: time.Second},
},
0,
"",
)
return updater, sa, dbMap, fc, cleanUp
}
func TestGenerateAndStoreOCSPResponse(t *testing.T) {
updater, sa, _, _, cleanUp := setup(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
parsedCert, err := core.LoadCert("test-cert.pem")
test.AssertNotError(t, err, "Couldn't read test certificate")
_, err = sa.AddCertificate(parsedCert.Raw, reg.ID)
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
status, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber))
test.AssertNotError(t, err, "Couldn't get the core.CertificateStatus from the database")
meta, err := updater.generateResponse(status)
test.AssertNotError(t, err, "Couldn't generate OCSP response")
err = updater.storeResponse(meta)
test.AssertNotError(t, err, "Couldn't store certificate status")
secondMeta, err := updater.generateRevokedResponse(status)
test.AssertNotError(t, err, "Couldn't generate revoked OCSP response")
err = updater.storeResponse(secondMeta)
test.AssertNotError(t, err, "Couldn't store certificate status")
newStatus, err := sa.GetCertificateStatus(status.Serial)
test.AssertNotError(t, err, "Couldn't retrieve certificate status")
test.AssertByteEquals(t, meta.OCSPResponse, newStatus.OCSPResponse)
}
func TestGenerateOCSPResponses(t *testing.T) {
updater, sa, _, fc, cleanUp := setup(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
parsedCert, err := core.LoadCert("test-cert.pem")
test.AssertNotError(t, err, "Couldn't read test certificate")
_, err = sa.AddCertificate(parsedCert.Raw, reg.ID)
test.AssertNotError(t, err, "Couldn't add test-cert.pem")
parsedCert, err = core.LoadCert("test-cert-b.pem")
test.AssertNotError(t, err, "Couldn't read test certificate")
_, err = sa.AddCertificate(parsedCert.Raw, reg.ID)
test.AssertNotError(t, err, "Couldn't add test-cert-b.pem")
earliest := fc.Now().Add(-time.Hour)
certs, err := updater.findStaleOCSPResponses(earliest, 10)
test.AssertNotError(t, err, "Couldn't find stale responses")
test.AssertEquals(t, len(certs), 2)
updater.generateOCSPResponses(certs)
certs, err = updater.findStaleOCSPResponses(earliest, 10)
test.AssertNotError(t, err, "Failed to find stale responses")
test.AssertEquals(t, len(certs), 0)
}
func TestFindStaleOCSPResponses(t *testing.T) {
updater, sa, _, fc, cleanUp := setup(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
parsedCert, err := core.LoadCert("test-cert.pem")
test.AssertNotError(t, err, "Couldn't read test certificate")
_, err = sa.AddCertificate(parsedCert.Raw, reg.ID)
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
earliest := fc.Now().Add(-time.Hour)
certs, err := updater.findStaleOCSPResponses(earliest, 10)
test.AssertNotError(t, err, "Couldn't find certificate")
test.AssertEquals(t, len(certs), 1)
status, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber))
test.AssertNotError(t, err, "Couldn't get the core.Certificate from the database")
meta, err := updater.generateResponse(status)
test.AssertNotError(t, err, "Couldn't generate OCSP response")
err = updater.storeResponse(meta)
test.AssertNotError(t, err, "Couldn't store OCSP response")
certs, err = updater.findStaleOCSPResponses(earliest, 10)
test.AssertNotError(t, err, "Failed to find stale responses")
test.AssertEquals(t, len(certs), 0)
}
func TestGetCertificatesWithMissingResponses(t *testing.T) {
updater, sa, _, _, cleanUp := setup(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
cert, err := core.LoadCert("test-cert.pem")
test.AssertNotError(t, err, "Couldn't read test certificate")
_, err = sa.AddCertificate(cert.Raw, reg.ID)
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
statuses, err := updater.getCertificatesWithMissingResponses(10)
test.AssertNotError(t, err, "Couldn't get status")
test.AssertEquals(t, len(statuses), 1)
}
func TestFindRevokedCertificatesToUpdate(t *testing.T) {
updater, sa, _, _, cleanUp := setup(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
cert, err := core.LoadCert("test-cert.pem")
test.AssertNotError(t, err, "Couldn't read test certificate")
_, err = sa.AddCertificate(cert.Raw, reg.ID)
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
statuses, err := updater.findRevokedCertificatesToUpdate(10)
test.AssertNotError(t, err, "Failed to find revoked certificates")
test.AssertEquals(t, len(statuses), 0)
err = sa.MarkCertificateRevoked(core.SerialToString(cert.SerialNumber), core.RevocationCode(1))
test.AssertNotError(t, err, "Failed to revoke certificate")
statuses, err = updater.findRevokedCertificatesToUpdate(10)
test.AssertNotError(t, err, "Failed to find revoked certificates")
test.AssertEquals(t, len(statuses), 1)
}
func TestNewCertificateTick(t *testing.T) {
updater, sa, _, fc, cleanUp := setup(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
parsedCert, err := core.LoadCert("test-cert.pem")
test.AssertNotError(t, err, "Couldn't read test certificate")
_, err = sa.AddCertificate(parsedCert.Raw, reg.ID)
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
prev := fc.Now().Add(-time.Hour)
updater.newCertificateTick(10)
certs, err := updater.findStaleOCSPResponses(prev, 10)
test.AssertNotError(t, err, "Failed to find stale responses")
test.AssertEquals(t, len(certs), 0)
}
func TestOldOCSPResponsesTick(t *testing.T) {
updater, sa, _, fc, cleanUp := setup(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
parsedCert, err := core.LoadCert("test-cert.pem")
test.AssertNotError(t, err, "Couldn't read test certificate")
_, err = sa.AddCertificate(parsedCert.Raw, reg.ID)
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
updater.ocspMinTimeToExpiry = 1 * time.Hour
updater.oldOCSPResponsesTick(10)
certs, err := updater.findStaleOCSPResponses(fc.Now().Add(-updater.ocspMinTimeToExpiry), 10)
test.AssertNotError(t, err, "Failed to find stale responses")
test.AssertEquals(t, len(certs), 0)
}
func TestMissingReceiptsTick(t *testing.T) {
updater, sa, _, _, cleanUp := setup(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
parsedCert, err := core.LoadCert("test-cert.pem")
test.AssertNotError(t, err, "Couldn't read test certificate")
_, err = sa.AddCertificate(parsedCert.Raw, reg.ID)
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
updater.numLogs = 1
updater.oldestIssuedSCT = 1 * time.Hour
updater.missingReceiptsTick(10)
count, err := updater.getNumberOfReceipts("00")
test.AssertNotError(t, err, "Couldn't get number of receipts")
test.AssertEquals(t, count, 1)
}
func TestRevokedCertificatesTick(t *testing.T) {
updater, sa, _, _, cleanUp := setup(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
parsedCert, err := core.LoadCert("test-cert.pem")
test.AssertNotError(t, err, "Couldn't read test certificate")
_, err = sa.AddCertificate(parsedCert.Raw, reg.ID)
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
err = sa.MarkCertificateRevoked(core.SerialToString(parsedCert.SerialNumber), core.RevocationCode(1))
test.AssertNotError(t, err, "Failed to revoke certificate")
statuses, err := updater.findRevokedCertificatesToUpdate(10)
test.AssertNotError(t, err, "Failed to find revoked certificates")
test.AssertEquals(t, len(statuses), 1)
updater.revokedCertificatesTick(10)
status, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber))
test.AssertNotError(t, err, "Failed to get certificate status")
test.AssertEquals(t, status.Status, core.OCSPStatusRevoked)
test.Assert(t, len(status.OCSPResponse) != 0, "Certificate status doesn't contain OCSP response")
}
func TestStoreResponseGuard(t *testing.T) {
updater, sa, _, _, cleanUp := setup(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
parsedCert, err := core.LoadCert("test-cert.pem")
test.AssertNotError(t, err, "Couldn't read test certificate")
_, err = sa.AddCertificate(parsedCert.Raw, reg.ID)
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
status, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber))
test.AssertNotError(t, err, "Failed to get certificate status")
err = sa.MarkCertificateRevoked(core.SerialToString(parsedCert.SerialNumber), 0)
test.AssertNotError(t, err, "Failed to revoked certificate")
// Attempt to update OCSP response where status.Status is good but stored status
// is revoked, this should fail silently
status.OCSPResponse = []byte{0, 1, 1}
err = updater.storeResponse(&status)
test.AssertNotError(t, err, "Failed to update certificate status")
// Make sure the OCSP response hasn't actually changed
unchangedStatus, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber))
test.AssertNotError(t, err, "Failed to get certificate status")
test.AssertEquals(t, len(unchangedStatus.OCSPResponse), 0)
// Changing the status to the stored status should allow the update to occur
status.Status = core.OCSPStatusRevoked
err = updater.storeResponse(&status)
test.AssertNotError(t, err, "Failed to updated certificate status")
// Make sure the OCSP response has been updated
changedStatus, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber))
test.AssertNotError(t, err, "Failed to get certificate status")
test.AssertEquals(t, len(changedStatus.OCSPResponse), 3)
}
func TestLoopTickBackoff(t *testing.T) {
fc := clock.NewFake()
stats, _ := statsd.NewNoopClient(nil)
l := looper{
clk: fc,
stats: stats,
failureBackoffFactor: 1.5,
failureBackoffMax: 10 * time.Minute,
tickDur: time.Minute,
tickFunc: func(_ int) error { return core.ServiceUnavailableError("sad HSM") },
}
start := l.clk.Now()
l.tick()
// Expected to sleep for 1m
backoff := float64(60000000000)
maxJittered := backoff * 1.2
test.AssertBetween(t, l.clk.Now().Sub(start).Nanoseconds(), int64(backoff), int64(maxJittered))
start = l.clk.Now()
l.tick()
// Expected to sleep for 1m30s
backoff = 90000000000
maxJittered = backoff * 1.2
test.AssertBetween(t, l.clk.Now().Sub(start).Nanoseconds(), int64(backoff), int64(maxJittered))
l.failures = 6
start = l.clk.Now()
l.tick()
// Expected to sleep for 11m23.4375s, should be truncated to 10m
backoff = 600000000000
maxJittered = backoff * 1.2
test.AssertBetween(t, l.clk.Now().Sub(start).Nanoseconds(), int64(backoff), int64(maxJittered))
l.tickFunc = func(_ int) error { return nil }
start = l.clk.Now()
l.tick()
test.AssertEquals(t, l.failures, 0)
test.AssertEquals(t, l.clk.Now(), start)
}