Remove precertificates.go (#6783)

This file contained both read-only and read-write methods. Its existence
is not reflected in any other gRPC or struct organization; it was easy
to forget that it exists. Merge its contents into both sa.go and
saro.go, so that the methods follow the same organization scheme as the
rest of the SA.

This makes it less likely that bugs like #6778 will happen again.
This commit is contained in:
Aaron Gable 2023-03-30 14:59:11 -07:00 committed by GitHub
parent 373d08bb80
commit 27f0860aed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 372 additions and 402 deletions

View File

@ -1,161 +0,0 @@
package sa
import (
"context"
"crypto/x509"
"fmt"
"time"
"google.golang.org/protobuf/types/known/emptypb"
"github.com/letsencrypt/boulder/core"
"github.com/letsencrypt/boulder/db"
berrors "github.com/letsencrypt/boulder/errors"
"github.com/letsencrypt/boulder/features"
sapb "github.com/letsencrypt/boulder/sa/proto"
)
// AddSerial writes a record of a serial number generation to the DB.
func (ssa *SQLStorageAuthority) AddSerial(ctx context.Context, req *sapb.AddSerialRequest) (*emptypb.Empty, error) {
if req.Serial == "" || req.RegID == 0 || req.Created == 0 || req.Expires == 0 {
return nil, errIncompleteRequest
}
err := ssa.dbMap.WithContext(ctx).Insert(&recordedSerialModel{
Serial: req.Serial,
RegistrationID: req.RegID,
Created: time.Unix(0, req.Created),
Expires: time.Unix(0, req.Expires),
})
if err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}
// GetSerialMetadata returns metadata stored alongside the serial number,
// such as the RegID whose certificate request created that serial, and when
// the certificate with that serial will expire.
func (ssa *SQLStorageAuthorityRO) GetSerialMetadata(ctx context.Context, req *sapb.Serial) (*sapb.SerialMetadata, error) {
if req == nil || req.Serial == "" {
return nil, errIncompleteRequest
}
if !core.ValidSerial(req.Serial) {
return nil, fmt.Errorf("invalid serial %q", req.Serial)
}
recordedSerial := recordedSerialModel{}
err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(
&recordedSerial,
"SELECT * FROM serials WHERE serial = ?",
req.Serial,
)
if err != nil {
if db.IsNoRows(err) {
return nil, berrors.NotFoundError("serial %q not found", req.Serial)
}
return nil, err
}
return &sapb.SerialMetadata{
Serial: recordedSerial.Serial,
RegistrationID: recordedSerial.RegistrationID,
Created: recordedSerial.Created.UnixNano(),
Expires: recordedSerial.Expires.UnixNano(),
}, nil
}
func (ssa *SQLStorageAuthority) GetSerialMetadata(ctx context.Context, req *sapb.Serial) (*sapb.SerialMetadata, error) {
return ssa.SQLStorageAuthorityRO.GetSerialMetadata(ctx, req)
}
// AddPrecertificate writes a record of a precertificate generation to the DB.
// Note: this is not idempotent: it does not protect against inserting the same
// certificate multiple times. Calling code needs to first insert the cert's
// serial into the Serials table to ensure uniqueness.
func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb.AddCertificateRequest) (*emptypb.Empty, error) {
if len(req.Der) == 0 || req.RegID == 0 || req.Issued == 0 || req.IssuerNameID == 0 {
return nil, errIncompleteRequest
}
parsed, err := x509.ParseCertificate(req.Der)
if err != nil {
return nil, err
}
serialHex := core.SerialToString(parsed.SerialNumber)
preCertModel := &precertificateModel{
Serial: serialHex,
RegistrationID: req.RegID,
DER: req.Der,
Issued: time.Unix(0, req.Issued),
Expires: parsed.NotAfter,
}
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) {
// Select to see if precert exists
var row struct {
Count int64
}
err := txWithCtx.SelectOne(&row, "SELECT COUNT(*) as count FROM precertificates WHERE serial=?", serialHex)
if err != nil {
return nil, err
}
if row.Count > 0 {
return nil, berrors.DuplicateError("cannot add a duplicate cert")
}
err = txWithCtx.Insert(preCertModel)
if err != nil {
return nil, err
}
cs := &core.CertificateStatus{
Serial: serialHex,
Status: core.OCSPStatusGood,
OCSPLastUpdated: ssa.clk.Now(),
RevokedDate: time.Time{},
RevokedReason: 0,
LastExpirationNagSent: time.Time{},
NotAfter: parsed.NotAfter,
IsExpired: false,
IssuerNameID: req.IssuerNameID,
}
if !features.Enabled(features.ROCSPStage6) {
cs.OCSPResponse = req.Ocsp
}
err = ssa.dbMap.WithContext(ctx).Insert(cs)
if err != nil {
return nil, err
}
// NOTE(@cpu): When we collect up names to check if an FQDN set exists (e.g.
// that it is a renewal) we use just the DNSNames from the certificate and
// ignore the Subject Common Name (if any). This is a safe assumption because
// if a certificate we issued were to have a Subj. CN not present as a SAN it
// would be a misissuance and miscalculating whether the cert is a renewal or
// not for the purpose of rate limiting is the least of our troubles.
isRenewal, err := ssa.checkFQDNSetExists(
txWithCtx.SelectOne,
parsed.DNSNames)
if err != nil {
return nil, err
}
err = addIssuedNames(txWithCtx, parsed, isRenewal)
if err != nil {
return nil, err
}
err = addKeyHash(txWithCtx, parsed)
if err != nil {
return nil, err
}
return nil, nil
})
if overallError != nil {
return nil, overallError
}
return &emptypb.Empty{}, nil
}

