github.com/letsencrypt/boulder@v0.20251208.0/cmd/ceremony/main_test.go (about)

     1  package main
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"crypto/elliptic"
     6  	"crypto/rand"
     7  	"crypto/x509"
     8  	"encoding/pem"
     9  	"fmt"
    10  	"io/fs"
    11  	"math/big"
    12  	"os"
    13  	"path"
    14  	"strings"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/jmhodges/clock"
    19  
    20  	"github.com/letsencrypt/boulder/test"
    21  )
    22  
    23  func TestLoadPubKey(t *testing.T) {
    24  	tmp := t.TempDir()
    25  	key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    26  
    27  	_, _, err := loadPubKey(path.Join(tmp, "does", "not", "exist"))
    28  	test.AssertError(t, err, "should fail on non-existent file")
    29  	test.AssertErrorIs(t, err, fs.ErrNotExist)
    30  
    31  	_, _, err = loadPubKey("../../test/hierarchy/README.md")
    32  	test.AssertError(t, err, "should fail on non-PEM file")
    33  
    34  	priv, _ := x509.MarshalPKCS8PrivateKey(key)
    35  	_ = os.WriteFile(path.Join(tmp, "priv.pem"), pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: priv}), 0644)
    36  	_, _, err = loadPubKey(path.Join(tmp, "priv.pem"))
    37  	test.AssertError(t, err, "should fail on non-pubkey PEM")
    38  
    39  	pub, _ := x509.MarshalPKIXPublicKey(key.Public())
    40  	_ = os.WriteFile(path.Join(tmp, "pub.pem"), pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pub}), 0644)
    41  	_, _, err = loadPubKey(path.Join(tmp, "pub.pem"))
    42  	test.AssertNotError(t, err, "should not have errored")
    43  }
    44  
    45  func TestCheckOutputFileSucceeds(t *testing.T) {
    46  	dir := t.TempDir()
    47  	err := checkOutputFile(dir+"/example", "foo")
    48  	if err != nil {
    49  		t.Fatal(err)
    50  	}
    51  }
    52  
    53  func TestCheckOutputFileEmpty(t *testing.T) {
    54  	err := checkOutputFile("", "foo")
    55  	if err == nil {
    56  		t.Fatal("expected error, got none")
    57  	}
    58  	if err.Error() != "outputs.foo is required" {
    59  		t.Fatalf("wrong error: %s", err)
    60  	}
    61  }
    62  
    63  func TestCheckOutputFileExists(t *testing.T) {
    64  	dir := t.TempDir()
    65  	filename := dir + "/example"
    66  	err := writeFile(filename, []byte("hi"))
    67  	if err != nil {
    68  		t.Fatal(err)
    69  	}
    70  	err = checkOutputFile(filename, "foo")
    71  	if err == nil {
    72  		t.Fatal("expected error, got none")
    73  	}
    74  	if !strings.Contains(err.Error(), "already exists") {
    75  		t.Fatalf("wrong error: %s", err)
    76  	}
    77  }
    78  
    79  func TestKeyGenConfigValidate(t *testing.T) {
    80  	cases := []struct {
    81  		name          string
    82  		config        keyGenConfig
    83  		expectedError string
    84  	}{
    85  		{
    86  			name:          "no key.type",
    87  			config:        keyGenConfig{},
    88  			expectedError: "key.type is required",
    89  		},
    90  		{
    91  			name: "bad key.type",
    92  			config: keyGenConfig{
    93  				Type: "doop",
    94  			},
    95  			expectedError: "key.type can only be 'rsa' or 'ecdsa'",
    96  		},
    97  		{
    98  			name: "bad key.rsa-mod-length",
    99  			config: keyGenConfig{
   100  				Type:         "rsa",
   101  				RSAModLength: 1337,
   102  			},
   103  			expectedError: "key.rsa-mod-length can only be 2048 or 4096",
   104  		},
   105  		{
   106  			name: "key.type is rsa but key.ecdsa-curve is present",
   107  			config: keyGenConfig{
   108  				Type:         "rsa",
   109  				RSAModLength: 2048,
   110  				ECDSACurve:   "bad",
   111  			},
   112  			expectedError: "if key.type = 'rsa' then key.ecdsa-curve is not used",
   113  		},
   114  		{
   115  			name: "bad key.ecdsa-curve",
   116  			config: keyGenConfig{
   117  				Type:       "ecdsa",
   118  				ECDSACurve: "bad",
   119  			},
   120  			expectedError: "key.ecdsa-curve can only be 'P-256', 'P-384', or 'P-521'",
   121  		},
   122  		{
   123  			name: "key.type is ecdsa but key.rsa-mod-length is present",
   124  			config: keyGenConfig{
   125  				Type:         "ecdsa",
   126  				RSAModLength: 2048,
   127  				ECDSACurve:   "P-256",
   128  			},
   129  			expectedError: "if key.type = 'ecdsa' then key.rsa-mod-length is not used",
   130  		},
   131  		{
   132  			name: "good rsa config",
   133  			config: keyGenConfig{
   134  				Type:         "rsa",
   135  				RSAModLength: 2048,
   136  			},
   137  		},
   138  		{
   139  			name: "good ecdsa config",
   140  			config: keyGenConfig{
   141  				Type:       "ecdsa",
   142  				ECDSACurve: "P-256",
   143  			},
   144  		},
   145  	}
   146  	for _, tc := range cases {
   147  		t.Run(tc.name, func(t *testing.T) {
   148  			err := tc.config.validate()
   149  			if err != nil && err.Error() != tc.expectedError {
   150  				t.Fatalf("Unexpected error, wanted: %q, got: %q", tc.expectedError, err)
   151  			} else if err == nil && tc.expectedError != "" {
   152  				t.Fatalf("validate didn't fail, wanted: %q", err)
   153  			}
   154  		})
   155  	}
   156  }
   157  
   158  func TestRootConfigValidate(t *testing.T) {
   159  	cases := []struct {
   160  		name          string
   161  		config        rootConfig
   162  		expectedError string
   163  	}{
   164  		{
   165  			name:          "no pkcs11.module",
   166  			config:        rootConfig{},
   167  			expectedError: "pkcs11.module is required",
   168  		},
   169  		{
   170  			name: "no pkcs11.store-key-with-label",
   171  			config: rootConfig{
   172  				PKCS11: PKCS11KeyGenConfig{
   173  					Module: "module",
   174  				},
   175  			},
   176  			expectedError: "pkcs11.store-key-with-label is required",
   177  		},
   178  		{
   179  			name: "bad key fields",
   180  			config: rootConfig{
   181  				PKCS11: PKCS11KeyGenConfig{
   182  					Module:     "module",
   183  					StoreLabel: "label",
   184  				},
   185  			},
   186  			expectedError: "key.type is required",
   187  		},
   188  		{
   189  			name: "no outputs.public-key-path",
   190  			config: rootConfig{
   191  				PKCS11: PKCS11KeyGenConfig{
   192  					Module:     "module",
   193  					StoreLabel: "label",
   194  				},
   195  				Key: keyGenConfig{
   196  					Type:         "rsa",
   197  					RSAModLength: 2048,
   198  				},
   199  			},
   200  			expectedError: "outputs.public-key-path is required",
   201  		},
   202  		{
   203  			name: "no outputs.certificate-path",
   204  			config: rootConfig{
   205  				PKCS11: PKCS11KeyGenConfig{
   206  					Module:     "module",
   207  					StoreLabel: "label",
   208  				},
   209  				Key: keyGenConfig{
   210  					Type:         "rsa",
   211  					RSAModLength: 2048,
   212  				},
   213  				Outputs: struct {
   214  					PublicKeyPath   string `yaml:"public-key-path"`
   215  					CertificatePath string `yaml:"certificate-path"`
   216  				}{
   217  					PublicKeyPath: "path",
   218  				},
   219  			},
   220  			expectedError: "outputs.certificate-path is required",
   221  		},
   222  		{
   223  			name: "bad certificate-profile",
   224  			config: rootConfig{
   225  				PKCS11: PKCS11KeyGenConfig{
   226  					Module:     "module",
   227  					StoreLabel: "label",
   228  				},
   229  				Key: keyGenConfig{
   230  					Type:         "rsa",
   231  					RSAModLength: 2048,
   232  				},
   233  				Outputs: struct {
   234  					PublicKeyPath   string `yaml:"public-key-path"`
   235  					CertificatePath string `yaml:"certificate-path"`
   236  				}{
   237  					PublicKeyPath:   "path",
   238  					CertificatePath: "path",
   239  				},
   240  			},
   241  			expectedError: "not-before is required",
   242  		},
   243  		{
   244  			name: "good config",
   245  			config: rootConfig{
   246  				PKCS11: PKCS11KeyGenConfig{
   247  					Module:     "module",
   248  					StoreLabel: "label",
   249  				},
   250  				Key: keyGenConfig{
   251  					Type:         "rsa",
   252  					RSAModLength: 2048,
   253  				},
   254  				Outputs: struct {
   255  					PublicKeyPath   string `yaml:"public-key-path"`
   256  					CertificatePath string `yaml:"certificate-path"`
   257  				}{
   258  					PublicKeyPath:   "path",
   259  					CertificatePath: "path",
   260  				},
   261  				CertProfile: certProfile{
   262  					NotBefore:          "a",
   263  					NotAfter:           "b",
   264  					SignatureAlgorithm: "c",
   265  					CommonName:         "d",
   266  					Organization:       "e",
   267  					Country:            "f",
   268  				},
   269  				SkipLints: []string{
   270  					"e_ext_authority_key_identifier_missing",
   271  					"e_ext_authority_key_identifier_no_key_identifier",
   272  					"e_sub_ca_aia_missing",
   273  					"e_sub_ca_certificate_policies_missing",
   274  					"e_sub_ca_crl_distribution_points_missing",
   275  					"n_ca_digital_signature_not_set",
   276  					"n_mp_allowed_eku",
   277  					"n_sub_ca_eku_missing",
   278  					"w_sub_ca_aia_does_not_contain_issuing_ca_url",
   279  				},
   280  			},
   281  		},
   282  	}
   283  	for _, tc := range cases {
   284  		t.Run(tc.name, func(t *testing.T) {
   285  			err := tc.config.validate()
   286  			if err != nil && err.Error() != tc.expectedError {
   287  				t.Fatalf("Unexpected error, wanted: %q, got: %q", tc.expectedError, err)
   288  			} else if err == nil && tc.expectedError != "" {
   289  				t.Fatalf("validate didn't fail, wanted: %q", err)
   290  			}
   291  		})
   292  	}
   293  }
   294  
   295  func TestIntermediateConfigValidate(t *testing.T) {
   296  	cases := []struct {
   297  		name          string
   298  		config        intermediateConfig
   299  		expectedError string
   300  	}{
   301  		{
   302  			name:          "no pkcs11.module",
   303  			config:        intermediateConfig{},
   304  			expectedError: "pkcs11.module is required",
   305  		},
   306  		{
   307  			name: "no pkcs11.signing-key-label",
   308  			config: intermediateConfig{
   309  				PKCS11: PKCS11SigningConfig{
   310  					Module: "module",
   311  				},
   312  			},
   313  			expectedError: "pkcs11.signing-key-label is required",
   314  		},
   315  		{
   316  			name: "no inputs.public-key-path",
   317  			config: intermediateConfig{
   318  				PKCS11: PKCS11SigningConfig{
   319  					Module:       "module",
   320  					SigningLabel: "label",
   321  				},
   322  			},
   323  			expectedError: "inputs.public-key-path is required",
   324  		},
   325  		{
   326  			name: "no inputs.issuer-certificate-path",
   327  			config: intermediateConfig{
   328  				PKCS11: PKCS11SigningConfig{
   329  					Module:       "module",
   330  					SigningLabel: "label",
   331  				},
   332  				Inputs: struct {
   333  					PublicKeyPath         string `yaml:"public-key-path"`
   334  					IssuerCertificatePath string `yaml:"issuer-certificate-path"`
   335  				}{
   336  					PublicKeyPath: "path",
   337  				},
   338  			},
   339  			expectedError: "inputs.issuer-certificate is required",
   340  		},
   341  		{
   342  			name: "no outputs.certificate-path",
   343  			config: intermediateConfig{
   344  				PKCS11: PKCS11SigningConfig{
   345  					Module:       "module",
   346  					SigningLabel: "label",
   347  				},
   348  				Inputs: struct {
   349  					PublicKeyPath         string `yaml:"public-key-path"`
   350  					IssuerCertificatePath string `yaml:"issuer-certificate-path"`
   351  				}{
   352  					PublicKeyPath:         "path",
   353  					IssuerCertificatePath: "path",
   354  				},
   355  			},
   356  			expectedError: "outputs.certificate-path is required",
   357  		},
   358  		{
   359  			name: "bad certificate-profile",
   360  			config: intermediateConfig{
   361  				PKCS11: PKCS11SigningConfig{
   362  					Module:       "module",
   363  					SigningLabel: "label",
   364  				},
   365  				Inputs: struct {
   366  					PublicKeyPath         string `yaml:"public-key-path"`
   367  					IssuerCertificatePath string `yaml:"issuer-certificate-path"`
   368  				}{
   369  					PublicKeyPath:         "path",
   370  					IssuerCertificatePath: "path",
   371  				},
   372  				Outputs: struct {
   373  					CertificatePath string `yaml:"certificate-path"`
   374  				}{
   375  					CertificatePath: "path",
   376  				},
   377  			},
   378  			expectedError: "not-before is required",
   379  		},
   380  		{
   381  			name: "too many policy OIDs",
   382  			config: intermediateConfig{
   383  				PKCS11: PKCS11SigningConfig{
   384  					Module:       "module",
   385  					SigningLabel: "label",
   386  				},
   387  				Inputs: struct {
   388  					PublicKeyPath         string `yaml:"public-key-path"`
   389  					IssuerCertificatePath string `yaml:"issuer-certificate-path"`
   390  				}{
   391  					PublicKeyPath:         "path",
   392  					IssuerCertificatePath: "path",
   393  				},
   394  				Outputs: struct {
   395  					CertificatePath string `yaml:"certificate-path"`
   396  				}{
   397  					CertificatePath: "path",
   398  				},
   399  				CertProfile: certProfile{
   400  					NotBefore:          "a",
   401  					NotAfter:           "b",
   402  					SignatureAlgorithm: "c",
   403  					CommonName:         "d",
   404  					Organization:       "e",
   405  					Country:            "f",
   406  					CRLURL:             "h",
   407  					IssuerURL:          "i",
   408  					Policies:           []policyInfoConfig{{OID: "2.23.140.1.2.1"}, {OID: "6.6.6"}},
   409  				},
   410  				SkipLints: []string{},
   411  			},
   412  			expectedError: "policy should be exactly BRs domain-validated for subordinate CAs",
   413  		},
   414  		{
   415  			name: "too few policy OIDs",
   416  			config: intermediateConfig{
   417  				PKCS11: PKCS11SigningConfig{
   418  					Module:       "module",
   419  					SigningLabel: "label",
   420  				},
   421  				Inputs: struct {
   422  					PublicKeyPath         string `yaml:"public-key-path"`
   423  					IssuerCertificatePath string `yaml:"issuer-certificate-path"`
   424  				}{
   425  					PublicKeyPath:         "path",
   426  					IssuerCertificatePath: "path",
   427  				},
   428  				Outputs: struct {
   429  					CertificatePath string `yaml:"certificate-path"`
   430  				}{
   431  					CertificatePath: "path",
   432  				},
   433  				CertProfile: certProfile{
   434  					NotBefore:          "a",
   435  					NotAfter:           "b",
   436  					SignatureAlgorithm: "c",
   437  					CommonName:         "d",
   438  					Organization:       "e",
   439  					Country:            "f",
   440  					CRLURL:             "h",
   441  					IssuerURL:          "i",
   442  					Policies:           []policyInfoConfig{},
   443  				},
   444  				SkipLints: []string{},
   445  			},
   446  			expectedError: "policy should be exactly BRs domain-validated for subordinate CAs",
   447  		},
   448  		{
   449  			name: "good config",
   450  			config: intermediateConfig{
   451  				PKCS11: PKCS11SigningConfig{
   452  					Module:       "module",
   453  					SigningLabel: "label",
   454  				},
   455  				Inputs: struct {
   456  					PublicKeyPath         string `yaml:"public-key-path"`
   457  					IssuerCertificatePath string `yaml:"issuer-certificate-path"`
   458  				}{
   459  					PublicKeyPath:         "path",
   460  					IssuerCertificatePath: "path",
   461  				},
   462  				Outputs: struct {
   463  					CertificatePath string `yaml:"certificate-path"`
   464  				}{
   465  					CertificatePath: "path",
   466  				},
   467  				CertProfile: certProfile{
   468  					NotBefore:          "a",
   469  					NotAfter:           "b",
   470  					SignatureAlgorithm: "c",
   471  					CommonName:         "d",
   472  					Organization:       "e",
   473  					Country:            "f",
   474  					CRLURL:             "h",
   475  					IssuerURL:          "i",
   476  					Policies:           []policyInfoConfig{{OID: "2.23.140.1.2.1"}},
   477  				},
   478  				SkipLints: []string{},
   479  			},
   480  		},
   481  	}
   482  	for _, tc := range cases {
   483  		t.Run(tc.name, func(t *testing.T) {
   484  			err := tc.config.validate()
   485  			if err != nil && err.Error() != tc.expectedError {
   486  				t.Fatalf("Unexpected error, wanted: %q, got: %q", tc.expectedError, err)
   487  			} else if err == nil && tc.expectedError != "" {
   488  				t.Fatalf("validate didn't fail, wanted: %q", err)
   489  			}
   490  		})
   491  	}
   492  }
   493  
   494  func TestCrossCertConfigValidate(t *testing.T) {
   495  	cases := []struct {
   496  		name          string
   497  		config        crossCertConfig
   498  		expectedError string
   499  	}{
   500  		{
   501  			name:          "no pkcs11.module",
   502  			config:        crossCertConfig{},
   503  			expectedError: "pkcs11.module is required",
   504  		},
   505  		{
   506  			name: "no pkcs11.signing-key-label",
   507  			config: crossCertConfig{
   508  				PKCS11: PKCS11SigningConfig{
   509  					Module: "module",
   510  				},
   511  			},
   512  			expectedError: "pkcs11.signing-key-label is required",
   513  		},
   514  		{
   515  			name: "no inputs.public-key-path",
   516  			config: crossCertConfig{
   517  				PKCS11: PKCS11SigningConfig{
   518  					Module:       "module",
   519  					SigningLabel: "label",
   520  				},
   521  			},
   522  			expectedError: "inputs.public-key-path is required",
   523  		},
   524  		{
   525  			name: "no inputs.issuer-certificate-path",
   526  			config: crossCertConfig{
   527  				PKCS11: PKCS11SigningConfig{
   528  					Module:       "module",
   529  					SigningLabel: "label",
   530  				},
   531  				Inputs: struct {
   532  					PublicKeyPath              string `yaml:"public-key-path"`
   533  					IssuerCertificatePath      string `yaml:"issuer-certificate-path"`
   534  					CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"`
   535  				}{
   536  					PublicKeyPath:              "path",
   537  					CertificateToCrossSignPath: "path",
   538  				},
   539  			},
   540  			expectedError: "inputs.issuer-certificate is required",
   541  		},
   542  		{
   543  			name: "no inputs.certificate-to-cross-sign-path",
   544  			config: crossCertConfig{
   545  				PKCS11: PKCS11SigningConfig{
   546  					Module:       "module",
   547  					SigningLabel: "label",
   548  				},
   549  				Inputs: struct {
   550  					PublicKeyPath              string `yaml:"public-key-path"`
   551  					IssuerCertificatePath      string `yaml:"issuer-certificate-path"`
   552  					CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"`
   553  				}{
   554  					PublicKeyPath:         "path",
   555  					IssuerCertificatePath: "path",
   556  				},
   557  			},
   558  			expectedError: "inputs.certificate-to-cross-sign-path is required",
   559  		},
   560  		{
   561  			name: "no outputs.certificate-path",
   562  			config: crossCertConfig{
   563  				PKCS11: PKCS11SigningConfig{
   564  					Module:       "module",
   565  					SigningLabel: "label",
   566  				},
   567  				Inputs: struct {
   568  					PublicKeyPath              string `yaml:"public-key-path"`
   569  					IssuerCertificatePath      string `yaml:"issuer-certificate-path"`
   570  					CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"`
   571  				}{
   572  					PublicKeyPath:              "path",
   573  					IssuerCertificatePath:      "path",
   574  					CertificateToCrossSignPath: "path",
   575  				},
   576  			},
   577  			expectedError: "outputs.certificate-path is required",
   578  		},
   579  		{
   580  			name: "bad certificate-profile",
   581  			config: crossCertConfig{
   582  				PKCS11: PKCS11SigningConfig{
   583  					Module:       "module",
   584  					SigningLabel: "label",
   585  				},
   586  				Inputs: struct {
   587  					PublicKeyPath              string `yaml:"public-key-path"`
   588  					IssuerCertificatePath      string `yaml:"issuer-certificate-path"`
   589  					CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"`
   590  				}{
   591  					PublicKeyPath:              "path",
   592  					IssuerCertificatePath:      "path",
   593  					CertificateToCrossSignPath: "path",
   594  				},
   595  				Outputs: struct {
   596  					CertificatePath string `yaml:"certificate-path"`
   597  				}{
   598  					CertificatePath: "path",
   599  				},
   600  			},
   601  			expectedError: "not-before is required",
   602  		},
   603  		{
   604  			name: "too many policy OIDs",
   605  			config: crossCertConfig{
   606  				PKCS11: PKCS11SigningConfig{
   607  					Module:       "module",
   608  					SigningLabel: "label",
   609  				},
   610  				Inputs: struct {
   611  					PublicKeyPath              string `yaml:"public-key-path"`
   612  					IssuerCertificatePath      string `yaml:"issuer-certificate-path"`
   613  					CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"`
   614  				}{
   615  					PublicKeyPath:              "path",
   616  					IssuerCertificatePath:      "path",
   617  					CertificateToCrossSignPath: "path",
   618  				},
   619  				Outputs: struct {
   620  					CertificatePath string `yaml:"certificate-path"`
   621  				}{
   622  					CertificatePath: "path",
   623  				},
   624  				CertProfile: certProfile{
   625  					NotBefore:          "a",
   626  					NotAfter:           "b",
   627  					SignatureAlgorithm: "c",
   628  					CommonName:         "d",
   629  					Organization:       "e",
   630  					Country:            "f",
   631  					CRLURL:             "h",
   632  					IssuerURL:          "i",
   633  					Policies:           []policyInfoConfig{{OID: "2.23.140.1.2.1"}, {OID: "6.6.6"}},
   634  				},
   635  				SkipLints: []string{},
   636  			},
   637  			expectedError: "policy should be exactly BRs domain-validated for subordinate CAs",
   638  		},
   639  		{
   640  			name: "too few policy OIDs",
   641  			config: crossCertConfig{
   642  				PKCS11: PKCS11SigningConfig{
   643  					Module:       "module",
   644  					SigningLabel: "label",
   645  				},
   646  				Inputs: struct {
   647  					PublicKeyPath              string `yaml:"public-key-path"`
   648  					IssuerCertificatePath      string `yaml:"issuer-certificate-path"`
   649  					CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"`
   650  				}{
   651  					PublicKeyPath:              "path",
   652  					IssuerCertificatePath:      "path",
   653  					CertificateToCrossSignPath: "path",
   654  				},
   655  				Outputs: struct {
   656  					CertificatePath string `yaml:"certificate-path"`
   657  				}{
   658  					CertificatePath: "path",
   659  				},
   660  				CertProfile: certProfile{
   661  					NotBefore:          "a",
   662  					NotAfter:           "b",
   663  					SignatureAlgorithm: "c",
   664  					CommonName:         "d",
   665  					Organization:       "e",
   666  					Country:            "f",
   667  					CRLURL:             "h",
   668  					IssuerURL:          "i",
   669  					Policies:           []policyInfoConfig{},
   670  				},
   671  				SkipLints: []string{},
   672  			},
   673  			expectedError: "policy should be exactly BRs domain-validated for subordinate CAs",
   674  		},
   675  		{
   676  			name: "good config",
   677  			config: crossCertConfig{
   678  				PKCS11: PKCS11SigningConfig{
   679  					Module:       "module",
   680  					SigningLabel: "label",
   681  				},
   682  				Inputs: struct {
   683  					PublicKeyPath              string `yaml:"public-key-path"`
   684  					IssuerCertificatePath      string `yaml:"issuer-certificate-path"`
   685  					CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"`
   686  				}{
   687  					PublicKeyPath:              "path",
   688  					IssuerCertificatePath:      "path",
   689  					CertificateToCrossSignPath: "path",
   690  				},
   691  				Outputs: struct {
   692  					CertificatePath string `yaml:"certificate-path"`
   693  				}{
   694  					CertificatePath: "path",
   695  				},
   696  				CertProfile: certProfile{
   697  					NotBefore:          "a",
   698  					NotAfter:           "b",
   699  					SignatureAlgorithm: "c",
   700  					CommonName:         "d",
   701  					Organization:       "e",
   702  					Country:            "f",
   703  					CRLURL:             "h",
   704  					IssuerURL:          "i",
   705  					Policies:           []policyInfoConfig{{OID: "2.23.140.1.2.1"}},
   706  				},
   707  				SkipLints: []string{},
   708  			},
   709  		},
   710  	}
   711  	for _, tc := range cases {
   712  		t.Run(tc.name, func(t *testing.T) {
   713  			err := tc.config.validate()
   714  			if err != nil && err.Error() != tc.expectedError {
   715  				t.Fatalf("Unexpected error, wanted: %q, got: %q", tc.expectedError, err)
   716  			} else if err == nil && tc.expectedError != "" {
   717  				t.Fatalf("validate didn't fail, wanted: %q", err)
   718  			}
   719  		})
   720  	}
   721  }
   722  
   723  func TestCSRConfigValidate(t *testing.T) {
   724  	cases := []struct {
   725  		name          string
   726  		config        csrConfig
   727  		expectedError string
   728  	}{
   729  		{
   730  			name:          "no pkcs11.module",
   731  			config:        csrConfig{},
   732  			expectedError: "pkcs11.module is required",
   733  		},
   734  		{
   735  			name: "no pkcs11.signing-key-label",
   736  			config: csrConfig{
   737  				PKCS11: PKCS11SigningConfig{
   738  					Module: "module",
   739  				},
   740  			},
   741  			expectedError: "pkcs11.signing-key-label is required",
   742  		},
   743  		{
   744  			name: "no inputs.public-key-path",
   745  			config: csrConfig{
   746  				PKCS11: PKCS11SigningConfig{
   747  					Module:       "module",
   748  					SigningLabel: "label",
   749  				},
   750  			},
   751  			expectedError: "inputs.public-key-path is required",
   752  		},
   753  		{
   754  			name: "no outputs.csr-path",
   755  			config: csrConfig{
   756  				PKCS11: PKCS11SigningConfig{
   757  					Module:       "module",
   758  					SigningLabel: "label",
   759  				},
   760  				Inputs: struct {
   761  					PublicKeyPath string `yaml:"public-key-path"`
   762  				}{
   763  					PublicKeyPath: "path",
   764  				},
   765  			},
   766  			expectedError: "outputs.csr-path is required",
   767  		},
   768  		{
   769  			name: "bad certificate-profile",
   770  			config: csrConfig{
   771  				PKCS11: PKCS11SigningConfig{
   772  					Module:       "module",
   773  					SigningLabel: "label",
   774  				},
   775  				Inputs: struct {
   776  					PublicKeyPath string `yaml:"public-key-path"`
   777  				}{
   778  					PublicKeyPath: "path",
   779  				},
   780  				Outputs: struct {
   781  					CSRPath string `yaml:"csr-path"`
   782  				}{
   783  					CSRPath: "path",
   784  				},
   785  			},
   786  			expectedError: "common-name is required",
   787  		},
   788  		{
   789  			name: "good config",
   790  			config: csrConfig{
   791  				PKCS11: PKCS11SigningConfig{
   792  					Module:       "module",
   793  					SigningLabel: "label",
   794  				},
   795  				Inputs: struct {
   796  					PublicKeyPath string `yaml:"public-key-path"`
   797  				}{
   798  					PublicKeyPath: "path",
   799  				},
   800  				Outputs: struct {
   801  					CSRPath string `yaml:"csr-path"`
   802  				}{
   803  					CSRPath: "path",
   804  				},
   805  				CertProfile: certProfile{
   806  					CommonName:   "d",
   807  					Organization: "e",
   808  					Country:      "f",
   809  				},
   810  			},
   811  		},
   812  	}
   813  	for _, tc := range cases {
   814  		t.Run(tc.name, func(t *testing.T) {
   815  			err := tc.config.validate()
   816  			if err != nil && err.Error() != tc.expectedError {
   817  				t.Fatalf("Unexpected error, wanted: %q, got: %q", tc.expectedError, err)
   818  			} else if err == nil && tc.expectedError != "" {
   819  				t.Fatalf("validate didn't fail, wanted: %q", err)
   820  			}
   821  		})
   822  	}
   823  }
   824  
   825  func TestKeyConfigValidate(t *testing.T) {
   826  	cases := []struct {
   827  		name          string
   828  		config        keyConfig
   829  		expectedError string
   830  	}{
   831  		{
   832  			name:          "no pkcs11.module",
   833  			config:        keyConfig{},
   834  			expectedError: "pkcs11.module is required",
   835  		},
   836  		{
   837  			name: "no pkcs11.store-key-with-label",
   838  			config: keyConfig{
   839  				PKCS11: PKCS11KeyGenConfig{
   840  					Module: "module",
   841  				},
   842  			},
   843  			expectedError: "pkcs11.store-key-with-label is required",
   844  		},
   845  		{
   846  			name: "bad key fields",
   847  			config: keyConfig{
   848  				PKCS11: PKCS11KeyGenConfig{
   849  					Module:     "module",
   850  					StoreLabel: "label",
   851  				},
   852  			},
   853  			expectedError: "key.type is required",
   854  		},
   855  		{
   856  			name: "no outputs.public-key-path",
   857  			config: keyConfig{
   858  				PKCS11: PKCS11KeyGenConfig{
   859  					Module:     "module",
   860  					StoreLabel: "label",
   861  				},
   862  				Key: keyGenConfig{
   863  					Type:         "rsa",
   864  					RSAModLength: 2048,
   865  				},
   866  			},
   867  			expectedError: "outputs.public-key-path is required",
   868  		},
   869  		{
   870  			name: "good config",
   871  			config: keyConfig{
   872  				PKCS11: PKCS11KeyGenConfig{
   873  					Module:     "module",
   874  					StoreLabel: "label",
   875  				},
   876  				Key: keyGenConfig{
   877  					Type:         "rsa",
   878  					RSAModLength: 2048,
   879  				},
   880  				Outputs: struct {
   881  					PublicKeyPath    string `yaml:"public-key-path"`
   882  					PKCS11ConfigPath string `yaml:"pkcs11-config-path"`
   883  				}{
   884  					PublicKeyPath:    "path",
   885  					PKCS11ConfigPath: "path.json",
   886  				},
   887  			},
   888  		},
   889  	}
   890  	for _, tc := range cases {
   891  		t.Run(tc.name, func(t *testing.T) {
   892  			err := tc.config.validate()
   893  			if err != nil && err.Error() != tc.expectedError {
   894  				t.Fatalf("Unexpected error, wanted: %q, got: %q", tc.expectedError, err)
   895  			} else if err == nil && tc.expectedError != "" {
   896  				t.Fatalf("validate didn't fail, wanted: %q", err)
   897  			}
   898  		})
   899  	}
   900  }
   901  
   902  func TestCRLConfig(t *testing.T) {
   903  	cases := []struct {
   904  		name          string
   905  		config        crlConfig
   906  		expectedError string
   907  	}{
   908  		{
   909  			name:          "no pkcs11.module",
   910  			config:        crlConfig{},
   911  			expectedError: "pkcs11.module is required",
   912  		},
   913  		{
   914  			name: "no pkcs11.signing-key-label",
   915  			config: crlConfig{
   916  				PKCS11: PKCS11SigningConfig{
   917  					Module: "module",
   918  				},
   919  			},
   920  			expectedError: "pkcs11.signing-key-label is required",
   921  		},
   922  		{
   923  			name: "no inputs.issuer-certificate-path",
   924  			config: crlConfig{
   925  				PKCS11: PKCS11SigningConfig{
   926  					Module:       "module",
   927  					SigningLabel: "label",
   928  				},
   929  			},
   930  			expectedError: "inputs.issuer-certificate-path is required",
   931  		},
   932  		{
   933  			name: "no outputs.crl-path",
   934  			config: crlConfig{
   935  				PKCS11: PKCS11SigningConfig{
   936  					Module:       "module",
   937  					SigningLabel: "label",
   938  				},
   939  				Inputs: struct {
   940  					IssuerCertificatePath string `yaml:"issuer-certificate-path"`
   941  				}{
   942  					IssuerCertificatePath: "path",
   943  				},
   944  			},
   945  			expectedError: "outputs.crl-path is required",
   946  		},
   947  		{
   948  			name: "no crl-profile.this-update",
   949  			config: crlConfig{
   950  				PKCS11: PKCS11SigningConfig{
   951  					Module:       "module",
   952  					SigningLabel: "label",
   953  				},
   954  				Inputs: struct {
   955  					IssuerCertificatePath string `yaml:"issuer-certificate-path"`
   956  				}{
   957  					IssuerCertificatePath: "path",
   958  				},
   959  				Outputs: struct {
   960  					CRLPath string `yaml:"crl-path"`
   961  				}{
   962  					CRLPath: "path",
   963  				},
   964  			},
   965  			expectedError: "crl-profile.this-update is required",
   966  		},
   967  		{
   968  			name: "no crl-profile.next-update",
   969  			config: crlConfig{
   970  				PKCS11: PKCS11SigningConfig{
   971  					Module:       "module",
   972  					SigningLabel: "label",
   973  				},
   974  				Inputs: struct {
   975  					IssuerCertificatePath string `yaml:"issuer-certificate-path"`
   976  				}{
   977  					IssuerCertificatePath: "path",
   978  				},
   979  				Outputs: struct {
   980  					CRLPath string `yaml:"crl-path"`
   981  				}{
   982  					CRLPath: "path",
   983  				},
   984  				CRLProfile: struct {
   985  					ThisUpdate          string `yaml:"this-update"`
   986  					NextUpdate          string `yaml:"next-update"`
   987  					Number              int64  `yaml:"number"`
   988  					RevokedCertificates []struct {
   989  						CertificatePath  string `yaml:"certificate-path"`
   990  						RevocationDate   string `yaml:"revocation-date"`
   991  						RevocationReason string `yaml:"revocation-reason"`
   992  					} `yaml:"revoked-certificates"`
   993  				}{
   994  					ThisUpdate: "this-update",
   995  				},
   996  			},
   997  			expectedError: "crl-profile.next-update is required",
   998  		},
   999  		{
  1000  			name: "no crl-profile.number",
  1001  			config: crlConfig{
  1002  				PKCS11: PKCS11SigningConfig{
  1003  					Module:       "module",
  1004  					SigningLabel: "label",
  1005  				},
  1006  				Inputs: struct {
  1007  					IssuerCertificatePath string `yaml:"issuer-certificate-path"`
  1008  				}{
  1009  					IssuerCertificatePath: "path",
  1010  				},
  1011  				Outputs: struct {
  1012  					CRLPath string `yaml:"crl-path"`
  1013  				}{
  1014  					CRLPath: "path",
  1015  				},
  1016  				CRLProfile: struct {
  1017  					ThisUpdate          string `yaml:"this-update"`
  1018  					NextUpdate          string `yaml:"next-update"`
  1019  					Number              int64  `yaml:"number"`
  1020  					RevokedCertificates []struct {
  1021  						CertificatePath  string `yaml:"certificate-path"`
  1022  						RevocationDate   string `yaml:"revocation-date"`
  1023  						RevocationReason string `yaml:"revocation-reason"`
  1024  					} `yaml:"revoked-certificates"`
  1025  				}{
  1026  					ThisUpdate: "this-update",
  1027  					NextUpdate: "next-update",
  1028  				},
  1029  			},
  1030  			expectedError: "crl-profile.number must be non-zero",
  1031  		},
  1032  		{
  1033  			name: "no crl-profile.revoked-certificates.certificate-path",
  1034  			config: crlConfig{
  1035  				PKCS11: PKCS11SigningConfig{
  1036  					Module:       "module",
  1037  					SigningLabel: "label",
  1038  				},
  1039  				Inputs: struct {
  1040  					IssuerCertificatePath string `yaml:"issuer-certificate-path"`
  1041  				}{
  1042  					IssuerCertificatePath: "path",
  1043  				},
  1044  				Outputs: struct {
  1045  					CRLPath string `yaml:"crl-path"`
  1046  				}{
  1047  					CRLPath: "path",
  1048  				},
  1049  				CRLProfile: struct {
  1050  					ThisUpdate          string `yaml:"this-update"`
  1051  					NextUpdate          string `yaml:"next-update"`
  1052  					Number              int64  `yaml:"number"`
  1053  					RevokedCertificates []struct {
  1054  						CertificatePath  string `yaml:"certificate-path"`
  1055  						RevocationDate   string `yaml:"revocation-date"`
  1056  						RevocationReason string `yaml:"revocation-reason"`
  1057  					} `yaml:"revoked-certificates"`
  1058  				}{
  1059  					ThisUpdate: "this-update",
  1060  					NextUpdate: "next-update",
  1061  					Number:     1,
  1062  					RevokedCertificates: []struct {
  1063  						CertificatePath  string `yaml:"certificate-path"`
  1064  						RevocationDate   string `yaml:"revocation-date"`
  1065  						RevocationReason string `yaml:"revocation-reason"`
  1066  					}{{}},
  1067  				},
  1068  			},
  1069  			expectedError: "crl-profile.revoked-certificates.certificate-path is required",
  1070  		},
  1071  		{
  1072  			name: "no crl-profile.revoked-certificates.revocation-date",
  1073  			config: crlConfig{
  1074  				PKCS11: PKCS11SigningConfig{
  1075  					Module:       "module",
  1076  					SigningLabel: "label",
  1077  				},
  1078  				Inputs: struct {
  1079  					IssuerCertificatePath string `yaml:"issuer-certificate-path"`
  1080  				}{
  1081  					IssuerCertificatePath: "path",
  1082  				},
  1083  				Outputs: struct {
  1084  					CRLPath string `yaml:"crl-path"`
  1085  				}{
  1086  					CRLPath: "path",
  1087  				},
  1088  				CRLProfile: struct {
  1089  					ThisUpdate          string `yaml:"this-update"`
  1090  					NextUpdate          string `yaml:"next-update"`
  1091  					Number              int64  `yaml:"number"`
  1092  					RevokedCertificates []struct {
  1093  						CertificatePath  string `yaml:"certificate-path"`
  1094  						RevocationDate   string `yaml:"revocation-date"`
  1095  						RevocationReason string `yaml:"revocation-reason"`
  1096  					} `yaml:"revoked-certificates"`
  1097  				}{
  1098  					ThisUpdate: "this-update",
  1099  					NextUpdate: "next-update",
  1100  					Number:     1,
  1101  					RevokedCertificates: []struct {
  1102  						CertificatePath  string `yaml:"certificate-path"`
  1103  						RevocationDate   string `yaml:"revocation-date"`
  1104  						RevocationReason string `yaml:"revocation-reason"`
  1105  					}{{
  1106  						CertificatePath: "path",
  1107  					}},
  1108  				},
  1109  			},
  1110  			expectedError: "crl-profile.revoked-certificates.revocation-date is required",
  1111  		},
  1112  		{
  1113  			name: "no revocation reason",
  1114  			config: crlConfig{
  1115  				PKCS11: PKCS11SigningConfig{
  1116  					Module:       "module",
  1117  					SigningLabel: "label",
  1118  				},
  1119  				Inputs: struct {
  1120  					IssuerCertificatePath string `yaml:"issuer-certificate-path"`
  1121  				}{
  1122  					IssuerCertificatePath: "path",
  1123  				},
  1124  				Outputs: struct {
  1125  					CRLPath string `yaml:"crl-path"`
  1126  				}{
  1127  					CRLPath: "path",
  1128  				},
  1129  				CRLProfile: struct {
  1130  					ThisUpdate          string `yaml:"this-update"`
  1131  					NextUpdate          string `yaml:"next-update"`
  1132  					Number              int64  `yaml:"number"`
  1133  					RevokedCertificates []struct {
  1134  						CertificatePath  string `yaml:"certificate-path"`
  1135  						RevocationDate   string `yaml:"revocation-date"`
  1136  						RevocationReason string `yaml:"revocation-reason"`
  1137  					} `yaml:"revoked-certificates"`
  1138  				}{
  1139  					ThisUpdate: "this-update",
  1140  					NextUpdate: "next-update",
  1141  					Number:     1,
  1142  					RevokedCertificates: []struct {
  1143  						CertificatePath  string `yaml:"certificate-path"`
  1144  						RevocationDate   string `yaml:"revocation-date"`
  1145  						RevocationReason string `yaml:"revocation-reason"`
  1146  					}{{
  1147  						CertificatePath: "path",
  1148  						RevocationDate:  "date",
  1149  					}},
  1150  				},
  1151  			},
  1152  			expectedError: "crl-profile.revoked-certificates.revocation-reason is required",
  1153  		},
  1154  		{
  1155  			name: "good",
  1156  			config: crlConfig{
  1157  				PKCS11: PKCS11SigningConfig{
  1158  					Module:       "module",
  1159  					SigningLabel: "label",
  1160  				},
  1161  				Inputs: struct {
  1162  					IssuerCertificatePath string `yaml:"issuer-certificate-path"`
  1163  				}{
  1164  					IssuerCertificatePath: "path",
  1165  				},
  1166  				Outputs: struct {
  1167  					CRLPath string `yaml:"crl-path"`
  1168  				}{
  1169  					CRLPath: "path",
  1170  				},
  1171  				CRLProfile: struct {
  1172  					ThisUpdate          string `yaml:"this-update"`
  1173  					NextUpdate          string `yaml:"next-update"`
  1174  					Number              int64  `yaml:"number"`
  1175  					RevokedCertificates []struct {
  1176  						CertificatePath  string `yaml:"certificate-path"`
  1177  						RevocationDate   string `yaml:"revocation-date"`
  1178  						RevocationReason string `yaml:"revocation-reason"`
  1179  					} `yaml:"revoked-certificates"`
  1180  				}{
  1181  					ThisUpdate: "this-update",
  1182  					NextUpdate: "next-update",
  1183  					Number:     1,
  1184  					RevokedCertificates: []struct {
  1185  						CertificatePath  string `yaml:"certificate-path"`
  1186  						RevocationDate   string `yaml:"revocation-date"`
  1187  						RevocationReason string `yaml:"revocation-reason"`
  1188  					}{{
  1189  						CertificatePath:  "path",
  1190  						RevocationDate:   "date",
  1191  						RevocationReason: "keyCompromise",
  1192  					}},
  1193  				},
  1194  			},
  1195  		},
  1196  	}
  1197  	for _, tc := range cases {
  1198  		t.Run(tc.name, func(t *testing.T) {
  1199  			err := tc.config.validate()
  1200  			if err != nil && err.Error() != tc.expectedError {
  1201  				t.Fatalf("Unexpected error, wanted: %q, got: %q", tc.expectedError, err)
  1202  			} else if err == nil && tc.expectedError != "" {
  1203  				t.Fatalf("validate didn't fail, wanted: %q", err)
  1204  			}
  1205  		})
  1206  	}
  1207  }
  1208  
  1209  func TestSignAndWriteNoLintCert(t *testing.T) {
  1210  	_, err := signAndWriteCert(nil, nil, nil, nil, nil, "")
  1211  	test.AssertError(t, err, "should have failed because no lintCert was provided")
  1212  	test.AssertDeepEquals(t, err, fmt.Errorf("linting was not performed prior to issuance"))
  1213  }
  1214  
  1215  func TestPostIssuanceLinting(t *testing.T) {
  1216  	clk := clock.New()
  1217  	err := postIssuanceLinting(nil, nil)
  1218  	test.AssertError(t, err, "should have failed because no certificate was provided")
  1219  
  1220  	testKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  1221  	test.AssertNotError(t, err, "unable to generate ECDSA private key")
  1222  	template := &x509.Certificate{
  1223  		NotAfter:     clk.Now().Add(1 * time.Hour),
  1224  		DNSNames:     []string{"example.com"},
  1225  		SerialNumber: big.NewInt(1),
  1226  	}
  1227  	certDer, err := x509.CreateCertificate(rand.Reader, template, template, &testKey.PublicKey, testKey)
  1228  	test.AssertNotError(t, err, "unable to create certificate")
  1229  	parsedCert, err := x509.ParseCertificate(certDer)
  1230  	test.AssertNotError(t, err, "unable to parse DER bytes")
  1231  	err = postIssuanceLinting(parsedCert, nil)
  1232  	test.AssertNotError(t, err, "should not have errored")
  1233  }