correct ca and sa revocation code and tests

The ca's TestRevoke was failing occasionally.

The test was saying "has the certificate's OCSPLastUpdated been set to a
time within the last second?" as a way to see if the revocation updated
the OCSPLastUpdated. OCSPLastUpdated was not being set on revocation,
but the test still passed most of the time.

The test still passed most of the time because the creation of the
certificate (which also sets the OCSPLastUpdated) has usually happened
within the last second. So, even without revocation, the OCSPLastUpdated
was set to something in the last second because the test is fast.

Threading a clock.FakeClock through the CA induced the test to fail
consistently. Debugging and threading a FakeClock through the SA caused
changes in times reported but did not fix the test because the
OCSPLastUpdated was simply not being updated. There were not tests for
the sa.MarkCertificateRevoked API that was being called by
ca.RevokeCertificate.

Now the SA has tests for its MarkCertificateRevoked method. It uses a
fake clock to ensure not just that OCSPLastUpdated is set correctly, but
that RevokedDate is, as well. The test also checks for the
CertificateStatus's status and RevocationCode changes.

The SA and CA now use Clocks throughout instead of time.Now() allowing
for more reliable and expansive testing in the future.

The CA had to gain a public Clock field in order for the RA to use the
CertificateAuthorityImpl struct without using its constructor
function. Otherwise, the field would be nil and cause panics in the RA
tests.

The RA tests are similarly also panicking when the CAImpl attempts to
log something with its private, nil-in-those-tests log field but we're
getting "lucky" because the RA tests only cause the CAImpl to log when
they are broken.

There is a TODO there to make the CAImpl's constructor function take
just what it needs to operate instead of taking large config objects and
doing file IO and such. The Clk field should be made private and the log
field filled in for the RA tests.

Fixes #734.
This commit is contained in:
Jeff Hodges 2015-09-01 17:58:09 -07:00
parent 8cfa7e5f32
commit 40d1c446d9
10 changed files with 161 additions and 65 deletions

View File