View File

@ -1,241 +0,0 @@
package sa
import (
"bytes"
"context"
"crypto/sha256"
"fmt"
"testing"
"time"
"github.com/letsencrypt/boulder/db"
berrors "github.com/letsencrypt/boulder/errors"
sapb "github.com/letsencrypt/boulder/sa/proto"
"github.com/letsencrypt/boulder/test"
)
// findIssuedName is a small helper test function to directly query the
// issuedNames table for a given name to find a serial (or return an err).
func findIssuedName(dbMap db.OneSelector, name string) (string, error) {
var issuedNamesSerial string
err := dbMap.SelectOne(
&issuedNamesSerial,
`SELECT serial FROM issuedNames
WHERE reversedName = ?
ORDER BY notBefore DESC
LIMIT 1`,
ReverseName(name))
return issuedNamesSerial, err
}
func TestAddSerial(t *testing.T) {
sa, _, cleanUp := initSA(t)
defer cleanUp()
reg := createWorkingRegistration(t, sa)
serial, testCert := test.ThrowAwayCert(t, 1)
_, err := sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
RegID: reg.Id,
Created: testCert.NotBefore.UnixNano(),
Expires: testCert.NotAfter.UnixNano(),
})
test.AssertError(t, err, "adding without serial should fail")
_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
Serial: serial,
Created: testCert.NotBefore.UnixNano(),
Expires: testCert.NotAfter.UnixNano(),
})
test.AssertError(t, err, "adding without regid should fail")
_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
Serial: serial,
RegID: reg.Id,
Expires: testCert.NotAfter.UnixNano(),
})
test.AssertError(t, err, "adding without created should fail")
_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
Serial: serial,
RegID: reg.Id,
Created: testCert.NotBefore.UnixNano(),
})
test.AssertError(t, err, "adding without expires should fail")
_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
Serial: serial,
RegID: reg.Id,
Created: testCert.NotBefore.UnixNano(),
Expires: testCert.NotAfter.UnixNano(),
})
test.AssertNotError(t, err, "adding serial should have succeeded")
}
func TestGetSerialMetadata(t *testing.T) {
sa, clk, cleanUp := initSA(t)
defer cleanUp()
reg := createWorkingRegistration(t, sa)
serial, _ := test.ThrowAwayCert(t, 1)
_, err := sa.GetSerialMetadata(context.Background(), &sapb.Serial{Serial: serial})
test.AssertError(t, err, "getting nonexistent serial should have failed")
_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
Serial: serial,
RegID: reg.Id,
Created: clk.Now().UnixNano(),
Expires: clk.Now().Add(time.Hour).UnixNano(),
})
test.AssertNotError(t, err, "failed to add test serial")
m, err := sa.GetSerialMetadata(context.Background(), &sapb.Serial{Serial: serial})
test.AssertNotError(t, err, "getting serial should have succeeded")
test.AssertEquals(t, m.Serial, serial)
test.AssertEquals(t, m.RegistrationID, reg.Id)
test.AssertEquals(t, time.Unix(0, m.Created).UTC(), clk.Now())
test.AssertEquals(t, time.Unix(0, m.Expires).UTC(), clk.Now().Add(time.Hour))
}
func TestAddPrecertificate(t *testing.T) {
sa, clk, cleanUp := initSA(t)
defer cleanUp()
reg := createWorkingRegistration(t, sa)
// Create a throw-away self signed certificate with a random name and
// serial number
serial, testCert := test.ThrowAwayCert(t, 1)
// Add the cert as a precertificate
ocspResp := []byte{0, 0, 1}
regID := reg.Id
issuedTime := time.Date(2018, 4, 1, 7, 0, 0, 0, time.UTC)
_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
Der: testCert.Raw,
RegID: regID,
Ocsp: ocspResp,
Issued: issuedTime.UnixNano(),
IssuerNameID: 1,
})
test.AssertNotError(t, err, "Couldn't add test cert")
// It should have the expected certificate status
certStatus, err := sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial})
test.AssertNotError(t, err, "Couldn't get status for test cert")
test.Assert(
t,
bytes.Equal(certStatus.OcspResponse, ocspResp),
fmt.Sprintf("OCSP responses don't match, expected: %x, got %x", certStatus.OcspResponse, ocspResp),
)
test.AssertEquals(t, clk.Now().UnixNano(), certStatus.OcspLastUpdated)
// It should show up in the issued names table
issuedNamesSerial, err := findIssuedName(sa.dbMap, testCert.DNSNames[0])
test.AssertNotError(t, err, "expected no err querying issuedNames for precert")
test.AssertEquals(t, issuedNamesSerial, serial)
// We should also be able to call AddCertificate with the same cert
// without it being an error. The duplicate err on inserting to
// issuedNames should be ignored.
_, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{
Der: testCert.Raw,
RegID: regID,
Issued: issuedTime.UnixNano(),
})
test.AssertNotError(t, err, "unexpected err adding final cert after precert")
}
func TestAddPrecertificateNoOCSP(t *testing.T) {
sa, _, cleanUp := initSA(t)
defer cleanUp()
reg := createWorkingRegistration(t, sa)
_, testCert := test.ThrowAwayCert(t, 1)
regID := reg.Id
issuedTime := time.Date(2018, 4, 1, 7, 0, 0, 0, time.UTC)
_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
Der: testCert.Raw,
RegID: regID,
Issued: issuedTime.UnixNano(),
IssuerNameID: 1,
})
test.AssertNotError(t, err, "Couldn't add test cert")
}
func TestAddPreCertificateDuplicate(t *testing.T) {
sa, clk, cleanUp := initSA(t)
defer cleanUp()
reg := createWorkingRegistration(t, sa)
_, testCert := test.ThrowAwayCert(t, 1)
_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
Der: testCert.Raw,
Issued: clk.Now().UnixNano(),
RegID: reg.Id,
IssuerNameID: 1,
})
test.AssertNotError(t, err, "Couldn't add test certificate")
_, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
Der: testCert.Raw,
Issued: clk.Now().UnixNano(),
RegID: reg.Id,
IssuerNameID: 1,
})
test.AssertDeepEquals(t, err, berrors.DuplicateError("cannot add a duplicate cert"))
}
func TestAddPrecertificateIncomplete(t *testing.T) {
sa, _, cleanUp := initSA(t)
defer cleanUp()
reg := createWorkingRegistration(t, sa)
// Create a throw-away self signed certificate with a random name and
// serial number
_, testCert := test.ThrowAwayCert(t, 1)
// Add the cert as a precertificate
ocspResp := []byte{0, 0, 1}
regID := reg.Id
_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
Der: testCert.Raw,
RegID: regID,
Ocsp: ocspResp,
Issued: time.Date(2018, 4, 1, 7, 0, 0, 0, time.UTC).UnixNano(),
// Leaving out IssuerNameID
})
test.AssertError(t, err, "Adding precert with no issuer did not fail")
}
func TestAddPrecertificateKeyHash(t *testing.T) {
sa, _, cleanUp := initSA(t)
defer cleanUp()
reg := createWorkingRegistration(t, sa)
serial, testCert := test.ThrowAwayCert(t, 1)
_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
Der: testCert.Raw,
RegID: reg.Id,
Ocsp: []byte{1, 2, 3},
Issued: testCert.NotBefore.UnixNano(),
IssuerNameID: 1,
})
test.AssertNotError(t, err, "failed to add precert")
var keyHashes []keyHashModel
_, err = sa.dbMap.Select(&keyHashes, "SELECT * FROM keyHashToSerial")
test.AssertNotError(t, err, "failed to retrieve rows from keyHashToSerial")
test.AssertEquals(t, len(keyHashes), 1)
test.AssertEquals(t, keyHashes[0].CertSerial, serial)
test.AssertEquals(t, keyHashes[0].CertNotAfter, testCert.NotAfter)
spkiHash := sha256.Sum256(testCert.RawSubjectPublicKeyInfo)
test.Assert(t, bytes.Equal(keyHashes[0].KeyHash, spkiHash[:]), "spki hash mismatch")
}

