github.com/letsencrypt/boulder@v0.20251208.0/ca/ca.go (about)

     1  package ca
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto"
     7  	"crypto/rand"
     8  	"crypto/sha256"
     9  	"crypto/x509"
    10  	"crypto/x509/pkix"
    11  	"encoding/asn1"
    12  	"encoding/hex"
    13  	"errors"
    14  	"fmt"
    15  	"math/big"
    16  	mrand "math/rand/v2"
    17  	"slices"
    18  
    19  	ct "github.com/google/certificate-transparency-go"
    20  	cttls "github.com/google/certificate-transparency-go/tls"
    21  	"github.com/jmhodges/clock"
    22  	"github.com/miekg/pkcs11"
    23  	"github.com/prometheus/client_golang/prometheus"
    24  	"github.com/prometheus/client_golang/prometheus/promauto"
    25  	"go.opentelemetry.io/otel"
    26  	"go.opentelemetry.io/otel/attribute"
    27  	"go.opentelemetry.io/otel/trace"
    28  	"golang.org/x/crypto/cryptobyte"
    29  	cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
    30  	"google.golang.org/protobuf/types/known/timestamppb"
    31  
    32  	capb "github.com/letsencrypt/boulder/ca/proto"
    33  	"github.com/letsencrypt/boulder/core"
    34  	csrlib "github.com/letsencrypt/boulder/csr"
    35  	berrors "github.com/letsencrypt/boulder/errors"
    36  	"github.com/letsencrypt/boulder/goodkey"
    37  	"github.com/letsencrypt/boulder/identifier"
    38  	"github.com/letsencrypt/boulder/issuance"
    39  	"github.com/letsencrypt/boulder/linter"
    40  	blog "github.com/letsencrypt/boulder/log"
    41  	rapb "github.com/letsencrypt/boulder/ra/proto"
    42  	sapb "github.com/letsencrypt/boulder/sa/proto"
    43  )
    44  
    45  type certificateType string
    46  
    47  const (
    48  	precertType = certificateType("precertificate")
    49  	certType    = certificateType("certificate")
    50  )
    51  
    52  // issuanceEvent is logged before and after issuance of precertificates and certificates.
    53  // The `omitempty` fields are not always present.
    54  // CSR, Precertificate, and Certificate are hex-encoded DER bytes to make it easier to
    55  // ad-hoc search for sequences or OIDs in logs. Other data, like public key within CSR,
    56  // is logged as base64 because it doesn't have interesting DER structure.
    57  type issuanceEvent struct {
    58  	Requester       int64
    59  	OrderID         int64
    60  	Profile         string
    61  	Issuer          string
    62  	IssuanceRequest *issuance.IssuanceRequest
    63  	CSR             string `json:",omitempty"`
    64  	Result          issuanceEventResult
    65  }
    66  
    67  // issuanceEventResult exists just to lend some extra structure to the
    68  // issuanceEvent struct above.
    69  type issuanceEventResult struct {
    70  	Precertificate string `json:",omitempty"`
    71  	Certificate    string `json:",omitempty"`
    72  }
    73  
    74  // caMetrics holds various metrics which are shared between caImpl and crlImpl.
    75  type caMetrics struct {
    76  	signatureCount *prometheus.CounterVec
    77  	signErrorCount *prometheus.CounterVec
    78  	lintErrorCount prometheus.Counter
    79  	certificates   *prometheus.CounterVec
    80  }
    81  
    82  func NewCAMetrics(stats prometheus.Registerer) *caMetrics {
    83  	signatureCount := promauto.With(stats).NewCounterVec(prometheus.CounterOpts{
    84  		Name: "signatures",
    85  		Help: "Number of signatures",
    86  	}, []string{"purpose", "issuer"})
    87  
    88  	signErrorCount := promauto.With(stats).NewCounterVec(prometheus.CounterOpts{
    89  		Name: "signature_errors",
    90  		Help: "A counter of signature errors labelled by error type",
    91  	}, []string{"type"})
    92  
    93  	lintErrorCount := promauto.With(stats).NewCounter(prometheus.CounterOpts{
    94  		Name: "lint_errors",
    95  		Help: "Number of issuances that were halted by linting errors",
    96  	})
    97  
    98  	certificates := promauto.With(stats).NewCounterVec(prometheus.CounterOpts{
    99  		Name: "certificates",
   100  		Help: "Number of certificates issued",
   101  	}, []string{"profile"})
   102  
   103  	return &caMetrics{signatureCount, signErrorCount, lintErrorCount, certificates}
   104  }
   105  
   106  func (m *caMetrics) noteSignError(err error) {
   107  	var pkcs11Error pkcs11.Error
   108  	if errors.As(err, &pkcs11Error) {
   109  		m.signErrorCount.WithLabelValues("HSM").Inc()
   110  	}
   111  }
   112  
   113  // certificateAuthorityImpl represents a CA that signs certificates.
   114  type certificateAuthorityImpl struct {
   115  	capb.UnsafeCertificateAuthorityServer
   116  	sa        sapb.StorageAuthorityCertificateClient
   117  	sctClient rapb.SCTProviderClient
   118  	pa        core.PolicyAuthority
   119  	issuers   []*issuance.Issuer
   120  	profiles  map[string]*issuance.Profile
   121  
   122  	// The prefix is prepended to the serial number.
   123  	prefix    byte
   124  	maxNames  int
   125  	keyPolicy goodkey.KeyPolicy
   126  	clk       clock.Clock
   127  	log       blog.Logger
   128  	metrics   *caMetrics
   129  	tracer    trace.Tracer
   130  }
   131  
   132  var _ capb.CertificateAuthorityServer = (*certificateAuthorityImpl)(nil)
   133  
   134  // NewCertificateAuthorityImpl creates a CA instance that can sign certificates
   135  // from any number of issuance.Issuers and for any number of profiles.
   136  func NewCertificateAuthorityImpl(
   137  	sa sapb.StorageAuthorityCertificateClient,
   138  	sctService rapb.SCTProviderClient,
   139  	pa core.PolicyAuthority,
   140  	issuers []*issuance.Issuer,
   141  	profiles map[string]*issuance.Profile,
   142  	serialPrefix byte,
   143  	maxNames int,
   144  	keyPolicy goodkey.KeyPolicy,
   145  	logger blog.Logger,
   146  	metrics *caMetrics,
   147  	clk clock.Clock,
   148  ) (*certificateAuthorityImpl, error) {
   149  	if serialPrefix < 0x01 || serialPrefix > 0x7f {
   150  		return nil, errors.New("serial prefix must be between 0x01 (1) and 0x7f (127)")
   151  	}
   152  
   153  	if len(issuers) == 0 {
   154  		return nil, errors.New("must have at least one issuer")
   155  	}
   156  
   157  	if len(profiles) == 0 {
   158  		return nil, errors.New("must have at least one certificate profile")
   159  	}
   160  
   161  	issuableKeys := make(map[x509.PublicKeyAlgorithm]bool)
   162  	issuableProfiles := make(map[string]bool)
   163  	for _, issuer := range issuers {
   164  		if issuer.IsActive() && len(issuer.Profiles()) == 0 {
   165  			return nil, fmt.Errorf("issuer %q is active but has no profiles", issuer.Name())
   166  		}
   167  
   168  		for _, profile := range issuer.Profiles() {
   169  			_, ok := profiles[profile]
   170  			if !ok {
   171  				return nil, fmt.Errorf("issuer %q lists profile %q, which is not configured", issuer.Name(), profile)
   172  			}
   173  			issuableProfiles[profile] = true
   174  		}
   175  
   176  		issuableKeys[issuer.KeyType()] = true
   177  	}
   178  
   179  	for profile := range profiles {
   180  		if !issuableProfiles[profile] {
   181  			return nil, fmt.Errorf("profile %q configured, but no issuer lists it", profile)
   182  		}
   183  	}
   184  
   185  	for _, keyAlg := range []x509.PublicKeyAlgorithm{x509.ECDSA, x509.RSA} {
   186  		if !issuableKeys[keyAlg] {
   187  			return nil, fmt.Errorf("no %s issuers configured", keyAlg)
   188  		}
   189  	}
   190  
   191  	return &certificateAuthorityImpl{
   192  		sa:        sa,
   193  		sctClient: sctService,
   194  		pa:        pa,
   195  		issuers:   issuers,
   196  		profiles:  profiles,
   197  		prefix:    serialPrefix,
   198  		maxNames:  maxNames,
   199  		keyPolicy: keyPolicy,
   200  		log:       logger,
   201  		metrics:   metrics,
   202  		tracer:    otel.GetTracerProvider().Tracer("github.com/letsencrypt/boulder/ca"),
   203  		clk:       clk,
   204  	}, nil
   205  }
   206  
   207  // IssueCertificate is the gRPC handler responsible for the entire [issuance
   208  // cycle]. It takes as input just a CSR and a profile name. It generates the
   209  // unique serial number locally, and uses the profile and the CA's clock to
   210  // generate the validity period. It writes the serial to the database to prevent
   211  // duplicate use of serials, generates and stores the *linting* precertificate
   212  // as a record of what we intended to issue, contacts the SCTService (currently
   213  // an RA instance) to retrieve SCTs, and finally generates and saves the final
   214  // certificate.
   215  //
   216  // [issuance cycle]:
   217  // https://github.com/letsencrypt/boulder/blob/main/docs/ISSUANCE-CYCLE.md
   218  func (ca *certificateAuthorityImpl) IssueCertificate(ctx context.Context, req *capb.IssueCertificateRequest) (*capb.IssueCertificateResponse, error) {
   219  	// Step 1: Locally process the gRPC request and its embedded CSR to extract
   220  	// the relevant information, like the pubkey and SANs. Also generate
   221  	// some metadata from scratch, such as the serial and validity period.
   222  	if core.IsAnyNilOrZero(req, req.RegistrationID, req.OrderID, req.CertProfileName, req.Csr) {
   223  		return nil, berrors.InternalServerError("Incomplete issue certificate request")
   224  	}
   225  
   226  	if ca.sctClient == nil {
   227  		return nil, errors.New("IssueCertificate called with a nil SCT service")
   228  	}
   229  
   230  	profile, ok := ca.profiles[req.CertProfileName]
   231  	if !ok {
   232  		return nil, fmt.Errorf("incapable of using a profile named %q", req.CertProfileName)
   233  	}
   234  
   235  	notBefore, notAfter := profile.GenerateValidity(ca.clk.Now())
   236  
   237  	csr, err := x509.ParseCertificateRequest(req.Csr)
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  
   242  	err = csrlib.VerifyCSR(ctx, csr, ca.maxNames, &ca.keyPolicy, ca.pa)
   243  	if err != nil {
   244  		return nil, err
   245  	}
   246  
   247  	issuer, err := ca.pickIssuer(req.CertProfileName, csr.PublicKeyAlgorithm)
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  
   252  	if issuer.Cert.NotAfter.Before(notAfter) {
   253  		err = berrors.InternalServerError("cannot issue a certificate that expires after the issuer certificate")
   254  		ca.log.AuditErr(err.Error())
   255  		return nil, err
   256  	}
   257  
   258  	subjectKeyId, err := generateSKID(csr.PublicKey)
   259  	if err != nil {
   260  		return nil, fmt.Errorf("computing subject key ID: %w", err)
   261  	}
   262  
   263  	dnsNames, ipAddresses, err := identifier.FromCSR(csr).ToValues()
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  
   268  	var ipStrings []string
   269  	for _, ip := range csr.IPAddresses {
   270  		ipStrings = append(ipStrings, ip.String())
   271  	}
   272  
   273  	serialBigInt, err := ca.generateSerialNumber()
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  	serialHex := core.SerialToString(serialBigInt)
   278  
   279  	// Step 2: Persist the serial and minimal metadata, to ensure that we never
   280  	// duplicate a serial.
   281  	_, err = ca.sa.AddSerial(ctx, &sapb.AddSerialRequest{
   282  		Serial:  serialHex,
   283  		RegID:   req.RegistrationID,
   284  		Created: timestamppb.New(ca.clk.Now()),
   285  		Expires: timestamppb.New(notAfter),
   286  	})
   287  	if err != nil {
   288  		return nil, fmt.Errorf("persisting serial to database: %w", err)
   289  	}
   290  
   291  	// Step 3: Issue the linting precert, persist it to the database, and then
   292  	// issue the real precert.
   293  	precertReq := &issuance.IssuanceRequest{
   294  		PublicKey:       issuance.MarshalablePublicKey{PublicKey: csr.PublicKey},
   295  		SubjectKeyId:    subjectKeyId,
   296  		Serial:          serialBigInt.Bytes(),
   297  		NotBefore:       notBefore,
   298  		NotAfter:        notAfter,
   299  		CommonName:      csrlib.CNFromCSR(csr),
   300  		DNSNames:        dnsNames,
   301  		IPAddresses:     ipAddresses,
   302  		IncludeCTPoison: true,
   303  	}
   304  
   305  	_, span := ca.tracer.Start(ctx, "issuance", trace.WithAttributes(
   306  		attribute.String("serial", serialHex),
   307  		attribute.String("issuer", issuer.Name()),
   308  		attribute.String("certProfileName", req.CertProfileName),
   309  		attribute.StringSlice("names", csr.DNSNames),
   310  		attribute.StringSlice("ipAddresses", ipStrings),
   311  	))
   312  	defer span.End()
   313  
   314  	lintPrecertDER, issuanceToken, err := issuer.Prepare(profile, precertReq)
   315  	if err != nil {
   316  		ca.log.AuditErrf("Preparing precert failed: serial=[%s] err=[%v]", serialHex, err)
   317  		if errors.Is(err, linter.ErrLinting) {
   318  			ca.metrics.lintErrorCount.Inc()
   319  		}
   320  		return nil, fmt.Errorf("failed to prepare precertificate signing: %w", err)
   321  	}
   322  
   323  	// Note: we write the linting certificate bytes to this table, rather than the precertificate
   324  	// (which we audit log but do not put in the database). This is to ensure that even if there is
   325  	// an error immediately after signing the precertificate, we have a record in the DB of what we
   326  	// intended to sign, and can do revocations based on that. See #6807.
   327  	// The name of the SA method ("AddPrecertificate") is a historical artifact.
   328  	_, err = ca.sa.AddPrecertificate(context.Background(), &sapb.AddCertificateRequest{
   329  		Der:          lintPrecertDER,
   330  		RegID:        req.RegistrationID,
   331  		Issued:       timestamppb.New(ca.clk.Now()),
   332  		IssuerNameID: int64(issuer.NameID()),
   333  	})
   334  	if err != nil {
   335  		return nil, fmt.Errorf("persisting linting precert to database: %w", err)
   336  	}
   337  
   338  	ca.log.AuditObject("Signing precert", issuanceEvent{
   339  		Requester:       req.RegistrationID,
   340  		OrderID:         req.OrderID,
   341  		Profile:         req.CertProfileName,
   342  		Issuer:          issuer.Name(),
   343  		IssuanceRequest: precertReq,
   344  		CSR:             hex.EncodeToString(csr.Raw),
   345  	})
   346  
   347  	precertDER, err := issuer.Issue(issuanceToken)
   348  	if err != nil {
   349  		ca.metrics.noteSignError(err)
   350  		ca.log.AuditErrf("Signing precert failed: serial=[%s] err=[%v]", serialHex, err)
   351  		return nil, fmt.Errorf("failed to sign precertificate: %w", err)
   352  	}
   353  	ca.metrics.signatureCount.With(prometheus.Labels{"purpose": string(precertType), "issuer": issuer.Name()}).Inc()
   354  
   355  	ca.log.AuditObject("Signing precert success", issuanceEvent{
   356  		Requester:       req.RegistrationID,
   357  		OrderID:         req.OrderID,
   358  		Profile:         req.CertProfileName,
   359  		Issuer:          issuer.Name(),
   360  		IssuanceRequest: precertReq,
   361  		Result:          issuanceEventResult{Precertificate: hex.EncodeToString(precertDER)},
   362  	})
   363  
   364  	err = tbsCertIsDeterministic(lintPrecertDER, precertDER)
   365  	if err != nil {
   366  		return nil, err
   367  	}
   368  
   369  	precert, err := x509.ParseCertificate(precertDER)
   370  	if err != nil {
   371  		return nil, fmt.Errorf("parsing precertificate: %w", err)
   372  	}
   373  
   374  	// Step 4: Get SCTs for inclusion in the final certificate.
   375  	sctResp, err := ca.sctClient.GetSCTs(ctx, &rapb.SCTRequest{PrecertDER: precertDER})
   376  	if err != nil {
   377  		return nil, fmt.Errorf("getting SCTs: %w", err)
   378  	}
   379  
   380  	var scts []ct.SignedCertificateTimestamp
   381  	for _, singleSCTBytes := range sctResp.SctDER {
   382  		var sct ct.SignedCertificateTimestamp
   383  		_, err = cttls.Unmarshal(singleSCTBytes, &sct)
   384  		if err != nil {
   385  			return nil, err
   386  		}
   387  		scts = append(scts, sct)
   388  	}
   389  
   390  	// Step 5: Issue and save the final certificate.
   391  	//
   392  	// Given a precertificate, a set of SCTs for that precertificate, and the same
   393  	// issuer and profile which were used to generate that precert, generate a
   394  	// linting final certificate, then sign a final certificate using a real
   395  	// issuer. The poison extension is removed from the precertificate and a SCT
   396  	// list extension is inserted in its place. Except for this and the signature
   397  	// the final certificate exactly matches the precertificate.
   398  	//
   399  	// It's critical not to sign two different final certificates for the same
   400  	// precertificate. That's why this code is inline: the only way to reach this
   401  	// point is to already have generated a unique serial and unique precert; if
   402  	// any of the previous steps returned an error, then the whole certificate
   403  	// issuance attempt fails and any subsequent attempt to reach this code will
   404  	// generate a new serial.
   405  	certReq, err := issuance.RequestFromPrecert(precert, scts)
   406  	if err != nil {
   407  		return nil, err
   408  	}
   409  
   410  	lintCertDER, issuanceToken, err := issuer.Prepare(profile, certReq)
   411  	if err != nil {
   412  		ca.log.AuditErrf("Preparing cert failed: serial=[%s] err=[%v]", serialHex, err)
   413  		return nil, fmt.Errorf("failed to prepare certificate signing: %w", err)
   414  	}
   415  
   416  	ca.log.AuditObject("Signing cert", issuanceEvent{
   417  		Requester:       req.RegistrationID,
   418  		OrderID:         req.OrderID,
   419  		Profile:         req.CertProfileName,
   420  		Issuer:          issuer.Name(),
   421  		IssuanceRequest: certReq,
   422  	})
   423  
   424  	certDER, err := issuer.Issue(issuanceToken)
   425  	if err != nil {
   426  		ca.metrics.noteSignError(err)
   427  		ca.log.AuditErrf("Signing cert failed: serial=[%s] err=[%v]", serialHex, err)
   428  		return nil, fmt.Errorf("failed to sign certificate: %w", err)
   429  	}
   430  	ca.metrics.signatureCount.With(prometheus.Labels{"purpose": string(certType), "issuer": issuer.Name()}).Inc()
   431  	ca.metrics.certificates.With(prometheus.Labels{"profile": req.CertProfileName}).Inc()
   432  
   433  	ca.log.AuditObject("Signing cert success", issuanceEvent{
   434  		Requester:       req.RegistrationID,
   435  		OrderID:         req.OrderID,
   436  		Profile:         req.CertProfileName,
   437  		Issuer:          issuer.Name(),
   438  		IssuanceRequest: certReq,
   439  		Result:          issuanceEventResult{Certificate: hex.EncodeToString(certDER)},
   440  	})
   441  
   442  	err = tbsCertIsDeterministic(lintCertDER, certDER)
   443  	if err != nil {
   444  		return nil, err
   445  	}
   446  
   447  	_, err = ca.sa.AddCertificate(ctx, &sapb.AddCertificateRequest{
   448  		Der:    certDER,
   449  		RegID:  req.RegistrationID,
   450  		Issued: timestamppb.New(ca.clk.Now()),
   451  	})
   452  	if err != nil {
   453  		ca.log.AuditErrf("Failed RPC to store at SA: serial=[%s] err=[%v]", serialHex, hex.EncodeToString(certDER))
   454  		return nil, fmt.Errorf("persisting cert to database: %w", err)
   455  	}
   456  
   457  	return &capb.IssueCertificateResponse{DER: certDER}, nil
   458  }
   459  
   460  // pickIssuer returns an issuer which is willing to issue certificates for the
   461  // given profile and public key algorithm. If no such issuer exists, it returns
   462  // an error. If multiple such issuers exist, it selects one at random.
   463  func (ca *certificateAuthorityImpl) pickIssuer(profileName string, keyAlg x509.PublicKeyAlgorithm) (*issuance.Issuer, error) {
   464  	var pool []*issuance.Issuer
   465  	for _, issuer := range ca.issuers {
   466  		if !issuer.IsActive() {
   467  			continue
   468  		}
   469  		if issuer.KeyType() != keyAlg {
   470  			continue
   471  		}
   472  		if !slices.Contains(issuer.Profiles(), profileName) {
   473  			continue
   474  		}
   475  		pool = append(pool, issuer)
   476  	}
   477  
   478  	if len(pool) == 0 {
   479  		return nil, fmt.Errorf("no issuer found for profile %q and key algorithm %s", profileName, keyAlg)
   480  	}
   481  
   482  	return pool[mrand.IntN(len(pool))], nil
   483  }
   484  
   485  // generateSerialNumber produces a big.Int which has more than 64 bits of
   486  // entropy and has the CA's configured one-byte prefix.
   487  func (ca *certificateAuthorityImpl) generateSerialNumber() (*big.Int, error) {
   488  	// We want 136 bits of random number, plus an 8-bit instance id prefix.
   489  	const randBits = 136
   490  	serialBytes := make([]byte, randBits/8+1)
   491  	serialBytes[0] = ca.prefix
   492  	_, err := rand.Read(serialBytes[1:])
   493  	if err != nil {
   494  		err = berrors.InternalServerError("failed to generate serial: %s", err)
   495  		ca.log.AuditErrf("Serial randomness failed, err=[%v]", err)
   496  		return nil, err
   497  	}
   498  	serialBigInt := big.NewInt(0)
   499  	serialBigInt = serialBigInt.SetBytes(serialBytes)
   500  
   501  	return serialBigInt, nil
   502  }
   503  
   504  // generateSKID computes the Subject Key Identifier using one of the methods in
   505  // RFC 7093 Section 2 Additional Methods for Generating Key Identifiers:
   506  // The keyIdentifier [may be] composed of the leftmost 160-bits of the
   507  // SHA-256 hash of the value of the BIT STRING subjectPublicKey
   508  // (excluding the tag, length, and number of unused bits).
   509  func generateSKID(pk crypto.PublicKey) ([]byte, error) {
   510  	pkBytes, err := x509.MarshalPKIXPublicKey(pk)
   511  	if err != nil {
   512  		return nil, err
   513  	}
   514  
   515  	var pkixPublicKey struct {
   516  		Algo      pkix.AlgorithmIdentifier
   517  		BitString asn1.BitString
   518  	}
   519  	if _, err := asn1.Unmarshal(pkBytes, &pkixPublicKey); err != nil {
   520  		return nil, err
   521  	}
   522  
   523  	skid := sha256.Sum256(pkixPublicKey.BitString.Bytes)
   524  	return skid[0:20:20], nil
   525  }
   526  
   527  // verifyTBSCertIsDeterministic verifies that x509.CreateCertificate signing
   528  // operation is deterministic and produced identical DER bytes between the given
   529  // lint certificate and leaf certificate. If the DER byte equality check fails
   530  // it's mississuance, but it's better to know about the problem sooner than
   531  // later. The caller is responsible for passing the appropriate valid
   532  // certificate bytes in the correct position.
   533  func tbsCertIsDeterministic(lintCertBytes []byte, leafCertBytes []byte) error {
   534  	if core.IsAnyNilOrZero(lintCertBytes, leafCertBytes) {
   535  		return fmt.Errorf("lintCertBytes of leafCertBytes were nil")
   536  	}
   537  
   538  	// extractTBSCertBytes is a partial copy of //crypto/x509/parser.go to
   539  	// extract the RawTBSCertificate field from given DER bytes. It the
   540  	// RawTBSCertificate field bytes or an error if the given bytes cannot be
   541  	// parsed. This is far more performant than parsing the entire *Certificate
   542  	// structure with x509.ParseCertificate().
   543  	//
   544  	// RFC 5280, Section 4.1
   545  	//    Certificate  ::=  SEQUENCE  {
   546  	//      tbsCertificate       TBSCertificate,
   547  	//      signatureAlgorithm   AlgorithmIdentifier,
   548  	//      signatureValue       BIT STRING  }
   549  	//
   550  	//    TBSCertificate  ::=  SEQUENCE  {
   551  	//      ..
   552  	extractTBSCertBytes := func(inputDERBytes *[]byte) ([]byte, error) {
   553  		input := cryptobyte.String(*inputDERBytes)
   554  
   555  		// Extract the Certificate bytes
   556  		if !input.ReadASN1(&input, cryptobyte_asn1.SEQUENCE) {
   557  			return nil, errors.New("malformed certificate")
   558  		}
   559  
   560  		var tbs cryptobyte.String
   561  		// Extract the TBSCertificate bytes from the Certificate bytes
   562  		if !input.ReadASN1(&tbs, cryptobyte_asn1.SEQUENCE) {
   563  			return nil, errors.New("malformed tbs certificate")
   564  		}
   565  
   566  		if tbs.Empty() {
   567  			return nil, errors.New("parsed RawTBSCertificate field was empty")
   568  		}
   569  
   570  		return tbs, nil
   571  	}
   572  
   573  	lintRawTBSCert, err := extractTBSCertBytes(&lintCertBytes)
   574  	if err != nil {
   575  		return fmt.Errorf("while extracting lint TBS cert: %w", err)
   576  	}
   577  
   578  	leafRawTBSCert, err := extractTBSCertBytes(&leafCertBytes)
   579  	if err != nil {
   580  		return fmt.Errorf("while extracting leaf TBS cert: %w", err)
   581  	}
   582  
   583  	if !bytes.Equal(lintRawTBSCert, leafRawTBSCert) {
   584  		return fmt.Errorf("mismatch between lintCert and leafCert RawTBSCertificate DER bytes: \"%x\" != \"%x\"", lintRawTBSCert, leafRawTBSCert)
   585  	}
   586  
   587  	return nil
   588  }