173 lines
5.8 KiB
Go
173 lines
5.8 KiB
Go
// generate.go is a helper utility for integration tests.
|
|
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"regexp"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"github.com/letsencrypt/boulder/cmd"
|
|
blog "github.com/letsencrypt/boulder/log"
|
|
)
|
|
|
|
// createSlot initializes a SoftHSM slot and token. SoftHSM chooses the highest empty
|
|
// slot, initializes it, and then assigns it a new randomly chosen slot ID. Since we can't
|
|
// predict this ID we need to parse out the new ID so that we can use it in the ceremony
|
|
// configs.
|
|
func createSlot(label string) (string, error) {
|
|
output, err := exec.Command("softhsm2-util", "--init-token", "--free", "--label", label, "--pin", "1234", "--so-pin", "5678").CombinedOutput()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
re := regexp.MustCompile(`to slot (\d+)`)
|
|
matches := re.FindSubmatch(output)
|
|
if len(matches) != 2 {
|
|
return "", errors.New("unexpected number of slot matches")
|
|
}
|
|
return string(matches[1]), nil
|
|
}
|
|
|
|
// genKey is used to run a root key ceremony with a given config, replacing
|
|
// SlotID in the YAML with a specific slot ID.
|
|
func genKey(path string, inSlot string) error {
|
|
tmpPath, err := rewriteConfig(path, map[string]string{"SlotID": inSlot})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
output, err := exec.Command("./bin/ceremony", "-config", tmpPath).CombinedOutput()
|
|
if err != nil {
|
|
return fmt.Errorf("error running ceremony for %s: %s:\n%s", tmpPath, err, string(output))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// rewriteConfig creates a temporary config based on the template at path
|
|
// using the variables in rewrites.
|
|
func rewriteConfig(path string, rewrites map[string]string) (string, error) {
|
|
tmplBytes, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
tmp, err := os.CreateTemp(os.TempDir(), "ceremony-config")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer tmp.Close()
|
|
tmpl, err := template.New("config").Parse(string(tmplBytes))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
err = tmpl.Execute(tmp, rewrites)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return tmp.Name(), nil
|
|
}
|
|
|
|
// runCeremony is used to run a ceremony with a given config.
|
|
func runCeremony(path string) error {
|
|
output, err := exec.Command("./bin/ceremony", "-config", path).CombinedOutput()
|
|
if err != nil {
|
|
return fmt.Errorf("error running ceremony for %s: %s:\n%s", path, err, string(output))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
_ = blog.Set(blog.StdoutLogger(6))
|
|
defer cmd.AuditPanic()
|
|
|
|
// Create SoftHSM slots for the root signing keys
|
|
rsaRootKeySlot, err := createSlot("Root RSA")
|
|
cmd.FailOnError(err, "failed creating softhsm2 slot for RSA root key")
|
|
ecdsaRootKeySlot, err := createSlot("Root ECDSA")
|
|
cmd.FailOnError(err, "failed creating softhsm2 slot for ECDSA root key")
|
|
|
|
// Generate the root signing keys and certificates
|
|
err = genKey("test/certs/root-ceremony-rsa.yaml", rsaRootKeySlot)
|
|
cmd.FailOnError(err, "failed to generate RSA root key + root cert")
|
|
err = genKey("test/certs/root-ceremony-ecdsa.yaml", ecdsaRootKeySlot)
|
|
cmd.FailOnError(err, "failed to generate ECDSA root key + root cert")
|
|
|
|
// Do everything for all of the intermediates
|
|
for _, alg := range []string{"rsa", "ecdsa"} {
|
|
rootKeySlot := rsaRootKeySlot
|
|
if alg == "ecdsa" {
|
|
rootKeySlot = ecdsaRootKeySlot
|
|
}
|
|
|
|
for _, inst := range []string{"a", "b", "c"} {
|
|
name := fmt.Sprintf("int %s %s", alg, inst)
|
|
// Note: The file names produced by this script (as a combination of this
|
|
// line, and the rest of the file name as specified in the various yaml
|
|
// template files) are meaningful and are consumed by aia-test-srv. If
|
|
// you change the structure of these file names, you will need to change
|
|
// aia-test-srv as well to recognize and consume the resulting files.
|
|
fileName := strings.Replace(name, " ", "-", -1)
|
|
|
|
// Create SoftHSM slot
|
|
keySlot, err := createSlot(name)
|
|
cmd.FailOnError(err, "failed to create softhsm2 slot for intermediate key")
|
|
|
|
// Generate key
|
|
keyConfigTemplate := fmt.Sprintf("test/certs/intermediate-key-ceremony-%s.yaml", alg)
|
|
keyConfig, err := rewriteConfig(keyConfigTemplate, map[string]string{
|
|
"SlotID": keySlot,
|
|
"Label": name,
|
|
"FileName": fileName,
|
|
})
|
|
cmd.FailOnError(err, "failed to rewrite intermediate key ceremony config")
|
|
|
|
err = runCeremony(keyConfig)
|
|
cmd.FailOnError(err, "failed to generate intermediate key")
|
|
|
|
// Generate cert
|
|
certConfigTemplate := fmt.Sprintf("test/certs/intermediate-cert-ceremony-%s.yaml", alg)
|
|
certConfig, err := rewriteConfig(certConfigTemplate, map[string]string{
|
|
"SlotID": rootKeySlot,
|
|
"CommonName": name,
|
|
"FileName": fileName,
|
|
})
|
|
cmd.FailOnError(err, "failed to rewrite intermediate cert ceremony config")
|
|
|
|
err = runCeremony(certConfig)
|
|
cmd.FailOnError(err, "failed to generate intermediate cert")
|
|
|
|
// Generate cross-certs, if necessary
|
|
if alg == "rsa" {
|
|
continue
|
|
}
|
|
|
|
crossConfigTemplate := fmt.Sprintf("test/certs/intermediate-cert-ceremony-%s-cross.yaml", alg)
|
|
crossConfig, err := rewriteConfig(crossConfigTemplate, map[string]string{
|
|
"SlotID": rsaRootKeySlot,
|
|
"CommonName": name,
|
|
"FileName": fileName,
|
|
})
|
|
cmd.FailOnError(err, "failed to rewrite intermediate cross-cert ceremony config")
|
|
|
|
err = runCeremony(crossConfig)
|
|
cmd.FailOnError(err, "failed to generate intermediate cross-cert")
|
|
}
|
|
}
|
|
|
|
// Create CRLs stating that the intermediates are not revoked.
|
|
rsaTmpCRLConfig, err := rewriteConfig("test/certs/root-crl-rsa.yaml", map[string]string{
|
|
"SlotID": rsaRootKeySlot,
|
|
})
|
|
cmd.FailOnError(err, "failed to rewrite RSA root CRL config with key ID")
|
|
err = runCeremony(rsaTmpCRLConfig)
|
|
cmd.FailOnError(err, "failed to generate RSA root CRL")
|
|
|
|
ecdsaTmpCRLConfig, err := rewriteConfig("test/certs/root-crl-ecdsa.yaml", map[string]string{
|
|
"SlotID": ecdsaRootKeySlot,
|
|
})
|
|
cmd.FailOnError(err, "failed to rewrite ECDSA root CRL config with key ID")
|
|
err = runCeremony(ecdsaTmpCRLConfig)
|
|
cmd.FailOnError(err, "failed to generate ECDSA root CRL")
|
|
}
|