108
sa/sa.go
View File

@ -159,6 +159,114 @@ func (ssa *SQLStorageAuthority) UpdateRegistration(ctx context.Context, req *cor
return &emptypb.Empty{}, nil
}
// AddSerial writes a record of a serial number generation to the DB.
func (ssa *SQLStorageAuthority) AddSerial(ctx context.Context, req *sapb.AddSerialRequest) (*emptypb.Empty, error) {
if req.Serial == "" || req.RegID == 0 || req.Created == 0 || req.Expires == 0 {
return nil, errIncompleteRequest
}
err := ssa.dbMap.WithContext(ctx).Insert(&recordedSerialModel{
Serial: req.Serial,
RegistrationID: req.RegID,
Created: time.Unix(0, req.Created),
Expires: time.Unix(0, req.Expires),
})
if err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}
// AddPrecertificate writes a record of a precertificate generation to the DB.
// Note: this is not idempotent: it does not protect against inserting the same
// certificate multiple times. Calling code needs to first insert the cert's
// serial into the Serials table to ensure uniqueness.
func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb.AddCertificateRequest) (*emptypb.Empty, error) {
if len(req.Der) == 0 || req.RegID == 0 || req.Issued == 0 || req.IssuerNameID == 0 {
return nil, errIncompleteRequest
}
parsed, err := x509.ParseCertificate(req.Der)
if err != nil {
return nil, err
}
serialHex := core.SerialToString(parsed.SerialNumber)
preCertModel := &precertificateModel{
Serial: serialHex,
RegistrationID: req.RegID,
DER: req.Der,
Issued: time.Unix(0, req.Issued),
Expires: parsed.NotAfter,
}
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) {
// Select to see if precert exists
var row struct {
Count int64
}
err := txWithCtx.SelectOne(&row, "SELECT COUNT(*) as count FROM precertificates WHERE serial=?", serialHex)
if err != nil {
return nil, err
}
if row.Count > 0 {
return nil, berrors.DuplicateError("cannot add a duplicate cert")
}
err = txWithCtx.Insert(preCertModel)
if err != nil {
return nil, err
}
cs := &core.CertificateStatus{
Serial: serialHex,
Status: core.OCSPStatusGood,
OCSPLastUpdated: ssa.clk.Now(),
RevokedDate: time.Time{},
RevokedReason: 0,
LastExpirationNagSent: time.Time{},
NotAfter: parsed.NotAfter,
IsExpired: false,
IssuerNameID: req.IssuerNameID,
}
if !features.Enabled(features.ROCSPStage6) {
cs.OCSPResponse = req.Ocsp
}
err = ssa.dbMap.WithContext(ctx).Insert(cs)
if err != nil {
return nil, err
}
// NOTE(@cpu): When we collect up names to check if an FQDN set exists (e.g.
// that it is a renewal) we use just the DNSNames from the certificate and
// ignore the Subject Common Name (if any). This is a safe assumption because
// if a certificate we issued were to have a Subj. CN not present as a SAN it
// would be a misissuance and miscalculating whether the cert is a renewal or
// not for the purpose of rate limiting is the least of our troubles.
isRenewal, err := ssa.checkFQDNSetExists(
txWithCtx.SelectOne,
parsed.DNSNames)
if err != nil {
return nil, err
}
err = addIssuedNames(txWithCtx, parsed, isRenewal)
if err != nil {
return nil, err
}
err = addKeyHash(txWithCtx, parsed)
if err != nil {
return nil, err
}
return nil, nil
})
if overallError != nil {
return nil, overallError
}
return &emptypb.Empty{}, nil
}
// AddCertificate stores an issued certificate, returning an error if it is a
// duplicate or if any other failure occurs.
func (ssa *SQLStorageAuthority) AddCertificate(ctx context.Context, req *sapb.AddCertificateRequest) (*emptypb.Empty, error) {

View File

@ -5,6 +5,7 @@ import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"database/sql"
"encoding/base64"
@ -274,6 +275,232 @@ func TestReplicationLagRetries(t *testing.T) {
test.AssertMetricWithLabelsEquals(t, sa.lagFactorCounter, prometheus.Labels{"method": "GetRegistration", "result": "notfound"}, 1)
}
// findIssuedName is a small helper test function to directly query the
// issuedNames table for a given name to find a serial (or return an err).
func findIssuedName(dbMap db.OneSelector, name string) (string, error) {
var issuedNamesSerial string
err := dbMap.SelectOne(
&issuedNamesSerial,
`SELECT serial FROM issuedNames
WHERE reversedName = ?
ORDER BY notBefore DESC
LIMIT 1`,
ReverseName(name))
return issuedNamesSerial, err
}
func TestAddSerial(t *testing.T) {
sa, _, cleanUp := initSA(t)
defer cleanUp()
reg := createWorkingRegistration(t, sa)
serial, testCert := test.ThrowAwayCert(t, 1)
_, err := sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
RegID: reg.Id,
Created: testCert.NotBefore.UnixNano(),
Expires: testCert.NotAfter.UnixNano(),
})
test.AssertError(t, err, "adding without serial should fail")
_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
Serial: serial,
Created: testCert.NotBefore.UnixNano(),
Expires: testCert.NotAfter.UnixNano(),
})
test.AssertError(t, err, "adding without regid should fail")
_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
Serial: serial,
RegID: reg.Id,
Expires: testCert.NotAfter.UnixNano(),
})
test.AssertError(t, err, "adding without created should fail")
_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
Serial: serial,
RegID: reg.Id,
Created: testCert.NotBefore.UnixNano(),
})
test.AssertError(t, err, "adding without expires should fail")
_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
Serial: serial,
RegID: reg.Id,
Created: testCert.NotBefore.UnixNano(),
Expires: testCert.NotAfter.UnixNano(),
})
test.AssertNotError(t, err, "adding serial should have succeeded")
}
func TestGetSerialMetadata(t *testing.T) {
sa, clk, cleanUp := initSA(t)
defer cleanUp()
reg := createWorkingRegistration(t, sa)
serial, _ := test.ThrowAwayCert(t, 1)
_, err := sa.GetSerialMetadata(context.Background(), &sapb.Serial{Serial: serial})
test.AssertError(t, err, "getting nonexistent serial should have failed")
_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
Serial: serial,
RegID: reg.Id,
Created: clk.Now().UnixNano(),
Expires: clk.Now().Add(time.Hour).UnixNano(),
})
test.AssertNotError(t, err, "failed to add test serial")
m, err := sa.GetSerialMetadata(context.Background(), &sapb.Serial{Serial: serial})
test.AssertNotError(t, err, "getting serial should have succeeded")
test.AssertEquals(t, m.Serial, serial)
test.AssertEquals(t, m.RegistrationID, reg.Id)
test.AssertEquals(t, time.Unix(0, m.Created).UTC(), clk.Now())
test.AssertEquals(t, time.Unix(0, m.Expires).UTC(), clk.Now().Add(time.Hour))
}
func TestAddPrecertificate(t *testing.T) {
sa, clk, cleanUp := initSA(t)
defer cleanUp()
reg := createWorkingRegistration(t, sa)
// Create a throw-away self signed certificate with a random name and
// serial number
serial, testCert := test.ThrowAwayCert(t, 1)
// Add the cert as a precertificate
ocspResp := []byte{0, 0, 1}
regID := reg.Id
issuedTime := time.Date(2018, 4, 1, 7, 0, 0, 0, time.UTC)
_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
Der: testCert.Raw,
RegID: regID,
Ocsp: ocspResp,
Issued: issuedTime.UnixNano(),
IssuerNameID: 1,
})
test.AssertNotError(t, err, "Couldn't add test cert")
// It should have the expected certificate status
certStatus, err := sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial})
test.AssertNotError(t, err, "Couldn't get status for test cert")
test.Assert(
t,
bytes.Equal(certStatus.OcspResponse, ocspResp),
fmt.Sprintf("OCSP responses don't match, expected: %x, got %x", certStatus.OcspResponse, ocspResp),
)
test.AssertEquals(t, clk.Now().UnixNano(), certStatus.OcspLastUpdated)
// It should show up in the issued names table
issuedNamesSerial, err := findIssuedName(sa.dbMap, testCert.DNSNames[0])
test.AssertNotError(t, err, "expected no err querying issuedNames for precert")
test.AssertEquals(t, issuedNamesSerial, serial)
// We should also be able to call AddCertificate with the same cert
// without it being an error. The duplicate err on inserting to
// issuedNames should be ignored.
_, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{
Der: testCert.Raw,
RegID: regID,
Issued: issuedTime.UnixNano(),
})
test.AssertNotError(t, err, "unexpected err adding final cert after precert")
}
func TestAddPrecertificateNoOCSP(t *testing.T) {
sa, _, cleanUp := initSA(t)
defer cleanUp()
reg := createWorkingRegistration(t, sa)
_, testCert := test.ThrowAwayCert(t, 1)
regID := reg.Id
issuedTime := time.Date(2018, 4, 1, 7, 0, 0, 0, time.UTC)
_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
Der: testCert.Raw,
RegID: regID,
Issued: issuedTime.UnixNano(),
IssuerNameID: 1,
})
test.AssertNotError(t, err, "Couldn't add test cert")
}
func TestAddPreCertificateDuplicate(t *testing.T) {
sa, clk, cleanUp := initSA(t)
defer cleanUp()
reg := createWorkingRegistration(t, sa)
_, testCert := test.ThrowAwayCert(t, 1)
_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
Der: testCert.Raw,
Issued: clk.Now().UnixNano(),
RegID: reg.Id,
IssuerNameID: 1,
})
test.AssertNotError(t, err, "Couldn't add test certificate")
_, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
Der: testCert.Raw,
Issued: clk.Now().UnixNano(),
RegID: reg.Id,
IssuerNameID: 1,
})
test.AssertDeepEquals(t, err, berrors.DuplicateError("cannot add a duplicate cert"))
}
func TestAddPrecertificateIncomplete(t *testing.T) {
sa, _, cleanUp := initSA(t)
defer cleanUp()
reg := createWorkingRegistration(t, sa)
// Create a throw-away self signed certificate with a random name and
// serial number
_, testCert := test.ThrowAwayCert(t, 1)
// Add the cert as a precertificate
ocspResp := []byte{0, 0, 1}
regID := reg.Id
_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
Der: testCert.Raw,
RegID: regID,
Ocsp: ocspResp,
Issued: time.Date(2018, 4, 1, 7, 0, 0, 0, time.UTC).UnixNano(),
// Leaving out IssuerNameID
})
test.AssertError(t, err, "Adding precert with no issuer did not fail")
}
func TestAddPrecertificateKeyHash(t *testing.T) {
sa, _, cleanUp := initSA(t)
defer cleanUp()
reg := createWorkingRegistration(t, sa)
serial, testCert := test.ThrowAwayCert(t, 1)
_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
Der: testCert.Raw,
RegID: reg.Id,
Ocsp: []byte{1, 2, 3},
Issued: testCert.NotBefore.UnixNano(),
IssuerNameID: 1,
})
test.AssertNotError(t, err, "failed to add precert")
var keyHashes []keyHashModel
_, err = sa.dbMap.Select(&keyHashes, "SELECT * FROM keyHashToSerial")
test.AssertNotError(t, err, "failed to retrieve rows from keyHashToSerial")
test.AssertEquals(t, len(keyHashes), 1)
test.AssertEquals(t, keyHashes[0].CertSerial, serial)
test.AssertEquals(t, keyHashes[0].CertNotAfter, testCert.NotAfter)
spkiHash := sha256.Sum256(testCert.RawSubjectPublicKeyInfo)
test.Assert(t, bytes.Equal(keyHashes[0].KeyHash, spkiHash[:]), "spki hash mismatch")
}
func TestAddCertificate(t *testing.T) {
sa, clk, cleanUp := initSA(t)
defer cleanUp()

View File

@ -375,6 +375,43 @@ func ReverseName(domain string) string {
return strings.Join(labels, ".")
}
// GetSerialMetadata returns metadata stored alongside the serial number,
// such as the RegID whose certificate request created that serial, and when
// the certificate with that serial will expire.
func (ssa *SQLStorageAuthorityRO) GetSerialMetadata(ctx context.Context, req *sapb.Serial) (*sapb.SerialMetadata, error) {
if req == nil || req.Serial == "" {
return nil, errIncompleteRequest
}
if !core.ValidSerial(req.Serial) {
return nil, fmt.Errorf("invalid serial %q", req.Serial)
}
recordedSerial := recordedSerialModel{}
err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(
&recordedSerial,
"SELECT * FROM serials WHERE serial = ?",
req.Serial,
)
if err != nil {
if db.IsNoRows(err) {
return nil, berrors.NotFoundError("serial %q not found", req.Serial)
}
return nil, err
}
return &sapb.SerialMetadata{
Serial: recordedSerial.Serial,
RegistrationID: recordedSerial.RegistrationID,
Created: recordedSerial.Created.UnixNano(),
Expires: recordedSerial.Expires.UnixNano(),
}, nil
}
func (ssa *SQLStorageAuthority) GetSerialMetadata(ctx context.Context, req *sapb.Serial) (*sapb.SerialMetadata, error) {
return ssa.SQLStorageAuthorityRO.GetSerialMetadata(ctx, req)
}
// GetCertificate takes a serial number and returns the corresponding
// certificate, or error if it does not exist.
func (ssa *SQLStorageAuthorityRO) GetCertificate(ctx context.Context, req *sapb.Serial) (*corepb.Certificate, error) {