...

Source file src/github.com/letsencrypt/boulder/ca/ca_test.go

Documentation: github.com/letsencrypt/boulder/ca

     1  package ca
     2  
     3  import (
     4  	"context"
     5  	"crypto"
     6  	"crypto/x509"
     7  	"crypto/x509/pkix"
     8  	"encoding/asn1"
     9  	"errors"
    10  	"fmt"
    11  	"os"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  
    16  	ct "github.com/google/certificate-transparency-go"
    17  	cttls "github.com/google/certificate-transparency-go/tls"
    18  	ctx509 "github.com/google/certificate-transparency-go/x509"
    19  	"github.com/jmhodges/clock"
    20  	"github.com/prometheus/client_golang/prometheus"
    21  	"google.golang.org/grpc"
    22  	"google.golang.org/protobuf/types/known/emptypb"
    23  
    24  	capb "github.com/letsencrypt/boulder/ca/proto"
    25  	"github.com/letsencrypt/boulder/config"
    26  	"github.com/letsencrypt/boulder/core"
    27  	corepb "github.com/letsencrypt/boulder/core/proto"
    28  	berrors "github.com/letsencrypt/boulder/errors"
    29  	"github.com/letsencrypt/boulder/features"
    30  	"github.com/letsencrypt/boulder/goodkey"
    31  	"github.com/letsencrypt/boulder/issuance"
    32  	"github.com/letsencrypt/boulder/linter"
    33  	blog "github.com/letsencrypt/boulder/log"
    34  	"github.com/letsencrypt/boulder/metrics"
    35  	"github.com/letsencrypt/boulder/must"
    36  	"github.com/letsencrypt/boulder/policy"
    37  	sapb "github.com/letsencrypt/boulder/sa/proto"
    38  	"github.com/letsencrypt/boulder/test"
    39  )
    40  
    41  func TestImplementation(t *testing.T) {
    42  	test.AssertImplementsGRPCServer(t, &certificateAuthorityImpl{}, capb.UnimplementedCertificateAuthorityServer{})
    43  }
    44  
    45  var (
    46  	// * Random public key
    47  	// * CN = not-example.com
    48  	// * DNSNames = not-example.com, www.not-example.com
    49  	CNandSANCSR = mustRead("./testdata/cn_and_san.der.csr")
    50  
    51  	// CSR generated by Go:
    52  	// * Random public key
    53  	// * CN = not-example.com
    54  	// * Includes an extensionRequest attribute for a well-formed TLS Feature extension
    55  	MustStapleCSR = mustRead("./testdata/must_staple.der.csr")
    56  
    57  	// CSR generated by Go:
    58  	// * Random public key
    59  	// * CN = not-example.com
    60  	// * Includes an extensionRequest attribute for an unknown extension with an
    61  	//   empty value. That extension's OID, 2.25.123456789, is on the UUID arc.
    62  	//   It isn't a real randomly-generated UUID because Go represents the
    63  	//   components of the OID as 32-bit integers, which aren't large enough to
    64  	//   hold a real 128-bit UUID; this doesn't matter as far as what we're
    65  	//   testing here is concerned.
    66  	UnsupportedExtensionCSR = mustRead("./testdata/unsupported_extension.der.csr")
    67  
    68  	// CSR generated by Go:
    69  	// * Random public key
    70  	// * CN = not-example.com
    71  	// * Includes an extensionRequest attribute for the CT poison extension
    72  	//   with a valid NULL value.
    73  	CTPoisonExtensionCSR = mustRead("./testdata/ct_poison_extension.der.csr")
    74  
    75  	// CSR generated by Go:
    76  	// * Random public key
    77  	// * CN = not-example.com
    78  	// * Includes an extensionRequest attribute for the CT poison extension
    79  	//   with an invalid empty value.
    80  	CTPoisonExtensionEmptyCSR = mustRead("./testdata/ct_poison_extension_empty.der.csr")
    81  
    82  	// CSR generated by Go:
    83  	// * Random ECDSA public key.
    84  	// * CN = [none]
    85  	// * DNSNames = example.com, example2.com
    86  	ECDSACSR = mustRead("./testdata/ecdsa.der.csr")
    87  
    88  	// OIDExtensionCTPoison is defined in RFC 6962 s3.1.
    89  	OIDExtensionCTPoison = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
    90  
    91  	// OIDExtensionSCTList is defined in RFC 6962 s3.3.
    92  	OIDExtensionSCTList = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2}
    93  )
    94  
    95  const arbitraryRegID int64 = 1001
    96  
    97  // Useful key and certificate files.
    98  const caKeyFile = "../test/test-ca.key"
    99  const caCertFile = "../test/test-ca.pem"
   100  const caCertFile2 = "../test/test-ca2.pem"
   101  
   102  func mustRead(path string) []byte {
   103  	return must.Do(os.ReadFile(path))
   104  }
   105  
   106  type testCtx struct {
   107  	pa             core.PolicyAuthority
   108  	ocsp           *ocspImpl
   109  	crl            *crlImpl
   110  	certExpiry     time.Duration
   111  	certBackdate   time.Duration
   112  	serialPrefix   int
   113  	maxNames       int
   114  	boulderIssuers []*issuance.Issuer
   115  	keyPolicy      goodkey.KeyPolicy
   116  	fc             clock.FakeClock
   117  	stats          prometheus.Registerer
   118  	signatureCount *prometheus.CounterVec
   119  	signErrorCount *prometheus.CounterVec
   120  	logger         *blog.Mock
   121  }
   122  
   123  type mockSA struct {
   124  	certificate core.Certificate
   125  }
   126  
   127  func (m *mockSA) AddCertificate(ctx context.Context, req *sapb.AddCertificateRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) {
   128  	m.certificate.DER = req.Der
   129  	return nil, nil
   130  }
   131  
   132  func (m *mockSA) AddPrecertificate(ctx context.Context, req *sapb.AddCertificateRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) {
   133  	return &emptypb.Empty{}, nil
   134  }
   135  
   136  func (m *mockSA) AddSerial(ctx context.Context, req *sapb.AddSerialRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) {
   137  	return &emptypb.Empty{}, nil
   138  }
   139  
   140  func (m *mockSA) GetCertificate(ctx context.Context, req *sapb.Serial, _ ...grpc.CallOption) (*corepb.Certificate, error) {
   141  	return nil, berrors.NotFoundError("cannot find the cert")
   142  }
   143  
   144  func (m *mockSA) SetCertificateStatusReady(ctx context.Context, req *sapb.Serial, _ ...grpc.CallOption) (*emptypb.Empty, error) {
   145  	return &emptypb.Empty{}, nil
   146  }
   147  
   148  var caKey crypto.Signer
   149  var caCert *issuance.Certificate
   150  var caCert2 *issuance.Certificate
   151  var caLinter *linter.Linter
   152  var caLinter2 *linter.Linter
   153  var ctx = context.Background()
   154  
   155  func init() {
   156  	var err error
   157  	caCert, caKey, err = issuance.LoadIssuer(issuance.IssuerLoc{
   158  		File:     caKeyFile,
   159  		CertFile: caCertFile,
   160  	})
   161  	if err != nil {
   162  		panic(fmt.Sprintf("Unable to load %q and %q: %s", caKeyFile, caCertFile, err))
   163  	}
   164  	caCert2, err = issuance.LoadCertificate(caCertFile2)
   165  	if err != nil {
   166  		panic(fmt.Sprintf("Unable to parse %q: %s", caCertFile2, err))
   167  	}
   168  	caLinter, _ = linter.New(caCert.Certificate, caKey, []string{"n_subject_common_name_included"})
   169  	caLinter2, _ = linter.New(caCert2.Certificate, caKey, []string{"n_subject_common_name_included"})
   170  }
   171  
   172  func setup(t *testing.T) *testCtx {
   173  	features.Reset()
   174  	fc := clock.NewFake()
   175  	fc.Add(1 * time.Hour)
   176  
   177  	pa, err := policy.New(nil, blog.NewMock())
   178  	test.AssertNotError(t, err, "Couldn't create PA")
   179  	err = pa.LoadHostnamePolicyFile("../test/hostname-policy.yaml")
   180  	test.AssertNotError(t, err, "Couldn't set hostname policy")
   181  
   182  	boulderProfile := func(rsa, ecdsa bool) *issuance.Profile {
   183  		res, _ := issuance.NewProfile(
   184  			issuance.ProfileConfig{
   185  				AllowMustStaple: true,
   186  				AllowCTPoison:   true,
   187  				AllowSCTList:    true,
   188  				AllowCommonName: true,
   189  				Policies: []issuance.PolicyConfig{
   190  					{OID: "2.23.140.1.2.1"},
   191  				},
   192  				MaxValidityPeriod:   config.Duration{Duration: time.Hour * 8760},
   193  				MaxValidityBackdate: config.Duration{Duration: time.Hour},
   194  			},
   195  			issuance.IssuerConfig{
   196  				UseForECDSALeaves: ecdsa,
   197  				UseForRSALeaves:   rsa,
   198  				IssuerURL:         "http://not-example.com/issuer-url",
   199  				OCSPURL:           "http://not-example.com/ocsp",
   200  				CRLURL:            "http://not-example.com/crl",
   201  			},
   202  		)
   203  		return res
   204  	}
   205  	boulderIssuers := []*issuance.Issuer{
   206  		// Must list ECDSA-only issuer first, so it is the default for ECDSA.
   207  		{
   208  			Cert:    caCert2,
   209  			Signer:  caKey,
   210  			Profile: boulderProfile(false, true),
   211  			Linter:  caLinter2,
   212  			Clk:     fc,
   213  		},
   214  		{
   215  			Cert:    caCert,
   216  			Signer:  caKey,
   217  			Profile: boulderProfile(true, true),
   218  			Linter:  caLinter,
   219  			Clk:     fc,
   220  		},
   221  	}
   222  
   223  	keyPolicy := goodkey.KeyPolicy{
   224  		AllowRSA:           true,
   225  		AllowECDSANISTP256: true,
   226  		AllowECDSANISTP384: true,
   227  	}
   228  	signatureCount := prometheus.NewCounterVec(
   229  		prometheus.CounterOpts{
   230  			Name: "signatures",
   231  			Help: "Number of signatures",
   232  		},
   233  		[]string{"purpose", "issuer"})
   234  	signErrorCount := prometheus.NewCounterVec(prometheus.CounterOpts{
   235  		Name: "signature_errors",
   236  		Help: "A counter of signature errors labelled by error type",
   237  	}, []string{"type"})
   238  
   239  	ocsp, err := NewOCSPImpl(
   240  		boulderIssuers,
   241  		time.Hour,
   242  		0,
   243  		time.Second,
   244  		blog.NewMock(),
   245  		metrics.NoopRegisterer,
   246  		signatureCount,
   247  		signErrorCount,
   248  		fc,
   249  	)
   250  	test.AssertNotError(t, err, "Failed to create ocsp impl")
   251  
   252  	crl, err := NewCRLImpl(
   253  		boulderIssuers,
   254  		time.Hour,
   255  		"http://c.boulder.test",
   256  		100,
   257  		blog.NewMock(),
   258  	)
   259  	test.AssertNotError(t, err, "Failed to create crl impl")
   260  
   261  	return &testCtx{
   262  		pa:             pa,
   263  		ocsp:           ocsp,
   264  		crl:            crl,
   265  		certExpiry:     8760 * time.Hour,
   266  		certBackdate:   time.Hour,
   267  		serialPrefix:   17,
   268  		maxNames:       2,
   269  		boulderIssuers: boulderIssuers,
   270  		keyPolicy:      keyPolicy,
   271  		fc:             fc,
   272  		stats:          metrics.NoopRegisterer,
   273  		signatureCount: signatureCount,
   274  		signErrorCount: signErrorCount,
   275  		logger:         blog.NewMock(),
   276  	}
   277  }
   278  
   279  func TestFailNoSerialPrefix(t *testing.T) {
   280  	testCtx := setup(t)
   281  
   282  	_, err := NewCertificateAuthorityImpl(
   283  		nil,
   284  		nil,
   285  		nil,
   286  		nil,
   287  		testCtx.certExpiry,
   288  		testCtx.certBackdate,
   289  		0,
   290  		testCtx.maxNames,
   291  		testCtx.keyPolicy,
   292  		testCtx.logger,
   293  		testCtx.stats,
   294  		nil,
   295  		nil,
   296  		testCtx.fc)
   297  	test.AssertError(t, err, "CA should have failed with no SerialPrefix")
   298  }
   299  
   300  type TestCertificateIssuance struct {
   301  	ca      *certificateAuthorityImpl
   302  	sa      *mockSA
   303  	req     *x509.CertificateRequest
   304  	certDER []byte
   305  	cert    *x509.Certificate
   306  }
   307  
   308  func TestIssuePrecertificate(t *testing.T) {
   309  	testCases := []struct {
   310  		name    string
   311  		csr     []byte
   312  		subTest func(t *testing.T, i *TestCertificateIssuance)
   313  	}{
   314  		{"IssuePrecertificate", CNandSANCSR, issueCertificateSubTestIssuePrecertificate},
   315  		{"ValidityUsesCAClock", CNandSANCSR, issueCertificateSubTestValidityUsesCAClock},
   316  		{"ProfileSelectionRSA", CNandSANCSR, issueCertificateSubTestProfileSelectionRSA},
   317  		{"ProfileSelectionECDSA", ECDSACSR, issueCertificateSubTestProfileSelectionECDSA},
   318  		{"MustStaple", MustStapleCSR, issueCertificateSubTestMustStaple},
   319  		{"UnknownExtension", UnsupportedExtensionCSR, issueCertificateSubTestUnknownExtension},
   320  		{"CTPoisonExtension", CTPoisonExtensionCSR, issueCertificateSubTestCTPoisonExtension},
   321  		{"CTPoisonExtensionEmpty", CTPoisonExtensionEmptyCSR, issueCertificateSubTestCTPoisonExtension},
   322  	}
   323  
   324  	for _, testCase := range testCases {
   325  		// The loop through the issuance modes must be inside the loop through
   326  		// |testCases| because the "certificate-for-precertificate" tests use
   327  		// the precertificates previously generated from the preceding
   328  		// "precertificate" test.
   329  		for _, mode := range []string{"precertificate", "certificate-for-precertificate"} {
   330  			ca, sa := issueCertificateSubTestSetup(t, nil)
   331  
   332  			t.Run(fmt.Sprintf("%s - %s", mode, testCase.name), func(t *testing.T) {
   333  				req, err := x509.ParseCertificateRequest(testCase.csr)
   334  				test.AssertNotError(t, err, "Certificate request failed to parse")
   335  
   336  				issueReq := &capb.IssueCertificateRequest{Csr: testCase.csr, RegistrationID: arbitraryRegID}
   337  
   338  				var certDER []byte
   339  				response, err := ca.IssuePrecertificate(ctx, issueReq)
   340  
   341  				test.AssertNotError(t, err, "Failed to issue precertificate")
   342  				certDER = response.DER
   343  
   344  				cert, err := x509.ParseCertificate(certDER)
   345  				test.AssertNotError(t, err, "Certificate failed to parse")
   346  
   347  				poisonExtension := findExtension(cert.Extensions, OIDExtensionCTPoison)
   348  				test.AssertNotNil(t, poisonExtension, "Precert doesn't contain poison extension")
   349  				if poisonExtension != nil {
   350  					test.AssertEquals(t, poisonExtension.Critical, true)
   351  					test.AssertDeepEquals(t, poisonExtension.Value, []byte{0x05, 0x00}) // ASN.1 DER NULL
   352  				}
   353  
   354  				i := TestCertificateIssuance{
   355  					ca:      ca,
   356  					sa:      sa,
   357  					req:     req,
   358  					certDER: certDER,
   359  					cert:    cert,
   360  				}
   361  
   362  				testCase.subTest(t, &i)
   363  			})
   364  		}
   365  	}
   366  }
   367  
   368  func issueCertificateSubTestSetup(t *testing.T, e *ECDSAAllowList) (*certificateAuthorityImpl, *mockSA) {
   369  	testCtx := setup(t)
   370  	ecdsaAllowList := &ECDSAAllowList{}
   371  	if e == nil {
   372  		e = ecdsaAllowList
   373  	}
   374  	sa := &mockSA{}
   375  	ca, err := NewCertificateAuthorityImpl(
   376  		sa,
   377  		testCtx.pa,
   378  		testCtx.boulderIssuers,
   379  		e,
   380  		testCtx.certExpiry,
   381  		testCtx.certBackdate,
   382  		testCtx.serialPrefix,
   383  		testCtx.maxNames,
   384  		testCtx.keyPolicy,
   385  		testCtx.logger,
   386  		testCtx.stats,
   387  		testCtx.signatureCount,
   388  		testCtx.signErrorCount,
   389  		testCtx.fc)
   390  	test.AssertNotError(t, err, "Failed to create CA")
   391  	return ca, sa
   392  }
   393  
   394  func issueCertificateSubTestIssuePrecertificate(t *testing.T, i *TestCertificateIssuance) {
   395  	cert := i.cert
   396  
   397  	test.AssertEquals(t, cert.Subject.CommonName, "not-example.com")
   398  
   399  	if len(cert.DNSNames) == 1 {
   400  		if cert.DNSNames[0] != "not-example.com" {
   401  			t.Errorf("Improper list of domain names %v", cert.DNSNames)
   402  		}
   403  		t.Errorf("Improper list of domain names %v", cert.DNSNames)
   404  	}
   405  
   406  	if len(cert.Subject.Country) > 0 {
   407  		t.Errorf("Subject contained unauthorized values: %v", cert.Subject)
   408  	}
   409  }
   410  
   411  func issueCertificateSubTestValidityUsesCAClock(t *testing.T, i *TestCertificateIssuance) {
   412  	test.AssertEquals(t, i.cert.NotBefore, i.ca.clk.Now().Add(-1*i.ca.backdate))
   413  	test.AssertEquals(t, i.cert.NotAfter.Add(time.Second).Sub(i.cert.NotBefore), i.ca.validityPeriod)
   414  }
   415  
   416  // Test failure mode when no issuers are present.
   417  func TestNoIssuers(t *testing.T) {
   418  	testCtx := setup(t)
   419  	sa := &mockSA{}
   420  	_, err := NewCertificateAuthorityImpl(
   421  		sa,
   422  		testCtx.pa,
   423  		nil, // No issuers
   424  		nil,
   425  		testCtx.certExpiry,
   426  		testCtx.certBackdate,
   427  		testCtx.serialPrefix,
   428  		testCtx.maxNames,
   429  		testCtx.keyPolicy,
   430  		testCtx.logger,
   431  		testCtx.stats,
   432  		testCtx.signatureCount,
   433  		testCtx.signErrorCount,
   434  		testCtx.fc)
   435  	test.AssertError(t, err, "No issuers found during CA construction.")
   436  	test.AssertEquals(t, err.Error(), "must have at least one issuer")
   437  }
   438  
   439  // Test issuing when multiple issuers are present.
   440  func TestMultipleIssuers(t *testing.T) {
   441  	testCtx := setup(t)
   442  	sa := &mockSA{}
   443  	ca, err := NewCertificateAuthorityImpl(
   444  		sa,
   445  		testCtx.pa,
   446  		testCtx.boulderIssuers,
   447  		nil,
   448  		testCtx.certExpiry,
   449  		testCtx.certBackdate,
   450  		testCtx.serialPrefix,
   451  		testCtx.maxNames,
   452  		testCtx.keyPolicy,
   453  		testCtx.logger,
   454  		testCtx.stats,
   455  		testCtx.signatureCount,
   456  		testCtx.signErrorCount,
   457  		testCtx.fc)
   458  	test.AssertNotError(t, err, "Failed to remake CA")
   459  
   460  	// Test that an RSA CSR gets issuance from the RSA issuer, caCert.
   461  	issuedCert, err := ca.IssuePrecertificate(ctx, &capb.IssueCertificateRequest{Csr: CNandSANCSR, RegistrationID: arbitraryRegID})
   462  	test.AssertNotError(t, err, "Failed to issue certificate")
   463  	cert, err := x509.ParseCertificate(issuedCert.DER)
   464  	test.AssertNotError(t, err, "Certificate failed to parse")
   465  	err = cert.CheckSignatureFrom(caCert2.Certificate)
   466  	test.AssertNotError(t, err, "Certificate failed signature validation")
   467  
   468  	// Test that an ECDSA CSR gets issuance from the ECDSA issuer, caCert2.
   469  	issuedCert, err = ca.IssuePrecertificate(ctx, &capb.IssueCertificateRequest{Csr: ECDSACSR, RegistrationID: arbitraryRegID})
   470  	test.AssertNotError(t, err, "Failed to issue certificate")
   471  	cert, err = x509.ParseCertificate(issuedCert.DER)
   472  	test.AssertNotError(t, err, "Certificate failed to parse")
   473  	err = cert.CheckSignatureFrom(caCert2.Certificate)
   474  	test.AssertNotError(t, err, "Certificate failed signature validation")
   475  }
   476  
   477  func TestECDSAAllowList(t *testing.T) {
   478  	req := &capb.IssueCertificateRequest{Csr: ECDSACSR, RegistrationID: arbitraryRegID}
   479  
   480  	// With allowlist containing arbitraryRegID, issuance should come from ECDSA issuer.
   481  	regIDMap := makeRegIDsMap([]int64{arbitraryRegID})
   482  	ca, _ := issueCertificateSubTestSetup(t, &ECDSAAllowList{regIDMap})
   483  	result, err := ca.IssuePrecertificate(ctx, req)
   484  	test.AssertNotError(t, err, "Failed to issue certificate")
   485  	cert, err := x509.ParseCertificate(result.DER)
   486  	test.AssertNotError(t, err, "Certificate failed to parse")
   487  	test.AssertByteEquals(t, cert.RawIssuer, caCert2.RawSubject)
   488  
   489  	// With allowlist not containing arbitraryRegID, issuance should fall back to RSA issuer.
   490  	regIDMap = makeRegIDsMap([]int64{2002})
   491  	ca, _ = issueCertificateSubTestSetup(t, &ECDSAAllowList{regIDMap})
   492  	result, err = ca.IssuePrecertificate(ctx, req)
   493  	test.AssertNotError(t, err, "Failed to issue certificate")
   494  	cert, err = x509.ParseCertificate(result.DER)
   495  	test.AssertNotError(t, err, "Certificate failed to parse")
   496  	test.AssertByteEquals(t, cert.RawIssuer, caCert.RawSubject)
   497  
   498  	// With empty allowlist but ECDSAForAll enabled, issuance should come from ECDSA issuer.
   499  	ca, _ = issueCertificateSubTestSetup(t, nil)
   500  	_ = features.Set(map[string]bool{"ECDSAForAll": true})
   501  	defer features.Reset()
   502  	result, err = ca.IssuePrecertificate(ctx, req)
   503  	test.AssertNotError(t, err, "Failed to issue certificate")
   504  	cert, err = x509.ParseCertificate(result.DER)
   505  	test.AssertNotError(t, err, "Certificate failed to parse")
   506  	test.AssertByteEquals(t, cert.RawIssuer, caCert2.RawSubject)
   507  }
   508  
   509  func TestInvalidCSRs(t *testing.T) {
   510  	testCases := []struct {
   511  		name         string
   512  		csrPath      string
   513  		check        func(t *testing.T, ca *certificateAuthorityImpl, sa *mockSA)
   514  		errorMessage string
   515  		errorType    berrors.ErrorType
   516  	}{
   517  		// Test that the CA rejects CSRs that have no names.
   518  		//
   519  		// CSR generated by Go:
   520  		// * Random RSA public key.
   521  		// * CN = [none]
   522  		// * DNSNames = [none]
   523  		{"RejectNoHostnames", "./testdata/no_names.der.csr", nil, "Issued certificate with no names", berrors.BadCSR},
   524  
   525  		// Test that the CA rejects CSRs that have too many names.
   526  		//
   527  		// CSR generated by Go:
   528  		// * Random public key
   529  		// * CN = [none]
   530  		// * DNSNames = not-example.com, www.not-example.com, mail.example.com
   531  		{"RejectTooManyHostnames", "./testdata/too_many_names.der.csr", nil, "Issued certificate with too many names", berrors.BadCSR},
   532  
   533  		// Test that the CA rejects CSRs that have public keys that are too short.
   534  		//
   535  		// CSR generated by Go:
   536  		// * Random public key -- 512 bits long
   537  		// * CN = (none)
   538  		// * DNSNames = not-example.com, www.not-example.com, mail.not-example.com
   539  		{"RejectShortKey", "./testdata/short_key.der.csr", nil, "Issued a certificate with too short a key.", berrors.BadCSR},
   540  
   541  		// Test that the CA rejects CSRs that have bad signature algorithms.
   542  		//
   543  		// CSR generated by Go:
   544  		// * Random public key -- 2048 bits long
   545  		// * CN = (none)
   546  		// * DNSNames = not-example.com, www.not-example.com, mail.not-example.com
   547  		// * Signature Algorithm: sha1WithRSAEncryption
   548  		{"RejectBadAlgorithm", "./testdata/bad_algorithm.der.csr", nil, "Issued a certificate based on a CSR with a bad signature algorithm.", berrors.BadCSR},
   549  
   550  		// CSR generated by Go:
   551  		// * Random RSA public key.
   552  		// * CN = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com
   553  		// * DNSNames = [none]
   554  		{"RejectLongCommonName", "./testdata/long_cn.der.csr", nil, "Issued a certificate with a CN over 64 bytes.", berrors.BadCSR},
   555  
   556  		// CSR generated by OpenSSL:
   557  		// Edited signature to become invalid.
   558  		{"RejectWrongSignature", "./testdata/invalid_signature.der.csr", nil, "Issued a certificate based on a CSR with an invalid signature.", berrors.BadCSR},
   559  	}
   560  
   561  	for _, testCase := range testCases {
   562  		testCtx := setup(t)
   563  		sa := &mockSA{}
   564  		ca, err := NewCertificateAuthorityImpl(
   565  			sa,
   566  			testCtx.pa,
   567  			testCtx.boulderIssuers,
   568  			nil,
   569  			testCtx.certExpiry,
   570  			testCtx.certBackdate,
   571  			testCtx.serialPrefix,
   572  			testCtx.maxNames,
   573  			testCtx.keyPolicy,
   574  			testCtx.logger,
   575  			testCtx.stats,
   576  			testCtx.signatureCount,
   577  			testCtx.signErrorCount,
   578  			testCtx.fc)
   579  		test.AssertNotError(t, err, "Failed to create CA")
   580  
   581  		t.Run(testCase.name, func(t *testing.T) {
   582  			serializedCSR := mustRead(testCase.csrPath)
   583  			issueReq := &capb.IssueCertificateRequest{Csr: serializedCSR, RegistrationID: arbitraryRegID}
   584  			_, err = ca.IssuePrecertificate(ctx, issueReq)
   585  
   586  			test.AssertErrorIs(t, err, testCase.errorType)
   587  			test.AssertMetricWithLabelsEquals(t, ca.signatureCount, prometheus.Labels{"purpose": "cert"}, 0)
   588  
   589  			test.AssertError(t, err, testCase.errorMessage)
   590  			if testCase.check != nil {
   591  				testCase.check(t, ca, sa)
   592  			}
   593  		})
   594  	}
   595  }
   596  
   597  func TestRejectValidityTooLong(t *testing.T) {
   598  	testCtx := setup(t)
   599  	sa := &mockSA{}
   600  	ca, err := NewCertificateAuthorityImpl(
   601  		sa,
   602  		testCtx.pa,
   603  		testCtx.boulderIssuers,
   604  		nil,
   605  		testCtx.certExpiry,
   606  		testCtx.certBackdate,
   607  		testCtx.serialPrefix,
   608  		testCtx.maxNames,
   609  		testCtx.keyPolicy,
   610  		testCtx.logger,
   611  		testCtx.stats,
   612  		nil,
   613  		nil,
   614  		testCtx.fc)
   615  	test.AssertNotError(t, err, "Failed to create CA")
   616  
   617  	// This time is a few minutes before the notAfter in testdata/ca_cert.pem
   618  	future, err := time.Parse(time.RFC3339, "2025-02-10T00:30:00Z")
   619  
   620  	test.AssertNotError(t, err, "Failed to parse time")
   621  	testCtx.fc.Set(future)
   622  	// Test that the CA rejects CSRs that would expire after the intermediate cert
   623  	_, err = ca.IssuePrecertificate(ctx, &capb.IssueCertificateRequest{Csr: CNandSANCSR, RegistrationID: arbitraryRegID})
   624  	test.AssertError(t, err, "Cannot issue a certificate that expires after the intermediate certificate")
   625  	test.AssertErrorIs(t, err, berrors.InternalServer)
   626  }
   627  
   628  func issueCertificateSubTestProfileSelectionRSA(t *testing.T, i *TestCertificateIssuance) {
   629  	// Certificates for RSA keys should be marked as usable for signatures and encryption.
   630  	expectedKeyUsage := x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
   631  	t.Logf("expected key usage %v, got %v", expectedKeyUsage, i.cert.KeyUsage)
   632  	test.AssertEquals(t, i.cert.KeyUsage, expectedKeyUsage)
   633  }
   634  
   635  func issueCertificateSubTestProfileSelectionECDSA(t *testing.T, i *TestCertificateIssuance) {
   636  	// Certificates for ECDSA keys should be marked as usable for only signatures.
   637  	expectedKeyUsage := x509.KeyUsageDigitalSignature
   638  	t.Logf("expected key usage %v, got %v", expectedKeyUsage, i.cert.KeyUsage)
   639  	test.AssertEquals(t, i.cert.KeyUsage, expectedKeyUsage)
   640  }
   641  
   642  func countMustStaple(t *testing.T, cert *x509.Certificate) (count int) {
   643  	oidTLSFeature := asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24}
   644  	mustStapleFeatureValue := []byte{0x30, 0x03, 0x02, 0x01, 0x05}
   645  	for _, ext := range cert.Extensions {
   646  		if ext.Id.Equal(oidTLSFeature) {
   647  			test.Assert(t, !ext.Critical, "Extension was marked critical")
   648  			test.AssertByteEquals(t, ext.Value, mustStapleFeatureValue)
   649  			count++
   650  		}
   651  	}
   652  	return count
   653  }
   654  
   655  func issueCertificateSubTestMustStaple(t *testing.T, i *TestCertificateIssuance) {
   656  	test.AssertMetricWithLabelsEquals(t, i.ca.signatureCount, prometheus.Labels{"purpose": "precertificate"}, 1)
   657  	test.AssertEquals(t, countMustStaple(t, i.cert), 1)
   658  }
   659  
   660  func issueCertificateSubTestUnknownExtension(t *testing.T, i *TestCertificateIssuance) {
   661  	test.AssertMetricWithLabelsEquals(t, i.ca.signatureCount, prometheus.Labels{"purpose": "precertificate"}, 1)
   662  
   663  	// NOTE: The hard-coded value here will have to change over time as Boulder
   664  	// adds new (unrequested) extensions to certificates.
   665  	expectedExtensionCount := 10
   666  	test.AssertEquals(t, len(i.cert.Extensions), expectedExtensionCount)
   667  }
   668  
   669  func issueCertificateSubTestCTPoisonExtension(t *testing.T, i *TestCertificateIssuance) {
   670  	test.AssertMetricWithLabelsEquals(t, i.ca.signatureCount, prometheus.Labels{"purpose": "precertificate"}, 1)
   671  }
   672  
   673  func findExtension(extensions []pkix.Extension, id asn1.ObjectIdentifier) *pkix.Extension {
   674  	for _, ext := range extensions {
   675  		if ext.Id.Equal(id) {
   676  			return &ext
   677  		}
   678  	}
   679  	return nil
   680  }
   681  
   682  func makeSCTs() ([][]byte, error) {
   683  	sct := ct.SignedCertificateTimestamp{
   684  		SCTVersion: 0,
   685  		Timestamp:  2020,
   686  		Signature: ct.DigitallySigned{
   687  			Signature: []byte{0},
   688  		},
   689  	}
   690  	sctBytes, err := cttls.Marshal(sct)
   691  	if err != nil {
   692  		return nil, err
   693  	}
   694  	return [][]byte{sctBytes}, err
   695  }
   696  
   697  func TestIssueCertificateForPrecertificate(t *testing.T) {
   698  	testCtx := setup(t)
   699  	sa := &mockSA{}
   700  	ca, err := NewCertificateAuthorityImpl(
   701  		sa,
   702  		testCtx.pa,
   703  		testCtx.boulderIssuers,
   704  		nil,
   705  		testCtx.certExpiry,
   706  		testCtx.certBackdate,
   707  		testCtx.serialPrefix,
   708  		testCtx.maxNames,
   709  		testCtx.keyPolicy,
   710  		testCtx.logger,
   711  		testCtx.stats,
   712  		testCtx.signatureCount,
   713  		testCtx.signErrorCount,
   714  		testCtx.fc)
   715  	test.AssertNotError(t, err, "Failed to create CA")
   716  
   717  	issueReq := capb.IssueCertificateRequest{Csr: CNandSANCSR, RegistrationID: arbitraryRegID, OrderID: 0}
   718  	precert, err := ca.IssuePrecertificate(ctx, &issueReq)
   719  	test.AssertNotError(t, err, "Failed to issue precert")
   720  	parsedPrecert, err := x509.ParseCertificate(precert.DER)
   721  	test.AssertNotError(t, err, "Failed to parse precert")
   722  
   723  	// Check for poison extension
   724  	poisonExtension := findExtension(parsedPrecert.Extensions, OIDExtensionCTPoison)
   725  	test.AssertNotNil(t, poisonExtension, "Couldn't find CTPoison extension")
   726  	test.AssertEquals(t, poisonExtension.Critical, true)
   727  	test.AssertDeepEquals(t, poisonExtension.Value, []byte{0x05, 0x00}) // ASN.1 DER NULL
   728  
   729  	sctBytes, err := makeSCTs()
   730  	if err != nil {
   731  		t.Fatal(err)
   732  	}
   733  
   734  	test.AssertNotError(t, err, "Failed to marshal SCT")
   735  	cert, err := ca.IssueCertificateForPrecertificate(ctx, &capb.IssueCertificateForPrecertificateRequest{
   736  		DER:            precert.DER,
   737  		SCTs:           sctBytes,
   738  		RegistrationID: arbitraryRegID,
   739  		OrderID:        0,
   740  	})
   741  	test.AssertNotError(t, err, "Failed to issue cert from precert")
   742  	parsedCert, err := x509.ParseCertificate(cert.Der)
   743  	test.AssertNotError(t, err, "Failed to parse cert")
   744  
   745  	// Check for SCT list extension
   746  	sctListExtension := findExtension(parsedCert.Extensions, OIDExtensionSCTList)
   747  	test.AssertNotNil(t, sctListExtension, "Couldn't find SCTList extension")
   748  	test.AssertEquals(t, sctListExtension.Critical, false)
   749  	var rawValue []byte
   750  	_, err = asn1.Unmarshal(sctListExtension.Value, &rawValue)
   751  	test.AssertNotError(t, err, "Failed to unmarshal extension value")
   752  	sctList, err := deserializeSCTList(rawValue)
   753  	test.AssertNotError(t, err, "Failed to deserialize SCT list")
   754  	test.Assert(t, len(sctList) == 1, fmt.Sprintf("Wrong number of SCTs, wanted: 1, got: %d", len(sctList)))
   755  }
   756  
   757  // deserializeSCTList deserializes a list of SCTs.
   758  // Forked from github.com/cloudflare/cfssl/helpers
   759  func deserializeSCTList(serializedSCTList []byte) ([]ct.SignedCertificateTimestamp, error) {
   760  	var sctList ctx509.SignedCertificateTimestampList
   761  	rest, err := cttls.Unmarshal(serializedSCTList, &sctList)
   762  	if err != nil {
   763  		return nil, err
   764  	}
   765  	if len(rest) != 0 {
   766  		return nil, errors.New("serialized SCT list contained trailing garbage")
   767  	}
   768  	list := make([]ct.SignedCertificateTimestamp, len(sctList.SCTList))
   769  	for i, serializedSCT := range sctList.SCTList {
   770  		var sct ct.SignedCertificateTimestamp
   771  		rest, err := cttls.Unmarshal(serializedSCT.Val, &sct)
   772  		if err != nil {
   773  			return nil, err
   774  		}
   775  		if len(rest) != 0 {
   776  			return nil, errors.New("serialized SCT contained trailing garbage")
   777  		}
   778  		list[i] = sct
   779  	}
   780  	return list, nil
   781  }
   782  
   783  // dupeSA returns a non-error to GetCertificate in order to simulate a request
   784  // to issue a final certificate with a duplicate serial.
   785  type dupeSA struct {
   786  	mockSA
   787  }
   788  
   789  func (m *dupeSA) GetCertificate(ctx context.Context, req *sapb.Serial, _ ...grpc.CallOption) (*corepb.Certificate, error) {
   790  	return nil, nil
   791  }
   792  
   793  // getCertErrorSA always returns an error for GetCertificate
   794  type getCertErrorSA struct {
   795  	mockSA
   796  }
   797  
   798  func (m *getCertErrorSA) GetCertificate(ctx context.Context, req *sapb.Serial, _ ...grpc.CallOption) (*corepb.Certificate, error) {
   799  	return nil, fmt.Errorf("i don't like it")
   800  }
   801  
   802  func TestIssueCertificateForPrecertificateDuplicateSerial(t *testing.T) {
   803  	testCtx := setup(t)
   804  	sa := &dupeSA{}
   805  	ca, err := NewCertificateAuthorityImpl(
   806  		sa,
   807  		testCtx.pa,
   808  		testCtx.boulderIssuers,
   809  		nil,
   810  		testCtx.certExpiry,
   811  		testCtx.certBackdate,
   812  		testCtx.serialPrefix,
   813  		testCtx.maxNames,
   814  		testCtx.keyPolicy,
   815  		testCtx.logger,
   816  		testCtx.stats,
   817  		testCtx.signatureCount,
   818  		testCtx.signErrorCount,
   819  		testCtx.fc)
   820  	test.AssertNotError(t, err, "Failed to create CA")
   821  
   822  	sctBytes, err := makeSCTs()
   823  	if err != nil {
   824  		t.Fatal(err)
   825  	}
   826  
   827  	issueReq := capb.IssueCertificateRequest{Csr: CNandSANCSR, RegistrationID: arbitraryRegID, OrderID: 0}
   828  	precert, err := ca.IssuePrecertificate(ctx, &issueReq)
   829  	test.AssertNotError(t, err, "Failed to issue precert")
   830  	_, err = ca.IssueCertificateForPrecertificate(ctx, &capb.IssueCertificateForPrecertificateRequest{
   831  		DER:            precert.DER,
   832  		SCTs:           sctBytes,
   833  		RegistrationID: arbitraryRegID,
   834  		OrderID:        0,
   835  	})
   836  	if err == nil {
   837  		t.Error("Expected error issuing duplicate serial but got none.")
   838  	}
   839  	if !strings.Contains(err.Error(), "issuance of duplicate final certificate requested") {
   840  		t.Errorf("Wrong type of error issuing duplicate serial. Expected 'issuance of duplicate', got '%s'", err)
   841  	}
   842  
   843  	// Now check what happens if there is an error (e.g. timeout) while checking
   844  	// for the duplicate.
   845  	errorsa := &getCertErrorSA{}
   846  	errorca, err := NewCertificateAuthorityImpl(
   847  		errorsa,
   848  		testCtx.pa,
   849  		testCtx.boulderIssuers,
   850  		nil,
   851  		testCtx.certExpiry,
   852  		testCtx.certBackdate,
   853  		testCtx.serialPrefix,
   854  		testCtx.maxNames,
   855  		testCtx.keyPolicy,
   856  		testCtx.logger,
   857  		testCtx.stats,
   858  		testCtx.signatureCount,
   859  		testCtx.signErrorCount,
   860  		testCtx.fc)
   861  	test.AssertNotError(t, err, "Failed to create CA")
   862  
   863  	_, err = errorca.IssueCertificateForPrecertificate(ctx, &capb.IssueCertificateForPrecertificateRequest{
   864  		DER:            precert.DER,
   865  		SCTs:           sctBytes,
   866  		RegistrationID: arbitraryRegID,
   867  		OrderID:        0,
   868  	})
   869  	if err == nil {
   870  		t.Fatal("Expected error issuing duplicate serial but got none.")
   871  	}
   872  	if !strings.Contains(err.Error(), "error checking for duplicate") {
   873  		t.Fatalf("Wrong type of error issuing duplicate serial. Expected 'error checking for duplicate', got '%s'", err)
   874  	}
   875  }
   876  

View as plain text