sa: use internal certificateModel (#8130)

This follows the system we've used for other types, where the SA has a
model type that is converted to a proto message for use outside the SA.

Part of #8112.
This commit is contained in:
Jacob Hoffman-Andrews 2025-04-21 13:48:29 -07:00 committed by GitHub
parent 37147d4dfa
commit 967d722cf4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 108 additions and 124 deletions

View File

@ -100,7 +100,7 @@ type certChecker struct {
kp goodkey.KeyPolicy kp goodkey.KeyPolicy
dbMap certDB dbMap certDB
getPrecert precertGetter getPrecert precertGetter
certs chan core.Certificate certs chan *corepb.Certificate
clock clock.Clock clock clock.Clock
rMu *sync.Mutex rMu *sync.Mutex
issuedReport report issuedReport report
@ -124,14 +124,14 @@ func newChecker(saDbMap certDB,
if err != nil { if err != nil {
return nil, err return nil, err
} }
return precertPb.DER, nil return precertPb.Der, nil
} }
return certChecker{ return certChecker{
pa: pa, pa: pa,
kp: kp, kp: kp,
dbMap: saDbMap, dbMap: saDbMap,
getPrecert: precertGetter, getPrecert: precertGetter,
certs: make(chan core.Certificate, batchSize), certs: make(chan *corepb.Certificate, batchSize),
rMu: new(sync.Mutex), rMu: new(sync.Mutex),
clock: clk, clock: clk,
issuedReport: report{Entries: make(map[string]reportEntry)}, issuedReport: report{Entries: make(map[string]reportEntry)},
@ -214,7 +214,7 @@ func (c *certChecker) getCerts(ctx context.Context) error {
batchStartID := initialID batchStartID := initialID
var retries int var retries int
for { for {
certs, err := sa.SelectCertificates( certs, highestID, err := sa.SelectCertificates(
ctx, ctx,
c.dbMap, c.dbMap,
`WHERE id > :id AND `WHERE id > :id AND
@ -239,16 +239,16 @@ func (c *certChecker) getCerts(ctx context.Context) error {
} }
retries = 0 retries = 0
for _, cert := range certs { for _, cert := range certs {
c.certs <- cert.Certificate c.certs <- cert
} }
if len(certs) == 0 { if len(certs) == 0 {
break break
} }
lastCert := certs[len(certs)-1] lastCert := certs[len(certs)-1]
batchStartID = lastCert.ID if lastCert.Issued.AsTime().After(c.issuedReport.end) {
if lastCert.Issued.After(c.issuedReport.end) {
break break
} }
batchStartID = highestID
} }
// Close channel so range operations won't block once the channel empties out // Close channel so range operations won't block once the channel empties out
@ -302,8 +302,8 @@ var expectedExtensionContent = map[string][]byte{
// likely valid at the time the certificate was issued. Authorizations with // likely valid at the time the certificate was issued. Authorizations with
// status = "deactivated" are counted for this, so long as their validatedAt // status = "deactivated" are counted for this, so long as their validatedAt
// is before the issuance and expiration is after. // is before the issuance and expiration is after.
func (c *certChecker) checkValidations(ctx context.Context, cert core.Certificate, idents identifier.ACMEIdentifiers) error { func (c *certChecker) checkValidations(ctx context.Context, cert *corepb.Certificate, idents identifier.ACMEIdentifiers) error {
authzs, err := sa.SelectAuthzsMatchingIssuance(ctx, c.dbMap, cert.RegistrationID, cert.Issued, idents) authzs, err := sa.SelectAuthzsMatchingIssuance(ctx, c.dbMap, cert.RegistrationID, cert.Issued.AsTime(), idents)
if err != nil { if err != nil {
return fmt.Errorf("error checking authzs for certificate %s: %w", cert.Serial, err) return fmt.Errorf("error checking authzs for certificate %s: %w", cert.Serial, err)
} }
@ -334,16 +334,16 @@ func (c *certChecker) checkValidations(ctx context.Context, cert core.Certificat
} }
// checkCert returns a list of DNS names in the certificate and a list of problems with the certificate. // checkCert returns a list of DNS names in the certificate and a list of problems with the certificate.
func (c *certChecker) checkCert(ctx context.Context, cert core.Certificate) ([]string, []string) { func (c *certChecker) checkCert(ctx context.Context, cert *corepb.Certificate) ([]string, []string) {
var dnsNames []string var dnsNames []string
var problems []string var problems []string
// Check that the digests match. // Check that the digests match.
if cert.Digest != core.Fingerprint256(cert.DER) { if cert.Digest != core.Fingerprint256(cert.Der) {
problems = append(problems, "Stored digest doesn't match certificate digest") problems = append(problems, "Stored digest doesn't match certificate digest")
} }
// Parse the certificate. // Parse the certificate.
parsedCert, err := zX509.ParseCertificate(cert.DER) parsedCert, err := zX509.ParseCertificate(cert.Der)
if err != nil { if err != nil {
problems = append(problems, fmt.Sprintf("Couldn't parse stored certificate: %s", err)) problems = append(problems, fmt.Sprintf("Couldn't parse stored certificate: %s", err))
} else { } else {
@ -368,7 +368,7 @@ func (c *certChecker) checkCert(ctx context.Context, cert core.Certificate) ([]s
problems = append(problems, "Stored serial doesn't match certificate serial") problems = append(problems, "Stored serial doesn't match certificate serial")
} }
// Check that we have the correct expiration time. // Check that we have the correct expiration time.
if !parsedCert.NotAfter.Equal(cert.Expires) { if !parsedCert.NotAfter.Equal(cert.Expires.AsTime()) {
problems = append(problems, "Stored expiration doesn't match certificate NotAfter") problems = append(problems, "Stored expiration doesn't match certificate NotAfter")
} }
// Check if basic constraints are set. // Check if basic constraints are set.
@ -388,7 +388,7 @@ func (c *certChecker) checkCert(ctx context.Context, cert core.Certificate) ([]s
problems = append(problems, "Certificate has unacceptable validity period") problems = append(problems, "Certificate has unacceptable validity period")
} }
// Check that the stored issuance time isn't too far back/forward dated. // Check that the stored issuance time isn't too far back/forward dated.
if parsedCert.NotBefore.Before(cert.Issued.Add(-6*time.Hour)) || parsedCert.NotBefore.After(cert.Issued.Add(6*time.Hour)) { if parsedCert.NotBefore.Before(cert.Issued.AsTime().Add(-6*time.Hour)) || parsedCert.NotBefore.After(cert.Issued.AsTime().Add(6*time.Hour)) {
problems = append(problems, "Stored issuance date is outside of 6 hour window of certificate NotBefore") problems = append(problems, "Stored issuance date is outside of 6 hour window of certificate NotBefore")
} }
if parsedCert.Subject.CommonName != "" { if parsedCert.Subject.CommonName != "" {
@ -451,7 +451,7 @@ func (c *certChecker) checkCert(ctx context.Context, cert core.Certificate) ([]s
// checks which rely on external resources such as weak or blocked key // checks which rely on external resources such as weak or blocked key
// lists, or the list of blocked keys in the database. This only performs // lists, or the list of blocked keys in the database. This only performs
// static checks, such as against the RSA key size and the ECDSA curve. // static checks, such as against the RSA key size and the ECDSA curve.
p, err := x509.ParseCertificate(cert.DER) p, err := x509.ParseCertificate(cert.Der)
if err != nil { if err != nil {
problems = append(problems, fmt.Sprintf("Couldn't parse stored certificate: %s", err)) problems = append(problems, fmt.Sprintf("Couldn't parse stored certificate: %s", err))
} }
@ -467,7 +467,7 @@ func (c *certChecker) checkCert(ctx context.Context, cert core.Certificate) ([]s
c.logger.Errf("fetching linting precertificate for %s: %s", cert.Serial, err) c.logger.Errf("fetching linting precertificate for %s: %s", cert.Serial, err)
atomic.AddInt64(&c.issuedReport.DbErrs, 1) atomic.AddInt64(&c.issuedReport.DbErrs, 1)
} else { } else {
err = precert.Correspond(precertDER, cert.DER) err = precert.Correspond(precertDER, cert.Der)
if err != nil { if err != nil {
problems = append(problems, problems = append(problems,
fmt.Sprintf("Certificate does not correspond to precert for %s: %s", cert.Serial, err)) fmt.Sprintf("Certificate does not correspond to precert for %s: %s", cert.Serial, err))

View File

@ -27,6 +27,7 @@ import (
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
"github.com/letsencrypt/boulder/core" "github.com/letsencrypt/boulder/core"
corepb "github.com/letsencrypt/boulder/core/proto"
"github.com/letsencrypt/boulder/ctpolicy/loglist" "github.com/letsencrypt/boulder/ctpolicy/loglist"
"github.com/letsencrypt/boulder/goodkey" "github.com/letsencrypt/boulder/goodkey"
"github.com/letsencrypt/boulder/goodkey/sagoodkey" "github.com/letsencrypt/boulder/goodkey/sagoodkey"
@ -79,12 +80,12 @@ func BenchmarkCheckCert(b *testing.B) {
SerialNumber: serial, SerialNumber: serial,
} }
certDer, _ := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) certDer, _ := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey)
cert := core.Certificate{ cert := &corepb.Certificate{
Serial: core.SerialToString(serial), Serial: core.SerialToString(serial),
Digest: core.Fingerprint256(certDer), Digest: core.Fingerprint256(certDer),
DER: certDer, Der: certDer,
Issued: time.Now(), Issued: timestamppb.New(time.Now()),
Expires: expiry, Expires: timestamppb.New(expiry),
} }
b.ResetTimer() b.ResetTimer()
for range b.N { for range b.N {
@ -125,12 +126,12 @@ func TestCheckWildcardCert(t *testing.T) {
test.AssertNotError(t, err, "Couldn't create certificate") test.AssertNotError(t, err, "Couldn't create certificate")
parsed, err := x509.ParseCertificate(wildcardCertDer) parsed, err := x509.ParseCertificate(wildcardCertDer)
test.AssertNotError(t, err, "Couldn't parse created certificate") test.AssertNotError(t, err, "Couldn't parse created certificate")
cert := core.Certificate{ cert := &corepb.Certificate{
Serial: core.SerialToString(serial), Serial: core.SerialToString(serial),
Digest: core.Fingerprint256(wildcardCertDer), Digest: core.Fingerprint256(wildcardCertDer),
Expires: parsed.NotAfter, Expires: timestamppb.New(parsed.NotAfter),
Issued: parsed.NotBefore, Issued: timestamppb.New(parsed.NotBefore),
DER: wildcardCertDer, Der: wildcardCertDer,
} }
_, problems := checker.checkCert(context.Background(), cert) _, problems := checker.checkCert(context.Background(), cert)
for _, p := range problems { for _, p := range problems {
@ -157,12 +158,12 @@ func TestCheckCertReturnsDNSNames(t *testing.T) {
t.Fatal("failed to parse cert PEM") t.Fatal("failed to parse cert PEM")
} }
cert := core.Certificate{ cert := &corepb.Certificate{
Serial: "00000000000", Serial: "00000000000",
Digest: core.Fingerprint256(block.Bytes), Digest: core.Fingerprint256(block.Bytes),
Expires: time.Now().Add(time.Hour), Expires: timestamppb.New(time.Now().Add(time.Hour)),
Issued: time.Now(), Issued: timestamppb.New(time.Now()),
DER: block.Bytes, Der: block.Bytes,
} }
names, problems := checker.checkCert(context.Background(), cert) names, problems := checker.checkCert(context.Background(), cert)
@ -262,11 +263,11 @@ func TestCheckCert(t *testing.T) {
// Serial doesn't match // Serial doesn't match
// Expiry doesn't match // Expiry doesn't match
// Issued doesn't match // Issued doesn't match
cert := core.Certificate{ cert := &corepb.Certificate{
Serial: "8485f2687eba29ad455ae4e31c8679206fec", Serial: "8485f2687eba29ad455ae4e31c8679206fec",
DER: brokenCertDer, Der: brokenCertDer,
Issued: issued.Add(12 * time.Hour), Issued: timestamppb.New(issued.Add(12 * time.Hour)),
Expires: goodExpiry.AddDate(0, 0, 2), // Expiration doesn't match Expires: timestamppb.New(goodExpiry.AddDate(0, 0, 2)), // Expiration doesn't match
} }
_, problems := checker.checkCert(context.Background(), cert) _, problems := checker.checkCert(context.Background(), cert)
@ -318,9 +319,9 @@ func TestCheckCert(t *testing.T) {
test.AssertNotError(t, err, "Couldn't parse created certificate") test.AssertNotError(t, err, "Couldn't parse created certificate")
cert.Serial = core.SerialToString(serial) cert.Serial = core.SerialToString(serial)
cert.Digest = core.Fingerprint256(goodCertDer) cert.Digest = core.Fingerprint256(goodCertDer)
cert.DER = goodCertDer cert.Der = goodCertDer
cert.Expires = parsed.NotAfter cert.Expires = timestamppb.New(parsed.NotAfter)
cert.Issued = parsed.NotBefore cert.Issued = timestamppb.New(parsed.NotBefore)
_, problems = checker.checkCert(context.Background(), cert) _, problems = checker.checkCert(context.Background(), cert)
test.AssertEquals(t, len(problems), 0) test.AssertEquals(t, len(problems), 0)
}) })
@ -396,9 +397,6 @@ func (db mismatchedCountDB) SelectNullInt(_ context.Context, _ string, _ ...inte
// `getCerts` then calls `Select` to retrieve the Certificate rows. We pull // `getCerts` then calls `Select` to retrieve the Certificate rows. We pull
// a dastardly switch-a-roo here and return an empty set // a dastardly switch-a-roo here and return an empty set
func (db mismatchedCountDB) Select(_ context.Context, output interface{}, _ string, _ ...interface{}) ([]interface{}, error) { func (db mismatchedCountDB) Select(_ context.Context, output interface{}, _ string, _ ...interface{}) ([]interface{}, error) {
// But actually return nothing
outputPtr, _ := output.(*[]sa.CertWithID)
*outputPtr = []sa.CertWithID{}
return nil, nil return nil, nil
} }
@ -624,12 +622,12 @@ func TestIgnoredLint(t *testing.T) {
subjectCert, err := x509.ParseCertificate(subjectCertDer) subjectCert, err := x509.ParseCertificate(subjectCertDer)
test.AssertNotError(t, err, "failed to parse EE cert") test.AssertNotError(t, err, "failed to parse EE cert")
cert := core.Certificate{ cert := &corepb.Certificate{
Serial: core.SerialToString(serial), Serial: core.SerialToString(serial),
DER: subjectCertDer, Der: subjectCertDer,
Digest: core.Fingerprint256(subjectCertDer), Digest: core.Fingerprint256(subjectCertDer),
Issued: subjectCert.NotBefore, Issued: timestamppb.New(subjectCert.NotBefore),
Expires: subjectCert.NotAfter, Expires: timestamppb.New(subjectCert.NotAfter),
} }
// Without any ignored lints we expect several errors and warnings about SCTs, // Without any ignored lints we expect several errors and warnings about SCTs,
@ -679,12 +677,12 @@ func TestPrecertCorrespond(t *testing.T) {
SerialNumber: serial, SerialNumber: serial,
} }
certDer, _ := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) certDer, _ := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey)
cert := core.Certificate{ cert := &corepb.Certificate{
Serial: core.SerialToString(serial), Serial: core.SerialToString(serial),
Digest: core.Fingerprint256(certDer), Digest: core.Fingerprint256(certDer),
DER: certDer, Der: certDer,
Issued: time.Now(), Issued: timestamppb.New(time.Now()),
Expires: expiry, Expires: timestamppb.New(expiry),
} }
_, problems := checker.checkCert(context.Background(), cert) _, problems := checker.checkCert(context.Background(), cert)
if len(problems) == 0 { if len(problems) == 0 {

View File

@ -608,7 +608,6 @@ func (m *mailer) getCerts(ctx context.Context, left, right time.Time, expiresIn
if ctx.Err() != nil { if ctx.Err() != nil {
return nil, ctx.Err() return nil, ctx.Err()
} }
var cert core.Certificate
cert, err := sa.SelectCertificate(ctx, m.dbMap, serial) cert, err := sa.SelectCertificate(ctx, m.dbMap, serial)
if err != nil { if err != nil {
// We can get a NoRowsErr when processing a serial number corresponding // We can get a NoRowsErr when processing a serial number corresponding
@ -623,13 +622,13 @@ func (m *mailer) getCerts(ctx context.Context, left, right time.Time, expiresIn
continue continue
} }
certs = append(certs, certDERWithRegID{ certs = append(certs, certDERWithRegID{
DER: cert.DER, DER: cert.Der,
RegID: cert.RegistrationID, RegID: cert.RegistrationID,
}) })
if i == 0 { if i == 0 {
// Report the send delay metric. Note: this is the worst-case send delay // Report the send delay metric. Note: this is the worst-case send delay
// of any certificate in this batch because it's based on the first (oldest). // of any certificate in this batch because it's based on the first (oldest).
sendDelay := expiresIn - cert.Expires.Sub(m.clk.Now()) sendDelay := expiresIn - cert.Expires.AsTime().Sub(m.clk.Now())
m.stats.sendDelay.With(prometheus.Labels{"nag_group": expiresIn.String()}).Set( m.stats.sendDelay.With(prometheus.Labels{"nag_group": expiresIn.String()}).Set(
sendDelay.Truncate(time.Second).Seconds()) sendDelay.Truncate(time.Second).Seconds())
} }

View File

@ -343,28 +343,6 @@ func newOrderValid(order *corepb.Order) bool {
return !(order.RegistrationID == 0 || order.Expires == nil || len(order.Identifiers) == 0) return !(order.RegistrationID == 0 || order.Expires == nil || len(order.Identifiers) == 0)
} }
func CertToPB(cert core.Certificate) *corepb.Certificate {
return &corepb.Certificate{
RegistrationID: cert.RegistrationID,
Serial: cert.Serial,
Digest: cert.Digest,
Der: cert.DER,
Issued: timestamppb.New(cert.Issued),
Expires: timestamppb.New(cert.Expires),
}
}
func PBToCert(pb *corepb.Certificate) core.Certificate {
return core.Certificate{
RegistrationID: pb.RegistrationID,
Serial: pb.Serial,
Digest: pb.Digest,
DER: pb.Der,
Issued: pb.Issued.AsTime(),
Expires: pb.Expires.AsTime(),
}
}
func CertStatusToPB(certStatus core.CertificateStatus) *corepb.CertificateStatus { func CertStatusToPB(certStatus core.CertificateStatus) *corepb.CertificateStatus {
return &corepb.CertificateStatus{ return &corepb.CertificateStatus{
Serial: certStatus.Serial, Serial: certStatus.Serial,

View File

@ -267,23 +267,6 @@ func TestAuthz(t *testing.T) {
test.AssertDeepEquals(t, inAuthzNilExpires, outAuthz2) test.AssertDeepEquals(t, inAuthzNilExpires, outAuthz2)
} }
func TestCert(t *testing.T) {
now := time.Now().Round(0).UTC()
cert := core.Certificate{
RegistrationID: 1,
Serial: "serial",
Digest: "digest",
DER: []byte{255},
Issued: now,
Expires: now.Add(time.Hour),
}
certPB := CertToPB(cert)
outCert := PBToCert(certPB)
test.AssertDeepEquals(t, cert, outCert)
}
func TestOrderValid(t *testing.T) { func TestOrderValid(t *testing.T) {
created := time.Now() created := time.Now()
expires := created.Add(1 * time.Hour) expires := created.Add(1 * time.Hour)

View File

@ -139,65 +139,59 @@ func selectRegistration(ctx context.Context, s db.OneSelector, whereCol string,
return &model, err return &model, err
} }
const certFields = "registrationID, serial, digest, der, issued, expires" const certFields = "id, registrationID, serial, digest, der, issued, expires"
// SelectCertificate selects all fields of one certificate object identified by // SelectCertificate selects all fields of one certificate object identified by
// a serial. If more than one row contains the same serial only the first is // a serial. If more than one row contains the same serial only the first is
// returned. // returned.
func SelectCertificate(ctx context.Context, s db.OneSelector, serial string) (core.Certificate, error) { func SelectCertificate(ctx context.Context, s db.OneSelector, serial string) (*corepb.Certificate, error) {
var model core.Certificate var model certificateModel
err := s.SelectOne( err := s.SelectOne(
ctx, ctx,
&model, &model,
"SELECT "+certFields+" FROM certificates WHERE serial = ? LIMIT 1", "SELECT "+certFields+" FROM certificates WHERE serial = ? LIMIT 1",
serial, serial,
) )
return model, err return model.toPb(), err
} }
const precertFields = "registrationID, serial, der, issued, expires" const precertFields = "registrationID, serial, der, issued, expires"
// SelectPrecertificate selects all fields of one precertificate object // SelectPrecertificate selects all fields of one precertificate object
// identified by serial. // identified by serial.
func SelectPrecertificate(ctx context.Context, s db.OneSelector, serial string) (core.Certificate, error) { func SelectPrecertificate(ctx context.Context, s db.OneSelector, serial string) (*corepb.Certificate, error) {
var model lintingCertModel var model lintingCertModel
err := s.SelectOne( err := s.SelectOne(
ctx, ctx,
&model, &model,
"SELECT "+precertFields+" FROM precertificates WHERE serial = ? LIMIT 1", "SELECT "+precertFields+" FROM precertificates WHERE serial = ? LIMIT 1",
serial) serial)
return core.Certificate{ if err != nil {
RegistrationID: model.RegistrationID, return nil, err
Serial: model.Serial,
DER: model.DER,
Issued: model.Issued,
Expires: model.Expires,
}, err
} }
return model.toPb(), nil
type CertWithID struct {
ID int64
core.Certificate
} }
// SelectCertificates selects all fields of multiple certificate objects // SelectCertificates selects all fields of multiple certificate objects
func SelectCertificates(ctx context.Context, s db.Selector, q string, args map[string]interface{}) ([]CertWithID, error) { //
var models []CertWithID // Returns a slice of *corepb.Certificate along with the highest ID field seen
// (which can be used as input to a subsequent query when iterating in primary
// key order).
func SelectCertificates(ctx context.Context, s db.Selector, q string, args map[string]interface{}) ([]*corepb.Certificate, int64, error) {
var models []certificateModel
_, err := s.Select( _, err := s.Select(
ctx, ctx,
&models, &models,
"SELECT id, "+certFields+" FROM certificates "+q, args) "SELECT "+certFields+" FROM certificates "+q, args)
return models, err var pbs []*corepb.Certificate
var highestID int64
for _, m := range models {
pbs = append(pbs, m.toPb())
if m.ID > highestID {
highestID = m.ID
} }
}
// SelectPrecertificates selects all fields of multiple precertificate objects. return pbs, highestID, err
func SelectPrecertificates(ctx context.Context, s db.Selector, q string, args map[string]interface{}) ([]CertWithID, error) {
var models []CertWithID
_, err := s.Select(
ctx,
&models,
"SELECT id, "+precertFields+" FROM precertificates "+q, args)
return models, err
} }
type CertStatusMetadata struct { type CertStatusMetadata struct {
@ -366,6 +360,38 @@ type lintingCertModel struct {
Expires time.Time Expires time.Time
} }
func (model lintingCertModel) toPb() *corepb.Certificate {
return &corepb.Certificate{
RegistrationID: model.RegistrationID,
Serial: model.Serial,
Digest: "",
Der: model.DER,
Issued: timestamppb.New(model.Issued),
Expires: timestamppb.New(model.Expires),
}
}
type certificateModel struct {
ID int64 `db:"id"`
RegistrationID int64 `db:"registrationID"`
Serial string `db:"serial"`
Digest string `db:"digest"`
DER []byte `db:"der"`
Issued time.Time `db:"issued"`
Expires time.Time `db:"expires"`
}
func (model certificateModel) toPb() *corepb.Certificate {
return &corepb.Certificate{
RegistrationID: model.RegistrationID,
Serial: model.Serial,
Digest: model.Digest,
Der: model.DER,
Issued: timestamppb.New(model.Issued),
Expires: timestamppb.New(model.Expires),
}
}
// orderModel represents one row in the orders table. The CertificateProfileName // orderModel represents one row in the orders table. The CertificateProfileName
// column is a pointer because the column is NULL-able. // column is a pointer because the column is NULL-able.
type orderModel struct { type orderModel struct {

View File

@ -305,7 +305,7 @@ func TestCertificatesTableContainsDuplicateSerials(t *testing.T) {
test.AssertNotError(t, err, "received an error for a valid query") test.AssertNotError(t, err, "received an error for a valid query")
// Ensure that `certA` and `certB` are the same. // Ensure that `certA` and `certB` are the same.
test.AssertByteEquals(t, certA.DER, certB.DER) test.AssertByteEquals(t, certA.Der, certB.Der)
} }
func insertCertificate(ctx context.Context, dbMap *db.WrappedMap, fc clock.FakeClock, hostname, cn string, serial, regID int64) error { func insertCertificate(ctx context.Context, dbMap *db.WrappedMap, fc clock.FakeClock, hostname, cn string, serial, regID int64) error {

View File

@ -213,7 +213,7 @@ func (ssa *SQLStorageAuthorityRO) GetCertificate(ctx context.Context, req *sapb.
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bgrpc.CertToPB(cert), nil return cert, nil
} }
// GetLintPrecertificate takes a serial number and returns the corresponding // GetLintPrecertificate takes a serial number and returns the corresponding
@ -235,7 +235,7 @@ func (ssa *SQLStorageAuthorityRO) GetLintPrecertificate(ctx context.Context, req
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bgrpc.CertToPB(cert), nil return cert, nil
} }
// GetCertificateStatus takes a hexadecimal string representing the full 128-bit serial // GetCertificateStatus takes a hexadecimal string representing the full 128-bit serial