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

     1  package ca
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/ecdsa"
     7  	"crypto/elliptic"
     8  	"crypto/rand"
     9  	"crypto/x509"
    10  	"crypto/x509/pkix"
    11  	"encoding/asn1"
    12  	"errors"
    13  	"fmt"
    14  	"math/big"
    15  	"os"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  
    20  	ct "github.com/google/certificate-transparency-go"
    21  	cttls "github.com/google/certificate-transparency-go/tls"
    22  	ctx509 "github.com/google/certificate-transparency-go/x509"
    23  	"github.com/jmhodges/clock"
    24  	"github.com/miekg/pkcs11"
    25  	"github.com/prometheus/client_golang/prometheus"
    26  	"google.golang.org/grpc"
    27  	"google.golang.org/protobuf/types/known/emptypb"
    28  
    29  	capb "github.com/letsencrypt/boulder/ca/proto"
    30  	"github.com/letsencrypt/boulder/config"
    31  	"github.com/letsencrypt/boulder/core"
    32  	corepb "github.com/letsencrypt/boulder/core/proto"
    33  	berrors "github.com/letsencrypt/boulder/errors"
    34  	"github.com/letsencrypt/boulder/features"
    35  	"github.com/letsencrypt/boulder/goodkey"
    36  	"github.com/letsencrypt/boulder/identifier"
    37  	"github.com/letsencrypt/boulder/issuance"
    38  	blog "github.com/letsencrypt/boulder/log"
    39  	"github.com/letsencrypt/boulder/must"
    40  	"github.com/letsencrypt/boulder/policy"
    41  	rapb "github.com/letsencrypt/boulder/ra/proto"
    42  	sapb "github.com/letsencrypt/boulder/sa/proto"
    43  	"github.com/letsencrypt/boulder/test"
    44  )
    45  
    46  var (
    47  	// * Random public key
    48  	// * CN = not-example.com
    49  	// * DNSNames = not-example.com, www.not-example.com
    50  	CNandSANCSR = mustRead("./testdata/cn_and_san.der.csr")
    51  
    52  	// CSR generated by Go:
    53  	// * Random public key
    54  	// * CN = not-example.com
    55  	// * Includes an extensionRequest attribute for a well-formed TLS Feature extension
    56  	MustStapleCSR = mustRead("./testdata/must_staple.der.csr")
    57  
    58  	// CSR generated by Go:
    59  	// * Random public key
    60  	// * CN = not-example.com
    61  	// * Includes an extensionRequest attribute for an unknown extension with an
    62  	//   empty value. That extension's OID, 2.25.123456789, is on the UUID arc.
    63  	//   It isn't a real randomly-generated UUID because Go represents the
    64  	//   components of the OID as 32-bit integers, which aren't large enough to
    65  	//   hold a real 128-bit UUID; this doesn't matter as far as what we're
    66  	//   testing here is concerned.
    67  	UnsupportedExtensionCSR = mustRead("./testdata/unsupported_extension.der.csr")
    68  
    69  	// CSR generated by Go:
    70  	// * Random public key
    71  	// * CN = not-example.com
    72  	// * Includes an extensionRequest attribute for the CT poison extension
    73  	//   with a valid NULL value.
    74  	CTPoisonExtensionCSR = mustRead("./testdata/ct_poison_extension.der.csr")
    75  
    76  	// CSR generated by Go:
    77  	// * Random public key
    78  	// * CN = not-example.com
    79  	// * Includes an extensionRequest attribute for the CT poison extension
    80  	//   with an invalid empty value.
    81  	CTPoisonExtensionEmptyCSR = mustRead("./testdata/ct_poison_extension_empty.der.csr")
    82  
    83  	// CSR generated by Go:
    84  	// * Random ECDSA public key.
    85  	// * CN = [none]
    86  	// * DNSNames = example.com, example2.com
    87  	ECDSACSR = mustRead("./testdata/ecdsa.der.csr")
    88  
    89  	// OIDExtensionCTPoison is defined in RFC 6962 s3.1.
    90  	OIDExtensionCTPoison = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
    91  
    92  	// OIDExtensionSCTList is defined in RFC 6962 s3.3.
    93  	OIDExtensionSCTList = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2}
    94  )
    95  
    96  func mustRead(path string) []byte {
    97  	return must.Do(os.ReadFile(path))
    98  }
    99  
   100  // caArgs is a container for all of the arguments to
   101  // NewCertificateAuthorityImpl. It exists so that tests can easily build a
   102  // default certificateAuthorityImpl, but can also easily customize that object
   103  // to exercise various behaviors. The expected usage flow is:
   104  //
   105  //	cargs := newTestCA(t)
   106  //	cargs.foo = someOverride
   107  //	ca := cargs.make()
   108  //
   109  // Its fields should remain identical to the NewCertificateAuthorityImpl args.
   110  type caArgs struct {
   111  	sa           sapb.StorageAuthorityCertificateClient
   112  	sctService   rapb.SCTProviderClient
   113  	pa           core.PolicyAuthority
   114  	issuers      []*issuance.Issuer
   115  	profiles     map[string]*issuance.Profile
   116  	serialPrefix byte
   117  	maxNames     int
   118  	keyPolicy    goodkey.KeyPolicy
   119  	logger       *blog.Mock
   120  	metrics      *caMetrics
   121  	clk          clock.FakeClock
   122  }
   123  
   124  // newCAArgs returns a caArgs populated with reasonable default values for testing.
   125  func newCAArgs(t *testing.T) *caArgs {
   126  	features.Reset()
   127  
   128  	fc := clock.NewFake()
   129  	fc.Add(1 * time.Hour)
   130  
   131  	pa, err := policy.New(map[identifier.IdentifierType]bool{"dns": true}, nil, blog.NewMock())
   132  	test.AssertNotError(t, err, "Couldn't create PA")
   133  	err = pa.LoadIdentPolicyFile("../test/ident-policy.yaml")
   134  	test.AssertNotError(t, err, "Couldn't set identifier policy")
   135  
   136  	legacy, err := issuance.NewProfile(issuance.ProfileConfig{
   137  		MaxValidityPeriod:   config.Duration{Duration: time.Hour * 24 * 90},
   138  		MaxValidityBackdate: config.Duration{Duration: time.Hour},
   139  		IgnoredLints:        []string{"w_subject_common_name_included"},
   140  	})
   141  	test.AssertNotError(t, err, "Loading test profile")
   142  	modern, err := issuance.NewProfile(issuance.ProfileConfig{
   143  		OmitCommonName:      true,
   144  		OmitKeyEncipherment: true,
   145  		OmitClientAuth:      true,
   146  		OmitSKID:            true,
   147  		MaxValidityPeriod:   config.Duration{Duration: time.Hour * 24 * 6},
   148  		MaxValidityBackdate: config.Duration{Duration: time.Hour},
   149  		IgnoredLints:        []string{"w_ext_subject_key_identifier_missing_sub_cert"},
   150  	})
   151  	test.AssertNotError(t, err, "Loading test profile")
   152  	profiles := map[string]*issuance.Profile{
   153  		"legacy": legacy,
   154  		"modern": modern,
   155  	}
   156  
   157  	issuers := make([]*issuance.Issuer, 4)
   158  	for i, name := range []string{"int-r3", "int-r4", "int-e1", "int-e2"} {
   159  		issuers[i], err = issuance.LoadIssuer(issuance.IssuerConfig{
   160  			Active:     true,
   161  			IssuerURL:  fmt.Sprintf("http://not-example.com/i/%s", name),
   162  			CRLURLBase: fmt.Sprintf("http://not-example.com/c/%s/", name),
   163  			CRLShards:  10,
   164  			Location: issuance.IssuerLoc{
   165  				File:     fmt.Sprintf("../test/hierarchy/%s.key.pem", name),
   166  				CertFile: fmt.Sprintf("../test/hierarchy/%s.cert.pem", name),
   167  			},
   168  			Profiles: []string{"legacy", "modern"},
   169  		}, fc)
   170  		test.AssertNotError(t, err, "Couldn't load test issuer")
   171  	}
   172  
   173  	keyPolicy, err := goodkey.NewPolicy(nil, nil)
   174  	test.AssertNotError(t, err, "Failed to create test keypolicy")
   175  
   176  	signatureCount := prometheus.NewCounterVec(
   177  		prometheus.CounterOpts{
   178  			Name: "signatures",
   179  			Help: "Number of signatures",
   180  		},
   181  		[]string{"purpose", "issuer"})
   182  	signErrorCount := prometheus.NewCounterVec(prometheus.CounterOpts{
   183  		Name: "signature_errors",
   184  		Help: "A counter of signature errors labelled by error type",
   185  	}, []string{"type"})
   186  	lintErrorCount := prometheus.NewCounter(
   187  		prometheus.CounterOpts{
   188  			Name: "lint_errors",
   189  			Help: "Number of issuances that were halted by linting errors",
   190  		})
   191  	certificatesCount := prometheus.NewCounterVec(
   192  		prometheus.CounterOpts{
   193  			Name: "certificates",
   194  			Help: "Number of certificates issued",
   195  		}, []string{"profile"})
   196  	cametrics := &caMetrics{signatureCount, signErrorCount, lintErrorCount, certificatesCount}
   197  
   198  	return &caArgs{
   199  		sa:           &mockSA{},
   200  		sctService:   &mockSCTService{},
   201  		pa:           pa,
   202  		issuers:      issuers,
   203  		profiles:     profiles,
   204  		serialPrefix: 0x11,
   205  		maxNames:     2,
   206  		keyPolicy:    keyPolicy,
   207  		logger:       blog.NewMock(),
   208  		metrics:      cametrics,
   209  		clk:          fc,
   210  	}
   211  }
   212  
   213  // make passes all of the caArgs' fields to the NewCertificateAuthorityImpl
   214  // constructor and returns the result.
   215  func (c *caArgs) make() (*certificateAuthorityImpl, error) {
   216  	return NewCertificateAuthorityImpl(
   217  		c.sa, c.sctService, c.pa, c.issuers, c.profiles, c.serialPrefix,
   218  		c.maxNames, c.keyPolicy, c.logger, c.metrics, c.clk)
   219  }
   220  
   221  type mockSA struct{}
   222  
   223  func (m *mockSA) AddSerial(ctx context.Context, req *sapb.AddSerialRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) {
   224  	return &emptypb.Empty{}, nil
   225  }
   226  
   227  func (m *mockSA) AddPrecertificate(ctx context.Context, req *sapb.AddCertificateRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) {
   228  	return &emptypb.Empty{}, nil
   229  }
   230  
   231  func (m *mockSA) AddCertificate(ctx context.Context, req *sapb.AddCertificateRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) {
   232  	return &emptypb.Empty{}, nil
   233  }
   234  
   235  func (m *mockSA) GetCertificate(ctx context.Context, req *sapb.Serial, _ ...grpc.CallOption) (*corepb.Certificate, error) {
   236  	return nil, berrors.NotFoundError("cannot find the cert")
   237  }
   238  
   239  func (m *mockSA) GetLintPrecertificate(ctx context.Context, req *sapb.Serial, _ ...grpc.CallOption) (*corepb.Certificate, error) {
   240  	return nil, berrors.NotFoundError("cannot find the precert")
   241  }
   242  
   243  type mockSCTService struct{}
   244  
   245  func (m mockSCTService) GetSCTs(ctx context.Context, sctRequest *rapb.SCTRequest, _ ...grpc.CallOption) (*rapb.SCTResponse, error) {
   246  	sct := ct.SignedCertificateTimestamp{
   247  		SCTVersion: 0,
   248  		Timestamp:  2020,
   249  		Signature: ct.DigitallySigned{
   250  			Signature: []byte{0},
   251  		},
   252  	}
   253  
   254  	sctBytes, err := cttls.Marshal(sct)
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  
   259  	return &rapb.SCTResponse{SctDER: [][]byte{sctBytes}}, nil
   260  }
   261  
   262  func TestNewCertificateAuthorityImpl_BadSerialPrefix(t *testing.T) {
   263  	t.Parallel()
   264  	cargs := newCAArgs(t)
   265  
   266  	cargs.serialPrefix = 0x00
   267  	_, err := cargs.make()
   268  	if err == nil {
   269  		t.Errorf("NewCertificateAuthorityImpl(serialPrefix=0x00) succeeded, but want error")
   270  	}
   271  
   272  	cargs.serialPrefix = 0x80
   273  	_, err = cargs.make()
   274  	if err == nil {
   275  		t.Errorf("NewCertificateAuthorityImpl(serialPrefix=0x80) succeeded, but want error")
   276  	}
   277  }
   278  
   279  func TestNewCertificateAuthorityImpl_InsufficientIssuers(t *testing.T) {
   280  	t.Parallel()
   281  	cargs := newCAArgs(t)
   282  	origIssuers := cargs.issuers
   283  
   284  	for _, tc := range []struct {
   285  		name    string
   286  		issuers []*issuance.Issuer
   287  		wantErr string
   288  	}{
   289  		{
   290  			name:    "no issuers",
   291  			issuers: nil,
   292  			wantErr: "at least one issuer",
   293  		},
   294  		{
   295  			name:    "ecdsa only",
   296  			issuers: origIssuers[2:],
   297  			wantErr: "no RSA issuers configured",
   298  		},
   299  		{
   300  			name:    "rsa only",
   301  			issuers: origIssuers[:2],
   302  			wantErr: "no ECDSA issuers configured",
   303  		},
   304  	} {
   305  		t.Run(tc.name, func(t *testing.T) {
   306  			cargs.issuers = tc.issuers
   307  			_, err := cargs.make()
   308  			if err == nil {
   309  				t.Fatalf("NewCertificateAuthorityImpl(%s) succeeded, but want error", tc.name)
   310  			}
   311  
   312  			if !strings.Contains(err.Error(), tc.wantErr) {
   313  				t.Fatalf("NewCertificateAuthorityImpl(%s) = %q, but want %q", tc.name, err, tc.wantErr)
   314  			}
   315  		})
   316  	}
   317  }
   318  
   319  func TestNewCertificateAuthorityImpl_InsufficientProfiles(t *testing.T) {
   320  	t.Parallel()
   321  	cargs := newCAArgs(t)
   322  	cargs.profiles = nil
   323  
   324  	_, err := cargs.make()
   325  	if err == nil {
   326  		t.Fatalf("NewCertificateAuthorityImpl(profiles=nil) succeeded, but want error")
   327  	}
   328  
   329  	wantErr := "at least one certificate profile"
   330  	if !strings.Contains(err.Error(), wantErr) {
   331  		t.Fatalf("NewCertificateAuthorityImpl(profiles=nil) = %q, but want %q", err, wantErr)
   332  	}
   333  }
   334  
   335  // recordingSA keeps track of the serial, precertificate, and certificate which
   336  // are written to it. We use recordingSA only for the _HappyPath test because
   337  // it's a pain to mitigate the data-races inherent in writing to it from many
   338  // parallel subtests.
   339  type recordingSA struct {
   340  	serial         *sapb.AddSerialRequest
   341  	precertificate *sapb.AddCertificateRequest
   342  	certificate    *sapb.AddCertificateRequest
   343  }
   344  
   345  func (m *recordingSA) AddSerial(ctx context.Context, req *sapb.AddSerialRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) {
   346  	m.serial = req
   347  	return &emptypb.Empty{}, nil
   348  }
   349  
   350  func (m *recordingSA) AddPrecertificate(ctx context.Context, req *sapb.AddCertificateRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) {
   351  	m.precertificate = req
   352  	return &emptypb.Empty{}, nil
   353  }
   354  
   355  func (m *recordingSA) AddCertificate(ctx context.Context, req *sapb.AddCertificateRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) {
   356  	m.certificate = req
   357  	return &emptypb.Empty{}, nil
   358  }
   359  
   360  func (m *recordingSA) GetCertificate(ctx context.Context, req *sapb.Serial, _ ...grpc.CallOption) (*corepb.Certificate, error) {
   361  	return nil, berrors.NotFoundError("cannot find the cert")
   362  }
   363  
   364  func (m *recordingSA) GetLintPrecertificate(ctx context.Context, req *sapb.Serial, _ ...grpc.CallOption) (*corepb.Certificate, error) {
   365  	return nil, berrors.NotFoundError("cannot find the precert")
   366  }
   367  
   368  func findExtension(extensions []pkix.Extension, id asn1.ObjectIdentifier) *pkix.Extension {
   369  	for _, ext := range extensions {
   370  		if ext.Id.Equal(id) {
   371  			return &ext
   372  		}
   373  	}
   374  	return nil
   375  }
   376  
   377  // deserializeSCTList deserializes a list of SCTs.
   378  // Forked from github.com/cloudflare/cfssl/helpers
   379  func deserializeSCTList(sctListExtensionValue []byte) ([]ct.SignedCertificateTimestamp, error) {
   380  	var serializedSCTList []byte
   381  	_, err := asn1.Unmarshal(sctListExtensionValue, &serializedSCTList)
   382  	if err != nil {
   383  		return nil, err
   384  	}
   385  
   386  	var sctList ctx509.SignedCertificateTimestampList
   387  	rest, err := cttls.Unmarshal(serializedSCTList, &sctList)
   388  	if err != nil {
   389  		return nil, err
   390  	}
   391  	if len(rest) != 0 {
   392  		return nil, errors.New("serialized SCT list contained trailing garbage")
   393  	}
   394  	list := make([]ct.SignedCertificateTimestamp, len(sctList.SCTList))
   395  	for i, serializedSCT := range sctList.SCTList {
   396  		var sct ct.SignedCertificateTimestamp
   397  		rest, err := cttls.Unmarshal(serializedSCT.Val, &sct)
   398  		if err != nil {
   399  			return nil, err
   400  		}
   401  		if len(rest) != 0 {
   402  			return nil, errors.New("serialized SCT contained trailing garbage")
   403  		}
   404  		list[i] = sct
   405  	}
   406  	return list, nil
   407  }
   408  
   409  func TestIssueCertificate_HappyPath(t *testing.T) {
   410  	for _, profile := range []string{"legacy", "modern"} {
   411  		for _, tc := range []struct {
   412  			name string
   413  			csr  []byte
   414  		}{
   415  			{
   416  				name: "RSA",
   417  				csr:  CNandSANCSR,
   418  			},
   419  			{
   420  				name: "ECDSA",
   421  				csr:  ECDSACSR,
   422  			},
   423  			{
   424  				name: "unrecognized extension",
   425  				csr:  UnsupportedExtensionCSR,
   426  			},
   427  			{
   428  				name: "poison extension",
   429  				csr:  CTPoisonExtensionCSR,
   430  			},
   431  			{
   432  				name: "malformed poison extension",
   433  				csr:  CTPoisonExtensionEmptyCSR,
   434  			},
   435  			{
   436  				// Rejection of CSRs that request Must-Staple happens in the RA.
   437  				name: "must staple extension",
   438  				csr:  mustRead("./testdata/must_staple.der.csr"),
   439  			},
   440  		} {
   441  			t.Run(tc.name+"/"+profile, func(t *testing.T) {
   442  				t.Parallel()
   443  
   444  				// Use our own CA for each of these parallel subtests, because we plan
   445  				// to inspect the serial, precert, and final cert stored in the mock SA.
   446  				// Also so we can assert that certain metrics have specific values.
   447  				cargs := newCAArgs(t)
   448  				sa := &recordingSA{}
   449  				cargs.sa = sa
   450  				ca, err := cargs.make()
   451  				if err != nil {
   452  					t.Fatalf("making test ca: %s", err)
   453  				}
   454  
   455  				res, err := ca.IssueCertificate(t.Context(), &capb.IssueCertificateRequest{
   456  					RegistrationID: 1, OrderID: 1,
   457  					Csr: tc.csr, CertProfileName: profile,
   458  				})
   459  				if err != nil {
   460  					t.Fatalf("IssueCertificate(%s) = %q, but want success", tc.name, err)
   461  				}
   462  
   463  				test.AssertMetricWithLabelsEquals(t, ca.metrics.signatureCount, prometheus.Labels{"purpose": "precertificate", "status": "success"}, 1)
   464  				test.AssertMetricWithLabelsEquals(t, ca.metrics.signatureCount, prometheus.Labels{"purpose": "certificate", "status": "success"}, 1)
   465  
   466  				if sa.serial.RegID != 1 {
   467  					t.Errorf("want serial to be associated with acct %d, but got %d", 1, sa.serial.RegID)
   468  				}
   469  
   470  				storedPrecert, err := x509.ParseCertificate(sa.precertificate.Der)
   471  				if err != nil {
   472  					t.Fatalf("parsing precert: %s", err)
   473  				}
   474  
   475  				poisonExtension := findExtension(storedPrecert.Extensions, OIDExtensionCTPoison)
   476  				if poisonExtension == nil {
   477  					t.Fatal("failed to find ctpoison extension")
   478  				}
   479  
   480  				if !poisonExtension.Critical {
   481  					t.Error("precertificate ctpoison extension must be critical")
   482  				}
   483  
   484  				if !bytes.Equal(poisonExtension.Value, []byte{0x05, 0x00}) { // ASN.1 DER NULL
   485  					t.Errorf("precertificate poison extension has value %x, but want %x", poisonExtension.Value, []byte{0x05, 0x00})
   486  				}
   487  
   488  				storedCert, err := x509.ParseCertificate(sa.certificate.Der)
   489  				if err != nil {
   490  					t.Fatalf("parsing cert: %s", err)
   491  				}
   492  
   493  				sctExtension := findExtension(storedCert.Extensions, OIDExtensionSCTList)
   494  				if sctExtension == nil {
   495  					t.Fatal("failed to find sctList extension")
   496  				}
   497  
   498  				if sctExtension.Critical {
   499  					t.Error("sctList extension must not be critical")
   500  				}
   501  
   502  				sctList, err := deserializeSCTList(sctExtension.Value)
   503  				if err != nil {
   504  					t.Fatalf("parsing sctList extension: %s", err)
   505  				}
   506  
   507  				if len(sctList) != 1 {
   508  					t.Errorf("got %d SCTs, but want 1", len(sctList))
   509  				}
   510  
   511  				cert, err := x509.ParseCertificate(res.DER)
   512  				if err != nil {
   513  					t.Fatalf("parsing returned cert: %s", err)
   514  				}
   515  
   516  				if (sa.serial.Serial != core.SerialToString(storedPrecert.SerialNumber)) ||
   517  					(sa.serial.Serial != core.SerialToString(storedCert.SerialNumber)) ||
   518  					(sa.serial.Serial != core.SerialToString(cert.SerialNumber)) {
   519  					t.Errorf("expected all serials to match")
   520  				}
   521  
   522  				if !bytes.Equal(res.DER, sa.certificate.Der) {
   523  					t.Errorf("Expected stored and returned cert to be identical")
   524  				}
   525  			})
   526  		}
   527  	}
   528  }
   529  
   530  func TestIssueCertificate_BadCSR(t *testing.T) {
   531  	t.Parallel()
   532  
   533  	ca, err := newCAArgs(t).make()
   534  	if err != nil {
   535  		t.Fatalf("making test ca: %s", err)
   536  	}
   537  
   538  	for _, tc := range []struct {
   539  		name    string
   540  		csrPath string
   541  	}{
   542  		{
   543  			name:    "no names",
   544  			csrPath: "./testdata/no_names.der.csr",
   545  		},
   546  		{
   547  			name:    "too many names",
   548  			csrPath: "./testdata/too_many_names.der.csr",
   549  		},
   550  		{
   551  			name:    "short key",
   552  			csrPath: "./testdata/short_key.der.csr",
   553  		},
   554  		{
   555  			name:    "bad key algorithm",
   556  			csrPath: "./testdata/bad_algorithm.der.csr",
   557  		},
   558  		{
   559  			name:    "invalid signature",
   560  			csrPath: "./testdata/invalid_signature.der.csr",
   561  		},
   562  	} {
   563  		t.Run(tc.name, func(t *testing.T) {
   564  			t.Parallel()
   565  
   566  			_, err := ca.IssueCertificate(t.Context(), &capb.IssueCertificateRequest{
   567  				RegistrationID: 1, OrderID: 1,
   568  				Csr: mustRead(tc.csrPath), CertProfileName: "legacy",
   569  			})
   570  			if err == nil {
   571  				t.Fatalf("IssueCertificate(%q) succeeded, but want error", tc.csrPath)
   572  			}
   573  			if !errors.Is(err, berrors.BadCSR) {
   574  				t.Fatalf("IssueCertificate(%q) = %T, but want %T", tc.csrPath, err, berrors.BadCSR)
   575  			}
   576  		})
   577  	}
   578  }
   579  
   580  func TestIssueCertificate_ValidPastIssuer(t *testing.T) {
   581  	t.Parallel()
   582  	cargs := newCAArgs(t)
   583  
   584  	// Limit ourselves to only having one ECDSA issuer, just in case they have
   585  	// different notAfter dates.
   586  	cargs.issuers = cargs.issuers[:3]
   587  
   588  	// Jump to a time just moments before the test issuer expire.
   589  	future := cargs.issuers[2].Cert.Certificate.NotAfter.Add(-1 * time.Hour)
   590  	cargs.clk.Set(future)
   591  
   592  	ca, err := cargs.make()
   593  	if err != nil {
   594  		t.Fatalf("making test ca: %s", err)
   595  	}
   596  
   597  	_, err = ca.IssueCertificate(t.Context(), &capb.IssueCertificateRequest{
   598  		RegistrationID: 1, OrderID: 1,
   599  		Csr: ECDSACSR, CertProfileName: "legacy",
   600  	})
   601  	if err == nil {
   602  		t.Fatalf("IssueCertificate(notAfter > issuer.notAfter) succeeded, but want error")
   603  	}
   604  	if !errors.Is(err, berrors.InternalServer) {
   605  		t.Fatalf("IssueCertificate(notAfter > issuer.notAfter) = %T, but want %T", err, berrors.InternalServer)
   606  	}
   607  }
   608  
   609  func TestIssueCertificate_InvalidProfile(t *testing.T) {
   610  	t.Parallel()
   611  
   612  	ca, err := newCAArgs(t).make()
   613  	if err != nil {
   614  		t.Fatalf("making test ca: %s", err)
   615  	}
   616  
   617  	for _, tc := range []struct {
   618  		name    string
   619  		profile string
   620  		wantErr string
   621  	}{
   622  		{
   623  			name:    "no profile",
   624  			profile: "",
   625  			wantErr: "Incomplete issue certificate request",
   626  		},
   627  		{
   628  			name:    "unrecognized profile",
   629  			profile: "doesnotexist",
   630  			wantErr: "incapable of using a profile named",
   631  		},
   632  		{
   633  			name:    "invalid profile name",
   634  			profile: "🤓",
   635  			wantErr: "incapable of using a profile named",
   636  		},
   637  	} {
   638  		t.Run(tc.name, func(t *testing.T) {
   639  			t.Parallel()
   640  
   641  			_, err := ca.IssueCertificate(t.Context(), &capb.IssueCertificateRequest{
   642  				RegistrationID: 1, OrderID: 1,
   643  				Csr: ECDSACSR, CertProfileName: tc.profile,
   644  			})
   645  			if err == nil {
   646  				t.Fatalf("IssueCertificate(profile=%q) succeeded, but want error", tc.profile)
   647  			}
   648  			if !strings.Contains(err.Error(), tc.wantErr) {
   649  				t.Fatalf("IssueCertificate(profile=%q) = %q, but want %q", tc.profile, err, tc.wantErr)
   650  			}
   651  		})
   652  	}
   653  }
   654  
   655  func TestIssueCertificate_ProfileSelection(t *testing.T) {
   656  	t.Parallel()
   657  
   658  	ca, err := newCAArgs(t).make()
   659  	if err != nil {
   660  		t.Fatalf("making test ca: %s", err)
   661  	}
   662  
   663  	for _, tc := range []struct {
   664  		profile      string
   665  		wantValidity time.Duration
   666  	}{
   667  		{
   668  			profile:      "legacy",
   669  			wantValidity: 90 * 24 * time.Hour,
   670  		},
   671  		{
   672  			profile:      "modern",
   673  			wantValidity: 6 * 24 * time.Hour,
   674  		},
   675  	} {
   676  		t.Run(tc.profile, func(t *testing.T) {
   677  			t.Parallel()
   678  
   679  			res, err := ca.IssueCertificate(t.Context(), &capb.IssueCertificateRequest{
   680  				RegistrationID: 1, OrderID: 1,
   681  				Csr: ECDSACSR, CertProfileName: tc.profile,
   682  			})
   683  			if err != nil {
   684  				t.Fatalf("IssueCertificate(profile=%q) = %q, but want success", tc.profile, err)
   685  			}
   686  
   687  			cert, err := x509.ParseCertificate(res.DER)
   688  			if err != nil {
   689  				t.Fatalf("parsing certificate: %s", err)
   690  			}
   691  
   692  			// We use the validity period as a proxy for detecting whether the correct
   693  			// profile was selected and used, since we know that the validity period
   694  			// differs between the two test profiles.
   695  			validity := cert.NotAfter.Add(time.Second).Sub(cert.NotBefore)
   696  			if validity != tc.wantValidity {
   697  				t.Errorf("IssueCertificate(profile=%q) = validity %d, but want %d", tc.profile, validity, tc.wantValidity)
   698  			}
   699  		})
   700  	}
   701  }
   702  
   703  func TestIssueCertificate_IssuerSelection(t *testing.T) {
   704  	t.Parallel()
   705  	cargs := newCAArgs(t)
   706  	origIssuers := cargs.issuers
   707  
   708  	ca, err := cargs.make()
   709  	if err != nil {
   710  		t.Fatalf("making test ca: %s", err)
   711  	}
   712  
   713  	for _, tc := range []struct {
   714  		name        string
   715  		csr         []byte
   716  		wantIssuers []*issuance.Issuer
   717  		wantKUs     x509.KeyUsage
   718  	}{
   719  		{
   720  			name:        "ECDSA",
   721  			csr:         ECDSACSR,
   722  			wantIssuers: origIssuers[2:],
   723  			wantKUs:     x509.KeyUsageDigitalSignature,
   724  		},
   725  		{
   726  			name:        "RSA",
   727  			csr:         CNandSANCSR,
   728  			wantIssuers: origIssuers[:2],
   729  			wantKUs:     x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   730  		},
   731  	} {
   732  		t.Run(tc.name, func(t *testing.T) {
   733  			t.Parallel()
   734  
   735  			res, err := ca.IssueCertificate(t.Context(), &capb.IssueCertificateRequest{
   736  				RegistrationID: 1, OrderID: 1,
   737  				Csr: tc.csr, CertProfileName: "legacy",
   738  			})
   739  			if err != nil {
   740  				t.Fatalf("IssueCertificate(csr=%q) = %q, but want success", tc.name, err)
   741  			}
   742  
   743  			cert, err := x509.ParseCertificate(res.DER)
   744  			if err != nil {
   745  				t.Fatalf("parsing certificate: %s", err)
   746  			}
   747  
   748  			if cert.KeyUsage != tc.wantKUs {
   749  				t.Errorf("IssueCertificate(csr=%q) has KU %v, but want %v", tc.name, cert.KeyUsage, tc.wantKUs)
   750  			}
   751  
   752  			validated := false
   753  			for _, issuer := range tc.wantIssuers {
   754  				err = cert.CheckSignatureFrom(issuer.Cert.Certificate)
   755  				if err == nil {
   756  					validated = true
   757  					break
   758  				}
   759  			}
   760  			if !validated {
   761  				t.Errorf("IssueCertificate(csr=%q) issued from unexpected issuer %q", tc.name, cert.Issuer.CommonName)
   762  			}
   763  		})
   764  	}
   765  }
   766  
   767  func TestIssueCertificate_UnpredictableIssuance(t *testing.T) {
   768  	ca, err := newCAArgs(t).make()
   769  	if err != nil {
   770  		t.Fatalf("creating test ca: %s", err)
   771  	}
   772  
   773  	// Issue the same (ECDSA-keyed) certificate 20 times. At least one issuance
   774  	// should come from each of the two active ECDSA issuers (int-e1 and int-e2).
   775  	// With 20 trials, the probability that all 20 issuances come from the same
   776  	// issuer is 0.5 ^ 20 = 9.5e-7 ~= 1e-6 = 1 in a million, so we do not consider
   777  	// this test to be flaky.
   778  	seenE1 := false
   779  	seenE2 := false
   780  	for range 20 {
   781  		res, err := ca.IssueCertificate(t.Context(), &capb.IssueCertificateRequest{
   782  			RegistrationID: 1, OrderID: 1,
   783  			Csr: ECDSACSR, CertProfileName: "legacy",
   784  		})
   785  		if err != nil {
   786  			t.Fatalf("issuing certificate: %s", err)
   787  		}
   788  
   789  		cert, err := x509.ParseCertificate(res.DER)
   790  		if err != nil {
   791  			t.Fatalf("parsing certificate: %s", err)
   792  		}
   793  
   794  		if strings.Contains(cert.Issuer.CommonName, "E1") {
   795  			seenE1 = true
   796  		} else if strings.Contains(cert.Issuer.CommonName, "E2") {
   797  			seenE2 = true
   798  		} else {
   799  			t.Fatalf("Issued certificate from unexpected issuer")
   800  		}
   801  	}
   802  
   803  	if !seenE1 {
   804  		t.Error("Expected at least one issuance from active issuer E1")
   805  	}
   806  	if !seenE2 {
   807  		t.Error("Expected at least one issuance from active issuer E2")
   808  	}
   809  }
   810  
   811  // TestPickIssuer tests the various deterministic cases, ensuring that the
   812  // function properly respects the issuers' key algorithms and profiles. The test
   813  // cases here are somewhat tightly coupled to the profiles populated by
   814  // newCAArgs; this full coverage is to ensure that pickIssuer doesn't have an
   815  // off-by-one error or similar bug lurking in it.
   816  //
   817  // The non-deterministic case is covered by TestIssueCertificate_UnpredictableIssuance.
   818  func TestPickIssuer(t *testing.T) {
   819  	t.Parallel()
   820  
   821  	ca, err := newCAArgs(t).make()
   822  	if err != nil {
   823  		t.Fatalf("creating test ca: %s", err)
   824  	}
   825  
   826  	for _, tc := range []struct {
   827  		name    string
   828  		profile string
   829  		keyAlg  x509.PublicKeyAlgorithm
   830  		wantErr bool
   831  	}{
   832  		{
   833  			name:    "unrecognized profile",
   834  			profile: "doesnotexist",
   835  			keyAlg:  x509.ECDSA,
   836  			wantErr: true,
   837  		},
   838  		{
   839  			name:    "unrecognized key algorithm",
   840  			profile: "modern",
   841  			keyAlg:  x509.Ed25519,
   842  			wantErr: true,
   843  		},
   844  		{
   845  			name:    "recognized/legacy+ecdsa",
   846  			profile: "legacy",
   847  			keyAlg:  x509.ECDSA,
   848  		},
   849  		{
   850  			name:    "recognized/legacy+rsa",
   851  			profile: "legacy",
   852  			keyAlg:  x509.RSA,
   853  		},
   854  		{
   855  			name:    "recognized/modern+ecdsa",
   856  			profile: "modern",
   857  			keyAlg:  x509.ECDSA,
   858  		},
   859  		{
   860  			name:    "recognized/modern+rsa",
   861  			profile: "modern",
   862  			keyAlg:  x509.RSA,
   863  		},
   864  	} {
   865  		t.Run(tc.name, func(t *testing.T) {
   866  			_, err := ca.pickIssuer(tc.profile, tc.keyAlg)
   867  			if err == nil && tc.wantErr {
   868  				t.Errorf("pickIssuer(%s, %s) = success, but want error", tc.profile, tc.keyAlg)
   869  			} else if err != nil && !tc.wantErr {
   870  				t.Errorf("pickIssuer(%s, %s) = %s, but want success", tc.profile, tc.keyAlg, err)
   871  			}
   872  		})
   873  	}
   874  }
   875  
   876  func TestPickIssuer_Inactive(t *testing.T) {
   877  	t.Parallel()
   878  	cargs := newCAArgs(t)
   879  
   880  	// Load our own set of issuers, but with half of them inactive.
   881  	var issuers []*issuance.Issuer
   882  	for i, name := range []string{"int-r3", "int-r4", "int-e1", "int-e2"} {
   883  		issuer, err := issuance.LoadIssuer(issuance.IssuerConfig{
   884  			Active:     i%2 == 0,
   885  			IssuerURL:  fmt.Sprintf("http://not-example.com/i/%s", name),
   886  			CRLURLBase: fmt.Sprintf("http://not-example.com/c/%s/", name),
   887  			CRLShards:  10,
   888  			Location: issuance.IssuerLoc{
   889  				File:     fmt.Sprintf("../test/hierarchy/%s.key.pem", name),
   890  				CertFile: fmt.Sprintf("../test/hierarchy/%s.cert.pem", name),
   891  			},
   892  			Profiles: []string{"legacy", "modern"},
   893  		}, cargs.clk)
   894  		if err != nil {
   895  			t.Fatalf("loading test issuer: %s", err)
   896  		}
   897  		issuers = append(issuers, issuer)
   898  	}
   899  	cargs.issuers = issuers
   900  
   901  	ca, err := cargs.make()
   902  	if err != nil {
   903  		t.Fatalf("creating test ca: %s", err)
   904  	}
   905  
   906  	// Calling pickIssuer should never return one of the inactive issuers.
   907  	for range 20 {
   908  		issuer, err := ca.pickIssuer("modern", x509.ECDSA)
   909  		if err != nil {
   910  			t.Fatalf("pickIssuer(modern, ECDSA) = %s, but want success", err)
   911  		}
   912  		if strings.Contains(issuer.Name(), "E2") {
   913  			t.Errorf("pickIssuer(modern, ECDSA) = E2, but only want E1")
   914  		}
   915  	}
   916  	for range 20 {
   917  		issuer, err := ca.pickIssuer("modern", x509.RSA)
   918  		if err != nil {
   919  			t.Fatalf("pickIssuer(modern, RSA) = %s, but want success", err)
   920  		}
   921  		if strings.Contains(issuer.Name(), "R4") {
   922  			t.Errorf("pickIssuer(modern, RSA) = R4, but only want R3")
   923  		}
   924  	}
   925  }
   926  
   927  func TestNoteSignError(t *testing.T) {
   928  	testCtx := newCAArgs(t)
   929  	metrics := testCtx.metrics
   930  
   931  	err := fmt.Errorf("wrapped non-signing error: %w", errors.New("oops"))
   932  	metrics.noteSignError(err)
   933  	test.AssertMetricWithLabelsEquals(t, metrics.signErrorCount, prometheus.Labels{"type": "HSM"}, 0)
   934  
   935  	err = fmt.Errorf("wrapped signing error: %w", pkcs11.Error(5))
   936  	metrics.noteSignError(err)
   937  	test.AssertMetricWithLabelsEquals(t, metrics.signErrorCount, prometheus.Labels{"type": "HSM"}, 1)
   938  }
   939  
   940  func TestGenerateSKID(t *testing.T) {
   941  	t.Parallel()
   942  	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   943  	test.AssertNotError(t, err, "Error generating key")
   944  
   945  	sha256skid, err := generateSKID(key.Public())
   946  	test.AssertNotError(t, err, "Error generating SKID")
   947  	test.AssertEquals(t, len(sha256skid), 20)
   948  	test.AssertEquals(t, cap(sha256skid), 20)
   949  	features.Reset()
   950  }
   951  
   952  func TestVerifyTBSCertIsDeterministic(t *testing.T) {
   953  	t.Parallel()
   954  
   955  	// Create first keypair and cert
   956  	testKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   957  	test.AssertNotError(t, err, "unable to generate ECDSA private key")
   958  	template := &x509.Certificate{
   959  		NotAfter:     time.Now().Add(1 * time.Hour),
   960  		DNSNames:     []string{"example.com"},
   961  		SerialNumber: big.NewInt(1),
   962  	}
   963  	certDer1, err := x509.CreateCertificate(rand.Reader, template, template, &testKey.PublicKey, testKey)
   964  	test.AssertNotError(t, err, "unable to create certificate")
   965  
   966  	// Create second keypair and cert
   967  	testKey2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   968  	test.AssertNotError(t, err, "unable to generate ECDSA private key")
   969  	template2 := &x509.Certificate{
   970  		NotAfter:     time.Now().Add(2 * time.Hour),
   971  		DNSNames:     []string{"example.net"},
   972  		SerialNumber: big.NewInt(2),
   973  	}
   974  	certDer2, err := x509.CreateCertificate(rand.Reader, template2, template2, &testKey2.PublicKey, testKey2)
   975  	test.AssertNotError(t, err, "unable to create certificate")
   976  
   977  	testCases := []struct {
   978  		name          string
   979  		lintCertBytes []byte
   980  		leafCertBytes []byte
   981  		errorSubstr   string
   982  	}{
   983  		{
   984  			name:          "Both nil",
   985  			lintCertBytes: nil,
   986  			leafCertBytes: nil,
   987  			errorSubstr:   "were nil",
   988  		},
   989  		{
   990  			name:          "Missing a value, invalid input",
   991  			lintCertBytes: nil,
   992  			leafCertBytes: []byte{0x6, 0x6, 0x6},
   993  			errorSubstr:   "were nil",
   994  		},
   995  		{
   996  			name:          "Missing a value, valid input",
   997  			lintCertBytes: nil,
   998  			leafCertBytes: certDer1,
   999  			errorSubstr:   "were nil",
  1000  		},
  1001  		{
  1002  			name:          "Mismatched bytes, invalid input",
  1003  			lintCertBytes: []byte{0x6, 0x6, 0x6},
  1004  			leafCertBytes: []byte{0x1, 0x2, 0x3},
  1005  			errorSubstr:   "malformed certificate",
  1006  		},
  1007  		{
  1008  			name:          "Mismatched bytes, invalider input",
  1009  			lintCertBytes: certDer1,
  1010  			leafCertBytes: []byte{0x1, 0x2, 0x3},
  1011  			errorSubstr:   "malformed certificate",
  1012  		},
  1013  		{
  1014  			// This case is an example of when a linting cert's DER bytes are
  1015  			// mismatched compared to then precert or final cert created from
  1016  			// that linting cert's DER bytes.
  1017  			name:          "Mismatched bytes, valid input",
  1018  			lintCertBytes: certDer1,
  1019  			leafCertBytes: certDer2,
  1020  			errorSubstr:   "mismatch between",
  1021  		},
  1022  		{
  1023  			// Take this with a grain of salt since this test is not actually
  1024  			// creating a linting certificate and performing two
  1025  			// x509.CreateCertificate() calls like
  1026  			// ca.IssueCertificateForPrecertificate and
  1027  			// ca.issuePrecertificateInner do. However, we're still going to
  1028  			// verify the equality.
  1029  			name:          "Valid",
  1030  			lintCertBytes: certDer1,
  1031  			leafCertBytes: certDer1,
  1032  		},
  1033  	}
  1034  
  1035  	for _, testCase := range testCases {
  1036  		t.Run(testCase.name, func(t *testing.T) {
  1037  			t.Parallel()
  1038  			err := tbsCertIsDeterministic(testCase.lintCertBytes, testCase.leafCertBytes)
  1039  			if testCase.errorSubstr != "" {
  1040  				test.AssertError(t, err, "your lack of errors is disturbing")
  1041  				test.AssertContains(t, err.Error(), testCase.errorSubstr)
  1042  			} else {
  1043  				test.AssertNotError(t, err, "unexpected error")
  1044  			}
  1045  		})
  1046  	}
  1047  }