github.com/letsencrypt/boulder@v0.20251208.0/test/certs/webpki.go (about)

     1  // generate.go is a helper utility for integration tests.
     2  package main
     3  
     4  import (
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"os/exec"
     9  	"regexp"
    10  	"strings"
    11  	"text/template"
    12  
    13  	"github.com/letsencrypt/boulder/cmd"
    14  	blog "github.com/letsencrypt/boulder/log"
    15  )
    16  
    17  // createSlot initializes a SoftHSM slot and token. SoftHSM chooses the highest empty
    18  // slot, initializes it, and then assigns it a new randomly chosen slot ID. Since we can't
    19  // predict this ID we need to parse out the new ID so that we can use it in the ceremony
    20  // configs.
    21  func createSlot(label string) (string, error) {
    22  	output, err := exec.Command("softhsm2-util", "--init-token", "--free", "--label", label, "--pin", "1234", "--so-pin", "5678").CombinedOutput()
    23  	if err != nil {
    24  		return "", err
    25  	}
    26  	re := regexp.MustCompile(`to slot (\d+)`)
    27  	matches := re.FindSubmatch(output)
    28  	if len(matches) != 2 {
    29  		return "", errors.New("unexpected number of slot matches")
    30  	}
    31  	return string(matches[1]), nil
    32  }
    33  
    34  // genKey is used to run a root key ceremony with a given config, replacing
    35  // SlotID in the YAML with a specific slot ID.
    36  func genKey(path string, inSlot string) error {
    37  	tmpPath, err := rewriteConfig(path, map[string]string{"SlotID": inSlot})
    38  	if err != nil {
    39  		return err
    40  	}
    41  	output, err := exec.Command("./bin/ceremony", "-config", tmpPath).CombinedOutput()
    42  	if err != nil {
    43  		return fmt.Errorf("error running ceremony for %s: %s:\n%s", tmpPath, err, string(output))
    44  	}
    45  	return nil
    46  }
    47  
    48  // rewriteConfig creates a temporary config based on the template at path
    49  // using the variables in rewrites.
    50  func rewriteConfig(path string, rewrites map[string]string) (string, error) {
    51  	tmplBytes, err := os.ReadFile(path)
    52  	if err != nil {
    53  		return "", err
    54  	}
    55  	tmp, err := os.CreateTemp(os.TempDir(), "ceremony-config")
    56  	if err != nil {
    57  		return "", err
    58  	}
    59  	defer tmp.Close()
    60  	tmpl, err := template.New("config").Parse(string(tmplBytes))
    61  	if err != nil {
    62  		return "", err
    63  	}
    64  	err = tmpl.Execute(tmp, rewrites)
    65  	if err != nil {
    66  		return "", err
    67  	}
    68  	return tmp.Name(), nil
    69  }
    70  
    71  // runCeremony is used to run a ceremony with a given config.
    72  func runCeremony(path string) error {
    73  	output, err := exec.Command("./bin/ceremony", "-config", path).CombinedOutput()
    74  	if err != nil {
    75  		return fmt.Errorf("error running ceremony for %s: %s:\n%s", path, err, string(output))
    76  	}
    77  	return nil
    78  }
    79  
    80  func main() {
    81  	_ = blog.Set(blog.StdoutLogger(6))
    82  	defer cmd.AuditPanic()
    83  
    84  	// Create SoftHSM slots for the root signing keys
    85  	rsaRootKeySlot, err := createSlot("Root RSA")
    86  	cmd.FailOnError(err, "failed creating softhsm2 slot for RSA root key")
    87  	ecdsaRootKeySlot, err := createSlot("Root ECDSA")
    88  	cmd.FailOnError(err, "failed creating softhsm2 slot for ECDSA root key")
    89  
    90  	// Generate the root signing keys and certificates
    91  	err = genKey("test/certs/root-ceremony-rsa.yaml", rsaRootKeySlot)
    92  	cmd.FailOnError(err, "failed to generate RSA root key + root cert")
    93  	err = genKey("test/certs/root-ceremony-ecdsa.yaml", ecdsaRootKeySlot)
    94  	cmd.FailOnError(err, "failed to generate ECDSA root key + root cert")
    95  
    96  	// Do everything for all of the intermediates
    97  	for _, alg := range []string{"rsa", "ecdsa"} {
    98  		rootKeySlot := rsaRootKeySlot
    99  		if alg == "ecdsa" {
   100  			rootKeySlot = ecdsaRootKeySlot
   101  		}
   102  
   103  		for _, inst := range []string{"a", "b", "c"} {
   104  			name := fmt.Sprintf("int %s %s", alg, inst)
   105  			// Note: The file names produced by this script (as a combination of this
   106  			// line, and the rest of the file name as specified in the various yaml
   107  			// template files) are meaningful and are consumed by aia-test-srv. If
   108  			// you change the structure of these file names, you will need to change
   109  			// aia-test-srv as well to recognize and consume the resulting files.
   110  			fileName := strings.Replace(name, " ", "-", -1)
   111  
   112  			// Create SoftHSM slot
   113  			keySlot, err := createSlot(name)
   114  			cmd.FailOnError(err, "failed to create softhsm2 slot for intermediate key")
   115  
   116  			// Generate key
   117  			keyConfigTemplate := fmt.Sprintf("test/certs/intermediate-key-ceremony-%s.yaml", alg)
   118  			keyConfig, err := rewriteConfig(keyConfigTemplate, map[string]string{
   119  				"SlotID":   keySlot,
   120  				"Label":    name,
   121  				"FileName": fileName,
   122  			})
   123  			cmd.FailOnError(err, "failed to rewrite intermediate key ceremony config")
   124  
   125  			err = runCeremony(keyConfig)
   126  			cmd.FailOnError(err, "failed to generate intermediate key")
   127  
   128  			// Generate cert
   129  			certConfigTemplate := fmt.Sprintf("test/certs/intermediate-cert-ceremony-%s.yaml", alg)
   130  			certConfig, err := rewriteConfig(certConfigTemplate, map[string]string{
   131  				"SlotID":     rootKeySlot,
   132  				"CommonName": name,
   133  				"FileName":   fileName,
   134  			})
   135  			cmd.FailOnError(err, "failed to rewrite intermediate cert ceremony config")
   136  
   137  			err = runCeremony(certConfig)
   138  			cmd.FailOnError(err, "failed to generate intermediate cert")
   139  
   140  			// Generate cross-certs, if necessary
   141  			if alg == "rsa" {
   142  				continue
   143  			}
   144  
   145  			crossConfigTemplate := fmt.Sprintf("test/certs/intermediate-cert-ceremony-%s-cross.yaml", alg)
   146  			crossConfig, err := rewriteConfig(crossConfigTemplate, map[string]string{
   147  				"SlotID":     rsaRootKeySlot,
   148  				"CommonName": name,
   149  				"FileName":   fileName,
   150  			})
   151  			cmd.FailOnError(err, "failed to rewrite intermediate cross-cert ceremony config")
   152  
   153  			err = runCeremony(crossConfig)
   154  			cmd.FailOnError(err, "failed to generate intermediate cross-cert")
   155  		}
   156  	}
   157  
   158  	// Create CRLs stating that the intermediates are not revoked.
   159  	rsaTmpCRLConfig, err := rewriteConfig("test/certs/root-crl-rsa.yaml", map[string]string{
   160  		"SlotID": rsaRootKeySlot,
   161  	})
   162  	cmd.FailOnError(err, "failed to rewrite RSA root CRL config with key ID")
   163  	err = runCeremony(rsaTmpCRLConfig)
   164  	cmd.FailOnError(err, "failed to generate RSA root CRL")
   165  
   166  	ecdsaTmpCRLConfig, err := rewriteConfig("test/certs/root-crl-ecdsa.yaml", map[string]string{
   167  		"SlotID": ecdsaRootKeySlot,
   168  	})
   169  	cmd.FailOnError(err, "failed to rewrite ECDSA root CRL config with key ID")
   170  	err = runCeremony(ecdsaTmpCRLConfig)
   171  	cmd.FailOnError(err, "failed to generate ECDSA root CRL")
   172  }