CA/SA: Store issuer info in certificateStatus, use for OCSP generation (#4546)

This avoids needing to send the entire certificate in OCSP generation
RPCs.

Ended up including a few cleanups that made the implementation easier.

Initially I was struggling with how to derive the issuer identification info.
We could just stick the full SPKI hash in certificateStatus, but that takes a
significant amount of space, we could configure unique issuer IDs in the CA
config, but that would require being very careful about keeping the IDs
constant, and never reusing an ID, or we could store issuers in a table in the
database and use that as a lookup table, but that requires figuring out how to
get that info into the table etc. Instead I've just gone with what I found to
be the easiest solution, deriving a stable ID from the cert hash. This means we
don't need to remember to configure anything special and the CA config stays
the same as it is now.

Fixes #4469.
This commit is contained in:
Roland Bracewell Shoemaker 2019-11-18 06:15:29 -08:00 committed by Daniel McCarney
parent 6ed4ce23a8
commit b557d870c7
25 changed files with 594 additions and 238 deletions

View File

@ -7,6 +7,7 @@ import (
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/hex"
@ -36,6 +37,7 @@ import (
corepb "github.com/letsencrypt/boulder/core/proto"
csrlib "github.com/letsencrypt/boulder/csr"
berrors "github.com/letsencrypt/boulder/errors"
"github.com/letsencrypt/boulder/features"
"github.com/letsencrypt/boulder/goodkey"
blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/metrics"
@ -103,6 +105,7 @@ type certificateStorage interface {
AddCertificate(context.Context, []byte, int64, []byte, *time.Time) (string, error)
AddPrecertificate(ctx context.Context, req *sapb.AddCertificateRequest) (*corepb.Empty, error)
AddSerial(ctx context.Context, req *sapb.AddSerialRequest) (*corepb.Empty, error)
SerialExists(ctx context.Context, req *sapb.Serial) (*sapb.Exists, error)
}
type certificateType string
@ -119,6 +122,8 @@ type CertificateAuthorityImpl struct {
ecdsaProfile string
// A map from issuer cert common name to an internalIssuer struct
issuers map[string]*internalIssuer
// A map from issuer ID to internalIssuer
idToIssuer map[int64]*internalIssuer
// The common name of the default issuer cert
defaultIssuer *internalIssuer
sa certificateStorage
@ -190,6 +195,14 @@ func makeInternalIssuers(
return internalIssuers, nil
}
// idForIssuer generates a stable ID for an issuer certificate. This
// is used for identifying which issuer issued a certificate in the
// certificateStatus table.
func idForIssuer(cert *x509.Certificate) int64 {
h := sha256.Sum256(cert.Raw)
return big.NewInt(0).SetBytes(h[:4]).Int64()
}
// NewCertificateAuthorityImpl creates a CA instance that can sign certificates
// from a single issuer (the first first in the issuers slice), and can sign OCSP
// for any of the issuer certificates provided.
@ -284,6 +297,12 @@ func NewCertificateAuthorityImpl(
ocspLifetime: config.LifespanOCSP.Duration,
}
ca.idToIssuer = make(map[int64]*internalIssuer)
for _, ii := range ca.issuers {
id := idForIssuer(ii.cert)
ca.idToIssuer[id] = ii
}
if config.Expiry == "" {
return nil, errors.New("Config must specify an expiry period.")
}
@ -393,29 +412,56 @@ var ocspStatusToCode = map[string]int{
// GenerateOCSP produces a new OCSP response and returns it
func (ca *CertificateAuthorityImpl) GenerateOCSP(ctx context.Context, req *caPB.GenerateOCSPRequest) (*caPB.OCSPResponse, error) {
cert, err := x509.ParseCertificate(req.CertDER)
if err != nil {
ca.log.AuditErr(err.Error())
return nil, err
}
var issuer *internalIssuer
var serial *big.Int
// Once the feature is enabled we need to support both RPCs that include
// IssuerID and those that don't as we still need to be able to update rows
// that didn't have an IssuerID set when they were created. Once this featue
// has been enabled for a full OCSP lifetime cycle we can remove this
// functionality.
if features.Enabled(features.StoreIssuerInfo) && req.IssuerID != nil {
serialInt, err := core.StringToSerial(*req.Serial)
if err != nil {
return nil, err
}
serial = serialInt
var ok bool
issuer, ok = ca.idToIssuer[*req.IssuerID]
if !ok {
return nil, fmt.Errorf("This CA doesn't have an issuer cert with ID %d", *req.IssuerID)
}
exists, err := ca.sa.SerialExists(ctx, &sapb.Serial{Serial: req.Serial})
if err != nil {
return nil, err
}
if !*exists.Exists {
return nil, fmt.Errorf("GenerateOCSP was asked to sign OCSP for certification with unknown serial %q", *req.Serial)
}
} else {
cert, err := x509.ParseCertificate(req.CertDER)
if err != nil {
ca.log.AuditErr(err.Error())
return nil, err
}
cn := cert.Issuer.CommonName
issuer := ca.issuers[cn]
if issuer == nil {
return nil, fmt.Errorf("This CA doesn't have an issuer cert with CommonName %q", cn)
}
err = cert.CheckSignatureFrom(issuer.cert)
if err != nil {
return nil, fmt.Errorf("GenerateOCSP was asked to sign OCSP for cert "+
"%s from %q, but the cert's signature was not valid: %s.",
core.SerialToString(cert.SerialNumber), cn, err)
serial = cert.SerialNumber
cn := cert.Issuer.CommonName
issuer = ca.issuers[cn]
if issuer == nil {
return nil, fmt.Errorf("This CA doesn't have an issuer cert with CommonName %q", cn)
}
err = cert.CheckSignatureFrom(issuer.cert)
if err != nil {
return nil, fmt.Errorf("GenerateOCSP was asked to sign OCSP for cert "+
"%s from %q, but the cert's signature was not valid: %s.",
core.SerialToString(cert.SerialNumber), cn, err)
}
}
now := ca.clk.Now().Truncate(time.Hour)
tbsResponse := ocsp.Response{
Status: ocspStatusToCode[*req.Status],
SerialNumber: cert.SerialNumber,
SerialNumber: serial,
ThisUpdate: now,
NextUpdate: now.Add(ca.ocspLifetime),
}
@ -469,12 +515,19 @@ func (ca *CertificateAuthorityImpl) IssuePrecertificate(ctx context.Context, iss
return nil, err
}
_, err = ca.sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
req := &sapb.AddCertificateRequest{
Der: precertDER,
RegID: &regID,
Ocsp: ocspResp.Response,
Issued: &nowNanos,
})
}
// we currently only use one issuer, in the future when we support multiple
// the issuer will need to be derived from issueReq
issuerID := idForIssuer(ca.defaultIssuer.cert)
req.IssuerID = &issuerID
_, err = ca.sa.AddPrecertificate(ctx, req)
if err != nil {
err = berrors.InternalServerError(err.Error())
ca.log.AuditErrf("Failed RPC to store at SA, orphaning precertificate: serial=[%s] cert=[%s] err=[%v], regID=[%d], orderID=[%d]",

View File

@ -187,6 +187,11 @@ func (m *mockSA) AddSerial(ctx context.Context, req *sapb.AddSerialRequest) (*co
return &corepb.Empty{}, nil
}
func (m *mockSA) SerialExists(ctx context.Context, req *sapb.Serial) (*sapb.Exists, error) {
e := true
return &sapb.Exists{Exists: &e}, nil
}
var caKey crypto.Signer
var caCert *x509.Certificate
var ctx = context.Background()
@ -932,6 +937,11 @@ func (qsa *queueSA) AddSerial(ctx context.Context, req *sapb.AddSerialRequest) (
return &corepb.Empty{}, nil
}
func (qsa *queueSA) SerialExists(ctx context.Context, req *sapb.Serial) (*sapb.Exists, error) {
e := true
return &sapb.Exists{Exists: &e}, nil
}
// TestPrecertOrphanQueue tests that IssuePrecertificate writes precertificates
// to the orphan queue if storage fails, and that `integrateOrphan` later
// successfully writes those precertificates to the database. To do this, it
@ -1165,3 +1175,50 @@ func TestIssuePrecertificateLinting(t *testing.T) {
matches := testCtx.logger.GetAllMatching(regex)
test.AssertEquals(t, len(matches), 1)
}
func TestGenerateOCSPWithIssuerID(t *testing.T) {
testCtx := setup(t)
sa := &mockSA{}
_ = features.Set(map[string]bool{"StoreIssuerInfo": true})
ca, err := NewCertificateAuthorityImpl(
testCtx.caConfig,
sa,
testCtx.pa,
testCtx.fc,
testCtx.stats,
testCtx.issuers,
testCtx.keyPolicy,
testCtx.logger,
nil)
test.AssertNotError(t, err, "Failed to create CA")
// GenerateOCSP with feature enabled + req contains bad IssuerID
issuerID := int64(666)
serial := "DEADDEADDEADDEADDEADDEADDEADDEADDEAD"
status := string(core.OCSPStatusGood)
_, err = ca.GenerateOCSP(context.Background(), &caPB.GenerateOCSPRequest{
IssuerID: &issuerID,
Serial: &serial,
Status: &status,
})
test.AssertError(t, err, "GenerateOCSP didn't fail with invalid IssuerID")
// GenerateOCSP with feature enabled + req contains good IssuerID
issuerID = idForIssuer(ca.defaultIssuer.cert)
_, err = ca.GenerateOCSP(context.Background(), &caPB.GenerateOCSPRequest{
IssuerID: &issuerID,
Serial: &serial,
Status: &status,
})
test.AssertNotError(t, err, "GenerateOCSP failed")
// GenerateOCSP with feature enabled + req doesn't contain IssuerID
issueReq := caPB.IssueCertificateRequest{Csr: CNandSANCSR, RegistrationID: &arbitraryRegID}
cert, err := ca.IssuePrecertificate(ctx, &issueReq)
test.AssertNotError(t, err, "Failed to issue")
_, err = ca.GenerateOCSP(context.Background(), &caPB.GenerateOCSPRequest{
CertDER: cert.DER,
Status: &status,
})
test.AssertNotError(t, err, "GenerateOCSP failed")
}

View File

@ -184,11 +184,14 @@ func (m *IssueCertificateForPrecertificateRequest) GetOrderID() int64 {
return 0
}
// Exactly one of certDER or [serial and issuerID] must be set.
type GenerateOCSPRequest struct {
CertDER []byte `protobuf:"bytes,1,opt,name=certDER" json:"certDER,omitempty"`
Status *string `protobuf:"bytes,2,opt,name=status" json:"status,omitempty"`
Reason *int32 `protobuf:"varint,3,opt,name=reason" json:"reason,omitempty"`
RevokedAt *int64 `protobuf:"varint,4,opt,name=revokedAt" json:"revokedAt,omitempty"`
Serial *string `protobuf:"bytes,5,opt,name=serial" json:"serial,omitempty"`
IssuerID *int64 `protobuf:"varint,6,opt,name=issuerID" json:"issuerID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -247,6 +250,20 @@ func (m *GenerateOCSPRequest) GetRevokedAt() int64 {
return 0
}
func (m *GenerateOCSPRequest) GetSerial() string {
if m != nil && m.Serial != nil {
return *m.Serial
}
return ""
}
func (m *GenerateOCSPRequest) GetIssuerID() int64 {
if m != nil && m.IssuerID != nil {
return *m.IssuerID
}
return 0
}
type OCSPResponse struct {
Response []byte `protobuf:"bytes,1,opt,name=response" json:"response,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -297,32 +314,33 @@ func init() {
func init() { proto.RegisterFile("ca/proto/ca.proto", fileDescriptor_8f9fdc2529716820) }
var fileDescriptor_8f9fdc2529716820 = []byte{
// 389 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x53, 0x41, 0xaf, 0xd2, 0x40,
0x10, 0xa6, 0xed, 0x7b, 0xe2, 0x9b, 0x54, 0xf3, 0x58, 0x54, 0x9a, 0x62, 0x22, 0xf6, 0x60, 0x1a,
0x63, 0x4a, 0xc2, 0xd5, 0x13, 0x52, 0x34, 0x24, 0x26, 0x92, 0x45, 0x2f, 0xde, 0x36, 0xcb, 0xa0,
0x8d, 0xb1, 0x8b, 0xb3, 0x5b, 0x13, 0x0f, 0xfe, 0x09, 0x7e, 0xb1, 0xe9, 0xd2, 0x42, 0x21, 0x25,
0x1c, 0xbc, 0xcd, 0x37, 0x33, 0xbb, 0xdf, 0x37, 0xf3, 0xed, 0x42, 0x4f, 0x8a, 0xf1, 0x96, 0x94,
0x51, 0x63, 0x29, 0x12, 0x1b, 0x30, 0x57, 0x8a, 0xf0, 0xa9, 0x54, 0x84, 0x75, 0x41, 0x11, 0xee,
0x4b, 0xd1, 0x4f, 0x18, 0x2c, 0xb4, 0x2e, 0x70, 0x86, 0x64, 0xb2, 0x4d, 0x26, 0x85, 0x41, 0x8e,
0xbf, 0x0a, 0xd4, 0x86, 0xdd, 0x83, 0x27, 0x35, 0x05, 0xce, 0xc8, 0x89, 0x7d, 0x5e, 0x86, 0xec,
0x15, 0x3c, 0x26, 0xfc, 0x96, 0x69, 0x43, 0xc2, 0x64, 0x2a, 0x5f, 0xa4, 0x81, 0x3b, 0x72, 0x62,
0x8f, 0x9f, 0x65, 0x59, 0x00, 0x5d, 0x45, 0x6b, 0xa4, 0x45, 0x1a, 0x78, 0xb6, 0xa1, 0x86, 0xd1,
0x18, 0x86, 0x96, 0x6e, 0x49, 0x28, 0x9b, 0x8c, 0x7a, 0xab, 0x72, 0x8d, 0x25, 0x65, 0x3a, 0xe7,
0x35, 0x65, 0x3a, 0xe7, 0xd1, 0xce, 0x81, 0xf8, 0x5c, 0xe0, 0x7b, 0x45, 0xe7, 0xe7, 0x0f, 0x8a,
0x4f, 0x8f, 0x33, 0x06, 0x37, 0xab, 0xd9, 0x67, 0x1d, 0xb8, 0x23, 0x2f, 0xf6, 0xb9, 0x8d, 0x5b,
0xa6, 0xf0, 0xae, 0x4d, 0x71, 0x73, 0x3a, 0xc5, 0x5f, 0xe8, 0x7f, 0xc0, 0x1c, 0x49, 0x18, 0xfc,
0x34, 0x5b, 0x2d, 0x6b, 0xfa, 0x00, 0xba, 0xa5, 0xa8, 0xa3, 0x84, 0x1a, 0xb2, 0x67, 0xf0, 0x40,
0x1b, 0x61, 0x0a, 0x6d, 0x17, 0x76, 0xc7, 0x2b, 0x54, 0xe6, 0x09, 0x85, 0x56, 0xb9, 0x95, 0x70,
0xcb, 0x2b, 0xc4, 0x9e, 0xc3, 0x1d, 0xe1, 0x6f, 0xf5, 0x03, 0xd7, 0x53, 0x53, 0x91, 0x1f, 0x13,
0xd1, 0x6b, 0xf0, 0xf7, 0xb4, 0xd5, 0xd6, 0x42, 0x78, 0x48, 0x55, 0x5c, 0x11, 0x1f, 0xf0, 0x64,
0xe7, 0xc2, 0x93, 0xc6, 0xea, 0xa6, 0x85, 0xf9, 0xae, 0x28, 0x33, 0x7f, 0xd8, 0x17, 0xe8, 0xb7,
0x38, 0xc1, 0x86, 0x89, 0x14, 0xc9, 0x85, 0x17, 0x11, 0xbe, 0x38, 0x14, 0xdb, 0xfd, 0x8b, 0x3a,
0x6c, 0x03, 0x2f, 0xaf, 0xda, 0xc5, 0xde, 0xb4, 0x91, 0x5c, 0x72, 0x35, 0xec, 0x25, 0xf6, 0xbd,
0x36, 0x5a, 0xa3, 0x0e, 0x7b, 0x0b, 0x7e, 0xd3, 0x02, 0x36, 0x28, 0xaf, 0x6c, 0x31, 0x25, 0xbc,
0x2f, 0x0b, 0xcd, 0x75, 0x45, 0x9d, 0xc9, 0x47, 0x78, 0x54, 0x66, 0xaa, 0x76, 0x45, 0xff, 0x75,
0xdb, 0xbb, 0xee, 0xd7, 0x5b, 0xfb, 0x97, 0xfe, 0x05, 0x00, 0x00, 0xff, 0xff, 0xf9, 0x16, 0x85,
0x42, 0x7a, 0x03, 0x00, 0x00,
// 415 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x53, 0xcd, 0x6e, 0xd3, 0x40,
0x10, 0x8e, 0xed, 0xa6, 0x69, 0x47, 0x06, 0xb5, 0x5b, 0xa0, 0x96, 0x8b, 0x44, 0xf0, 0x01, 0x59,
0x08, 0x39, 0x52, 0xaf, 0x9c, 0x4a, 0x5c, 0x50, 0x24, 0x24, 0xaa, 0x2d, 0x5c, 0xb8, 0xad, 0xb6,
0x53, 0xb0, 0x00, 0x6f, 0x99, 0x5d, 0x23, 0xf1, 0x1a, 0x7d, 0x13, 0xde, 0x10, 0xed, 0x66, 0xed,
0x38, 0x91, 0xa3, 0x1c, 0x7a, 0x9b, 0x6f, 0x66, 0x67, 0xbe, 0x6f, 0x7e, 0x16, 0x8e, 0xa5, 0x98,
0xdd, 0x91, 0x32, 0x6a, 0x26, 0x45, 0xe1, 0x0c, 0x16, 0x4a, 0x91, 0x3e, 0x95, 0x8a, 0xb0, 0x0d,
0x28, 0xc2, 0x65, 0x28, 0xfb, 0x05, 0xa7, 0x0b, 0xad, 0x1b, 0x9c, 0x23, 0x99, 0xea, 0xb6, 0x92,
0xc2, 0x20, 0xc7, 0xdf, 0x0d, 0x6a, 0xc3, 0x8e, 0x20, 0x92, 0x9a, 0x92, 0x60, 0x1a, 0xe4, 0x31,
0xb7, 0x26, 0x7b, 0x05, 0x8f, 0x09, 0xbf, 0x55, 0xda, 0x90, 0x30, 0x95, 0xaa, 0x17, 0x65, 0x12,
0x4e, 0x83, 0x3c, 0xe2, 0x1b, 0x5e, 0x96, 0xc0, 0x44, 0xd1, 0x0d, 0xd2, 0xa2, 0x4c, 0x22, 0xf7,
0xa0, 0x85, 0xd9, 0x0c, 0xce, 0x1c, 0xdd, 0x15, 0xa1, 0xec, 0x33, 0xea, 0x3b, 0x55, 0x6b, 0xb4,
0x94, 0xe5, 0x25, 0x6f, 0x29, 0xcb, 0x4b, 0x9e, 0xdd, 0x07, 0x90, 0x6f, 0x0a, 0x7c, 0xaf, 0x68,
0x33, 0xbf, 0x53, 0xbc, 0x9e, 0xce, 0x18, 0xec, 0x5d, 0xcf, 0x3f, 0xeb, 0x24, 0x9c, 0x46, 0x79,
0xcc, 0x9d, 0x3d, 0xd0, 0x45, 0xb4, 0xab, 0x8b, 0xbd, 0xf5, 0x2e, 0xfe, 0x05, 0x70, 0xf2, 0x01,
0x6b, 0x24, 0x61, 0xf0, 0xd3, 0xfc, 0xfa, 0xaa, 0xe5, 0x4f, 0x60, 0x62, 0x55, 0xad, 0x34, 0xb4,
0x90, 0x3d, 0x83, 0x7d, 0x6d, 0x84, 0x69, 0xb4, 0x9b, 0xd8, 0x21, 0xf7, 0xc8, 0xfa, 0x09, 0x85,
0x56, 0xb5, 0xd3, 0x30, 0xe6, 0x1e, 0xb1, 0xe7, 0x70, 0x48, 0xf8, 0x47, 0xfd, 0xc0, 0x9b, 0x0b,
0xe3, 0xd9, 0x57, 0x0e, 0x57, 0x0d, 0xa9, 0x12, 0x3f, 0x93, 0xb1, 0xaf, 0xe6, 0x10, 0x4b, 0xe1,
0xa0, 0xb2, 0xb3, 0xb2, 0x92, 0xf7, 0x5d, 0x52, 0x87, 0xb3, 0xd7, 0x10, 0x2f, 0xa5, 0xfa, 0x51,
0xa7, 0x70, 0x40, 0xde, 0xf6, 0x62, 0x3b, 0x7c, 0x7e, 0x1f, 0xc2, 0x93, 0xde, 0xbc, 0x2f, 0x1a,
0xf3, 0x5d, 0x51, 0x65, 0xfe, 0xb2, 0x2f, 0x70, 0x32, 0xb0, 0x3e, 0x76, 0x56, 0x48, 0x51, 0x6c,
0x39, 0xa3, 0xf4, 0x45, 0x17, 0x1c, 0x5e, 0x7a, 0x36, 0x62, 0xb7, 0xf0, 0x72, 0xe7, 0x8e, 0xd9,
0x9b, 0x21, 0x92, 0x6d, 0xa7, 0x90, 0x1e, 0x17, 0xee, 0xc8, 0x7b, 0x4f, 0xb3, 0x11, 0x7b, 0x0b,
0x71, 0x7f, 0x6d, 0xec, 0xd4, 0x96, 0x1c, 0x58, 0x64, 0x7a, 0x64, 0x03, 0xfd, 0x71, 0x65, 0xa3,
0xf3, 0x8f, 0xf0, 0xc8, 0x7a, 0xfc, 0x73, 0x45, 0x0f, 0xaa, 0xf6, 0x6e, 0xf2, 0x75, 0xec, 0x3e,
0xe0, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa3, 0xb6, 0x36, 0xac, 0xaf, 0x03, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.

View File

@ -37,11 +37,14 @@ message IssueCertificateForPrecertificateRequest {
optional int64 orderID = 4;
}
// Exactly one of certDER or [serial and issuerID] must be set.
message GenerateOCSPRequest {
optional bytes certDER = 1;
optional string status = 2;
optional int32 reason = 3;
optional int64 revokedAt = 4;
optional string serial = 5;
optional int64 issuerID = 6;
}
message OCSPResponse {

View File

@ -129,19 +129,20 @@ func (updater *OCSPUpdater) findStaleOCSPResponses(oldestLastUpdatedTime time.Ti
now := updater.clk.Now()
maxAgeCutoff := now.Add(-updater.ocspStaleMaxAge)
certStatusFields := "cs.serial, cs.status, cs.revokedDate, cs.notAfter"
if features.Enabled(features.StoreIssuerInfo) {
certStatusFields += ", cs.issuerID"
}
_, err := updater.dbMap.Select(
&statuses,
`SELECT
cs.serial,
cs.status,
cs.revokedDate,
cs.notAfter
fmt.Sprintf(`SELECT
%s
FROM certificateStatus AS cs
WHERE cs.ocspLastUpdated > :maxAge
AND cs.ocspLastUpdated < :lastUpdate
AND NOT cs.isExpired
ORDER BY cs.ocspLastUpdated ASC
LIMIT :limit`,
LIMIT :limit`, certStatusFields),
map[string]interface{}{
"lastUpdate": oldestLastUpdatedTime,
"maxAge": maxAgeCutoff,
@ -154,15 +155,15 @@ func (updater *OCSPUpdater) findStaleOCSPResponses(oldestLastUpdatedTime time.Ti
return statuses, err
}
func (updater *OCSPUpdater) generateResponse(ctx context.Context, status core.CertificateStatus) (*core.CertificateStatus, error) {
func getCertDER(selector ocspDB, serial string) ([]byte, error) {
cert, err := sa.SelectCertificate(
updater.dbMap,
selector,
"WHERE serial = ?",
status.Serial,
serial,
)
if err != nil {
if err == sql.ErrNoRows {
cert, err = sa.SelectPrecertificate(updater.dbMap, status.Serial)
cert, err = sa.SelectPrecertificate(selector, serial)
// If there was still a non-nil error return it. If we can't find
// a precert row something is amiss, we have a certificateStatus row with
// no matching certificate or precertificate.
@ -173,16 +174,30 @@ func (updater *OCSPUpdater) generateResponse(ctx context.Context, status core.Ce
return nil, err
}
}
return cert.DER, nil
}
func (updater *OCSPUpdater) generateResponse(ctx context.Context, status core.CertificateStatus) (*core.CertificateStatus, error) {
reason := int32(status.RevokedReason)
statusStr := string(status.Status)
revokedAt := status.RevokedDate.UnixNano()
ocspResponse, err := updater.ogc.GenerateOCSP(ctx, &capb.GenerateOCSPRequest{
CertDER: cert.DER,
ocspReq := capb.GenerateOCSPRequest{
Reason: &reason,
Status: &statusStr,
RevokedAt: &revokedAt,
})
}
if status.IssuerID != nil {
ocspReq.Serial = &status.Serial
ocspReq.IssuerID = status.IssuerID
} else {
certDER, err := getCertDER(updater.dbMap, status.Serial)
if err != nil {
return nil, err
}
ocspReq.CertDER = certDER
}
ocspResponse, err := updater.ogc.GenerateOCSP(ctx, &ocspReq)
if err != nil {
return nil, err
}
@ -399,10 +414,7 @@ func setupClients(c OCSPUpdaterConfig, stats metrics.Scope, clk clock.Clock) (
clientMetrics := bgrpc.NewClientMetrics(stats)
caConn, err := bgrpc.ClientSetup(c.OCSPGeneratorService, tls, clientMetrics, clk)
cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to CA")
// Make a CA client that is only capable of signing OCSP.
// TODO(jsha): Once we've fully moved to gRPC, replace this
// with a plain caPB.NewOCSPGeneratorClient.
ogc := capb.NewOCSPGeneratorClient(caConn)
ogc := bgrpc.NewOCSPGeneratorClient(capb.NewOCSPGeneratorClient(caConn))
saConn, err := bgrpc.ClientSetup(c.SAService, tls, clientMetrics, clk)
cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to SA")

View File

@ -2,9 +2,15 @@ package main
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"errors"
"fmt"
"io/ioutil"
"math/big"
"os"
"strings"
"testing"
"time"
@ -13,6 +19,7 @@ import (
caPB "github.com/letsencrypt/boulder/ca/proto"
"github.com/letsencrypt/boulder/cmd"
"github.com/letsencrypt/boulder/core"
"github.com/letsencrypt/boulder/features"
blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/metrics"
"github.com/letsencrypt/boulder/sa"
@ -481,3 +488,68 @@ func TestGenerateOCSPResponsePrecert(t *testing.T) {
_, err = updater.generateResponse(ctx, certs[0])
test.AssertNotError(t, err, "generateResponse for precert errored")
}
type mockOCSPRecordIssuer struct {
gotIssuer bool
}
func (ca *mockOCSPRecordIssuer) GenerateOCSP(_ context.Context, req *caPB.GenerateOCSPRequest, _ ...grpc.CallOption) (*caPB.OCSPResponse, error) {
ca.gotIssuer = req.IssuerID != nil && req.Serial != nil
return &caPB.OCSPResponse{Response: []byte{1, 2, 3}}, nil
}
func TestIssuerInfo(t *testing.T) {
if !strings.HasSuffix(os.Getenv("BOULDER_CONFIG_DIR"), "config-next") {
return
}
updater, sa, _, fc, cleanUp := setup(t)
defer cleanUp()
m := mockOCSPRecordIssuer{}
updater.ogc = &m
reg := satest.CreateWorkingRegistration(t, sa)
_ = features.Set(map[string]bool{"StoreIssuerInfo": true})
k, err := rsa.GenerateKey(rand.Reader, 512)
test.AssertNotError(t, err, "rsa.GenerateKey failed")
template := &x509.Certificate{
SerialNumber: big.NewInt(1),
}
certA, err := x509.CreateCertificate(rand.Reader, template, template, &k.PublicKey, k)
test.AssertNotError(t, err, "x509.CreateCertificate failed")
template.SerialNumber = big.NewInt(2)
certB, err := x509.CreateCertificate(rand.Reader, template, template, &k.PublicKey, k)
test.AssertNotError(t, err, "x509.CreateCertificate failed")
now := fc.Now().UnixNano()
id := int64(1234)
_, err = sa.AddPrecertificate(context.Background(), &sapb.AddCertificateRequest{
Der: certA,
RegID: &reg.ID,
Ocsp: []byte{1, 2, 3},
Issued: &now,
IssuerID: &id,
})
test.AssertNotError(t, err, "sa.AddPrecertificate failed")
_, err = sa.AddPrecertificate(context.Background(), &sapb.AddCertificateRequest{
Der: certB,
RegID: &reg.ID,
Ocsp: []byte{1, 2, 3},
Issued: &now,
})
test.AssertNotError(t, err, "sa.AddPrecertificate failed")
fc.Add(time.Hour * 24 * 4)
statuses, err := updater.findStaleOCSPResponses(fc.Now().Add(-time.Hour), 10)
test.AssertNotError(t, err, "findStaleOCSPResponses failed")
test.AssertEquals(t, len(statuses), 2)
test.AssertEquals(t, *statuses[0].IssuerID, id)
test.Assert(t, statuses[1].IssuerID == nil, "second status doesn't have nil IssuerID")
_, err = updater.generateResponse(context.Background(), statuses[0])
test.AssertNotError(t, err, "generateResponse failed")
test.Assert(t, m.gotIssuer, "generateResponse didn't send issuer information and serial")
_, err = updater.generateResponse(context.Background(), statuses[1])
test.AssertNotError(t, err, "generateResponse failed")
test.Assert(t, !m.gotIssuer, "generateResponse did send issuer information and serial when it shouldn't")
}

View File

@ -134,6 +134,7 @@ type StorageGetter interface {
GetValidOrderAuthorizations2(ctx context.Context, req *sapb.GetValidOrderAuthorizationsRequest) (*sapb.Authorizations, error)
CountInvalidAuthorizations2(ctx context.Context, req *sapb.CountInvalidAuthorizationsRequest) (*sapb.Count, error)
GetValidAuthorizations2(ctx context.Context, req *sapb.GetValidAuthorizationsRequest) (*sapb.Authorizations, error)
SerialExists(ctx context.Context, req *sapb.Serial) (*sapb.Exists, error)
}
// StorageAdder are the Boulder SA's write/update methods

View File

@ -490,6 +490,8 @@ type CertificateStatus struct {
// [0]: https://github.com/letsencrypt/boulder/issues/1864
NotAfter time.Time `db:"notAfter"`
IsExpired bool `db:"isExpired"`
IssuerID *int64
}
// OCSPResponse is a (large) table of OCSP responses. This contains all

View File

@ -38,11 +38,12 @@ func _() {
_ = x[PrecertificateRevocation-27]
_ = x[StripDefaultSchemePort-28]
_ = x[GetAuthorizationsPerf-29]
_ = x[StoreIssuerInfo-30]
}
const _FeatureFlag_name = "unusedPerformValidationRPCACME13KeyRolloverSimplifiedVAHTTPTLSSNIRevalidationAllowRenewalFirstRLSetIssuedNamesRenewalBitFasterRateLimitProbeCTLogsRevokeAtRANewAuthorizationSchemaDisableAuthz2OrdersEarlyOrderRateLimitFasterGetOrderForNamesPrecertificateOCSPCAAValidationMethodsCAAAccountURIHeadNonceStatusOKEnforceMultiVAMultiVAFullResultsRemoveWFE2AccountIDCheckRenewalFirstMandatoryPOSTAsGETAllowV1RegistrationParallelCheckFailedValidationDeleteUnusedChallengesV1DisableNewValidationsPrecertificateRevocationStripDefaultSchemePortGetAuthorizationsPerf"
const _FeatureFlag_name = "unusedPerformValidationRPCACME13KeyRolloverSimplifiedVAHTTPTLSSNIRevalidationAllowRenewalFirstRLSetIssuedNamesRenewalBitFasterRateLimitProbeCTLogsRevokeAtRANewAuthorizationSchemaDisableAuthz2OrdersEarlyOrderRateLimitFasterGetOrderForNamesPrecertificateOCSPCAAValidationMethodsCAAAccountURIHeadNonceStatusOKEnforceMultiVAMultiVAFullResultsRemoveWFE2AccountIDCheckRenewalFirstMandatoryPOSTAsGETAllowV1RegistrationParallelCheckFailedValidationDeleteUnusedChallengesV1DisableNewValidationsPrecertificateRevocationStripDefaultSchemePortGetAuthorizationsPerfStoreIssuerInfo"
var _FeatureFlag_index = [...]uint16{0, 6, 26, 43, 59, 77, 96, 120, 135, 146, 156, 178, 197, 216, 238, 256, 276, 289, 306, 320, 338, 357, 374, 392, 411, 440, 462, 485, 509, 531, 552}
var _FeatureFlag_index = [...]uint16{0, 6, 26, 43, 59, 77, 96, 120, 135, 146, 156, 178, 197, 216, 238, 256, 276, 289, 306, 320, 338, 357, 374, 392, 411, 440, 462, 485, 509, 531, 552, 567}
func (i FeatureFlag) String() string {
if i < 0 || i >= FeatureFlag(len(_FeatureFlag_index)-1) {

View File

@ -68,6 +68,9 @@ const (
// GetAuthorizationsPerf enables a more performant GetAuthorizations2 query
// at the SA.
GetAuthorizationsPerf
// StoreIssuerInfo enables storage of information identifying the issuer of
// a certificate in the certificateStatus table.
StoreIssuerInfo
)
// List of features and their default value, protected by fMu
@ -102,6 +105,7 @@ var features = map[FeatureFlag]bool{
PrecertificateRevocation: false,
StripDefaultSchemePort: false,
GetAuthorizationsPerf: false,
StoreIssuerInfo: false,
}
var fMu = new(sync.RWMutex)

View File

@ -100,7 +100,7 @@ func (cas *CertificateAuthorityServerWrapper) IssueCertificateForPrecertificate(
}
func (cas *CertificateAuthorityServerWrapper) GenerateOCSP(ctx context.Context, req *capb.GenerateOCSPRequest) (*capb.OCSPResponse, error) {
if req.CertDER == nil || req.Status == nil || req.Reason == nil || req.RevokedAt == nil {
if (req.CertDER == nil && (req.Serial == nil || req.IssuerID == nil)) || req.Status == nil || req.Reason == nil || req.RevokedAt == nil {
return nil, errIncompleteRequest
}
return cas.inner.GenerateOCSP(ctx, req)

View File

@ -489,6 +489,17 @@ func (sas StorageAuthorityClientWrapper) DeactivateAuthorization2(ctx context.Co
return nil, err
}
func (sas StorageAuthorityClientWrapper) SerialExists(ctx context.Context, req *sapb.Serial) (*sapb.Exists, error) {
res, err := sas.inner.SerialExists(ctx, req)
if err != nil {
return nil, err
}
if res == nil || res.Exists == nil {
return nil, errIncompleteResponse
}
return res, nil
}
// StorageAuthorityServerWrapper is the gRPC version of a core.ServerAuthority server
type StorageAuthorityServerWrapper struct {
// TODO(#3119): Don't use core.StorageAuthority
@ -898,3 +909,10 @@ func (sas StorageAuthorityServerWrapper) DeactivateAuthorization2(ctx context.Co
return sas.inner.DeactivateAuthorization2(ctx, req)
}
func (sas StorageAuthorityServerWrapper) SerialExists(ctx context.Context, req *sapb.Serial) (*sapb.Exists, error) {
if req == nil || req.Serial == nil {
return nil, errIncompleteRequest
}
return sas.inner.SerialExists(ctx, req)
}

View File

@ -619,6 +619,10 @@ func (sa *StorageAuthority) GetPendingAuthorization2(ctx context.Context, req *s
return nil, nil
}
func (sa *StorageAuthority) SerialExists(ctx context.Context, req *sapb.Serial) (*sapb.Exists, error) {
return nil, nil
}
var (
authzIdValid = int64(1)
authzIdPending = int64(2)

View File

@ -0,0 +1,10 @@
-- +goose Up
-- SQL in section 'Up' is executed when this migration is applied
ALTER TABLE certificateStatus ADD `issuerID` BIGINT(20) DEFAULT NULL;
-- +goose Down
-- SQL section 'Down' is executed when this migration is rolled back
ALTER TABLE certificateStatus DROP `issuerID`;

View File

@ -137,26 +137,19 @@ const certStatusFields = "serial, status, ocspLastUpdated, revokedDate, revokedR
// SelectCertificateStatus selects all fields of one certificate status model
func SelectCertificateStatus(s db.OneSelector, q string, args ...interface{}) (certStatusModel, error) {
fields := certStatusFields
if features.Enabled(features.StoreIssuerInfo) {
fields += ", issuerID"
}
var model certStatusModel
err := s.SelectOne(
&model,
"SELECT "+certStatusFields+" FROM certificateStatus "+q,
"SELECT "+fields+" FROM certificateStatus "+q,
args...,
)
return model, err
}
// SelectCertificateStatuses selects all fields of multiple certificate status objects
func SelectCertificateStatuses(s db.Selector, q string, args ...interface{}) ([]core.CertificateStatus, error) {
var models []core.CertificateStatus
_, err := s.Select(
&models,
"SELECT "+certStatusFields+" FROM certificateStatus "+q,
args...,
)
return models, err
}
var mediumBlobSize = int(math.Pow(2, 24))
type issuedNameModel struct {
@ -191,6 +184,7 @@ type certStatusModel struct {
OCSPResponse []byte `db:"ocspResponse"`
NotAfter time.Time `db:"notAfter"`
IsExpired bool `db:"isExpired"`
IssuerID *int64 `db:"issuerID"`
}
// challModel is the description of a core.Challenge in the database

View File

@ -12,6 +12,7 @@ import (
"github.com/letsencrypt/boulder/core"
corepb "github.com/letsencrypt/boulder/core/proto"
berrors "github.com/letsencrypt/boulder/errors"
"github.com/letsencrypt/boulder/features"
bgrpc "github.com/letsencrypt/boulder/grpc"
sapb "github.com/letsencrypt/boulder/sa/proto"
)
@ -62,15 +63,40 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb
return nil, err
}
err = ssa.dbMap.WithContext(ctx).Insert(&certStatusModel{
Status: core.OCSPStatusGood,
OCSPLastUpdated: ssa.clk.Now(),
OCSPResponse: req.Ocsp,
Serial: serialHex,
RevokedDate: time.Time{},
RevokedReason: 0,
NotAfter: parsed.NotAfter,
})
// With feature.StoreIssuerInfo we've added a new field to certStatusModel
// so when we try and use dbMap.Insert it will always try to insert that field.
// That will break when the relevant migration hasn't been applied so we need
// to use an explicit INSERT statement that we can manipulate to include the
// field only when the feature is enabled (and as such the migration has been
// applied).
csFields := certStatusFields
if features.Enabled(features.StoreIssuerInfo) && req.IssuerID != nil {
csFields += ", issuerID"
}
qmarks := []string{}
for range strings.Split(csFields, ",") {
qmarks = append(qmarks, "?")
}
args := []interface{}{
serialHex, // serial
string(core.OCSPStatusGood), // stauts
ssa.clk.Now(), // ocspLastUpdated
time.Time{}, // revokedDate
0, // revokedReason
time.Time{}, // lastExpirationNagSent
req.Ocsp, // ocspResponse
parsed.NotAfter, // notAfter
false, // isExpired
}
if features.Enabled(features.StoreIssuerInfo) && req.IssuerID != nil {
args = append(args, req.IssuerID)
}
_, err = ssa.dbMap.WithContext(ctx).Exec(fmt.Sprintf(
"INSERT INTO certificateStatus (%s) VALUES (%s)",
csFields,
strings.Join(qmarks, ","),
), args...)
if err != nil {
return nil, err
}

View File

@ -1103,6 +1103,7 @@ type AddCertificateRequest struct {
// the current time. The orphan-finder uses this parameter to add
// certificates with the correct historic issued date
Issued *int64 `protobuf:"varint,4,opt,name=issued" json:"issued,omitempty"`
IssuerID *int64 `protobuf:"varint,5,opt,name=issuerID" json:"issuerID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1161,6 +1162,13 @@ func (m *AddCertificateRequest) GetIssued() int64 {
return 0
}
func (m *AddCertificateRequest) GetIssuerID() int64 {
if m != nil && m.IssuerID != nil {
return *m.IssuerID
}
return 0
}
type AddCertificateResponse struct {
Digest *string `protobuf:"bytes,1,opt,name=digest" json:"digest,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -1838,115 +1846,116 @@ func init() {
func init() { proto.RegisterFile("sa/proto/sa.proto", fileDescriptor_099fb35e782a48a6) }
var fileDescriptor_099fb35e782a48a6 = []byte{
// 1717 bytes of a gzipped FileDescriptorProto
// 1739 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xdd, 0x6e, 0xdb, 0xc8,
0x15, 0xd6, 0x8f, 0x65, 0x4b, 0xc7, 0x7f, 0xd2, 0xc4, 0x56, 0xb9, 0x8a, 0x9d, 0xc8, 0xb3, 0xd9,
0xc0, 0x8b, 0x02, 0x5e, 0x97, 0x5d, 0xec, 0x16, 0x70, 0x9b, 0xc4, 0x8e, 0x1c, 0xc7, 0x69, 0x62,
0x2b, 0x54, 0xe3, 0x16, 0x45, 0x6f, 0x18, 0x71, 0xa2, 0xb0, 0x91, 0x49, 0x65, 0x66, 0x64, 0x47,
0xbe, 0x2e, 0xd0, 0x3e, 0x41, 0xd1, 0xcb, 0x3e, 0x47, 0x5f, 0xa2, 0xcf, 0xd2, 0x37, 0x28, 0xe6,
0x70, 0x48, 0x91, 0x14, 0x29, 0xd7, 0x68, 0xd1, 0xbb, 0x39, 0x67, 0xce, 0xdf, 0xcc, 0x9c, 0x9f,
0x8f, 0x84, 0x86, 0xb0, 0xbf, 0x1b, 0x71, 0x5f, 0xfa, 0xdf, 0x09, 0x7b, 0x0f, 0x17, 0xa4, 0x24,
0xec, 0xd6, 0x66, 0xdf, 0xe7, 0x4c, 0x6f, 0xa8, 0x65, 0xb0, 0x45, 0xdb, 0xb0, 0x66, 0xb1, 0x81,
0x2b, 0x24, 0xb7, 0xa5, 0xeb, 0x7b, 0xa7, 0x1d, 0xb2, 0x06, 0x25, 0xd7, 0x31, 0x8a, 0xed, 0xe2,
0x6e, 0xd9, 0x2a, 0xb9, 0x0e, 0x7d, 0x00, 0xf0, 0xaa, 0x77, 0x7e, 0xf6, 0x5b, 0xf6, 0xfe, 0xd7,
0x6c, 0x42, 0xea, 0x50, 0xfe, 0xe3, 0xf5, 0x27, 0xdc, 0x5e, 0xb1, 0xd4, 0x92, 0xee, 0xc0, 0xfa,
0xe1, 0x58, 0x7e, 0xf4, 0xb9, 0x7b, 0x33, 0x6b, 0xa2, 0x86, 0x26, 0xfe, 0x51, 0x84, 0x07, 0x27,
0x4c, 0x76, 0x99, 0xe7, 0xb8, 0xde, 0x20, 0x21, 0x6d, 0xb1, 0xcf, 0x63, 0x26, 0x24, 0x79, 0x0c,
0x6b, 0x3c, 0x11, 0x87, 0x8e, 0x20, 0xc5, 0x55, 0x72, 0xae, 0xc3, 0x3c, 0xe9, 0x7e, 0x70, 0x19,
0xff, 0xcd, 0x64, 0xc4, 0x8c, 0x12, 0xba, 0x49, 0x71, 0xc9, 0x2e, 0xac, 0x4f, 0x39, 0x17, 0xf6,
0x70, 0xcc, 0x8c, 0x32, 0x0a, 0xa6, 0xd9, 0xe4, 0x01, 0xc0, 0x95, 0x3d, 0x74, 0x9d, 0x77, 0x9e,
0x74, 0x87, 0xc6, 0x02, 0x7a, 0x8d, 0x71, 0xa8, 0x80, 0xed, 0x13, 0x26, 0x2f, 0x14, 0x23, 0x11,
0xb9, 0xb8, 0x6b, 0xe8, 0x06, 0x2c, 0x39, 0xfe, 0xa5, 0xed, 0x7a, 0xc2, 0x28, 0xb5, 0xcb, 0xbb,
0x35, 0x2b, 0x24, 0xd5, 0xa5, 0x7a, 0xfe, 0x35, 0x06, 0x58, 0xb6, 0xd4, 0x92, 0xfe, 0xbd, 0x08,
0xf7, 0x32, 0x5c, 0x92, 0x5f, 0x40, 0x05, 0x43, 0x33, 0x8a, 0xed, 0xf2, 0xee, 0xb2, 0x49, 0xf7,
0x84, 0xbd, 0x97, 0x21, 0xb7, 0xf7, 0xc6, 0x1e, 0x1d, 0x0f, 0xd9, 0x25, 0xf3, 0xa4, 0x15, 0x28,
0xb4, 0xce, 0x01, 0xa6, 0x4c, 0xd2, 0x84, 0xc5, 0xc0, 0xb9, 0x7e, 0x25, 0x4d, 0x91, 0x6f, 0xa1,
0x62, 0x8f, 0xe5, 0xc7, 0x1b, 0xbc, 0xd5, 0x65, 0xf3, 0xde, 0x1e, 0xa6, 0x4a, 0xf2, 0xc5, 0x02,
0x09, 0xfa, 0xcf, 0x12, 0x34, 0x9e, 0x33, 0xae, 0xae, 0xb2, 0x6f, 0x4b, 0xd6, 0x93, 0xb6, 0x1c,
0x0b, 0x65, 0x58, 0x30, 0xee, 0xda, 0xc3, 0xd0, 0x70, 0x40, 0x21, 0x1f, 0x25, 0xf4, 0x33, 0x68,
0x4a, 0xbd, 0x93, 0xdf, 0x17, 0xa3, 0xd7, 0xb6, 0x90, 0xef, 0x46, 0x8e, 0x2d, 0x99, 0xa3, 0x9f,
0x20, 0xcd, 0x26, 0x6d, 0x58, 0xe6, 0xec, 0xca, 0xff, 0xc4, 0x9c, 0x8e, 0x2d, 0x99, 0x51, 0x41,
0xa9, 0x38, 0x8b, 0x3c, 0x82, 0x55, 0x4d, 0x5a, 0xcc, 0x16, 0xbe, 0x67, 0x2c, 0xa2, 0x4c, 0x92,
0x49, 0xbe, 0x87, 0xcd, 0xa1, 0x2d, 0xe4, 0xf1, 0x97, 0x91, 0x1b, 0x3c, 0xcd, 0x99, 0x3d, 0xe8,
0x31, 0x4f, 0x1a, 0x4b, 0x28, 0x9d, 0xbd, 0x49, 0x28, 0xac, 0xa8, 0x80, 0x2c, 0x26, 0x46, 0xbe,
0x27, 0x98, 0x51, 0xc5, 0x02, 0x48, 0xf0, 0x48, 0x0b, 0xaa, 0x9e, 0x2f, 0x0f, 0x3f, 0x48, 0xc6,
0x8d, 0x1a, 0x1a, 0x8b, 0x68, 0xb2, 0x05, 0x35, 0x57, 0xa0, 0x59, 0xe6, 0x18, 0xd0, 0x2e, 0xee,
0x56, 0xad, 0x29, 0xe3, 0xd5, 0x42, 0xb5, 0x54, 0x2f, 0xd3, 0x36, 0x2c, 0xf6, 0xa6, 0xb7, 0x95,
0x71, 0x8b, 0xf4, 0x00, 0x2a, 0x96, 0xed, 0x0d, 0xd0, 0x15, 0xb3, 0xf9, 0xd0, 0x65, 0x42, 0xea,
0x6c, 0x8b, 0x68, 0xa5, 0x3c, 0xb4, 0xa5, 0xda, 0x29, 0xe1, 0x8e, 0xa6, 0xe8, 0x36, 0x54, 0x9e,
0xfb, 0x63, 0x4f, 0x92, 0x0d, 0xa8, 0xf4, 0xd5, 0x42, 0x6b, 0x06, 0x04, 0xfd, 0x1d, 0x3c, 0xc4,
0xed, 0xd8, 0x9b, 0x8a, 0xa3, 0xc9, 0x99, 0x7d, 0xc9, 0xa2, 0x4c, 0x7f, 0x08, 0x15, 0xae, 0xdc,
0xa3, 0xe2, 0xb2, 0x59, 0x53, 0xd9, 0x87, 0xf1, 0x58, 0x01, 0x5f, 0x59, 0xf6, 0x94, 0x82, 0x4e,
0xf0, 0x80, 0xa0, 0x7f, 0x2e, 0xc2, 0x0a, 0x9a, 0xd6, 0xe6, 0xc8, 0x53, 0x58, 0xe9, 0xc7, 0x68,
0x9d, 0xcc, 0xf7, 0x95, 0xb9, 0xb8, 0x5c, 0x3c, 0x8b, 0x13, 0x0a, 0xad, 0x1f, 0x12, 0xc9, 0x4c,
0x60, 0x41, 0x39, 0xd2, 0x77, 0x85, 0xeb, 0xe9, 0x19, 0x4b, 0xf1, 0x33, 0x76, 0x61, 0x1b, 0x1d,
0xc4, 0x5b, 0x9e, 0x38, 0x9a, 0x9c, 0x76, 0xc3, 0x13, 0xaa, 0xce, 0x35, 0xd2, 0xdd, 0xad, 0xe4,
0x8e, 0xa6, 0x27, 0x2e, 0x65, 0x9f, 0x98, 0xfe, 0xa5, 0x08, 0x3b, 0x68, 0xf2, 0xd4, 0xbb, 0xfa,
0xef, 0x5b, 0x44, 0x0b, 0xaa, 0x1f, 0x7d, 0x21, 0xf1, 0x34, 0x41, 0x5f, 0x8b, 0xe8, 0x69, 0x28,
0xe5, 0x9c, 0x50, 0x7a, 0x40, 0x30, 0x92, 0x73, 0xee, 0x30, 0x1e, 0xb9, 0xde, 0x82, 0x9a, 0xdd,
0xc7, 0xd3, 0x47, 0x5e, 0xa7, 0x8c, 0xdb, 0xcf, 0xf7, 0x12, 0x36, 0xd0, 0xe8, 0x8b, 0xb7, 0x9d,
0xb3, 0x1e, 0x93, 0x91, 0xd9, 0x26, 0x2c, 0x5e, 0xbb, 0x9e, 0xe3, 0x5f, 0x6b, 0x9b, 0x9a, 0xca,
0x6f, 0x72, 0x74, 0x1f, 0x36, 0xb4, 0x91, 0xe3, 0x2f, 0xae, 0x98, 0x5a, 0x8a, 0x69, 0x14, 0x93,
0x1a, 0x5d, 0x68, 0x77, 0x39, 0xbb, 0x72, 0xfd, 0xb1, 0x88, 0x25, 0x65, 0x52, 0x3b, 0xaf, 0x91,
0x6d, 0x40, 0x85, 0xb3, 0xc1, 0x69, 0x27, 0x7c, 0x7f, 0x24, 0x54, 0x85, 0x05, 0xea, 0x4a, 0x8f,
0xe1, 0x0a, 0xf5, 0xaa, 0x96, 0xa6, 0xa8, 0x84, 0xfa, 0xa1, 0xe3, 0x04, 0x65, 0x18, 0xfa, 0x88,
0x6c, 0x15, 0x63, 0xb6, 0x62, 0x35, 0x5a, 0x4a, 0x74, 0x3a, 0x03, 0x96, 0xfa, 0x9c, 0x61, 0x27,
0x0b, 0x1a, 0x7a, 0x48, 0xaa, 0x1d, 0x86, 0x05, 0x2f, 0x74, 0x8f, 0x0b, 0x49, 0xfa, 0x09, 0x36,
0x0f, 0x1d, 0x27, 0x76, 0xc8, 0xd0, 0x75, 0x1d, 0xca, 0x0e, 0xe3, 0xe1, 0xb8, 0x75, 0x18, 0xcf,
0x3e, 0x98, 0x2a, 0x01, 0xd5, 0x8a, 0xd0, 0xe3, 0x8a, 0x85, 0x6b, 0x15, 0xa0, 0x2b, 0xc4, 0x38,
0xea, 0xa8, 0x9a, 0xa2, 0xfb, 0xd0, 0x4c, 0x3b, 0xd3, 0x0d, 0x4c, 0x5d, 0xa6, 0x3b, 0x08, 0x7b,
0x8a, 0xba, 0x4c, 0xa4, 0x68, 0x17, 0x56, 0x30, 0xa9, 0xe2, 0x55, 0x12, 0x83, 0x08, 0x64, 0x1f,
0xee, 0x8d, 0x05, 0xbb, 0x30, 0x93, 0xc9, 0x8f, 0x11, 0x56, 0xad, 0xac, 0x2d, 0xfa, 0x1a, 0x68,
0x38, 0x54, 0xd1, 0x72, 0x76, 0xd9, 0xa4, 0xfd, 0x34, 0x61, 0xd1, 0xee, 0xf7, 0x65, 0x74, 0x78,
0x4d, 0xd1, 0x09, 0xfc, 0xe4, 0x84, 0x05, 0x79, 0xff, 0xc2, 0xe7, 0x89, 0x96, 0x35, 0x55, 0x29,
0xc6, 0x55, 0xb2, 0x3b, 0x55, 0xde, 0x41, 0xca, 0xf9, 0x07, 0xf9, 0x5b, 0x11, 0x8c, 0x13, 0x26,
0xff, 0x6f, 0xc8, 0x40, 0x0d, 0x4c, 0xce, 0x3e, 0x8f, 0x5d, 0xae, 0x63, 0xb9, 0x09, 0x92, 0xa9,
0x6a, 0xa5, 0xd9, 0xf4, 0xaf, 0x45, 0x58, 0x4b, 0xc1, 0x87, 0x9f, 0x87, 0xe3, 0x3d, 0xe8, 0xb8,
0xdb, 0xaa, 0xdc, 0xe7, 0x20, 0x07, 0x94, 0xfd, 0xdf, 0x23, 0x87, 0xd7, 0xf0, 0xf0, 0xd0, 0x71,
0xb2, 0xd0, 0x60, 0x74, 0x73, 0xdf, 0x26, 0x03, 0x9d, 0x67, 0xed, 0x11, 0xd4, 0x53, 0xf8, 0x13,
0xaf, 0xcd, 0x75, 0xc2, 0x7e, 0xa2, 0x96, 0x94, 0xce, 0x48, 0x99, 0x33, 0x48, 0xf7, 0x1b, 0x68,
0x24, 0x64, 0xcc, 0x94, 0xa9, 0x72, 0x60, 0xea, 0x06, 0x0c, 0x0b, 0x11, 0x45, 0x46, 0xbd, 0xce,
0x81, 0x3f, 0x3c, 0xc0, 0x24, 0x3a, 0x73, 0x03, 0x4a, 0xd5, 0xad, 0x42, 0x37, 0xfa, 0x81, 0x71,
0xad, 0x86, 0x00, 0x0f, 0x61, 0xc6, 0x02, 0xd6, 0x73, 0x44, 0xd3, 0x3f, 0x95, 0x60, 0xeb, 0x85,
0xeb, 0xd9, 0x43, 0xf7, 0x86, 0x65, 0xe2, 0xe8, 0x8c, 0x92, 0xd1, 0xb8, 0xab, 0x94, 0xc0, 0x5d,
0xb1, 0x5e, 0x54, 0x4e, 0xf4, 0x22, 0x1c, 0x18, 0x52, 0xb2, 0xcb, 0x51, 0x88, 0xc5, 0x6a, 0xd6,
0x94, 0x41, 0x3a, 0xd0, 0xc0, 0x39, 0xa7, 0x9d, 0xf6, 0x7d, 0xee, 0x08, 0xa3, 0x82, 0x8f, 0xd4,
0x0c, 0x1e, 0xe9, 0x22, 0xb5, 0x6d, 0xcd, 0x2a, 0x90, 0x27, 0xb0, 0x3e, 0x65, 0x1e, 0x73, 0xee,
0x73, 0xc4, 0x6a, 0xcb, 0xe6, 0x46, 0x60, 0xa3, 0xcb, 0xfd, 0xf7, 0x43, 0x76, 0xd9, 0x61, 0xd2,
0x76, 0x87, 0xc2, 0x4a, 0x0b, 0x9b, 0xff, 0x6a, 0x40, 0xbd, 0x27, 0x7d, 0x6e, 0x0f, 0xc2, 0x5b,
0x90, 0x13, 0x72, 0x00, 0xeb, 0x27, 0x2c, 0x31, 0xda, 0x09, 0xc1, 0x79, 0x96, 0x28, 0xb6, 0x16,
0x09, 0x5c, 0xc4, 0xb9, 0xb4, 0x40, 0x7e, 0x09, 0x1b, 0x29, 0xe5, 0xa3, 0x89, 0xfa, 0xde, 0x59,
0x53, 0x16, 0xa6, 0xdf, 0x3f, 0x39, 0xda, 0x3f, 0x83, 0xb5, 0x13, 0x16, 0x47, 0x4e, 0x04, 0x94,
0x5e, 0x30, 0x46, 0x5a, 0x8d, 0x40, 0x27, 0xb6, 0x4d, 0x0b, 0xe4, 0x7b, 0x68, 0xa8, 0x4f, 0x22,
0xce, 0xfa, 0x77, 0xd1, 0x3a, 0xc0, 0x30, 0x67, 0x61, 0x77, 0x5c, 0x71, 0x13, 0x71, 0x54, 0x5a,
0x84, 0x16, 0x48, 0x0f, 0x8c, 0x3c, 0x84, 0x47, 0xbe, 0x8e, 0xc0, 0x57, 0x3e, 0xfe, 0x6b, 0xd5,
0xd3, 0x08, 0x8d, 0x16, 0xc8, 0x4b, 0x68, 0x66, 0x43, 0x2a, 0xb2, 0x13, 0x49, 0xe7, 0xc1, 0xad,
0x56, 0x2d, 0x12, 0xa1, 0x05, 0xf2, 0x06, 0xee, 0xe7, 0x48, 0x23, 0xb6, 0xbc, 0xab, 0x39, 0x13,
0x96, 0x63, 0x70, 0x88, 0x34, 0xa3, 0xbd, 0x04, 0x3e, 0x4a, 0xea, 0xfc, 0x00, 0xab, 0x09, 0xb4,
0x43, 0x8c, 0x68, 0x37, 0x05, 0x80, 0x92, 0x7a, 0x3f, 0xc2, 0x6a, 0x02, 0xdb, 0x04, 0x7a, 0x59,
0x70, 0xa7, 0x85, 0x2f, 0x15, 0xb0, 0x68, 0x81, 0x9c, 0xc3, 0x57, 0xb9, 0x10, 0x87, 0x3c, 0x52,
0xa2, 0xb7, 0x21, 0xa0, 0x94, 0xc1, 0x67, 0x98, 0x56, 0xc9, 0x36, 0x46, 0x36, 0x66, 0xfa, 0xfc,
0x69, 0xc7, 0x6c, 0x65, 0x35, 0x55, 0x7c, 0x50, 0x32, 0x33, 0xd0, 0x4c, 0xb2, 0xa5, 0x4c, 0xe4,
0x0d, 0xba, 0x16, 0x99, 0x1d, 0x24, 0xb4, 0x40, 0xde, 0xe1, 0x68, 0xcc, 0xea, 0xf3, 0x26, 0xa1,
0xda, 0xde, 0x9c, 0x7f, 0x02, 0x79, 0x01, 0x3e, 0xd1, 0x79, 0x92, 0x39, 0x40, 0xcc, 0xcc, 0x9a,
0x4f, 0x3c, 0xd6, 0x1f, 0x60, 0x6b, 0x0e, 0xf6, 0x30, 0xc9, 0x63, 0x1d, 0xda, 0x2d, 0xe8, 0x24,
0xe7, 0xd0, 0x6f, 0x75, 0x74, 0x99, 0xdf, 0x03, 0x26, 0xf9, 0x26, 0x8a, 0x64, 0xde, 0x07, 0x43,
0x32, 0x60, 0x0b, 0xe1, 0xcd, 0x45, 0x96, 0xb9, 0x9d, 0x78, 0xac, 0x77, 0x09, 0xf3, 0x00, 0xd6,
0xcf, 0xd8, 0x75, 0xaa, 0x59, 0xce, 0xb4, 0xb6, 0x9c, 0x76, 0xf7, 0x23, 0x90, 0xe0, 0xab, 0xfc,
0x56, 0xfd, 0xe5, 0x80, 0x77, 0x7c, 0x39, 0x92, 0x13, 0x5a, 0x20, 0xa7, 0xb0, 0x96, 0x84, 0x9e,
0xe4, 0x2b, 0x8c, 0x2e, 0x0b, 0xfb, 0xb6, 0x5a, 0x59, 0x5b, 0x7a, 0x0e, 0x16, 0xc8, 0xaf, 0xa0,
0xa1, 0x40, 0x44, 0xb2, 0x7f, 0xce, 0xb1, 0x96, 0x8a, 0x64, 0x1f, 0x6a, 0x11, 0xce, 0xd7, 0xf5,
0x91, 0x82, 0xfd, 0x69, 0x8d, 0x03, 0x68, 0x76, 0x98, 0xdd, 0x97, 0xee, 0xd5, 0xec, 0xc1, 0x67,
0x33, 0x2e, 0xa5, 0xfc, 0x18, 0xaa, 0x67, 0xec, 0x1a, 0x93, 0x89, 0xe8, 0x2d, 0x24, 0x5a, 0x71,
0x02, 0xc3, 0x22, 0x3d, 0x8d, 0x64, 0xbb, 0xdc, 0xef, 0x33, 0x21, 0x5c, 0x6f, 0x90, 0xa9, 0x11,
0x5a, 0xfe, 0x29, 0xac, 0x86, 0x1a, 0x38, 0x1b, 0x6f, 0x13, 0x0e, 0xd1, 0x43, 0x7e, 0x2c, 0x53,
0xe1, 0x6a, 0x88, 0xaa, 0x09, 0x76, 0xfe, 0xf8, 0x37, 0x40, 0x3a, 0xf0, 0x27, 0x50, 0x4f, 0x43,
0x70, 0x72, 0x5f, 0x27, 0x67, 0x16, 0x30, 0x4f, 0xeb, 0x3f, 0x83, 0xc6, 0x0c, 0xa8, 0x0a, 0x9a,
0x4e, 0x1e, 0xd6, 0x4a, 0x87, 0x6b, 0x01, 0x39, 0x63, 0xd7, 0xe9, 0x02, 0xf9, 0x5a, 0x3f, 0xed,
0x3c, 0xb4, 0x19, 0x4c, 0xcc, 0x19, 0xe8, 0x87, 0xf9, 0xda, 0xcc, 0x44, 0x5b, 0x26, 0x69, 0x63,
0x83, 0x9f, 0x83, 0xc4, 0xd2, 0xe1, 0x3d, 0x05, 0x63, 0x9a, 0x3e, 0xff, 0x51, 0x7f, 0x4e, 0x1a,
0x38, 0x5a, 0xfa, 0x7d, 0x05, 0x7f, 0xd9, 0xfe, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x7e, 0xd6, 0x49,
0xb0, 0xe1, 0x15, 0x00, 0x00,
0xbe, 0x2e, 0xd0, 0x3e, 0x41, 0x51, 0xa0, 0x37, 0x7d, 0x8e, 0xbe, 0x44, 0x5f, 0xa9, 0x98, 0xc3,
0x21, 0x45, 0x52, 0xa4, 0x5c, 0xa3, 0x45, 0xef, 0xe6, 0x9c, 0x39, 0x7f, 0x33, 0x73, 0x7e, 0x3e,
0x12, 0x1a, 0xc2, 0xfe, 0x6e, 0xc4, 0x7d, 0xe9, 0x7f, 0x27, 0xec, 0x3d, 0x5c, 0x90, 0x92, 0xb0,
0x5b, 0x9b, 0x7d, 0x9f, 0x33, 0xbd, 0xa1, 0x96, 0xc1, 0x16, 0x6d, 0xc3, 0x9a, 0xc5, 0x06, 0xae,
0x90, 0xdc, 0x96, 0xae, 0xef, 0x9d, 0x76, 0xc8, 0x1a, 0x94, 0x5c, 0xc7, 0x28, 0xb6, 0x8b, 0xbb,
0x65, 0xab, 0xe4, 0x3a, 0xf4, 0x01, 0xc0, 0xab, 0xde, 0xf9, 0xd9, 0x6f, 0xd9, 0xfb, 0x5f, 0xb3,
0x09, 0xa9, 0x43, 0xf9, 0x8f, 0xd7, 0x9f, 0x70, 0x7b, 0xc5, 0x52, 0x4b, 0xba, 0x03, 0xeb, 0x87,
0x63, 0xf9, 0xd1, 0xe7, 0xee, 0xcd, 0xac, 0x89, 0x1a, 0x9a, 0xf8, 0x67, 0x11, 0x1e, 0x9c, 0x30,
0xd9, 0x65, 0x9e, 0xe3, 0x7a, 0x83, 0x84, 0xb4, 0xc5, 0x3e, 0x8f, 0x99, 0x90, 0xe4, 0x31, 0xac,
0xf1, 0x44, 0x1c, 0x3a, 0x82, 0x14, 0x57, 0xc9, 0xb9, 0x0e, 0xf3, 0xa4, 0xfb, 0xc1, 0x65, 0xfc,
0x37, 0x93, 0x11, 0x33, 0x4a, 0xe8, 0x26, 0xc5, 0x25, 0xbb, 0xb0, 0x3e, 0xe5, 0x5c, 0xd8, 0xc3,
0x31, 0x33, 0xca, 0x28, 0x98, 0x66, 0x93, 0x07, 0x00, 0x57, 0xf6, 0xd0, 0x75, 0xde, 0x79, 0xd2,
0x1d, 0x1a, 0x0b, 0xe8, 0x35, 0xc6, 0xa1, 0x02, 0xb6, 0x4f, 0x98, 0xbc, 0x50, 0x8c, 0x44, 0xe4,
0xe2, 0xae, 0xa1, 0x1b, 0xb0, 0xe4, 0xf8, 0x97, 0xb6, 0xeb, 0x09, 0xa3, 0xd4, 0x2e, 0xef, 0xd6,
0xac, 0x90, 0x54, 0x97, 0xea, 0xf9, 0xd7, 0x18, 0x60, 0xd9, 0x52, 0x4b, 0xfa, 0x8f, 0x22, 0xdc,
0xcb, 0x70, 0x49, 0x7e, 0x01, 0x15, 0x0c, 0xcd, 0x28, 0xb6, 0xcb, 0xbb, 0xcb, 0x26, 0xdd, 0x13,
0xf6, 0x5e, 0x86, 0xdc, 0xde, 0x1b, 0x7b, 0x74, 0x3c, 0x64, 0x97, 0xcc, 0x93, 0x56, 0xa0, 0xd0,
0x3a, 0x07, 0x98, 0x32, 0x49, 0x13, 0x16, 0x03, 0xe7, 0xfa, 0x95, 0x34, 0x45, 0xbe, 0x85, 0x8a,
0x3d, 0x96, 0x1f, 0x6f, 0xf0, 0x56, 0x97, 0xcd, 0x7b, 0x7b, 0x98, 0x2a, 0xc9, 0x17, 0x0b, 0x24,
0xe8, 0xbf, 0x4a, 0xd0, 0x78, 0xce, 0xb8, 0xba, 0xca, 0xbe, 0x2d, 0x59, 0x4f, 0xda, 0x72, 0x2c,
0x94, 0x61, 0xc1, 0xb8, 0x6b, 0x0f, 0x43, 0xc3, 0x01, 0x85, 0x7c, 0x94, 0xd0, 0xcf, 0xa0, 0x29,
0xf5, 0x4e, 0x7e, 0x5f, 0x8c, 0x5e, 0xdb, 0x42, 0xbe, 0x1b, 0x39, 0xb6, 0x64, 0x8e, 0x7e, 0x82,
0x34, 0x9b, 0xb4, 0x61, 0x99, 0xb3, 0x2b, 0xff, 0x13, 0x73, 0x3a, 0xb6, 0x64, 0x46, 0x05, 0xa5,
0xe2, 0x2c, 0xf2, 0x08, 0x56, 0x35, 0x69, 0x31, 0x5b, 0xf8, 0x9e, 0xb1, 0x88, 0x32, 0x49, 0x26,
0xf9, 0x1e, 0x36, 0x87, 0xb6, 0x90, 0xc7, 0x5f, 0x46, 0x6e, 0xf0, 0x34, 0x67, 0xf6, 0xa0, 0xc7,
0x3c, 0x69, 0x2c, 0xa1, 0x74, 0xf6, 0x26, 0xa1, 0xb0, 0xa2, 0x02, 0xb2, 0x98, 0x18, 0xf9, 0x9e,
0x60, 0x46, 0x15, 0x0b, 0x20, 0xc1, 0x23, 0x2d, 0xa8, 0x7a, 0xbe, 0x3c, 0xfc, 0x20, 0x19, 0x37,
0x6a, 0x68, 0x2c, 0xa2, 0xc9, 0x16, 0xd4, 0x5c, 0x81, 0x66, 0x99, 0x63, 0x40, 0xbb, 0xb8, 0x5b,
0xb5, 0xa6, 0x8c, 0x57, 0x0b, 0xd5, 0x52, 0xbd, 0x4c, 0xdb, 0xb0, 0xd8, 0x9b, 0xde, 0x56, 0xc6,
0x2d, 0xd2, 0x03, 0xa8, 0x58, 0xb6, 0x37, 0x40, 0x57, 0xcc, 0xe6, 0x43, 0x97, 0x09, 0xa9, 0xb3,
0x2d, 0xa2, 0x95, 0xf2, 0xd0, 0x96, 0x6a, 0xa7, 0x84, 0x3b, 0x9a, 0xa2, 0xdb, 0x50, 0x79, 0xee,
0x8f, 0x3d, 0x49, 0x36, 0xa0, 0xd2, 0x57, 0x0b, 0xad, 0x19, 0x10, 0xf4, 0x77, 0xf0, 0x10, 0xb7,
0x63, 0x6f, 0x2a, 0x8e, 0x26, 0x67, 0xf6, 0x25, 0x8b, 0x32, 0xfd, 0x21, 0x54, 0xb8, 0x72, 0x8f,
0x8a, 0xcb, 0x66, 0x4d, 0x65, 0x1f, 0xc6, 0x63, 0x05, 0x7c, 0x65, 0xd9, 0x53, 0x0a, 0x3a, 0xc1,
0x03, 0x82, 0xfe, 0xb9, 0x08, 0x2b, 0x68, 0x5a, 0x9b, 0x23, 0x4f, 0x61, 0xa5, 0x1f, 0xa3, 0x75,
0x32, 0xdf, 0x57, 0xe6, 0xe2, 0x72, 0xf1, 0x2c, 0x4e, 0x28, 0xb4, 0x7e, 0x48, 0x24, 0x33, 0x81,
0x05, 0xe5, 0x48, 0xdf, 0x15, 0xae, 0xa7, 0x67, 0x2c, 0xc5, 0xcf, 0xd8, 0x85, 0x6d, 0x74, 0x10,
0x6f, 0x79, 0xe2, 0x68, 0x72, 0xda, 0x0d, 0x4f, 0xa8, 0x3a, 0xd7, 0x48, 0x77, 0xb7, 0x92, 0x3b,
0x9a, 0x9e, 0xb8, 0x94, 0x7d, 0x62, 0xfa, 0x97, 0x22, 0xec, 0xa0, 0xc9, 0x53, 0xef, 0xea, 0xbf,
0x6f, 0x11, 0x2d, 0xa8, 0x7e, 0xf4, 0x85, 0xc4, 0xd3, 0x04, 0x7d, 0x2d, 0xa2, 0xa7, 0xa1, 0x94,
0x73, 0x42, 0xe9, 0x01, 0xc1, 0x48, 0xce, 0xb9, 0xc3, 0x78, 0xe4, 0x7a, 0x0b, 0x6a, 0x76, 0x1f,
0x4f, 0x1f, 0x79, 0x9d, 0x32, 0x6e, 0x3f, 0xdf, 0x4b, 0xd8, 0x40, 0xa3, 0x2f, 0xde, 0x76, 0xce,
0x7a, 0x4c, 0x46, 0x66, 0x9b, 0xb0, 0x78, 0xed, 0x7a, 0x8e, 0x7f, 0xad, 0x6d, 0x6a, 0x2a, 0xbf,
0xc9, 0xd1, 0x7d, 0xd8, 0xd0, 0x46, 0x8e, 0xbf, 0xb8, 0x62, 0x6a, 0x29, 0xa6, 0x51, 0x4c, 0x6a,
0x74, 0xa1, 0xdd, 0xe5, 0xec, 0xca, 0xf5, 0xc7, 0x22, 0x96, 0x94, 0x49, 0xed, 0xbc, 0x46, 0xb6,
0x01, 0x15, 0xce, 0x06, 0xa7, 0x9d, 0xf0, 0xfd, 0x91, 0x50, 0x15, 0x16, 0xa8, 0x2b, 0x3d, 0x86,
0x2b, 0xd4, 0xab, 0x5a, 0x9a, 0xa2, 0x12, 0xea, 0x87, 0x8e, 0x13, 0x94, 0x61, 0xe8, 0x23, 0xb2,
0x55, 0x8c, 0xd9, 0x8a, 0xd5, 0x68, 0x29, 0xd1, 0xe9, 0x0c, 0x58, 0xea, 0x73, 0x86, 0x9d, 0x2c,
0x68, 0xe8, 0x21, 0xa9, 0x76, 0x18, 0x16, 0xbc, 0xd0, 0x3d, 0x2e, 0x24, 0x55, 0x85, 0x6c, 0x1e,
0x3a, 0x4e, 0xec, 0x94, 0xa1, 0xef, 0x3a, 0x94, 0x1d, 0xc6, 0xc3, 0x79, 0xeb, 0x30, 0x9e, 0x7d,
0x32, 0x55, 0x03, 0xaa, 0x17, 0xa1, 0xcb, 0x15, 0x0b, 0xd7, 0x2a, 0x42, 0x57, 0x88, 0x71, 0xd4,
0x52, 0x35, 0xa5, 0xb2, 0x0c, 0x57, 0xfc, 0xb4, 0xa3, 0xdb, 0x68, 0x44, 0xd3, 0x7d, 0x68, 0xa6,
0x03, 0xd1, 0xdd, 0x4d, 0xdd, 0xb4, 0x3b, 0x08, 0x1b, 0x8e, 0xba, 0x69, 0xa4, 0x68, 0x17, 0x56,
0x30, 0xe3, 0xe2, 0x25, 0x14, 0xc3, 0x0f, 0x64, 0x1f, 0xee, 0x8d, 0x05, 0xbb, 0x30, 0x93, 0x95,
0x81, 0xd1, 0x57, 0xad, 0xac, 0x2d, 0xfa, 0x1a, 0x68, 0x38, 0x71, 0xd1, 0x72, 0x76, 0x4d, 0xa5,
0xfd, 0x34, 0x61, 0xd1, 0xee, 0xf7, 0x65, 0x74, 0x31, 0x9a, 0xa2, 0x13, 0xf8, 0xc9, 0x09, 0x0b,
0x8a, 0xe2, 0x85, 0xcf, 0x13, 0xfd, 0x6c, 0xaa, 0x52, 0x8c, 0xab, 0x64, 0xb7, 0xb1, 0xbc, 0x83,
0x94, 0xf3, 0x0f, 0xf2, 0xb7, 0x22, 0x18, 0x27, 0x4c, 0xfe, 0xdf, 0x60, 0x83, 0x9a, 0xa6, 0x9c,
0x7d, 0x1e, 0xbb, 0x5c, 0xc7, 0x72, 0x13, 0x64, 0x5a, 0xd5, 0x4a, 0xb3, 0xe9, 0x5f, 0x8b, 0xb0,
0x96, 0xc2, 0x16, 0x3f, 0x0f, 0x67, 0x7f, 0xd0, 0x8e, 0xb7, 0x55, 0x2f, 0x98, 0x03, 0x2b, 0x50,
0xf6, 0x7f, 0x0f, 0x2b, 0x5e, 0xc3, 0xc3, 0x43, 0xc7, 0xc9, 0x82, 0x8a, 0xd1, 0xcd, 0x7d, 0x9b,
0x0c, 0x74, 0x9e, 0xb5, 0x47, 0x50, 0x4f, 0x81, 0x53, 0xbc, 0x36, 0xd7, 0x09, 0x9b, 0x8d, 0x5a,
0x52, 0x3a, 0x23, 0x65, 0xce, 0xc0, 0xe0, 0x6f, 0xa0, 0x91, 0x90, 0x31, 0x53, 0xa6, 0xca, 0x81,
0xa9, 0x1b, 0x30, 0x2c, 0x84, 0x1b, 0x19, 0xb5, 0x3c, 0x07, 0x1b, 0xf1, 0x00, 0xb0, 0xe8, 0xcc,
0x0d, 0x28, 0x55, 0xd3, 0x0a, 0xfa, 0xe8, 0x07, 0xc6, 0xb5, 0xaa, 0x5d, 0x1e, 0x62, 0x90, 0x05,
0xac, 0xf5, 0x88, 0xa6, 0x7f, 0x2a, 0xc1, 0xd6, 0x0b, 0xd7, 0xb3, 0x87, 0xee, 0x0d, 0xcb, 0x04,
0xd9, 0x19, 0x25, 0xa3, 0x41, 0x59, 0x29, 0x01, 0xca, 0x62, 0x8d, 0xaa, 0x9c, 0x68, 0x54, 0x38,
0x4d, 0xa4, 0x64, 0x97, 0xa3, 0x10, 0xa8, 0xd5, 0xac, 0x29, 0x83, 0x74, 0xa0, 0x81, 0x43, 0x50,
0x3b, 0xed, 0xfb, 0xdc, 0x11, 0x46, 0x05, 0x1f, 0xa9, 0x19, 0x3c, 0xd2, 0x45, 0x6a, 0xdb, 0x9a,
0x55, 0x20, 0x4f, 0x60, 0x7d, 0xca, 0x3c, 0xe6, 0xdc, 0xe7, 0x08, 0xe4, 0x96, 0xcd, 0x8d, 0xc0,
0x46, 0x97, 0xfb, 0xef, 0x87, 0xec, 0xb2, 0xc3, 0xa4, 0xed, 0x0e, 0x85, 0x95, 0x16, 0x36, 0xff,
0x4e, 0xa0, 0xde, 0x93, 0x3e, 0xb7, 0x07, 0xe1, 0x2d, 0xc8, 0x09, 0x39, 0x80, 0xf5, 0x13, 0x96,
0x98, 0xfb, 0x84, 0xe0, 0xb0, 0x4b, 0x14, 0x5b, 0x8b, 0x04, 0x2e, 0xe2, 0x5c, 0x5a, 0x20, 0xbf,
0x84, 0x8d, 0x94, 0xf2, 0xd1, 0x44, 0x7d, 0x0c, 0xad, 0x29, 0x0b, 0xd3, 0x8f, 0xa3, 0x1c, 0xed,
0x9f, 0xc1, 0xda, 0x09, 0x8b, 0xc3, 0x2a, 0x02, 0x4a, 0x2f, 0x98, 0x31, 0xad, 0x46, 0xa0, 0x13,
0xdb, 0xa6, 0x05, 0xf2, 0x3d, 0x34, 0xd4, 0xf7, 0x12, 0x67, 0xfd, 0xbb, 0x68, 0x1d, 0x60, 0x98,
0xb3, 0x98, 0x3c, 0xae, 0xb8, 0x89, 0x20, 0x2b, 0x2d, 0x42, 0x0b, 0xa4, 0x07, 0x46, 0x1e, 0xfc,
0x23, 0x5f, 0x47, 0xc8, 0x2c, 0x1f, 0x1c, 0xb6, 0xea, 0x69, 0xf8, 0x46, 0x0b, 0xe4, 0x25, 0x34,
0xb3, 0xf1, 0x16, 0xd9, 0x89, 0xa4, 0xf3, 0xb0, 0x58, 0xab, 0x16, 0x89, 0xd0, 0x02, 0x79, 0x03,
0xf7, 0x73, 0xa4, 0x11, 0x78, 0xde, 0xd5, 0x9c, 0x09, 0xcb, 0x31, 0xac, 0x44, 0x9a, 0xd1, 0x5e,
0x02, 0x3c, 0x25, 0x75, 0x7e, 0x80, 0xd5, 0x04, 0x14, 0x22, 0x46, 0xb4, 0x9b, 0x42, 0x47, 0x49,
0xbd, 0x1f, 0x61, 0x35, 0x01, 0x7c, 0x02, 0xbd, 0x2c, 0x2c, 0xd4, 0xc2, 0x97, 0x0a, 0x58, 0xb4,
0x40, 0xce, 0xe1, 0xab, 0x5c, 0xfc, 0x43, 0x1e, 0x29, 0xd1, 0xdb, 0xe0, 0x51, 0xca, 0xe0, 0x33,
0x4c, 0xab, 0x64, 0x1b, 0x23, 0x1b, 0x33, 0x7d, 0xfe, 0xb4, 0x63, 0xb6, 0xb2, 0x9a, 0x2a, 0x3e,
0x28, 0x99, 0x19, 0x68, 0x26, 0xd9, 0x52, 0x26, 0xf2, 0x06, 0x5d, 0x8b, 0xcc, 0x0e, 0x12, 0x5a,
0x20, 0xef, 0x70, 0x34, 0x66, 0xf5, 0x79, 0x93, 0x50, 0x6d, 0x6f, 0xce, 0x0f, 0x83, 0xbc, 0x00,
0x9f, 0xe8, 0x3c, 0xc9, 0x1c, 0x20, 0x66, 0x66, 0xcd, 0x27, 0x1e, 0xeb, 0x0f, 0xb0, 0x35, 0x07,
0x7b, 0x98, 0xe4, 0xb1, 0x0e, 0xed, 0x16, 0x74, 0x92, 0x73, 0xe8, 0xb7, 0x3a, 0xba, 0xcc, 0x8f,
0x05, 0x93, 0x7c, 0x13, 0x45, 0x32, 0xef, 0x6b, 0x22, 0x19, 0xb0, 0x85, 0xf0, 0xe6, 0x22, 0xcb,
0xdc, 0x4e, 0x3c, 0xd6, 0xbb, 0x84, 0x79, 0x00, 0xeb, 0x67, 0xec, 0x3a, 0xd5, 0x2c, 0x67, 0x5a,
0x5b, 0x4e, 0xbb, 0xfb, 0x11, 0x48, 0xf0, 0xc9, 0x7e, 0xab, 0xfe, 0x72, 0xc0, 0x3b, 0xbe, 0x1c,
0xc9, 0x09, 0x2d, 0x90, 0x53, 0x58, 0x4b, 0x42, 0x4f, 0xf2, 0x15, 0x46, 0x97, 0x85, 0x8b, 0x5b,
0xad, 0xac, 0x2d, 0x3d, 0x07, 0x0b, 0xe4, 0x57, 0xd0, 0x50, 0x20, 0x22, 0xd9, 0x3f, 0xe7, 0x58,
0x4b, 0x45, 0xb2, 0x0f, 0xb5, 0xe8, 0x23, 0x40, 0xd7, 0x47, 0xea, 0x9b, 0x20, 0xad, 0x71, 0x00,
0xcd, 0x0e, 0xb3, 0xfb, 0xd2, 0xbd, 0x9a, 0x3d, 0xf8, 0x6c, 0xc6, 0xa5, 0x94, 0x1f, 0x43, 0xf5,
0x8c, 0x5d, 0x63, 0x32, 0x11, 0xbd, 0x85, 0x44, 0x2b, 0x4e, 0x60, 0x58, 0xa4, 0xa7, 0x91, 0x6c,
0x97, 0xfb, 0x7d, 0x26, 0x84, 0xeb, 0x0d, 0x32, 0x35, 0x42, 0xcb, 0x3f, 0x85, 0xd5, 0x50, 0x03,
0x67, 0xe3, 0x6d, 0xc2, 0x21, 0x7a, 0xc8, 0x8f, 0x65, 0x2a, 0x5c, 0x0d, 0x51, 0x35, 0xc1, 0xce,
0x1f, 0xff, 0x06, 0x48, 0x07, 0xfe, 0x04, 0xea, 0x69, 0x08, 0x4e, 0xee, 0xeb, 0xe4, 0xcc, 0x02,
0xe6, 0x69, 0xfd, 0x67, 0xd0, 0x98, 0x01, 0x55, 0x41, 0xd3, 0xc9, 0xc3, 0x5a, 0xe9, 0x70, 0x2d,
0x20, 0x67, 0xec, 0x3a, 0x5d, 0x20, 0x5f, 0xeb, 0xa7, 0x9d, 0x87, 0x36, 0x83, 0x89, 0x39, 0x03,
0xfd, 0x30, 0x5f, 0x9b, 0x99, 0x68, 0xcb, 0x24, 0x6d, 0x6c, 0xf0, 0x73, 0x90, 0x58, 0x3a, 0xbc,
0xa7, 0x60, 0x4c, 0xd3, 0xe7, 0x3f, 0xea, 0xcf, 0x29, 0x03, 0xbb, 0xb0, 0x12, 0xe4, 0xa7, 0x9e,
0x0e, 0xf1, 0x91, 0x9f, 0x98, 0x01, 0x47, 0x4b, 0xbf, 0xaf, 0xe0, 0x9f, 0xdf, 0x7f, 0x07, 0x00,
0x00, 0xff, 0xff, 0x71, 0xbc, 0xd9, 0x84, 0x28, 0x16, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@ -2000,6 +2009,7 @@ type StorageAuthorityClient interface {
NewAuthorizations2(ctx context.Context, in *AddPendingAuthorizationsRequest, opts ...grpc.CallOption) (*Authorization2IDs, error)
FinalizeAuthorization2(ctx context.Context, in *FinalizeAuthorizationRequest, opts ...grpc.CallOption) (*proto1.Empty, error)
DeactivateAuthorization2(ctx context.Context, in *AuthorizationID2, opts ...grpc.CallOption) (*proto1.Empty, error)
SerialExists(ctx context.Context, in *Serial, opts ...grpc.CallOption) (*Exists, error)
}
type storageAuthorityClient struct {
@ -2325,6 +2335,15 @@ func (c *storageAuthorityClient) DeactivateAuthorization2(ctx context.Context, i
return out, nil
}
func (c *storageAuthorityClient) SerialExists(ctx context.Context, in *Serial, opts ...grpc.CallOption) (*Exists, error) {
out := new(Exists)
err := c.cc.Invoke(ctx, "/sa.StorageAuthority/SerialExists", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// StorageAuthorityServer is the server API for StorageAuthority service.
type StorageAuthorityServer interface {
// Getters
@ -2366,6 +2385,7 @@ type StorageAuthorityServer interface {
NewAuthorizations2(context.Context, *AddPendingAuthorizationsRequest) (*Authorization2IDs, error)
FinalizeAuthorization2(context.Context, *FinalizeAuthorizationRequest) (*proto1.Empty, error)
DeactivateAuthorization2(context.Context, *AuthorizationID2) (*proto1.Empty, error)
SerialExists(context.Context, *Serial) (*Exists, error)
}
// UnimplementedStorageAuthorityServer can be embedded to have forward compatible implementations.
@ -2477,6 +2497,9 @@ func (*UnimplementedStorageAuthorityServer) FinalizeAuthorization2(ctx context.C
func (*UnimplementedStorageAuthorityServer) DeactivateAuthorization2(ctx context.Context, req *AuthorizationID2) (*proto1.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeactivateAuthorization2 not implemented")
}
func (*UnimplementedStorageAuthorityServer) SerialExists(ctx context.Context, req *Serial) (*Exists, error) {
return nil, status.Errorf(codes.Unimplemented, "method SerialExists not implemented")
}
func RegisterStorageAuthorityServer(s *grpc.Server, srv StorageAuthorityServer) {
s.RegisterService(&_StorageAuthority_serviceDesc, srv)
@ -3112,6 +3135,24 @@ func _StorageAuthority_DeactivateAuthorization2_Handler(srv interface{}, ctx con
return interceptor(ctx, in, info, handler)
}
func _StorageAuthority_SerialExists_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Serial)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(StorageAuthorityServer).SerialExists(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/sa.StorageAuthority/SerialExists",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(StorageAuthorityServer).SerialExists(ctx, req.(*Serial))
}
return interceptor(ctx, in, info, handler)
}
var _StorageAuthority_serviceDesc = grpc.ServiceDesc{
ServiceName: "sa.StorageAuthority",
HandlerType: (*StorageAuthorityServer)(nil),
@ -3256,6 +3297,10 @@ var _StorageAuthority_serviceDesc = grpc.ServiceDesc{
MethodName: "DeactivateAuthorization2",
Handler: _StorageAuthority_DeactivateAuthorization2_Handler,
},
{
MethodName: "SerialExists",
Handler: _StorageAuthority_SerialExists_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "sa/proto/sa.proto",

View File

@ -45,6 +45,7 @@ service StorageAuthority {
rpc NewAuthorizations2(AddPendingAuthorizationsRequest) returns (Authorization2IDs) {}
rpc FinalizeAuthorization2(FinalizeAuthorizationRequest) returns (core.Empty) {}
rpc DeactivateAuthorization2(AuthorizationID2) returns (core.Empty) {}
rpc SerialExists(Serial) returns (Exists) {}
}
message RegistrationID {
@ -172,6 +173,7 @@ message AddCertificateRequest {
// the current time. The orphan-finder uses this parameter to add
// certificates with the correct historic issued date
optional int64 issued = 4;
optional int64 issuerID = 5;
}
message AddCertificateResponse {

View File

@ -363,16 +363,16 @@ func (ssa *SQLStorageAuthority) GetCertificateStatus(ctx context.Context, serial
return core.CertificateStatus{}, err
}
var status core.CertificateStatus
statusObj, err := ssa.dbMap.WithContext(ctx).Get(certStatusModel{}, serial)
statusModel, err := SelectCertificateStatus(
ssa.dbMap.WithContext(ctx),
"WHERE serial = ?",
serial,
)
if err != nil {
return status, err
return core.CertificateStatus{}, err
}
if statusObj == nil {
return status, nil
}
statusModel := statusObj.(*certStatusModel)
status = core.CertificateStatus{
return core.CertificateStatus{
Serial: statusModel.Serial,
Status: statusModel.Status,
OCSPLastUpdated: statusModel.OCSPLastUpdated,
@ -382,9 +382,7 @@ func (ssa *SQLStorageAuthority) GetCertificateStatus(ctx context.Context, serial
OCSPResponse: statusModel.OCSPResponse,
NotAfter: statusModel.NotAfter,
IsExpired: statusModel.IsExpired,
}
return status, nil
}, nil
}
// NewRegistration stores a new Registration
@ -1571,40 +1569,36 @@ func (ssa *SQLStorageAuthority) FinalizeAuthorization2(ctx context.Context, req
// RevokeCertificate stores revocation information about a certificate. It will only store this
// information if the certificate is not already marked as revoked.
func (ssa *SQLStorageAuthority) RevokeCertificate(ctx context.Context, req *sapb.RevokeCertificateRequest) error {
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Transaction) (interface{}, error) {
status, err := SelectCertificateStatus(
txWithCtx,
"WHERE serial = ? AND status != ?",
*req.Serial,
string(core.OCSPStatusRevoked),
)
if err != nil {
if err == sql.ErrNoRows {
// InternalServerError because we expected this certificate status to exist and
// not be revoked.
return nil, berrors.InternalServerError("no certificate with serial %s and status %s", *req.Serial, string(core.OCSPStatusRevoked))
}
return nil, err
}
revokedDate := time.Unix(0, *req.Date)
status.Status = core.OCSPStatusRevoked
status.RevokedReason = revocation.Reason(*req.Reason)
status.RevokedDate = revokedDate
status.OCSPLastUpdated = revokedDate
status.OCSPResponse = req.Response
n, err := txWithCtx.Update(&status)
if err != nil {
return nil, err
}
if n == 0 {
return nil, berrors.InternalServerError("no certificate updated")
}
return nil, nil
})
return overallError
revokedDate := time.Unix(0, *req.Date)
res, err := ssa.dbMap.Exec(
`UPDATE certificateStatus SET
status = ?,
revokedReason = ?,
revokedDate = ?,
ocspLastUpdated = ?,
ocspResponse = ?
WHERE serial = ? AND status != ?`,
string(core.OCSPStatusRevoked),
revocation.Reason(*req.Reason),
revokedDate,
revokedDate,
req.Response,
*req.Serial,
string(core.OCSPStatusRevoked),
)
if err != nil {
return err
}
rows, err := res.RowsAffected()
if err != nil {
return err
}
if rows == 0 {
// InternalServerError because we expected this certificate status to exist and
// not be revoked.
return berrors.InternalServerError("no certificate with serial %s and status %s", *req.Serial, string(core.OCSPStatusRevoked))
}
return nil
}
// GetPendingAuthorization2 returns the most recent Pending authorization with
@ -1779,3 +1773,16 @@ func (ssa *SQLStorageAuthority) GetValidAuthorizations2(ctx context.Context, req
}
return authz2ModelMapToPB(authzMap)
}
// SerialExists returns a bool indicating whether the provided serial
// exists in the serial table. This is currently only used to determine
// if a serial passed to ca.GenerateOCSP is one which we have previously
// generated a certificate for.
func (ssa *SQLStorageAuthority) SerialExists(ctx context.Context, req *sapb.Serial) (*sapb.Exists, error) {
err := ssa.dbMap.SelectOne(&recordedSerialModel{}, "SELECT * FROM serials WHERE serial = ?", req.Serial)
if err != nil && err != sql.ErrNoRows {
return nil, err
}
exists := err != sql.ErrNoRows
return &sapb.Exists{Exists: &exists}, nil
}

View File

@ -2083,3 +2083,26 @@ func TestGetOrderExpired(t *testing.T) {
test.AssertError(t, err, "GetOrder didn't fail for an expired order")
test.Assert(t, berrors.Is(err, berrors.NotFound), "GetOrder error wasn't of type NotFound")
}
func TestSerialExists(t *testing.T) {
sa, _, cleanUp := initSA(t)
defer cleanUp()
reg := satest.CreateWorkingRegistration(t, sa)
serial := "asd"
resp, err := sa.SerialExists(context.Background(), &sapb.Serial{Serial: &serial})
test.AssertNotError(t, err, "SerialExists failed")
test.AssertEquals(t, *resp.Exists, false)
zero := int64(0)
_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
RegID: &reg.ID,
Serial: &serial,
Created: &zero,
Expires: &zero,
})
test.AssertNotError(t, err, "AddSerial failed")
resp, err = sa.SerialExists(context.Background(), &sapb.Serial{Serial: &serial})
test.AssertNotError(t, err, "SerialExists failed")
test.AssertEquals(t, *resp.Exists, true)
}

View File

@ -137,6 +137,7 @@
"maxConcurrentRPCServerRequests": 100000,
"orphanQueueDir": "/tmp/orphaned-certificates-a",
"features": {
"StoreIssuerInfo": true
}
},

View File

@ -138,6 +138,7 @@
"maxConcurrentRPCServerRequests": 100000,
"orphanQueueDir": "/tmp/orphaned-certificates-b",
"features": {
"StoreIssuerInfo": true
}
},

View File

@ -27,6 +27,7 @@
"timeout": "15s"
},
"features": {
"StoreIssuerInfo": true
}
},

View File

@ -26,7 +26,8 @@
"features": {
"DeleteUnusedChallenges": true,
"FasterGetOrderForNames": true,
"GetAuthorizationsPerf": true
"GetAuthorizationsPerf": true,
"StoreIssuerInfo": true
}
},

View File

@ -27,7 +27,7 @@ GRANT SELECT,INSERT ON requestedNames TO 'sa'@'localhost';
GRANT SELECT,INSERT,DELETE ON orderFqdnSets TO 'sa'@'localhost';
GRANT SELECT,INSERT,UPDATE ON authz2 TO 'sa'@'localhost';
GRANT SELECT,INSERT ON orderToAuthz2 TO 'sa'@'localhost';
GRANT INSERT ON serials TO 'sa'@'localhost';
GRANT INSERT,SELECT ON serials TO 'sa'@'localhost';
GRANT SELECT,INSERT ON precertificates TO 'sa'@'localhost';
-- OCSP Responder