Merge pull request #735 from letsencrypt/clock_ca
correct ca and sa revocation code and tests
This commit is contained in:
commit
82f134dd92
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue