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

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto"
     7  	"crypto/ecdsa"
     8  	"crypto/rsa"
     9  	"crypto/x509"
    10  	"crypto/x509/pkix"
    11  	"encoding/asn1"
    12  	"encoding/pem"
    13  	"errors"
    14  	"flag"
    15  	"fmt"
    16  	"log"
    17  	"os"
    18  	"slices"
    19  	"time"
    20  
    21  	"gopkg.in/yaml.v3"
    22  
    23  	zlintx509 "github.com/zmap/zcrypto/x509"
    24  	"github.com/zmap/zlint/v3"
    25  
    26  	"github.com/letsencrypt/boulder/goodkey"
    27  	"github.com/letsencrypt/boulder/linter"
    28  	"github.com/letsencrypt/boulder/pkcs11helpers"
    29  	"github.com/letsencrypt/boulder/revocation"
    30  	"github.com/letsencrypt/boulder/strictyaml"
    31  )
    32  
    33  var kp goodkey.KeyPolicy
    34  
    35  func init() {
    36  	var err error
    37  	kp, err = goodkey.NewPolicy(nil, nil)
    38  	if err != nil {
    39  		log.Fatal("Could not create goodkey.KeyPolicy")
    40  	}
    41  }
    42  
    43  type lintCert *x509.Certificate
    44  
    45  // issueLintCertAndPerformLinting issues a linting certificate from a given
    46  // template certificate signed by a given issuer and returns a *lintCert or an
    47  // error. The lint certificate is linted prior to being returned. The public key
    48  // from the just issued lint certificate is checked by the GoodKey package.
    49  func issueLintCertAndPerformLinting(tbs, issuer *x509.Certificate, subjectPubKey crypto.PublicKey, signer crypto.Signer, skipLints []string) (lintCert, error) {
    50  	bytes, err := linter.Check(tbs, subjectPubKey, issuer, signer, skipLints)
    51  	if err != nil {
    52  		return nil, fmt.Errorf("certificate failed pre-issuance lint: %w", err)
    53  	}
    54  	lc, err := x509.ParseCertificate(bytes)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	err = kp.GoodKey(context.Background(), lc.PublicKey)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	return lc, nil
    64  }
    65  
    66  // postIssuanceLinting performs post-issuance linting on the raw bytes of a
    67  // given certificate with the same set of lints as
    68  // issueLintCertAndPerformLinting. The public key is also checked by the GoodKey
    69  // package.
    70  func postIssuanceLinting(fc *x509.Certificate, skipLints []string) error {
    71  	if fc == nil {
    72  		return fmt.Errorf("certificate was not provided")
    73  	}
    74  	parsed, err := zlintx509.ParseCertificate(fc.Raw)
    75  	if err != nil {
    76  		// If zlintx509.ParseCertificate fails, the certificate is too broken to
    77  		// lint. This should be treated as ZLint rejecting the certificate
    78  		return fmt.Errorf("unable to parse certificate: %s", err)
    79  	}
    80  	registry, err := linter.NewRegistry(skipLints)
    81  	if err != nil {
    82  		return fmt.Errorf("unable to create zlint registry: %s", err)
    83  	}
    84  	lintRes := zlint.LintCertificateEx(parsed, registry)
    85  	err = linter.ProcessResultSet(lintRes)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	err = kp.GoodKey(context.Background(), fc.PublicKey)
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	return nil
    95  }
    96  
    97  type keyGenConfig struct {
    98  	Type         string `yaml:"type"`
    99  	RSAModLength int    `yaml:"rsa-mod-length"`
   100  	ECDSACurve   string `yaml:"ecdsa-curve"`
   101  }
   102  
   103  var allowedCurves = map[string]bool{
   104  	"P-256": true,
   105  	"P-384": true,
   106  	"P-521": true,
   107  }
   108  
   109  func (kgc keyGenConfig) validate() error {
   110  	if kgc.Type == "" {
   111  		return errors.New("key.type is required")
   112  	}
   113  	if kgc.Type != "rsa" && kgc.Type != "ecdsa" {
   114  		return errors.New("key.type can only be 'rsa' or 'ecdsa'")
   115  	}
   116  	if kgc.Type == "rsa" && (kgc.RSAModLength != 2048 && kgc.RSAModLength != 4096) {
   117  		return errors.New("key.rsa-mod-length can only be 2048 or 4096")
   118  	}
   119  	if kgc.Type == "rsa" && kgc.ECDSACurve != "" {
   120  		return errors.New("if key.type = 'rsa' then key.ecdsa-curve is not used")
   121  	}
   122  	if kgc.Type == "ecdsa" && !allowedCurves[kgc.ECDSACurve] {
   123  		return errors.New("key.ecdsa-curve can only be 'P-256', 'P-384', or 'P-521'")
   124  	}
   125  	if kgc.Type == "ecdsa" && kgc.RSAModLength != 0 {
   126  		return errors.New("if key.type = 'ecdsa' then key.rsa-mod-length is not used")
   127  	}
   128  
   129  	return nil
   130  }
   131  
   132  type PKCS11KeyGenConfig struct {
   133  	Module     string `yaml:"module"`
   134  	PIN        string `yaml:"pin"`
   135  	StoreSlot  uint   `yaml:"store-key-in-slot"`
   136  	StoreLabel string `yaml:"store-key-with-label"`
   137  }
   138  
   139  func (pkgc PKCS11KeyGenConfig) validate() error {
   140  	if pkgc.Module == "" {
   141  		return errors.New("pkcs11.module is required")
   142  	}
   143  	if pkgc.StoreLabel == "" {
   144  		return errors.New("pkcs11.store-key-with-label is required")
   145  	}
   146  	// key-slot is allowed to be 0 (which is a valid slot).
   147  	// PIN is allowed to be "", which will commonly happen when
   148  	// PIN entry is done via PED.
   149  	return nil
   150  }
   151  
   152  // checkOutputFile returns an error if the filename is empty,
   153  // or if a file already exists with that filename.
   154  func checkOutputFile(filename, fieldname string) error {
   155  	if filename == "" {
   156  		return fmt.Errorf("outputs.%s is required", fieldname)
   157  	}
   158  	if _, err := os.Stat(filename); !os.IsNotExist(err) {
   159  		return fmt.Errorf("outputs.%s is %q, which already exists",
   160  			fieldname, filename)
   161  	}
   162  
   163  	return nil
   164  }
   165  
   166  type rootConfig struct {
   167  	CeremonyType string             `yaml:"ceremony-type"`
   168  	PKCS11       PKCS11KeyGenConfig `yaml:"pkcs11"`
   169  	Key          keyGenConfig       `yaml:"key"`
   170  	Outputs      struct {
   171  		PublicKeyPath   string `yaml:"public-key-path"`
   172  		CertificatePath string `yaml:"certificate-path"`
   173  	} `yaml:"outputs"`
   174  	CertProfile certProfile `yaml:"certificate-profile"`
   175  	SkipLints   []string    `yaml:"skip-lints"`
   176  }
   177  
   178  func (rc rootConfig) validate() error {
   179  	err := rc.PKCS11.validate()
   180  	if err != nil {
   181  		return err
   182  	}
   183  
   184  	// Key gen fields
   185  	err = rc.Key.validate()
   186  	if err != nil {
   187  		return err
   188  	}
   189  
   190  	// Output fields
   191  	err = checkOutputFile(rc.Outputs.PublicKeyPath, "public-key-path")
   192  	if err != nil {
   193  		return err
   194  	}
   195  	err = checkOutputFile(rc.Outputs.CertificatePath, "certificate-path")
   196  	if err != nil {
   197  		return err
   198  	}
   199  
   200  	// Certificate profile
   201  	err = rc.CertProfile.verifyProfile(rootCert)
   202  	if err != nil {
   203  		return err
   204  	}
   205  
   206  	return nil
   207  }
   208  
   209  type PKCS11SigningConfig struct {
   210  	Module       string `yaml:"module"`
   211  	PIN          string `yaml:"pin"`
   212  	SigningSlot  uint   `yaml:"signing-key-slot"`
   213  	SigningLabel string `yaml:"signing-key-label"`
   214  }
   215  
   216  func (psc PKCS11SigningConfig) validate() error {
   217  	if psc.Module == "" {
   218  		return errors.New("pkcs11.module is required")
   219  	}
   220  	if psc.SigningLabel == "" {
   221  		return errors.New("pkcs11.signing-key-label is required")
   222  	}
   223  	// key-slot is allowed to be 0 (which is a valid slot).
   224  	return nil
   225  }
   226  
   227  type intermediateConfig struct {
   228  	CeremonyType string              `yaml:"ceremony-type"`
   229  	PKCS11       PKCS11SigningConfig `yaml:"pkcs11"`
   230  	Inputs       struct {
   231  		PublicKeyPath         string `yaml:"public-key-path"`
   232  		IssuerCertificatePath string `yaml:"issuer-certificate-path"`
   233  	} `yaml:"inputs"`
   234  	Outputs struct {
   235  		CertificatePath string `yaml:"certificate-path"`
   236  	} `yaml:"outputs"`
   237  	CertProfile certProfile `yaml:"certificate-profile"`
   238  	SkipLints   []string    `yaml:"skip-lints"`
   239  }
   240  
   241  func (ic intermediateConfig) validate() error {
   242  	err := ic.PKCS11.validate()
   243  	if err != nil {
   244  		return err
   245  	}
   246  
   247  	// Input fields
   248  	if ic.Inputs.PublicKeyPath == "" {
   249  		return errors.New("inputs.public-key-path is required")
   250  	}
   251  	if ic.Inputs.IssuerCertificatePath == "" {
   252  		return errors.New("inputs.issuer-certificate is required")
   253  	}
   254  
   255  	// Output fields
   256  	err = checkOutputFile(ic.Outputs.CertificatePath, "certificate-path")
   257  	if err != nil {
   258  		return err
   259  	}
   260  
   261  	// Certificate profile
   262  	err = ic.CertProfile.verifyProfile(intermediateCert)
   263  	if err != nil {
   264  		return err
   265  	}
   266  
   267  	return nil
   268  }
   269  
   270  type crossCertConfig struct {
   271  	CeremonyType string              `yaml:"ceremony-type"`
   272  	PKCS11       PKCS11SigningConfig `yaml:"pkcs11"`
   273  	Inputs       struct {
   274  		PublicKeyPath              string `yaml:"public-key-path"`
   275  		IssuerCertificatePath      string `yaml:"issuer-certificate-path"`
   276  		CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"`
   277  	} `yaml:"inputs"`
   278  	Outputs struct {
   279  		CertificatePath string `yaml:"certificate-path"`
   280  	} `yaml:"outputs"`
   281  	CertProfile certProfile `yaml:"certificate-profile"`
   282  	SkipLints   []string    `yaml:"skip-lints"`
   283  }
   284  
   285  func (csc crossCertConfig) validate() error {
   286  	err := csc.PKCS11.validate()
   287  	if err != nil {
   288  		return err
   289  	}
   290  	if csc.Inputs.PublicKeyPath == "" {
   291  		return errors.New("inputs.public-key-path is required")
   292  	}
   293  	if csc.Inputs.IssuerCertificatePath == "" {
   294  		return errors.New("inputs.issuer-certificate is required")
   295  	}
   296  	if csc.Inputs.CertificateToCrossSignPath == "" {
   297  		return errors.New("inputs.certificate-to-cross-sign-path is required")
   298  	}
   299  	err = checkOutputFile(csc.Outputs.CertificatePath, "certificate-path")
   300  	if err != nil {
   301  		return err
   302  	}
   303  	err = csc.CertProfile.verifyProfile(crossCert)
   304  	if err != nil {
   305  		return err
   306  	}
   307  
   308  	return nil
   309  }
   310  
   311  type csrConfig struct {
   312  	CeremonyType string              `yaml:"ceremony-type"`
   313  	PKCS11       PKCS11SigningConfig `yaml:"pkcs11"`
   314  	Inputs       struct {
   315  		PublicKeyPath string `yaml:"public-key-path"`
   316  	} `yaml:"inputs"`
   317  	Outputs struct {
   318  		CSRPath string `yaml:"csr-path"`
   319  	} `yaml:"outputs"`
   320  	CertProfile certProfile `yaml:"certificate-profile"`
   321  }
   322  
   323  func (cc csrConfig) validate() error {
   324  	err := cc.PKCS11.validate()
   325  	if err != nil {
   326  		return err
   327  	}
   328  
   329  	// Input fields
   330  	if cc.Inputs.PublicKeyPath == "" {
   331  		return errors.New("inputs.public-key-path is required")
   332  	}
   333  
   334  	// Output fields
   335  	err = checkOutputFile(cc.Outputs.CSRPath, "csr-path")
   336  	if err != nil {
   337  		return err
   338  	}
   339  
   340  	// Certificate profile
   341  	err = cc.CertProfile.verifyProfile(requestCert)
   342  	if err != nil {
   343  		return err
   344  	}
   345  
   346  	return nil
   347  }
   348  
   349  type keyConfig struct {
   350  	CeremonyType string             `yaml:"ceremony-type"`
   351  	PKCS11       PKCS11KeyGenConfig `yaml:"pkcs11"`
   352  	Key          keyGenConfig       `yaml:"key"`
   353  	Outputs      struct {
   354  		PublicKeyPath    string `yaml:"public-key-path"`
   355  		PKCS11ConfigPath string `yaml:"pkcs11-config-path"`
   356  	} `yaml:"outputs"`
   357  }
   358  
   359  func (kc keyConfig) validate() error {
   360  	err := kc.PKCS11.validate()
   361  	if err != nil {
   362  		return err
   363  	}
   364  
   365  	// Key gen fields
   366  	err = kc.Key.validate()
   367  	if err != nil {
   368  		return err
   369  	}
   370  
   371  	// Output fields
   372  	err = checkOutputFile(kc.Outputs.PublicKeyPath, "public-key-path")
   373  	if err != nil {
   374  		return err
   375  	}
   376  
   377  	return nil
   378  }
   379  
   380  type crlConfig struct {
   381  	CeremonyType string              `yaml:"ceremony-type"`
   382  	PKCS11       PKCS11SigningConfig `yaml:"pkcs11"`
   383  	Inputs       struct {
   384  		IssuerCertificatePath string `yaml:"issuer-certificate-path"`
   385  	} `yaml:"inputs"`
   386  	Outputs struct {
   387  		CRLPath string `yaml:"crl-path"`
   388  	} `yaml:"outputs"`
   389  	CRLProfile struct {
   390  		ThisUpdate          string `yaml:"this-update"`
   391  		NextUpdate          string `yaml:"next-update"`
   392  		Number              int64  `yaml:"number"`
   393  		RevokedCertificates []struct {
   394  			CertificatePath  string `yaml:"certificate-path"`
   395  			RevocationDate   string `yaml:"revocation-date"`
   396  			RevocationReason string `yaml:"revocation-reason"`
   397  		} `yaml:"revoked-certificates"`
   398  	} `yaml:"crl-profile"`
   399  	SkipLints []string `yaml:"skip-lints"`
   400  }
   401  
   402  func (cc crlConfig) validate() error {
   403  	err := cc.PKCS11.validate()
   404  	if err != nil {
   405  		return err
   406  	}
   407  
   408  	// Input fields
   409  	if cc.Inputs.IssuerCertificatePath == "" {
   410  		return errors.New("inputs.issuer-certificate-path is required")
   411  	}
   412  
   413  	// Output fields
   414  	err = checkOutputFile(cc.Outputs.CRLPath, "crl-path")
   415  	if err != nil {
   416  		return err
   417  	}
   418  
   419  	// CRL profile fields
   420  	if cc.CRLProfile.ThisUpdate == "" {
   421  		return errors.New("crl-profile.this-update is required")
   422  	}
   423  	if cc.CRLProfile.NextUpdate == "" {
   424  		return errors.New("crl-profile.next-update is required")
   425  	}
   426  	if cc.CRLProfile.Number == 0 {
   427  		return errors.New("crl-profile.number must be non-zero")
   428  	}
   429  	for _, rc := range cc.CRLProfile.RevokedCertificates {
   430  		if rc.CertificatePath == "" {
   431  			return errors.New("crl-profile.revoked-certificates.certificate-path is required")
   432  		}
   433  		if rc.RevocationDate == "" {
   434  			return errors.New("crl-profile.revoked-certificates.revocation-date is required")
   435  		}
   436  		if rc.RevocationReason == "" {
   437  			return errors.New("crl-profile.revoked-certificates.revocation-reason is required")
   438  		}
   439  	}
   440  
   441  	return nil
   442  }
   443  
   444  // loadCert loads a PEM certificate specified by filename or returns an error.
   445  // The public key from the loaded certificate is checked by the GoodKey package.
   446  func loadCert(filename string) (*x509.Certificate, error) {
   447  	certPEM, err := os.ReadFile(filename)
   448  	if err != nil {
   449  		return nil, err
   450  	}
   451  	log.Printf("Loaded certificate from %s\n", filename)
   452  	block, _ := pem.Decode(certPEM)
   453  	if block == nil {
   454  		return nil, fmt.Errorf("no data in cert PEM file %q", filename)
   455  	}
   456  	cert, err := x509.ParseCertificate(block.Bytes)
   457  	if err != nil {
   458  		return nil, err
   459  	}
   460  	goodkeyErr := kp.GoodKey(context.Background(), cert.PublicKey)
   461  	if goodkeyErr != nil {
   462  		return nil, goodkeyErr
   463  	}
   464  
   465  	return cert, nil
   466  }
   467  
   468  // publicKeysEqual determines whether two public keys are identical.
   469  func publicKeysEqual(a, b crypto.PublicKey) (bool, error) {
   470  	switch ak := a.(type) {
   471  	case *rsa.PublicKey:
   472  		return ak.Equal(b), nil
   473  	case *ecdsa.PublicKey:
   474  		return ak.Equal(b), nil
   475  	default:
   476  		return false, fmt.Errorf("unsupported public key type %T", ak)
   477  	}
   478  }
   479  
   480  func openSigner(cfg PKCS11SigningConfig, pubKey crypto.PublicKey) (crypto.Signer, *hsmRandReader, error) {
   481  	session, err := pkcs11helpers.Initialize(cfg.Module, cfg.SigningSlot, cfg.PIN)
   482  	if err != nil {
   483  		return nil, nil, fmt.Errorf("failed to setup session and PKCS#11 context for slot %d: %s",
   484  			cfg.SigningSlot, err)
   485  	}
   486  	log.Printf("Opened PKCS#11 session for slot %d\n", cfg.SigningSlot)
   487  	signer, err := session.NewSigner(cfg.SigningLabel, pubKey)
   488  	if err != nil {
   489  		return nil, nil, fmt.Errorf("failed to retrieve private key handle: %s", err)
   490  	}
   491  	ok, err := publicKeysEqual(signer.Public(), pubKey)
   492  	if !ok {
   493  		return nil, nil, err
   494  	}
   495  
   496  	return signer, newRandReader(session), nil
   497  }
   498  
   499  func signAndWriteCert(tbs, issuer *x509.Certificate, lintCert lintCert, subjectPubKey crypto.PublicKey, signer crypto.Signer, certPath string) (*x509.Certificate, error) {
   500  	if lintCert == nil {
   501  		return nil, fmt.Errorf("linting was not performed prior to issuance")
   502  	}
   503  	// x509.CreateCertificate uses a io.Reader here for signing methods that require
   504  	// a source of randomness. Since PKCS#11 based signing generates needed randomness
   505  	// at the HSM we don't need to pass a real reader. Instead of passing a nil reader
   506  	// we use one that always returns errors in case the internal usage of this reader
   507  	// changes.
   508  	certBytes, err := x509.CreateCertificate(&failReader{}, tbs, issuer, subjectPubKey, signer)
   509  	if err != nil {
   510  		return nil, fmt.Errorf("failed to create certificate: %s", err)
   511  	}
   512  	pemBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes})
   513  	log.Printf("Signed certificate PEM:\n%s", pemBytes)
   514  	cert, err := x509.ParseCertificate(certBytes)
   515  	if err != nil {
   516  		return nil, fmt.Errorf("failed to parse signed certificate: %s", err)
   517  	}
   518  	if tbs == issuer {
   519  		// If cert is self-signed we need to populate the issuer subject key to
   520  		// verify the signature
   521  		issuer.PublicKey = cert.PublicKey
   522  		issuer.PublicKeyAlgorithm = cert.PublicKeyAlgorithm
   523  	}
   524  	err = cert.CheckSignatureFrom(issuer)
   525  	if err != nil {
   526  		return nil, fmt.Errorf("failed to verify certificate signature: %s", err)
   527  	}
   528  	err = writeFile(certPath, pemBytes)
   529  	if err != nil {
   530  		return nil, fmt.Errorf("failed to write certificate to %q: %s", certPath, err)
   531  	}
   532  	log.Printf("Certificate written to %q\n", certPath)
   533  
   534  	return cert, nil
   535  }
   536  
   537  // loadPubKey loads a PEM public key specified by filename. It returns a
   538  // crypto.PublicKey, the PEM bytes of the public key, and an error. If an error
   539  // exists, no public key or bytes are returned. The public key is checked by the
   540  // GoodKey package.
   541  func loadPubKey(filename string) (crypto.PublicKey, []byte, error) {
   542  	keyPEM, err := os.ReadFile(filename)
   543  	if err != nil {
   544  		return nil, nil, err
   545  	}
   546  	log.Printf("Loaded public key from %s\n", filename)
   547  	block, _ := pem.Decode(keyPEM)
   548  	if block == nil {
   549  		return nil, nil, fmt.Errorf("no data in cert PEM file %q", filename)
   550  	}
   551  	key, err := x509.ParsePKIXPublicKey(block.Bytes)
   552  	if err != nil {
   553  		return nil, nil, err
   554  	}
   555  	err = kp.GoodKey(context.Background(), key)
   556  	if err != nil {
   557  		return nil, nil, err
   558  	}
   559  
   560  	return key, block.Bytes, nil
   561  }
   562  
   563  func rootCeremony(configBytes []byte) error {
   564  	var config rootConfig
   565  	err := strictyaml.Unmarshal(configBytes, &config)
   566  	if err != nil {
   567  		return fmt.Errorf("failed to parse config: %s", err)
   568  	}
   569  	log.Printf("Preparing root ceremony for %s\n", config.Outputs.CertificatePath)
   570  	err = config.validate()
   571  	if err != nil {
   572  		return fmt.Errorf("failed to validate config: %s", err)
   573  	}
   574  	session, err := pkcs11helpers.Initialize(config.PKCS11.Module, config.PKCS11.StoreSlot, config.PKCS11.PIN)
   575  	if err != nil {
   576  		return fmt.Errorf("failed to setup session and PKCS#11 context for slot %d: %s", config.PKCS11.StoreSlot, err)
   577  	}
   578  	log.Printf("Opened PKCS#11 session for slot %d\n", config.PKCS11.StoreSlot)
   579  	keyInfo, err := generateKey(session, config.PKCS11.StoreLabel, config.Outputs.PublicKeyPath, config.Key)
   580  	if err != nil {
   581  		return err
   582  	}
   583  	signer, err := session.NewSigner(config.PKCS11.StoreLabel, keyInfo.key)
   584  	if err != nil {
   585  		return fmt.Errorf("failed to retrieve signer: %s", err)
   586  	}
   587  	template, err := makeTemplate(newRandReader(session), &config.CertProfile, keyInfo.der, nil, rootCert)
   588  	if err != nil {
   589  		return fmt.Errorf("failed to create certificate profile: %s", err)
   590  	}
   591  	lintCert, err := issueLintCertAndPerformLinting(template, template, keyInfo.key, signer, config.SkipLints)
   592  	if err != nil {
   593  		return err
   594  	}
   595  	finalCert, err := signAndWriteCert(template, template, lintCert, keyInfo.key, signer, config.Outputs.CertificatePath)
   596  	if err != nil {
   597  		return err
   598  	}
   599  	err = postIssuanceLinting(finalCert, config.SkipLints)
   600  	if err != nil {
   601  		return err
   602  	}
   603  	log.Printf("Post issuance linting completed for %s\n", config.Outputs.CertificatePath)
   604  
   605  	return nil
   606  }
   607  
   608  func intermediateCeremony(configBytes []byte) error {
   609  	var config intermediateConfig
   610  	err := strictyaml.Unmarshal(configBytes, &config)
   611  	if err != nil {
   612  		return fmt.Errorf("failed to parse config: %s", err)
   613  	}
   614  	log.Printf("Preparing intermediate ceremony for %s\n", config.Outputs.CertificatePath)
   615  	err = config.validate()
   616  	if err != nil {
   617  		return fmt.Errorf("failed to validate config: %s", err)
   618  	}
   619  	pub, pubBytes, err := loadPubKey(config.Inputs.PublicKeyPath)
   620  	if err != nil {
   621  		return err
   622  	}
   623  	issuer, err := loadCert(config.Inputs.IssuerCertificatePath)
   624  	if err != nil {
   625  		return fmt.Errorf("failed to load issuer certificate %q: %s", config.Inputs.IssuerCertificatePath, err)
   626  	}
   627  	signer, randReader, err := openSigner(config.PKCS11, issuer.PublicKey)
   628  	if err != nil {
   629  		return err
   630  	}
   631  	template, err := makeTemplate(randReader, &config.CertProfile, pubBytes, nil, intermediateCert)
   632  	if err != nil {
   633  		return fmt.Errorf("failed to create certificate profile: %s", err)
   634  	}
   635  	template.AuthorityKeyId = issuer.SubjectKeyId
   636  	lintCert, err := issueLintCertAndPerformLinting(template, issuer, pub, signer, config.SkipLints)
   637  	if err != nil {
   638  		return err
   639  	}
   640  	finalCert, err := signAndWriteCert(template, issuer, lintCert, pub, signer, config.Outputs.CertificatePath)
   641  	if err != nil {
   642  		return err
   643  	}
   644  	// Verify that x509.CreateCertificate is deterministic and produced
   645  	// identical DER bytes between the lintCert and finalCert signing
   646  	// operations. If this fails it's mississuance, but it's better to know
   647  	// about the problem sooner than later.
   648  	if !bytes.Equal(lintCert.RawTBSCertificate, finalCert.RawTBSCertificate) {
   649  		return fmt.Errorf("mismatch between lintCert and finalCert RawTBSCertificate DER bytes: \"%x\" != \"%x\"", lintCert.RawTBSCertificate, finalCert.RawTBSCertificate)
   650  	}
   651  	err = postIssuanceLinting(finalCert, config.SkipLints)
   652  	if err != nil {
   653  		return err
   654  	}
   655  	log.Printf("Post issuance linting completed for %s\n", config.Outputs.CertificatePath)
   656  
   657  	return nil
   658  }
   659  
   660  func crossCertCeremony(configBytes []byte) error {
   661  	var config crossCertConfig
   662  	err := strictyaml.Unmarshal(configBytes, &config)
   663  	if err != nil {
   664  		return fmt.Errorf("failed to parse config: %s", err)
   665  	}
   666  	log.Printf("Preparing cross-certificate ceremony for %s\n", config.Outputs.CertificatePath)
   667  	err = config.validate()
   668  	if err != nil {
   669  		return fmt.Errorf("failed to validate config: %s", err)
   670  	}
   671  	pub, pubBytes, err := loadPubKey(config.Inputs.PublicKeyPath)
   672  	if err != nil {
   673  		return err
   674  	}
   675  	issuer, err := loadCert(config.Inputs.IssuerCertificatePath)
   676  	if err != nil {
   677  		return fmt.Errorf("failed to load issuer certificate %q: %s", config.Inputs.IssuerCertificatePath, err)
   678  	}
   679  	toBeCrossSigned, err := loadCert(config.Inputs.CertificateToCrossSignPath)
   680  	if err != nil {
   681  		return fmt.Errorf("failed to load toBeCrossSigned certificate %q: %s", config.Inputs.CertificateToCrossSignPath, err)
   682  	}
   683  	signer, randReader, err := openSigner(config.PKCS11, issuer.PublicKey)
   684  	if err != nil {
   685  		return err
   686  	}
   687  	template, err := makeTemplate(randReader, &config.CertProfile, pubBytes, toBeCrossSigned, crossCert)
   688  	if err != nil {
   689  		return fmt.Errorf("failed to create certificate profile: %s", err)
   690  	}
   691  	template.AuthorityKeyId = issuer.SubjectKeyId
   692  	lintCert, err := issueLintCertAndPerformLinting(template, issuer, pub, signer, config.SkipLints)
   693  	if err != nil {
   694  		return err
   695  	}
   696  	// Ensure that we've configured the correct certificate to cross-sign compared to the profile.
   697  	//
   698  	// Example of a misconfiguration below:
   699  	//      ...
   700  	//	 	inputs:
   701  	//  		certificate-to-cross-sign-path: int-e6.cert.pem
   702  	//		certificate-profile:
   703  	//  		common-name: (FAKE) E5
   704  	//  		organization: (FAKE) Let's Encrypt
   705  	//      ...
   706  	//
   707  	if !bytes.Equal(toBeCrossSigned.RawSubject, lintCert.RawSubject) {
   708  		return fmt.Errorf("mismatch between toBeCrossSigned and lintCert RawSubject DER bytes: \"%x\" != \"%x\"", toBeCrossSigned.RawSubject, lintCert.RawSubject)
   709  	}
   710  	// BR 7.1.2.2.1 Cross-Certified Subordinate CA Validity
   711  	// The earlier of one day prior to the time of signing or the earliest
   712  	// notBefore date of the existing CA Certificate(s).
   713  	if lintCert.NotBefore.Before(toBeCrossSigned.NotBefore) {
   714  		return fmt.Errorf("cross-signed subordinate CA's NotBefore predates the existing CA's NotBefore")
   715  	}
   716  	// BR 7.1.2.2.3 Cross-Certified Subordinate CA Extensions
   717  	// We want the Extended Key Usages of our cross-signs to be identical to those
   718  	// in the cert being cross-signed, for the sake of consistency. However, our
   719  	// Root CA Certificates do not contain any EKUs, as required by BR 7.1.2.1.2.
   720  	// Therefore, cross-signs of our roots count as "unrestricted" cross-signs per
   721  	// the definition in BR 7.1.2.2.3, and are subject to the requirement that
   722  	// the cross-sign's Issuer and Subject fields must either:
   723  	// - have identical organizationNames; or
   724  	// - have orgnaizationNames which are affiliates of each other.
   725  	// Therefore, we enforce that cross-signs with empty EKUs have identical
   726  	// Subject Organization Name fields... or allow one special case where the
   727  	// issuer is "Internet Security Research Group" and the subject is "ISRG" to
   728  	// allow us to migrate from the longer string to the shorter one.
   729  	if !slices.Equal(lintCert.ExtKeyUsage, toBeCrossSigned.ExtKeyUsage) {
   730  		return fmt.Errorf("lint cert and toBeCrossSigned cert EKUs differ")
   731  	}
   732  	if len(lintCert.ExtKeyUsage) == 0 {
   733  		if !slices.Equal(lintCert.Subject.Organization, issuer.Subject.Organization) &&
   734  			!(slices.Equal(issuer.Subject.Organization, []string{"Internet Security Research Group"}) && slices.Equal(lintCert.Subject.Organization, []string{"ISRG"})) {
   735  			return fmt.Errorf("attempted unrestricted cross-sign of certificate operated by a different organization")
   736  		}
   737  	}
   738  	// Issue the cross-signed certificate.
   739  	finalCert, err := signAndWriteCert(template, issuer, lintCert, pub, signer, config.Outputs.CertificatePath)
   740  	if err != nil {
   741  		return err
   742  	}
   743  	// Verify that x509.CreateCertificate is deterministic and produced
   744  	// identical DER bytes between the lintCert and finalCert signing
   745  	// operations. If this fails it's mississuance, but it's better to know
   746  	// about the problem sooner than later.
   747  	if !bytes.Equal(lintCert.RawTBSCertificate, finalCert.RawTBSCertificate) {
   748  		return fmt.Errorf("mismatch between lintCert and finalCert RawTBSCertificate DER bytes: \"%x\" != \"%x\"", lintCert.RawTBSCertificate, finalCert.RawTBSCertificate)
   749  	}
   750  	err = postIssuanceLinting(finalCert, config.SkipLints)
   751  	if err != nil {
   752  		return err
   753  	}
   754  	log.Printf("Post issuance linting completed for %s\n", config.Outputs.CertificatePath)
   755  
   756  	return nil
   757  }
   758  
   759  func csrCeremony(configBytes []byte) error {
   760  	var config csrConfig
   761  	err := strictyaml.Unmarshal(configBytes, &config)
   762  	if err != nil {
   763  		return fmt.Errorf("failed to parse config: %s", err)
   764  	}
   765  	err = config.validate()
   766  	if err != nil {
   767  		return fmt.Errorf("failed to validate config: %s", err)
   768  	}
   769  
   770  	pub, _, err := loadPubKey(config.Inputs.PublicKeyPath)
   771  	if err != nil {
   772  		return err
   773  	}
   774  
   775  	signer, _, err := openSigner(config.PKCS11, pub)
   776  	if err != nil {
   777  		return err
   778  	}
   779  
   780  	csrDER, err := generateCSR(&config.CertProfile, signer)
   781  	if err != nil {
   782  		return fmt.Errorf("failed to generate CSR: %s", err)
   783  	}
   784  	csrPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrDER})
   785  	err = writeFile(config.Outputs.CSRPath, csrPEM)
   786  	if err != nil {
   787  		return fmt.Errorf("failed to write CSR to %q: %s", config.Outputs.CSRPath, err)
   788  	}
   789  	log.Printf("CSR written to %q\n", config.Outputs.CSRPath)
   790  
   791  	return nil
   792  }
   793  
   794  func keyCeremony(configBytes []byte) error {
   795  	var config keyConfig
   796  	err := strictyaml.Unmarshal(configBytes, &config)
   797  	if err != nil {
   798  		return fmt.Errorf("failed to parse config: %s", err)
   799  	}
   800  	err = config.validate()
   801  	if err != nil {
   802  		return fmt.Errorf("failed to validate config: %s", err)
   803  	}
   804  	session, err := pkcs11helpers.Initialize(config.PKCS11.Module, config.PKCS11.StoreSlot, config.PKCS11.PIN)
   805  	if err != nil {
   806  		return fmt.Errorf("failed to setup session and PKCS#11 context for slot %d: %s", config.PKCS11.StoreSlot, err)
   807  	}
   808  	log.Printf("Opened PKCS#11 session for slot %d\n", config.PKCS11.StoreSlot)
   809  	if _, err = generateKey(session, config.PKCS11.StoreLabel, config.Outputs.PublicKeyPath, config.Key); err != nil {
   810  		return err
   811  	}
   812  
   813  	if config.Outputs.PKCS11ConfigPath != "" {
   814  		contents := fmt.Sprintf(
   815  			`{"module": %q, "tokenLabel": %q, "pin": %q}`,
   816  			config.PKCS11.Module, config.PKCS11.StoreLabel, config.PKCS11.PIN,
   817  		)
   818  		err = writeFile(config.Outputs.PKCS11ConfigPath, []byte(contents))
   819  		if err != nil {
   820  			return err
   821  		}
   822  	}
   823  
   824  	return nil
   825  }
   826  
   827  func crlCeremony(configBytes []byte) error {
   828  	var config crlConfig
   829  	err := strictyaml.Unmarshal(configBytes, &config)
   830  	if err != nil {
   831  		return fmt.Errorf("failed to parse config: %s", err)
   832  	}
   833  	err = config.validate()
   834  	if err != nil {
   835  		return fmt.Errorf("failed to validate config: %s", err)
   836  	}
   837  
   838  	issuer, err := loadCert(config.Inputs.IssuerCertificatePath)
   839  	if err != nil {
   840  		return fmt.Errorf("failed to load issuer certificate %q: %s", config.Inputs.IssuerCertificatePath, err)
   841  	}
   842  	signer, _, err := openSigner(config.PKCS11, issuer.PublicKey)
   843  	if err != nil {
   844  		return err
   845  	}
   846  
   847  	thisUpdate, err := time.Parse(time.DateTime, config.CRLProfile.ThisUpdate)
   848  	if err != nil {
   849  		return fmt.Errorf("unable to parse crl-profile.this-update: %s", err)
   850  	}
   851  	nextUpdate, err := time.Parse(time.DateTime, config.CRLProfile.NextUpdate)
   852  	if err != nil {
   853  		return fmt.Errorf("unable to parse crl-profile.next-update: %s", err)
   854  	}
   855  
   856  	var revokedCertificates []x509.RevocationListEntry
   857  	for _, rc := range config.CRLProfile.RevokedCertificates {
   858  		cert, err := loadCert(rc.CertificatePath)
   859  		if err != nil {
   860  			return fmt.Errorf("failed to load revoked certificate %q: %s", rc.CertificatePath, err)
   861  		}
   862  		if !cert.IsCA {
   863  			return fmt.Errorf("certificate with serial %d is not a CA certificate", cert.SerialNumber)
   864  		}
   865  		revokedAt, err := time.Parse(time.DateTime, rc.RevocationDate)
   866  		if err != nil {
   867  			return fmt.Errorf("unable to parse crl-profile.revoked-certificates.revocation-date")
   868  		}
   869  		revokedCert := x509.RevocationListEntry{
   870  			SerialNumber:   cert.SerialNumber,
   871  			RevocationTime: revokedAt,
   872  		}
   873  		reasonCode, err := revocation.StringToReason(rc.RevocationReason)
   874  		if err != nil {
   875  			return fmt.Errorf("looking up revocation reason: %w", err)
   876  		}
   877  		encReason, err := asn1.Marshal(reasonCode)
   878  		if err != nil {
   879  			return fmt.Errorf("failed to marshal revocation reason %d (%q): %s", reasonCode, rc.RevocationReason, err)
   880  		}
   881  		revokedCert.Extensions = []pkix.Extension{{
   882  			Id:    asn1.ObjectIdentifier{2, 5, 29, 21}, // id-ce-reasonCode
   883  			Value: encReason,
   884  		}}
   885  		revokedCertificates = append(revokedCertificates, revokedCert)
   886  	}
   887  
   888  	crlBytes, err := generateCRL(signer, issuer, thisUpdate, nextUpdate, config.CRLProfile.Number, revokedCertificates, config.SkipLints)
   889  	if err != nil {
   890  		return err
   891  	}
   892  
   893  	log.Printf("Signed CRL PEM:\n%s", crlBytes)
   894  
   895  	err = writeFile(config.Outputs.CRLPath, crlBytes)
   896  	if err != nil {
   897  		return fmt.Errorf("failed to write CRL to %q: %s", config.Outputs.CRLPath, err)
   898  	}
   899  
   900  	return nil
   901  }
   902  
   903  func main() {
   904  	configPath := flag.String("config", "", "Path to ceremony configuration file")
   905  	flag.Parse()
   906  
   907  	if *configPath == "" {
   908  		log.Fatal("--config is required")
   909  	}
   910  	configBytes, err := os.ReadFile(*configPath)
   911  	if err != nil {
   912  		log.Fatalf("Failed to read config file: %s", err)
   913  	}
   914  	var ct struct {
   915  		CeremonyType string `yaml:"ceremony-type"`
   916  	}
   917  
   918  	// We are intentionally using non-strict unmarshaling to read the top level
   919  	// tags to populate the "ct" struct for use in the switch statement below.
   920  	// Further strict processing of each yaml node is done on a case by case basis
   921  	// inside the switch statement.
   922  	err = yaml.Unmarshal(configBytes, &ct)
   923  	if err != nil {
   924  		log.Fatalf("Failed to parse config: %s", err)
   925  	}
   926  
   927  	switch ct.CeremonyType {
   928  	case "root":
   929  		err = rootCeremony(configBytes)
   930  		if err != nil {
   931  			log.Fatalf("root ceremony failed: %s", err)
   932  		}
   933  	case "cross-certificate":
   934  		err = crossCertCeremony(configBytes)
   935  		if err != nil {
   936  			log.Fatalf("cross-certificate ceremony failed: %s", err)
   937  		}
   938  	case "intermediate":
   939  		err = intermediateCeremony(configBytes)
   940  		if err != nil {
   941  			log.Fatalf("intermediate ceremony failed: %s", err)
   942  		}
   943  	case "cross-csr":
   944  		err = csrCeremony(configBytes)
   945  		if err != nil {
   946  			log.Fatalf("cross-csr ceremony failed: %s", err)
   947  		}
   948  	case "key":
   949  		err = keyCeremony(configBytes)
   950  		if err != nil {
   951  			log.Fatalf("key ceremony failed: %s", err)
   952  		}
   953  	case "crl":
   954  		err = crlCeremony(configBytes)
   955  		if err != nil {
   956  			log.Fatalf("crl ceremony failed: %s", err)
   957  		}
   958  	default:
   959  		log.Fatalf("unknown ceremony-type, must be one of: root, cross-certificate, intermediate, cross-csr, key, crl")
   960  	}
   961  }