@ -15,6 +15,7 @@ import (
"io/ioutil"
"time"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
"github.com/letsencrypt/boulder/cmd"
"github.com/letsencrypt/boulder/core"
blog "github.com/letsencrypt/boulder/log"
@ -52,6 +53,7 @@ type CertificateAuthorityImpl struct {
SA core.StorageAuthority
PA core.PolicyAuthority
DB core.CertificateAuthorityDatabase
Clk clock.Clock // TODO(jmhodges): should be private, like log
log *blog.AuditLogger
Prefix int // Prepended to the serial number
ValidityPeriod time.Duration
@ -66,7 +68,7 @@ type CertificateAuthorityImpl struct {
// using CFSSL's authenticated signature scheme. A CA created in this way
// issues for a single profile on the remote signer, which is indicated
// by name in this constructor.
func NewCertificateAuthorityImpl(cadb core.CertificateAuthorityDatabase, config cmd.CAConfig, issuerCert string) (*CertificateAuthorityImpl, error) {
func NewCertificateAuthorityImpl(cadb core.CertificateAuthorityDatabase, config cmd.CAConfig, clk clock.Clock, issuerCert string) (*CertificateAuthorityImpl, error) {
var ca *CertificateAuthorityImpl
var err error
logger := blog.GetAuditLogger()
@ -125,6 +127,7 @@ func NewCertificateAuthorityImpl(cadb core.CertificateAuthorityDatabase, config
profile: config.Profile,
DB: cadb,
Prefix: config.SerialPrefix,
Clk: clk,
log: logger,
NotAfter: issuer.NotAfter,
}
@ -212,7 +215,7 @@ func (ca *CertificateAuthorityImpl) RevokeCertificate(serial string, reasonCode
Certificate: cert,
Status: string(core.OCSPStatusRevoked),
Reason: int(reasonCode),
RevokedAt: time.Now(),
RevokedAt: ca.Clk.Now(),
}
ocspResponse, err := ca.OCSPSigner.Sign(signRequest)
if err != nil {
@ -292,7 +295,7 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
}
}
notAfter := time.Now().Add(ca.ValidityPeriod)
notAfter := ca.Clk.Now().Add(ca.ValidityPeriod)
if ca.NotAfter.Before(notAfter) {
// AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c

View File

@ -16,6 +16,7 @@ import (
cfsslConfig "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
ocspConfig "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ocsp/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
"github.com/letsencrypt/boulder/cmd"
"github.com/letsencrypt/boulder/mocks"
"github.com/letsencrypt/boulder/policy"
@ -112,6 +113,7 @@ type testCtx struct {
caConfig cmd.CAConfig
reg core.Registration
pa core.PolicyAuthority
fc clock.FakeClock
cleanUp func()
}
@ -121,7 +123,9 @@ func setup(t *testing.T) *testCtx {
if err != nil {
t.Fatalf("Failed to create dbMap: %s", err)
}
ssa, err := sa.NewSQLStorageAuthority(dbMap)
fc := clock.NewFake()
fc.Add(1 * time.Hour)
ssa, err := sa.NewSQLStorageAuthority(dbMap, fc)
if err != nil {
t.Fatalf("Failed to create SA: %s", err)
}
@ -188,7 +192,7 @@ func setup(t *testing.T) *testCtx {
},
},
}
return &testCtx{cadb, ssa, caConfig, reg, pa, cleanUp}
return &testCtx{cadb, ssa, caConfig, reg, pa, fc, cleanUp}
}
func TestFailNoSerial(t *testing.T) {
@ -196,31 +200,33 @@ func TestFailNoSerial(t *testing.T) {
defer ctx.cleanUp()
ctx.caConfig.SerialPrefix = 0
_, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, caCertFile)
_, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
test.AssertError(t, err, "CA should have failed with no SerialPrefix")
}
func TestRevoke(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, caCertFile)
ca.PA = ctx.pa
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
test.AssertNotError(t, err, "Failed to create CA")
if err != nil {
return
}
ca.PA = ctx.pa
ca.SA = ctx.sa
ca.MaxKeySize = 4096
csr, _ := x509.ParseCertificateRequest(CNandSANCSR)
certObj, err := ca.IssueCertificate(*csr, ctx.reg.ID, FarFuture)
test.AssertNotError(t, err, "Failed to sign certificate")
if err != nil {
return
}
cert, err := x509.ParseCertificate(certObj.DER)
test.AssertNotError(t, err, "Certificate failed to parse")
serialString := core.SerialToString(cert.SerialNumber)
beforeRevoke, err := ctx.sa.GetCertificateStatus(serialString)
test.AssertNotError(t, err, "Failed to get cert status")
ctx.fc.Add(1 * time.Hour)
err = ca.RevokeCertificate(serialString, 0)
test.AssertNotError(t, err, "Revocation failed")
@ -228,15 +234,22 @@ func TestRevoke(t *testing.T) {
test.AssertNotError(t, err, "Failed to get cert status")
test.AssertEquals(t, status.Status, core.OCSPStatusRevoked)
secondAgo := time.Now().Add(-time.Second)
test.Assert(t, status.OCSPLastUpdated.After(secondAgo),
fmt.Sprintf("OCSP LastUpdated was more than a second old: %v", status.OCSPLastUpdated))
if !ctx.fc.Now().Equal(status.OCSPLastUpdated) {
t.Errorf("OCSPLastUpdated, expected %s, got %s",
ctx.fc.Now(),
status.OCSPLastUpdated)
}
if !status.OCSPLastUpdated.After(beforeRevoke.OCSPLastUpdated) {
t.Errorf("OCSPLastUpdated, before revocation: %s; after: %s", beforeRevoke.OCSPLastUpdated, status.OCSPLastUpdated)
}
}
func TestIssueCertificate(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
test.AssertNotError(t, err, "Failed to create CA")
ca.PA = ctx.pa
ca.SA = ctx.sa
@ -313,7 +326,7 @@ func TestIssueCertificate(t *testing.T) {
func TestRejectNoName(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
test.AssertNotError(t, err, "Failed to create CA")
ca.PA = ctx.pa
ca.SA = ctx.sa
@ -330,7 +343,7 @@ func TestRejectNoName(t *testing.T) {
func TestRejectTooManyNames(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
test.AssertNotError(t, err, "Failed to create CA")
ca.PA = ctx.pa
ca.SA = ctx.sa
@ -344,7 +357,7 @@ func TestRejectTooManyNames(t *testing.T) {
func TestDeduplication(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
test.AssertNotError(t, err, "Failed to create CA")
ca.PA = ctx.pa
ca.SA = ctx.sa
@ -374,7 +387,7 @@ func TestDeduplication(t *testing.T) {
func TestRejectValidityTooLong(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
test.AssertNotError(t, err, "Failed to create CA")
ca.PA = ctx.pa
ca.SA = ctx.sa
@ -387,7 +400,7 @@ func TestRejectValidityTooLong(t *testing.T) {
// Test that the CA rejects CSRs that would expire after the intermediate cert
csr, _ = x509.ParseCertificateRequest(NoCNCSR)
ca.NotAfter = time.Now()
ca.NotAfter = ctx.fc.Now()
_, err = ca.IssueCertificate(*csr, 1, FarFuture)
test.AssertEquals(t, err.Error(), "Cannot issue a certificate that expires after the intermediate certificate.")
}
@ -395,7 +408,7 @@ func TestRejectValidityTooLong(t *testing.T) {
func TestShortKey(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
ca.PA = ctx.pa
ca.SA = ctx.sa
ca.MaxKeySize = 4096
@ -409,7 +422,7 @@ func TestShortKey(t *testing.T) {
func TestRejectBadAlgorithm(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
ca.PA = ctx.pa
ca.SA = ctx.sa
ca.MaxKeySize = 4096

View File

@ -7,6 +7,7 @@ package main
import (
"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/ca"
"github.com/letsencrypt/boulder/cmd"
blog "github.com/letsencrypt/boulder/log"
@ -43,7 +44,7 @@ func main() {
pa, err := policy.NewPolicyAuthorityImpl(paDbMap, c.PA.EnforcePolicyWhitelist)
cmd.FailOnError(err, "Couldn't create PA")
cai, err := ca.NewCertificateAuthorityImpl(cadb, c.CA, c.Common.IssuerCert)
cai, err := ca.NewCertificateAuthorityImpl(cadb, c.CA, clock.Default(), c.Common.IssuerCert)
cmd.FailOnError(err, "Failed to create CA impl")
cai.MaxKeySize = c.Common.MaxKeySize
cai.PA = pa

View File

@ -7,7 +7,7 @@ package main
import (
"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/cmd"
blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/rpc"
@ -34,7 +34,7 @@ func main() {
dbMap, err := sa.NewDbMap(c.SA.DBConnect)
cmd.FailOnError(err, "Couldn't connect to SA database")
sai, err := sa.NewSQLStorageAuthority(dbMap)
sai, err := sa.NewSQLStorageAuthority(dbMap, clock.Default())
cmd.FailOnError(err, "Failed to create SA impl")
sai.SetSQLDebug(c.SQL.SQLDebug)

View File

@ -74,14 +74,14 @@ type certChecker struct {
clock clock.Clock
}
func newChecker(saDbMap *gorp.DbMap, paDbMap *gorp.DbMap, enforceWhitelist bool) certChecker {
func newChecker(saDbMap *gorp.DbMap, paDbMap *gorp.DbMap, clk clock.Clock, enforceWhitelist bool) certChecker {
pa, err := policy.NewPolicyAuthorityImpl(paDbMap, enforceWhitelist)
cmd.FailOnError(err, "Failed to create PA")
c := certChecker{
pa: pa,
dbMap: saDbMap,
certs: make(chan core.Certificate, batchSize),
clock: clock.Default(),
clock: clk,
}
c.issuedReport.Entries = make(map[string]reportEntry)
return c
@ -234,7 +234,7 @@ func main() {
paDbMap, err := sa.NewDbMap(c.PA.DBConnect)
cmd.FailOnError(err, "Could not connect to policy database")
checker := newChecker(saDbMap, paDbMap, c.PA.EnforcePolicyWhitelist)
checker := newChecker(saDbMap, paDbMap, clock.Default(), c.PA.EnforcePolicyWhitelist)
auditlogger.Info("# Getting certificates issued in the last 90 days")
// Since we grab certificates in batches we don't want this to block, when it

View File

@ -49,7 +49,7 @@ func BenchmarkCheckCert(b *testing.B) {
os.Exit(1)
}()
checker := newChecker(saDbMap, paDbMap, false)
checker := newChecker(saDbMap, paDbMap, clock.Default(), false)
testKey, _ := rsa.GenerateKey(rand.Reader, 1024)
expiry := time.Now().AddDate(0, 0, 1)
serial := big.NewInt(1337)
@ -87,10 +87,10 @@ func TestCheckCert(t *testing.T) {
}()
testKey, _ := rsa.GenerateKey(rand.Reader, 1024)
checker := newChecker(saDbMap, paDbMap, false)
fc := clock.NewFake()
fc.Add(time.Hour * 24 * 90)
checker.clock = fc
checker := newChecker(saDbMap, paDbMap, fc, false)
issued := checker.clock.Now().Add(-time.Hour * 24 * 45)
goodExpiry := issued.Add(checkPeriod)
@ -146,13 +146,16 @@ func TestGetAndProcessCerts(t *testing.T) {
test.AssertNotError(t, err, "Couldn't connect to database")
paDbMap, err := sa.NewDbMap(paDbConnStr)
test.AssertNotError(t, err, "Couldn't connect to policy database")
checker := newChecker(saDbMap, paDbMap, false)
sa, err := sa.NewSQLStorageAuthority(saDbMap)
fc := clock.NewFake()
checker := newChecker(saDbMap, paDbMap, fc, false)
sa, err := sa.NewSQLStorageAuthority(saDbMap, fc)
test.AssertNotError(t, err, "Couldn't create SA to insert certificates")
saCleanUp := test.ResetTestDatabase(t, saDbMap.Db)
paCleanUp := test.ResetTestDatabase(t, paDbMap.Db)
defer func() {
saDbMap.TruncateTables()
paDbMap.TruncateTables()
test.AssertNotError(t, err, "Failed to truncate tables")
saCleanUp()
paCleanUp()
}()
testKey, _ := rsa.GenerateKey(rand.Reader, 1024)

View File

@ -456,7 +456,8 @@ func setup(t *testing.T, nagTimes []time.Duration) *testCtx {
if err != nil {
t.Fatalf("Couldn't connect the database: %s", err)
}
ssa, err := sa.NewSQLStorageAuthority(dbMap)
fc := clock.NewFake()
ssa, err := sa.NewSQLStorageAuthority(dbMap, fc)
if err != nil {
t.Fatalf("unable to create SQLStorageAuthority: %s", err)
}
@ -464,7 +465,6 @@ func setup(t *testing.T, nagTimes []time.Duration) *testCtx {
stats, _ := statsd.NewNoopClient(nil)
mc := &mockMail{}
fc := clock.NewFake()
m := &mailer{
log: blog.GetAuditLogger(),

View File

@ -20,6 +20,7 @@ import (
cfsslConfig "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ocsp"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/letsencrypt/go-jose"
"github.com/letsencrypt/boulder/ca"
"github.com/letsencrypt/boulder/core"
@ -144,11 +145,13 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut
err = json.Unmarshal(ShortKeyJSON, &ShortKey)
test.AssertNotError(t, err, "Failed to unmarshall JWK")
fc := clock.NewFake()
dbMap, err := sa.NewDbMap(saDBConnStr)
if err != nil {
t.Fatalf("Failed to create dbMap: %s", err)
}
ssa, err := sa.NewSQLStorageAuthority(dbMap)
ssa, err := sa.NewSQLStorageAuthority(dbMap, fc)
if err != nil {
t.Fatalf("Failed to create SA: %s", err)
}
@ -193,6 +196,7 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut
ValidityPeriod: time.Hour * 2190,
NotAfter: time.Now().Add(time.Hour * 8761),
MaxKeySize: 4096,
Clk: fc,
}
cleanUp := func() {
saDBCleanUp()

View File

@ -16,6 +16,7 @@ import (
"strings"
"time"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/letsencrypt/go-jose"
gorp "github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1"
"github.com/letsencrypt/boulder/core"
@ -27,6 +28,7 @@ const getChallengesQuery = "SELECT * FROM challenges WHERE authorizationID = :au
// SQLStorageAuthority defines a Storage Authority
type SQLStorageAuthority struct {
dbMap *gorp.DbMap
clk clock.Clock
log *blog.AuditLogger
}
@ -49,13 +51,14 @@ type authzModel struct {
// NewSQLStorageAuthority provides persistence using a SQL backend for
// Boulder. It will modify the given gorp.DbMap by adding relevent tables.
func NewSQLStorageAuthority(dbMap *gorp.DbMap) (*SQLStorageAuthority, error) {
func NewSQLStorageAuthority(dbMap *gorp.DbMap, clk clock.Clock) (*SQLStorageAuthority, error) {
logger := blog.GetAuditLogger()
logger.Notice("Storage Authority Starting")
ssa := &SQLStorageAuthority{
dbMap: dbMap,
clk: clk,
log: logger,
}
@ -287,7 +290,6 @@ func (ssa *SQLStorageAuthority) GetCertificateStatus(serial string) (status core
if err != nil {
return
}
status = *certificateStats.(*core.CertificateStatus)
return
}
@ -318,7 +320,7 @@ func (ssa *SQLStorageAuthority) UpdateOCSP(serial string, ocspResponse []byte) (
return
}
timeStamp := time.Now()
timeStamp := ssa.clk.Now()
// Record the response.
ocspResp := &core.OCSPResponse{Serial: serial, CreatedAt: timeStamp, Response: ocspResponse}
@ -358,7 +360,8 @@ func (ssa *SQLStorageAuthority) MarkCertificateRevoked(serial string, ocspRespon
return
}
ocspResp := &core.OCSPResponse{Serial: serial, CreatedAt: time.Now(), Response: ocspResponse}
ocspResp := &core.OCSPResponse{Serial: serial, CreatedAt: ssa.clk.Now(), Response: ocspResponse}
err = tx.Insert(ocspResp)
if err != nil {
tx.Rollback()
@ -375,18 +378,25 @@ func (ssa *SQLStorageAuthority) MarkCertificateRevoked(serial string, ocspRespon
tx.Rollback()
return
}
now := ssa.clk.Now()
status := statusObj.(*core.CertificateStatus)
status.Status = core.OCSPStatusRevoked
status.RevokedDate = time.Now()
status.RevokedDate = now
status.RevokedReason = reasonCode
status.OCSPLastUpdated = now
_, err = tx.Update(status)
n, err := tx.Update(status)
if err != nil {
tx.Rollback()
return
}
if n == 0 {
tx.Rollback()
err = errors.New("No certificate updated. Maybe the lock column was off?")
return
}
err = tx.Commit()
return
}
@ -561,7 +571,7 @@ func (ssa *SQLStorageAuthority) AddCertificate(certDER []byte, regID int64) (dig
Serial: serial,
Digest: digest,
DER: certDER,
Issued: time.Now(),
Issued: ssa.clk.Now(),
Expires: parsedCertificate.NotAfter,
}
certStatus := &core.CertificateStatus{

View File

@ -16,6 +16,7 @@ import (
"testing"
"time"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/letsencrypt/go-jose"
"github.com/letsencrypt/boulder/core"
"github.com/letsencrypt/boulder/mocks"
@ -29,18 +30,21 @@ var log = mocks.UseMockLog()
// initSA constructs a SQLStorageAuthority and a clean up function
// that should be defer'ed to the end of the test.
func initSA(t *testing.T) (*SQLStorageAuthority, func()) {
func initSA(t *testing.T) (*SQLStorageAuthority, clock.FakeClock, func()) {
dbMap, err := NewDbMap(dbConnStr)
if err != nil {
t.Fatalf("Failed to create dbMap: %s", err)
}
sa, err := NewSQLStorageAuthority(dbMap)
fc := clock.NewFake()
fc.Add(1 * time.Hour)
sa, err := NewSQLStorageAuthority(dbMap, fc)
if err != nil {
t.Fatalf("Failed to create SA: %s", err)
}
cleanUp := test.ResetTestDatabase(t, dbMap.Db)
return sa, cleanUp
return sa, fc, cleanUp
}
var (
@ -52,7 +56,7 @@ var (
)
func TestAddRegistration(t *testing.T) {
sa, cleanUp := initSA(t)
sa, _, cleanUp := initSA(t)
defer cleanUp()
jwk := satest.GoodJWK()
@ -104,7 +108,7 @@ func TestAddRegistration(t *testing.T) {
}
func TestNoSuchRegistrationErrors(t *testing.T) {
sa, cleanUp := initSA(t)
sa, _, cleanUp := initSA(t)
defer cleanUp()
_, err := sa.GetRegistration(100)
@ -125,7 +129,7 @@ func TestNoSuchRegistrationErrors(t *testing.T) {
}
func TestAddAuthorization(t *testing.T) {
sa, cleanUp := initSA(t)
sa, _, cleanUp := initSA(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
@ -196,7 +200,7 @@ func CreateDomainAuthWithRegId(t *testing.T, domainName string, sa *SQLStorageAu
// Ensure we get only valid authorization with correct RegID
func TestGetLatestValidAuthorizationBasic(t *testing.T) {
sa, cleanUp := initSA(t)
sa, _, cleanUp := initSA(t)
defer cleanUp()
// attempt to get unauthorized domain
@ -228,7 +232,7 @@ func TestGetLatestValidAuthorizationBasic(t *testing.T) {
// Ensure we get the latest valid authorization for an ident
func TestGetLatestValidAuthorizationMultiple(t *testing.T) {
sa, cleanUp := initSA(t)
sa, _, cleanUp := initSA(t)
defer cleanUp()
domain := "example.org"
@ -283,7 +287,7 @@ func TestGetLatestValidAuthorizationMultiple(t *testing.T) {
}
func TestAddCertificate(t *testing.T) {
sa, cleanUp := initSA(t)
sa, _, cleanUp := initSA(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
@ -338,7 +342,7 @@ func TestAddCertificate(t *testing.T) {
// TestGetCertificateByShortSerial tests some failure conditions for GetCertificate.
// Success conditions are tested above in TestAddCertificate.
func TestGetCertificateByShortSerial(t *testing.T) {
sa, cleanUp := initSA(t)
sa, _, cleanUp := initSA(t)
defer cleanUp()
_, err := sa.GetCertificateByShortSerial("")
@ -357,7 +361,7 @@ func TestDeniedCSR(t *testing.T) {
csrBytes, _ := x509.CreateCertificateRequest(rand.Reader, template, key)
csr, _ := x509.ParseCertificateRequest(csrBytes)
sa, cleanUp := initSA(t)
sa, _, cleanUp := initSA(t)
defer cleanUp()
exists, err := sa.AlreadyDeniedCSR(append(csr.DNSNames, csr.Subject.CommonName))
@ -366,7 +370,7 @@ func TestDeniedCSR(t *testing.T) {
}
func TestUpdateOCSP(t *testing.T) {
sa, cleanUp := initSA(t)
sa, fc, cleanUp := initSA(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
@ -378,15 +382,21 @@ func TestUpdateOCSP(t *testing.T) {
serial := "00000000000000000000000000021bd4"
const ocspResponse = "this is a fake OCSP response"
certificateStatusObj, err := sa.dbMap.Get(core.CertificateStatus{}, serial)
beforeUpdate := certificateStatusObj.(*core.CertificateStatus)
fc.Add(1 * time.Hour)
err = sa.UpdateOCSP(serial, []byte(ocspResponse))
test.AssertNotError(t, err, "UpdateOCSP failed")
certificateStatusObj, err := sa.dbMap.Get(core.CertificateStatus{}, serial)
certificateStatusObj, err = sa.dbMap.Get(core.CertificateStatus{}, serial)
certificateStatus := certificateStatusObj.(*core.CertificateStatus)
test.AssertNotError(t, err, "Failed to fetch certificate status")
test.Assert(t,
certificateStatus.OCSPLastUpdated.After(time.Now().Add(-time.Second)),
"OCSP last updated too old.")
certificateStatus.OCSPLastUpdated.After(beforeUpdate.OCSPLastUpdated),
fmt.Sprintf("UpdateOCSP did not take. before: %s; after: %s", beforeUpdate.OCSPLastUpdated, certificateStatus.OCSPLastUpdated))
var fetchedOcspResponse core.OCSPResponse
err = sa.dbMap.SelectOne(&fetchedOcspResponse,
@ -395,3 +405,55 @@ func TestUpdateOCSP(t *testing.T) {
test.AssertNotError(t, err, "Failed to fetch OCSP response")
test.AssertEquals(t, ocspResponse, string(fetchedOcspResponse.Response))
}
func TestMarkCertificateRevoked(t *testing.T) {
sa, fc, cleanUp := initSA(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
// Add a cert to the DB to test with.
certDER, err := ioutil.ReadFile("www.eff.org.der")
test.AssertNotError(t, err, "Couldn't read example cert DER")
_, err = sa.AddCertificate(certDER, reg.ID)
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
serial := "00000000000000000000000000021bd4"
const ocspResponse = "this is a fake OCSP response"
certificateStatusObj, err := sa.dbMap.Get(core.CertificateStatus{}, serial)
beforeStatus := certificateStatusObj.(*core.CertificateStatus)
test.AssertEquals(t, beforeStatus.Status, core.OCSPStatusGood)
fc.Add(1 * time.Hour)
code := core.RevocationCode(1)
err = sa.MarkCertificateRevoked(serial, []byte(ocspResponse), code)
test.AssertNotError(t, err, "MarkCertificateRevoked failed")
certificateStatusObj, err = sa.dbMap.Get(core.CertificateStatus{}, serial)
afterStatus := certificateStatusObj.(*core.CertificateStatus)
test.AssertNotError(t, err, "Failed to fetch certificate status")
if code != afterStatus.RevokedReason {
t.Errorf("RevokedReasons, expected %s, got %s", code, afterStatus.RevokedReason)
}
if !fc.Now().Equal(afterStatus.RevokedDate) {
t.Errorf("RevokedData, expected %s, got %s", fc.Now(), afterStatus.RevokedDate)
}
if !fc.Now().Equal(afterStatus.OCSPLastUpdated) {
t.Errorf("OCSPLastUpdated, expected %s, got %s", fc.Now(), afterStatus.OCSPLastUpdated)
}
if !afterStatus.OCSPLastUpdated.After(beforeStatus.OCSPLastUpdated) {
t.Errorf("OCSPLastUpdated did not update correctly. before: %s; after: %s",
beforeStatus.OCSPLastUpdated, afterStatus.OCSPLastUpdated)
}
var fetched core.OCSPResponse
err = sa.dbMap.SelectOne(&fetched,
`SELECT * from ocspResponses where serial = ? order by createdAt DESC limit 1;`,
serial)
test.AssertNotError(t, err, "Failed to fetch OCSP response")
if ocspResponse != string(fetched.Response) {
t.Errorf("OCSPResponse response, expected %#v, got %#v", ocspResponse, string(fetched.Response))
}
}