Skip to content

Commit

Permalink
Only resubmit missing SCTs. (#2342)
Browse files Browse the repository at this point in the history
This PR introduces the ability for the ocsp-updater to only resubmit certificates to logs that we are missing SCTs from. Prior to this commit when a certificate was missing one or more SCTs we would submit it to every log, causing unnecessary overhead for us and the log operator.

To accomplish this a new RPC endpoint is added to the Publisher service "SubmitToSingleCT". Unlike the existing "SubmitToCT" this RPC endpoint accepts a log URI and public key in addition to the certificate DER bytes. The certificate is submitted directly to that log, and a cache of constructed resources is maintained so that subsequent submissions to the same log can reuse the stat name, verifier, and submission client.

Resolves #1679
  • Loading branch information
cpu authored and Roland Bracewell Shoemaker committed Dec 5, 2016
1 parent 43bcc0b commit a2b8fae
Show file tree
Hide file tree
Showing 16 changed files with 706 additions and 61 deletions.
106 changes: 91 additions & 15 deletions cmd/ocsp-updater/main.go
@@ -1,6 +1,7 @@
package main

import (
"crypto/sha256"
"crypto/x509"
"database/sql"
"encoding/base64"
Expand Down Expand Up @@ -56,8 +57,8 @@ type OCSPUpdater struct {
ocspMinTimeToExpiry time.Duration
// Used to calculate how far back missing SCT receipts should be looked for
oldestIssuedSCT time.Duration
// Number of CT logs we expect to have receipts from
numLogs int
// Logs we expect to have SCT receipts for. Missing logs will be resubmitted to.
logs []*ctLog

loops []*looper

Expand All @@ -75,7 +76,7 @@ func newUpdater(
pub core.Publisher,
sac core.StorageAuthority,
config cmd.OCSPUpdaterConfig,
numLogs int,
logConfigs []cmd.LogDescription,
issuerPath string,
log blog.Logger,
) (*OCSPUpdater, error) {
Expand All @@ -90,6 +91,15 @@ func newUpdater(
return nil, fmt.Errorf("Loop window sizes must be non-zero")
}

logs := make([]*ctLog, len(logConfigs))
for i, logConfig := range logConfigs {
l, err := newLog(logConfig)
if err != nil {
return nil, err
}
logs[i] = l
}

updater := OCSPUpdater{
stats: stats,
clk: clk,
Expand All @@ -98,7 +108,7 @@ func newUpdater(
log: log,
sac: sac,
pubc: pub,
numLogs: numLogs,
logs: logs,
ocspMinTimeToExpiry: config.OCSPMinTimeToExpiry.Duration,
oldestIssuedSCT: config.OldestIssuedSCT.Duration,
}
Expand Down Expand Up @@ -474,14 +484,39 @@ func (updater *OCSPUpdater) getSerialsIssuedSince(since time.Time, batchSize int
return allSerials, nil
}

func (updater *OCSPUpdater) getNumberOfReceipts(serial string) (int, error) {
var count int
err := updater.dbMap.SelectOne(
&count,
"SELECT COUNT(id) FROM sctReceipts WHERE certificateSerial = :serial",
// getSubmittedReceipts returns the IDs of the CT logs that have returned a SCT
// receipt for the given certificate serial
func (updater *OCSPUpdater) getSubmittedReceipts(serial string) ([]string, error) {
var logIDs []string
_, err := updater.dbMap.Select(
&logIDs,
`SELECT logID
FROM sctReceipts
WHERE certificateSerial = :serial`,
map[string]interface{}{"serial": serial},
)
return count, err
return logIDs, err
}

// missingLogIDs examines a list of log IDs that have given a SCT receipt for
// a certificate and returns a list of the configured logs that are not
// present. This is the set of logs we need to resubmit this certificate to in
// order to obtain a full compliment of SCTs
func (updater *OCSPUpdater) missingLogs(logIDs []string) []*ctLog {
var missingLogs []*ctLog

presentMap := make(map[string]bool)
for _, logID := range logIDs {
presentMap[logID] = true
}

for _, l := range updater.logs {
if _, present := presentMap[l.logID]; !present {
missingLogs = append(missingLogs, l)
}
}

return missingLogs
}

// missingReceiptsTick looks for certificates without the correct number of SCT
Expand All @@ -496,20 +531,42 @@ func (updater *OCSPUpdater) missingReceiptsTick(ctx context.Context, batchSize i
}

for _, serial := range serials {
count, err := updater.getNumberOfReceipts(serial)
// First find the logIDs that have provided a SCT for the serial
logIDs, err := updater.getSubmittedReceipts(serial)
if err != nil {
updater.log.AuditErr(fmt.Sprintf("Failed to get number of SCT receipts for certificate: %s", err))
updater.log.AuditErr(fmt.Sprintf(
"Failed to get CT log IDs of SCT receipts for certificate: %s", err))
continue
}
if count >= updater.numLogs {

// Next, check if any of the configured CT logs are missing from the list of
// logs that have given SCTs for this serial
missingLogs := updater.missingLogs(logIDs)
if len(missingLogs) == 0 {
// If all of the logs have provided a SCT we're done for this serial
continue
}

// Otherwise, we need to get the certificate from the SA & submit it to each
// of the missing logs to obtain SCTs.
cert, err := updater.sac.GetCertificate(ctx, serial)
if err != nil {
updater.log.AuditErr(fmt.Sprintf("Failed to get certificate: %s", err))
continue
}
_ = updater.pubc.SubmitToCT(ctx, cert.DER)

// If the feature flag is enabled, only send the certificate to the missing
// logs using the `SubmitToSingleCT` endpoint that was added for this
// purpose
if features.Enabled(features.ResubmitMissingSCTsOnly) {
for _, log := range missingLogs {
_ = updater.pubc.SubmitToSingleCT(ctx, log.uri, log.key, cert.DER)
}
} else {
// Otherwise, use the classic behaviour and submit the certificate to
// every log to get SCTS using the pre-existing `SubmitToCT` endpoint
_ = updater.pubc.SubmitToCT(ctx, cert.DER)
}
}
return nil
}
Expand Down Expand Up @@ -565,6 +622,25 @@ func (l *looper) loop() error {
}
}

// a ctLog contains the pre-processed logID and URI for a CT log. The ocsp-updater
// creates these out of cmd.LogDescription's from its config
type ctLog struct {
logID string
key string
uri string
}

func newLog(logConfig cmd.LogDescription) (*ctLog, error) {
logPK, err := base64.StdEncoding.DecodeString(logConfig.Key)
if err != nil {
return nil, err
}

logPKHash := sha256.Sum256(logPK)
logID := base64.StdEncoding.EncodeToString(logPKHash[:])
return &ctLog{logID: logID, key: logConfig.Key, uri: logConfig.URI}, nil
}

const clientName = "OCSP"

type config struct {
Expand Down Expand Up @@ -654,7 +730,7 @@ func main() {
sac,
// Necessary evil for now
conf,
len(c.Common.CT.Logs),
c.Common.CT.Logs,
c.Common.IssuerCert,
auditlogger,
)
Expand Down

0 comments on commit a2b8fae

Please sign in to comment.