diff --git a/man/bootctl.xml b/man/bootctl.xml
index eab18f7575a..3159f42347d 100644
--- a/man/bootctl.xml
+++ b/man/bootctl.xml
@@ -529,8 +529,9 @@
-
+
+
Configure the ESP for secure boot auto-enrollment when invoking the
install command. Takes a boolean argument. Disabled by default. Enabling this
@@ -542,9 +543,12 @@
When specifying this option, a certificate and private key have to be provided as well using
the and options. The
- option takes a path to a PEM encoded X.509 certificate. The
- option can take a path or a URI that will be passed to the OpenSSL
- engine or provider, as specified by as a
+ option takes a path to a PEM encoded X.509 certificate or a URI
+ that's passed to the OpenSSL provider configured with which
+ takes one of file or provider, with the latter being followed
+ by a specific provider identifier, separated with a colon, e.g. provider:pkcs11.
+ The option can take a path or a URI that will be passed to the
+ OpenSSL engine or provider, as specified by as a
type:name tuple, such as engine:pkcs11. The specified OpenSSL
signing engine or provider will be used to sign the EFI signature lists.
diff --git a/man/systemd-measure.xml b/man/systemd-measure.xml
index b82aabac049..5ca373f1814 100644
--- a/man/systemd-measure.xml
+++ b/man/systemd-measure.xml
@@ -104,6 +104,16 @@
+
+
+ pcrpkey
+
+ This commands prints the public key either given with ,
+ or extracted from the certificate given with or the private key given
+ with .
+
+
+
@@ -188,8 +198,9 @@
-
-
+
+
+
As an alternative to for the
sign command, these switches can be used to sign with an hardware token. The
@@ -197,6 +208,11 @@
provider, as specified by as a type:name tuple, such as
engine:pkcs11. The specified OpenSSL signing engine or provider will be used to sign.
+ The option also takes a path or a URI that will be passed to
+ the OpenSSL provider, as specified by as a
+ type:name tuple, such as provider:pkcs11. Note that unlike
+ this option only supports providers and not engines.
+
diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml
index 1e6ffaa70fb..575be14912d 100644
--- a/man/systemd-repart.xml
+++ b/man/systemd-repart.xml
@@ -348,9 +348,9 @@
- Takes a file system path. Configures the signing key to use when creating verity
- signature partitions with the Verity=signature setting in partition files.
-
+ Takes a file system path or an engine or provider specific designation. Configures
+ the signing key to use when creating verity signature partitions with the
+ Verity=signature setting in partition files.
@@ -361,7 +361,7 @@
Takes one of file, engine or
provider. In the latter two cases, it is followed by the name of a provider or
engine, separated by colon, that will be passed to OpenSSL's "engine" or "provider" logic.
- Configures the signing mechanism to use when creating verity signature partitions with the
+ Configures how to load the private key to use when creating verity signature partitions with the
Verity=signature setting in partition files.
@@ -370,13 +370,24 @@
- Takes a file system path. Configures the PEM encoded X.509 certificate to use when
- creating verity signature partitions with the Verity=signature setting in
- partition files.
+ Takes a file system path or a provider specific designation. Configures the PEM
+ encoded X.509 certificate to use when creating verity signature partitions with the
+ Verity=signature setting in partition files.
+
+
+
+ Takes one of file, or provider. In the latter
+ case, it is followed by the name of a provider, separated by colon, that will be passed to OpenSSL's
+ "provider" logic. Configures how to load the X.509 certificate to use when creating verity signature
+ partitions with the Verity=signature setting in partition files.
+
+
+
+
diff --git a/man/systemd-sbsign.xml b/man/systemd-sbsign.xml
index 1e42d601d67..1248377845f 100644
--- a/man/systemd-sbsign.xml
+++ b/man/systemd-sbsign.xml
@@ -85,11 +85,16 @@
+
Set the Secure Boot private key and certificate for use with the
sign. The option takes a path to a PEM encoded
- X.509 certificate. The option can take a path or a URI that will be
- passed to the OpenSSL engine or provider, as specified by as a
+ X.509 certificate or a URI that's passed to the OpenSSL provider configured with
+ . The takes one of
+ file or provider, with the latter being followed by a specific
+ provider identifier, separated with a colon, e.g. provider:pkcs11. The
+ option can take a path or a URI that will be passed to the OpenSSL
+ engine or provider, as specified by as a
type:name tuple, such as engine:pkcs11. The specified OpenSSL
signing engine or provider will be used to sign the PE binary.
diff --git a/man/ukify.xml b/man/ukify.xml
index 6a697ee6e14..c42d6ae5c75 100644
--- a/man/ukify.xml
+++ b/man/ukify.xml
@@ -527,6 +527,17 @@
+
+ CertificateProvider=PROVIDER
+
+
+ An OpenSSL provider to be used for loading the certificate used to sign the
+ resulting binary and PCR measurements. This option can only be used when using
+ systemd-sbsign as the signing tool.
+
+
+
+
SignKernel=BOOL
diff --git a/mkosi.conf.d/05-tools/mkosi.conf.d/arch.conf b/mkosi.conf.d/05-tools/mkosi.conf.d/arch.conf
index 5188d045089..81a6ee3c974 100644
--- a/mkosi.conf.d/05-tools/mkosi.conf.d/arch.conf
+++ b/mkosi.conf.d/05-tools/mkosi.conf.d/arch.conf
@@ -9,5 +9,6 @@ ToolsTreePackages=
libcap
libmicrohttpd
python-jinja
+ python-pytest
tpm2-tss
util-linux-libs
diff --git a/mkosi.conf.d/05-tools/mkosi.conf.d/centos-fedora.conf b/mkosi.conf.d/05-tools/mkosi.conf.d/centos-fedora.conf
index 349c87b6a02..be93f4c4776 100644
--- a/mkosi.conf.d/05-tools/mkosi.conf.d/centos-fedora.conf
+++ b/mkosi.conf.d/05-tools/mkosi.conf.d/centos-fedora.conf
@@ -15,3 +15,4 @@ ToolsTreePackages=
pkgconfig(mount)
tss2-devel
python3-jinja2
+ python3-pytest
diff --git a/mkosi.conf.d/05-tools/mkosi.conf.d/debian-ubuntu.conf b/mkosi.conf.d/05-tools/mkosi.conf.d/debian-ubuntu.conf
index 74cb1e90e6a..2550c0f96eb 100644
--- a/mkosi.conf.d/05-tools/mkosi.conf.d/debian-ubuntu.conf
+++ b/mkosi.conf.d/05-tools/mkosi.conf.d/debian-ubuntu.conf
@@ -15,3 +15,4 @@ ToolsTreePackages=
libmount-dev
libtss2-dev
python3-jinja2
+ python3-pytest
diff --git a/mkosi.conf.d/05-tools/mkosi.conf.d/opensuse.conf b/mkosi.conf.d/05-tools/mkosi.conf.d/opensuse.conf
index 2f4f7e1d606..09c279037bb 100644
--- a/mkosi.conf.d/05-tools/mkosi.conf.d/opensuse.conf
+++ b/mkosi.conf.d/05-tools/mkosi.conf.d/opensuse.conf
@@ -14,3 +14,4 @@ ToolsTreePackages=
pkgconfig(mount)
tss2-devel
python3-jinja2
+ python3-pytest
diff --git a/src/bootctl/bootctl-install.c b/src/bootctl/bootctl-install.c
index a9e2eea4869..26ee2865b2f 100644
--- a/src/bootctl/bootctl-install.c
+++ b/src/bootctl/bootctl-install.c
@@ -956,10 +956,26 @@ int verb_install(int argc, char *argv[], void *userdata) {
graceful = !install && arg_graceful; /* support graceful mode for updates */
if (arg_secure_boot_auto_enroll) {
- r = openssl_load_x509_certificate(arg_certificate, &certificate);
+ if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
+ r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
+ if (r < 0)
+ return r;
+ }
+
+ r = openssl_load_x509_certificate(
+ arg_certificate_source_type,
+ arg_certificate_source,
+ arg_certificate,
+ &certificate);
if (r < 0)
return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
+ if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
+ r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key);
+ }
+
r = openssl_load_private_key(
arg_private_key_source_type,
arg_private_key_source,
diff --git a/src/bootctl/bootctl.c b/src/bootctl/bootctl.c
index 23a3d2f9228..98721347f4e 100644
--- a/src/bootctl/bootctl.c
+++ b/src/bootctl/bootctl.c
@@ -64,6 +64,8 @@ ImagePolicy *arg_image_policy = NULL;
bool arg_varlink = false;
bool arg_secure_boot_auto_enroll = false;
char *arg_certificate = NULL;
+CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE;
+char *arg_certificate_source = NULL;
char *arg_private_key = NULL;
KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
char *arg_private_key_source = NULL;
@@ -77,6 +79,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_efi_boot_option_description, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
@@ -295,9 +298,14 @@ static int help(int argc, char *argv[], void *userdata) {
" Specify how to use KEY for --private-key=. Allows\n"
" an OpenSSL engine/provider to be used when setting\n"
" up secure boot auto-enrollment\n"
- " --certificate=PATH\n"
- " PEM certificate to use when setting up secure boot\n"
- " auto-enrollment\n"
+ " --certificate=PATH|URI\n"
+ " PEM certificate to use when setting up Secure Boot\n"
+ " auto-enrollment, or a provider specific designation\n"
+ " if --certificate-source= is used\n"
+ " --certificate-source=file|provider:PROVIDER\n"
+ " Specify how to interpret the certificate from\n"
+ " --certificate=. Allows the certificate to be loaded\n"
+ " from an OpenSSL provider\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
@@ -332,6 +340,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PRINT_STUB_PATH,
ARG_SECURE_BOOT_AUTO_ENROLL,
ARG_CERTIFICATE,
+ ARG_CERTIFICATE_SOURCE,
ARG_PRIVATE_KEY,
ARG_PRIVATE_KEY_SOURCE,
};
@@ -366,6 +375,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "dry-run", no_argument, NULL, ARG_DRY_RUN },
{ "secure-boot-auto-enroll", required_argument, NULL, ARG_SECURE_BOOT_AUTO_ENROLL },
{ "certificate", required_argument, NULL, ARG_CERTIFICATE },
+ { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE },
{ "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
{ "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE },
{}
@@ -526,12 +536,20 @@ static int parse_argv(int argc, char *argv[]) {
return r;
break;
- case ARG_CERTIFICATE: {
- r = parse_path_argument(optarg, /*suppress_root=*/ false, &arg_certificate);
+ case ARG_CERTIFICATE:
+ r = free_and_strdup_warn(&arg_certificate, optarg);
+ if (r < 0)
+ return r;
+ break;
+
+ case ARG_CERTIFICATE_SOURCE:
+ r = parse_openssl_certificate_source_argument(
+ optarg,
+ &arg_certificate_source,
+ &arg_certificate_source_type);
if (r < 0)
return r;
break;
- }
case ARG_PRIVATE_KEY: {
r = free_and_strdup_warn(&arg_private_key, optarg);
diff --git a/src/bootctl/bootctl.h b/src/bootctl/bootctl.h
index 8a67f5d8f88..6d0dfec47f0 100644
--- a/src/bootctl/bootctl.h
+++ b/src/bootctl/bootctl.h
@@ -41,6 +41,8 @@ extern ImagePolicy *arg_image_policy;
extern bool arg_varlink;
extern bool arg_secure_boot_auto_enroll;
extern char *arg_certificate;
+extern CertificateSourceType arg_certificate_source_type;
+extern char *arg_certificate_source;
extern char *arg_private_key;
extern KeySourceType arg_private_key_source_type;
extern char *arg_private_key_source;
diff --git a/src/measure/measure.c b/src/measure/measure.c
index eacf90f08c0..ac294d28b36 100644
--- a/src/measure/measure.c
+++ b/src/measure/measure.c
@@ -38,6 +38,8 @@ static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
static char *arg_private_key_source = NULL;
static char *arg_public_key = NULL;
static char *arg_certificate = NULL;
+static char *arg_certificate_source = NULL;
+static CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE;
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO|SD_JSON_FORMAT_OFF;
static PagerFlags arg_pager_flags = 0;
static bool arg_current = false;
@@ -50,6 +52,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
STATIC_DESTRUCTOR_REGISTER(arg_public_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep);
STATIC_DESTRUCTOR_REGISTER(arg_phase, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_append, freep);
@@ -74,6 +77,7 @@ static int help(int argc, char *argv[], void *userdata) {
" status Show current PCR values\n"
" calculate Calculate expected PCR values\n"
" sign Calculate and sign expected PCR values\n"
+ " pcrpkey Extract the PCR public key\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Print version\n"
@@ -87,7 +91,13 @@ static int help(int argc, char *argv[], void *userdata) {
" Specify how to use KEY for --private-key=. Allows\n"
" an OpenSSL engine/provider to be used for signing\n"
" --public-key=KEY Public key (PEM) to validate against\n"
- " --certificate=PATH PEM certificate to use when signing with a URI\n"
+ " --certificate=PATH|URI\n"
+ " PEM certificate to use for signing, or a provider\n"
+ " specific designation if --certificate-source= is used\n"
+ " --certificate-source=file|provider:PROVIDER\n"
+ " Specify how to interpret the certificate from\n"
+ " --certificate=. Allows the certificate to be loaded\n"
+ " from an OpenSSL provider\n"
" --json=MODE Output as JSON\n"
" -j Same as --json=pretty on tty, --json=short otherwise\n"
" --append=PATH Load specified JSON signature, and append new signature to it\n"
@@ -156,6 +166,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PRIVATE_KEY_SOURCE,
ARG_PUBLIC_KEY,
ARG_CERTIFICATE,
+ ARG_CERTIFICATE_SOURCE,
ARG_TPM2_DEVICE,
ARG_JSON,
ARG_PHASE,
@@ -186,6 +197,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE },
{ "public-key", required_argument, NULL, ARG_PUBLIC_KEY },
{ "certificate", required_argument, NULL, ARG_CERTIFICATE },
+ { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE },
{ "json", required_argument, NULL, ARG_JSON },
{ "phase", required_argument, NULL, ARG_PHASE },
{ "append", required_argument, NULL, ARG_APPEND },
@@ -265,10 +277,18 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_CERTIFICATE:
- r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_certificate);
+ r = free_and_strdup_warn(&arg_certificate, optarg);
if (r < 0)
return r;
+ break;
+ case ARG_CERTIFICATE_SOURCE:
+ r = parse_openssl_certificate_source_argument(
+ optarg,
+ &arg_certificate_source,
+ &arg_certificate_source_type);
+ if (r < 0)
+ return r;
break;
case ARG_TPM2_DEVICE: {
@@ -841,7 +861,17 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
/* This must be done before openssl_load_private_key() otherwise it will get stuck */
if (arg_certificate) {
- r = openssl_load_x509_certificate(arg_certificate, &certificate);
+ if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
+ r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
+ if (r < 0)
+ return r;
+ }
+
+ r = openssl_load_x509_certificate(
+ arg_certificate_source_type,
+ arg_certificate_source,
+ arg_certificate,
+ &certificate);
if (r < 0)
return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
}
@@ -1144,12 +1174,100 @@ static int verb_status(int argc, char *argv[], void *userdata) {
return 0;
}
+static int verb_pcrpkey(int argc, char *argv[], void *userdata) {
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *public_key = NULL;
+ int r;
+
+ if (arg_public_key) {
+ _cleanup_fclose_ FILE *public_keyf = NULL;
+
+ public_keyf = fopen(arg_public_key, "re");
+ if (!public_keyf)
+ return log_error_errno(errno, "Failed to open public key file '%s': %m", arg_public_key);
+
+ public_key = PEM_read_PUBKEY(public_keyf, NULL, NULL, NULL);
+ if (!public_key)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse public key '%s'.", arg_public_key);
+
+ } else if (arg_certificate) {
+ _cleanup_(X509_freep) X509 *certificate = NULL;
+
+ if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
+ r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
+ if (r < 0)
+ return r;
+ }
+
+ r = openssl_load_x509_certificate(
+ arg_certificate_source_type,
+ arg_certificate_source,
+ arg_certificate,
+ &certificate);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
+
+ public_key = X509_get_pubkey(certificate);
+ if (!public_key)
+ return log_error_errno(
+ SYNTHETIC_ERRNO(EIO),
+ "Failed to extract public key from certificate %s.",
+ arg_certificate);
+
+ } else if (arg_private_key) {
+ _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
+
+ if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
+ r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key);
+ }
+
+ r = openssl_load_private_key(
+ arg_private_key_source_type,
+ arg_private_key_source,
+ arg_private_key,
+ &(AskPasswordRequest) {
+ .id = "measure-private-key-pin",
+ .keyring = arg_private_key,
+ .credential = "measure.private-key-pin",
+ },
+ &private_key,
+ &ui);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
+
+ _cleanup_(memstream_done) MemStream m = {};
+ FILE *tf = memstream_init(&m);
+ if (!tf)
+ return log_oom();
+
+ if (i2d_PUBKEY_fp(tf, private_key) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to extract public key from private key file '%s'.", arg_private_key);
+
+ fflush(tf);
+ rewind(tf);
+
+ if (!d2i_PUBKEY_fp(tf, &public_key))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to parse extracted public key of private key file '%s'.", arg_private_key);
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "One of --public-key=, --certificate=, or --private-key= must be specified");
+
+ if (PEM_write_PUBKEY(stdout, public_key) == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write public key to stdout");
+
+ return 0;
+}
+
static int measure_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
{ "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
{ "calculate", VERB_ANY, 1, 0, verb_calculate },
{ "sign", VERB_ANY, 1, 0, verb_sign },
+ { "pcrpkey", VERB_ANY, 1, 0, verb_pcrpkey },
{}
};
diff --git a/src/partition/repart.c b/src/partition/repart.c
index be5171d5e18..7e6fd2a29a4 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -154,6 +154,8 @@ static char *arg_private_key = NULL;
static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
static char *arg_private_key_source = NULL;
static char *arg_certificate = NULL;
+static CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE;
+static char *arg_certificate_source = NULL;
static char *arg_tpm2_device = NULL;
static uint32_t arg_tpm2_seal_key_handle = 0;
static char *arg_tpm2_device_key = NULL;
@@ -186,6 +188,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep);
@@ -7808,8 +7811,14 @@ static int help(void) {
" Specify how to use KEY for --private-key=. Allows\n"
" an OpenSSL engine/provider to be used when generating\n"
" verity roothash signatures\n"
- " --certificate=PATH PEM certificate to use when generating verity\n"
- " roothash signatures\n"
+ " --certificate=PATH|URI\n"
+ " PEM certificate to use when generating verity roothash\n"
+ " signatures, or a provider specific designation if\n"
+ " --certificate-source= is used\n"
+ " --certificate-source=file|provider:PROVIDER\n"
+ " Specify how to interpret the certificate from\n"
+ " --certificate=. Allows the certificate to be loaded\n"
+ " from an OpenSSL provider\n"
"\n%3$sEncryption:%4$s\n"
" --key-file=PATH Key to use when encrypting partitions\n"
" --tpm2-device=PATH Path to TPM2 device node to use\n"
@@ -7878,6 +7887,7 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY *
ARG_PRIVATE_KEY,
ARG_PRIVATE_KEY_SOURCE,
ARG_CERTIFICATE,
+ ARG_CERTIFICATE_SOURCE,
ARG_TPM2_DEVICE,
ARG_TPM2_DEVICE_KEY,
ARG_TPM2_SEAL_KEY_HANDLE,
@@ -7922,6 +7932,7 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY *
{ "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
{ "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE },
{ "certificate", required_argument, NULL, ARG_CERTIFICATE },
+ { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE },
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
{ "tpm2-device-key", required_argument, NULL, ARG_TPM2_DEVICE_KEY },
{ "tpm2-seal-key-handle", required_argument, NULL, ARG_TPM2_SEAL_KEY_HANDLE },
@@ -8130,12 +8141,20 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY *
return r;
break;
- case ARG_CERTIFICATE: {
- r = parse_path_argument(optarg, /*suppress_root=*/ false, &arg_certificate);
+ case ARG_CERTIFICATE:
+ r = free_and_strdup_warn(&arg_certificate, optarg);
+ if (r < 0)
+ return r;
+ break;
+
+ case ARG_CERTIFICATE_SOURCE:
+ r = parse_openssl_certificate_source_argument(
+ optarg,
+ &arg_certificate_source,
+ &arg_certificate_source_type);
if (r < 0)
return r;
break;
- }
case ARG_TPM2_DEVICE: {
_cleanup_free_ char *device = NULL;
@@ -8468,7 +8487,17 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY *
}
if (arg_certificate) {
- r = openssl_load_x509_certificate(arg_certificate, &certificate);
+ if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
+ r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
+ if (r < 0)
+ return r;
+ }
+
+ r = openssl_load_x509_certificate(
+ arg_certificate_source_type,
+ arg_certificate_source,
+ arg_certificate,
+ &certificate);
if (r < 0)
return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
}
diff --git a/src/sbsign/sbsign.c b/src/sbsign/sbsign.c
index 961a514dac6..d65f28b4c4e 100644
--- a/src/sbsign/sbsign.c
+++ b/src/sbsign/sbsign.c
@@ -21,12 +21,15 @@
static PagerFlags arg_pager_flags = 0;
static char *arg_output = NULL;
static char *arg_certificate = NULL;
+static CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE;
+static char *arg_certificate_source = NULL;
static char *arg_private_key = NULL;
static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
static char *arg_private_key_source = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_output, freep);
STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
@@ -42,13 +45,19 @@ static int help(int argc, char *argv[], void *userdata) {
"\n%5$sSign binaries for EFI Secure Boot%6$s\n"
"\n%3$sCommands:%4$s\n"
" sign EXEFILE Sign the given binary for EFI Secure Boot\n"
- " validate-key Load and validate the given private key\n"
+ " validate-key Load and validate the given certificate and private key\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Print version\n"
" --no-pager Do not pipe output into a pager\n"
" --output Where to write the signed PE binary\n"
- " --certificate=PATH PEM certificate to use when signing with a URI\n"
+ " --certificate=PATH|URI\n"
+ " PEM certificate to use for signing, or a provider\n"
+ " specific designation if --certificate-source= is used\n"
+ " --certificate-source=file|provider:PROVIDER\n"
+ " Specify how to interpret the certificate from\n"
+ " --certificate=. Allows the certificate to be loaded\n"
+ " from an OpenSSL provider\n"
" --private-key=KEY Private key (PEM) to sign with\n"
" --private-key-source=file|provider:PROVIDER|engine:ENGINE\n"
" Specify how to use KEY for --private-key=. Allows\n"
@@ -70,6 +79,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_NO_PAGER,
ARG_OUTPUT,
ARG_CERTIFICATE,
+ ARG_CERTIFICATE_SOURCE,
ARG_PRIVATE_KEY,
ARG_PRIVATE_KEY_SOURCE,
};
@@ -80,6 +90,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "version", no_argument, NULL, ARG_VERSION },
{ "output", required_argument, NULL, ARG_OUTPUT },
{ "certificate", required_argument, NULL, ARG_CERTIFICATE },
+ { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE },
{ "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
{ "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE },
{}
@@ -112,10 +123,18 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_CERTIFICATE:
- r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_certificate);
+ r = free_and_strdup_warn(&arg_certificate, optarg);
if (r < 0)
return r;
+ break;
+ case ARG_CERTIFICATE_SOURCE:
+ r = parse_openssl_certificate_source_argument(
+ optarg,
+ &arg_certificate_source,
+ &arg_certificate_source_type);
+ if (r < 0)
+ return r;
break;
case ARG_PRIVATE_KEY:
@@ -168,7 +187,17 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
if (!arg_output)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No output specified, use --output=");
- r = openssl_load_x509_certificate(arg_certificate, &certificate);
+ if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
+ r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
+ if (r < 0)
+ return r;
+ }
+
+ r = openssl_load_x509_certificate(
+ arg_certificate_source_type,
+ arg_certificate_source,
+ arg_certificate,
+ &certificate);
if (r < 0)
return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
@@ -470,14 +499,33 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
}
static int verb_validate_key(int argc, char *argv[], void *userdata) {
+ _cleanup_(X509_freep) X509 *certificate = NULL;
_cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
_cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
int r;
+ if (!arg_certificate)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "No certificate specified, use --certificate=");
+
if (!arg_private_key)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"No private key specified, use --private-key=.");
+ if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) {
+ r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate);
+ if (r < 0)
+ return r;
+ }
+
+ r = openssl_load_x509_certificate(
+ arg_certificate_source_type,
+ arg_certificate_source,
+ arg_certificate,
+ &certificate);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
+
if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key);
if (r < 0)
diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c
index 5688d541141..417f8425468 100644
--- a/src/shared/openssl-util.c
+++ b/src/shared/openssl-util.c
@@ -1340,6 +1340,9 @@ static int load_key_from_provider(
if (!store)
return log_openssl_errors("Failed to open OpenSSL store via '%s'", private_key_uri);
+ if (OSSL_STORE_expect(store, OSSL_STORE_INFO_PKEY) == 0)
+ return log_openssl_errors("Failed to filter store by private keys");
+
_cleanup_(OSSL_STORE_INFO_freep) OSSL_STORE_INFO *info = OSSL_STORE_load(store);
if (!info)
return log_openssl_errors("Failed to load OpenSSL store via '%s'", private_key_uri);
@@ -1479,6 +1482,80 @@ static int openssl_ask_password_ui_new(const AskPasswordRequest *request, OpenSS
*ret = TAKE_PTR(ui);
return 0;
}
+
+static int load_x509_certificate_from_file(const char *path, X509 **ret) {
+ _cleanup_free_ char *rawcert = NULL;
+ _cleanup_(X509_freep) X509 *cert = NULL;
+ _cleanup_(BIO_freep) BIO *cb = NULL;
+ size_t rawcertsz;
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ r = read_full_file_full(
+ AT_FDCWD, path, UINT64_MAX, SIZE_MAX,
+ READ_FULL_FILE_CONNECT_SOCKET,
+ NULL,
+ &rawcert, &rawcertsz);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read certificate file '%s': %m", path);
+
+ cb = BIO_new_mem_buf(rawcert, rawcertsz);
+ if (!cb)
+ return log_oom_debug();
+
+ cert = PEM_read_bio_X509(cb, NULL, NULL, NULL);
+ if (!cert)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+
+ if (ret)
+ *ret = TAKE_PTR(cert);
+
+ return 0;
+}
+
+static int load_x509_certificate_from_provider(const char *provider, const char *certificate_uri, X509 **ret) {
+ assert(provider);
+ assert(certificate_uri);
+ assert(ret);
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ /* Load the provider so that this can work without any custom written configuration in /etc/.
+ * Also load the 'default' as that seems to be the recommendation. */
+ if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, provider, /* retain_fallbacks= */ true))
+ return log_openssl_errors("Failed to load OpenSSL provider '%s'", provider);
+ if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, "default", /* retain_fallbacks= */ true))
+ return log_openssl_errors("Failed to load OpenSSL provider 'default'");
+
+ _cleanup_(OSSL_STORE_closep) OSSL_STORE_CTX *store = OSSL_STORE_open(
+ certificate_uri,
+ /*ui_method=*/ NULL,
+ /*ui_method=*/ NULL,
+ /* post_process= */ NULL,
+ /* post_process_data= */ NULL);
+ if (!store)
+ return log_openssl_errors("Failed to open OpenSSL store via '%s'", certificate_uri);
+
+ if (OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT) == 0)
+ return log_openssl_errors("Failed to filter store by X.509 certificates");
+
+ _cleanup_(OSSL_STORE_INFO_freep) OSSL_STORE_INFO *info = OSSL_STORE_load(store);
+ if (!info)
+ return log_openssl_errors("Failed to load OpenSSL store via '%s'", certificate_uri);
+
+ _cleanup_(X509_freep) X509 *cert = OSSL_STORE_INFO_get1_CERT(info);
+ if (!cert)
+ return log_openssl_errors("Failed to load certificate via '%s'", certificate_uri);
+
+ *ret = TAKE_PTR(cert);
+
+ return 0;
+#else
+ return -EOPNOTSUPP;
+#endif
+}
#endif
OpenSSLAskPasswordUI* openssl_ask_password_ui_free(OpenSSLAskPasswordUI *ui) {
@@ -1514,36 +1591,33 @@ int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) {
#endif
}
-int openssl_load_x509_certificate(const char *path, X509 **ret) {
+int openssl_load_x509_certificate(
+ CertificateSourceType certificate_source_type,
+ const char *certificate_source,
+ const char *certificate,
+ X509 **ret) {
#if HAVE_OPENSSL
- _cleanup_free_ char *rawcert = NULL;
- _cleanup_(X509_freep) X509 *cert = NULL;
- _cleanup_(BIO_freep) BIO *cb = NULL;
- size_t rawcertsz;
int r;
- assert(path);
- assert(ret);
+ assert(certificate);
- r = read_full_file_full(
- AT_FDCWD, path, UINT64_MAX, SIZE_MAX,
- READ_FULL_FILE_CONNECT_SOCKET,
- NULL,
- &rawcert, &rawcertsz);
+ switch (certificate_source_type) {
+
+ case OPENSSL_CERTIFICATE_SOURCE_FILE:
+ r = load_x509_certificate_from_file(certificate, ret);
+ break;
+ case OPENSSL_CERTIFICATE_SOURCE_PROVIDER:
+ r = load_x509_certificate_from_provider(certificate_source, certificate, ret);
+ break;
+ default:
+ assert_not_reached();
+ }
if (r < 0)
- return log_debug_errno(r, "Failed to read certificate file '%s': %m", path);
-
- cb = BIO_new_mem_buf(rawcert, rawcertsz);
- if (!cb)
- return log_oom_debug();
-
- cert = PEM_read_bio_X509(cb, NULL, NULL, NULL);
- if (!cert)
- return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s",
- ERR_error_string(ERR_get_error(), NULL));
-
- if (ret)
- *ret = TAKE_PTR(cert);
+ return log_debug_errno(
+ r,
+ "Failed to load certificate '%s' from OpenSSL certificate source %s: %m",
+ certificate,
+ certificate_source);
return 0;
#else
@@ -1603,6 +1677,35 @@ int openssl_load_private_key(
#endif
}
+int parse_openssl_certificate_source_argument(
+ const char *argument,
+ char **certificate_source,
+ CertificateSourceType *certificate_source_type) {
+
+ CertificateSourceType type;
+ const char *e = NULL;
+ int r;
+
+ assert(argument);
+ assert(certificate_source);
+ assert(certificate_source_type);
+
+ if (streq(argument, "file"))
+ type = OPENSSL_CERTIFICATE_SOURCE_FILE;
+ else if ((e = startswith(argument, "provider:")))
+ type = OPENSSL_CERTIFICATE_SOURCE_PROVIDER;
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid certificate source '%s'", argument);
+
+ r = free_and_strdup_warn(certificate_source, e);
+ if (r < 0)
+ return r;
+
+ *certificate_source_type = type;
+
+ return 0;
+}
+
int parse_openssl_key_source_argument(
const char *argument,
char **private_key_source,
diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h
index 853aded2c74..7eb1ea15c0f 100644
--- a/src/shared/openssl-util.h
+++ b/src/shared/openssl-util.h
@@ -6,6 +6,13 @@
#include "macro.h"
#include "sha256.h"
+typedef enum CertificateSourceType {
+ OPENSSL_CERTIFICATE_SOURCE_FILE,
+ OPENSSL_CERTIFICATE_SOURCE_PROVIDER,
+ _OPENSSL_CERTIFICATE_SOURCE_MAX,
+ _OPENSSL_CERTIFICATE_SOURCE_INVALID = -EINVAL,
+} CertificateSourceType;
+
typedef enum KeySourceType {
OPENSSL_KEY_SOURCE_FILE,
OPENSSL_KEY_SOURCE_ENGINE,
@@ -16,6 +23,8 @@ typedef enum KeySourceType {
typedef struct OpenSSLAskPasswordUI OpenSSLAskPasswordUI;
+int parse_openssl_certificate_source_argument(const char *argument, char **certificate_source, CertificateSourceType *certificate_source_type);
+
int parse_openssl_key_source_argument(const char *argument, char **private_key_source, KeySourceType *private_key_source_type);
#define X509_FINGERPRINT_SIZE SHA256_DIGEST_SIZE
@@ -182,7 +191,11 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(OpenSSLAskPasswordUI*, openssl_ask_password_ui_
int x509_fingerprint(X509 *cert, uint8_t buffer[static X509_FINGERPRINT_SIZE]);
-int openssl_load_x509_certificate(const char *path, X509 **ret);
+int openssl_load_x509_certificate(
+ CertificateSourceType certificate_source_type,
+ const char *certificate_source,
+ const char *certificate,
+ X509 **ret);
int openssl_load_private_key(
KeySourceType private_key_source_type,
diff --git a/src/ukify/test/test_ukify.py b/src/ukify/test/test_ukify.py
index 70e1a4e1d0a..9eebf7eca1b 100755
--- a/src/ukify/test/test_ukify.py
+++ b/src/ukify/test/test_ukify.py
@@ -138,7 +138,7 @@ def test_apply_config(tmp_path):
assert ns._groups == ['NAME']
assert ns.pcr_private_keys == ['some/path7']
- assert ns.pcr_public_keys == [pathlib.Path('some/path8')]
+ assert ns.pcr_public_keys == ['some/path8']
assert ns.phase_path_groups == [['enter-initrd:leave-initrd:sysinit:ready:shutdown:final']]
ukify.finalize_options(ns)
@@ -156,12 +156,12 @@ def test_apply_config(tmp_path):
assert ns.pcr_banks == ['sha512', 'sha1']
assert ns.signing_engine == 'engine1'
assert ns.sb_key == 'some/path5'
- assert ns.sb_cert == 'some/path6'
+ assert ns.sb_cert == pathlib.Path('some/path6')
assert ns.sign_kernel is False
assert ns._groups == ['NAME']
assert ns.pcr_private_keys == ['some/path7']
- assert ns.pcr_public_keys == [pathlib.Path('some/path8')]
+ assert ns.pcr_public_keys == ['some/path8']
assert ns.phase_path_groups == [['enter-initrd:leave-initrd:sysinit:ready:shutdown:final']]
def test_parse_args_minimal():
@@ -207,11 +207,11 @@ def test_parse_args_many_deprecated():
assert opts.uname == '1.2.3'
assert opts.stub == pathlib.Path('STUBPATH')
assert opts.pcr_private_keys == ['PKEY1']
- assert opts.pcr_public_keys == [pathlib.Path('PKEY2')]
+ assert opts.pcr_public_keys == ['PKEY2']
assert opts.pcr_banks == ['SHA1', 'SHA256']
assert opts.signing_engine == 'ENGINE'
assert opts.sb_key == 'SBKEY'
- assert opts.sb_cert == 'SBCERT'
+ assert opts.sb_cert == pathlib.Path('SBCERT')
assert opts.sign_kernel is False
assert opts.tools == [pathlib.Path('TOOLZ/')]
assert opts.output == pathlib.Path('OUTPUT')
@@ -253,11 +253,11 @@ def test_parse_args_many():
assert opts.uname == '1.2.3'
assert opts.stub == pathlib.Path('STUBPATH')
assert opts.pcr_private_keys == ['PKEY1']
- assert opts.pcr_public_keys == [pathlib.Path('PKEY2')]
+ assert opts.pcr_public_keys == ['PKEY2']
assert opts.pcr_banks == ['SHA1', 'SHA256']
assert opts.signing_engine == 'ENGINE'
assert opts.sb_key == 'SBKEY'
- assert opts.sb_cert == 'SBCERT'
+ assert opts.sb_cert == pathlib.Path('SBCERT')
assert opts.sign_kernel is False
assert opts.tools == [pathlib.Path('TOOLZ/')]
assert opts.output == pathlib.Path('OUTPUT')
@@ -360,13 +360,12 @@ def test_config_priority(tmp_path):
assert opts.uname == '1.2.3'
assert opts.stub == pathlib.Path('STUBPATH')
assert opts.pcr_private_keys == ['PKEY1', 'some/path7']
- assert opts.pcr_public_keys == [pathlib.Path('PKEY2'),
- pathlib.Path('some/path8')]
+ assert opts.pcr_public_keys == ['PKEY2', 'some/path8']
assert opts.pcr_banks == ['SHA1', 'SHA256']
assert opts.signing_engine == 'ENGINE'
assert opts.signtool == ukify.SbSign # from args
assert opts.sb_key == 'SBKEY' # from args
- assert opts.sb_cert == 'SBCERT' # from args
+ assert opts.sb_cert == pathlib.Path('SBCERT') # from args
assert opts.sb_certdir == 'some/path5' # from config
assert opts.sb_cert_name == 'some/name1' # from config
assert opts.sign_kernel is False
diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py
index ef4e9264c28..355e3f99f41 100755
--- a/src/ukify/ukify.py
+++ b/src/ukify/ukify.py
@@ -249,21 +249,22 @@ class UkifyConfig:
output: Optional[str]
pcr_banks: list[str]
pcr_private_keys: list[str]
- pcr_public_keys: list[Path]
+ pcr_public_keys: list[str]
pcrpkey: Optional[Path]
phase_path_groups: Optional[list[str]]
profile: Union[str, Path, None]
- sb_cert: Path
+ sb_cert: Union[str, Path, None]
sb_cert_name: Optional[str]
sb_cert_validity: int
sb_certdir: Path
- sb_key: Optional[Path]
+ sb_key: Union[str, Path, None]
sbat: Optional[list[str]]
sections: list['Section']
sections_by_name: dict[str, 'Section']
sign_kernel: bool
signing_engine: Optional[str]
signing_provider: Optional[str]
+ certificate_provider: Optional[str]
signtool: Optional[type['SignTool']]
splash: Optional[Path]
stub: Path
@@ -554,6 +555,11 @@ class SystemdSbSign(SignTool):
if opts.signing_provider is not None
else []
),
+ *(
+ ['--certificate-source', f'provider:{opts.certificate_provider}']
+ if opts.certificate_provider is not None
+ else []
+ ),
input_f,
'--output', output_f,
] # fmt: skip
@@ -666,7 +672,7 @@ def combine_signatures(pcrsigs: list[dict[str, str]]) -> str:
return json.dumps(combined)
-def key_path_groups(opts: UkifyConfig) -> Iterator[tuple[str, Optional[Path], Optional[str]]]:
+def key_path_groups(opts: UkifyConfig) -> Iterator[tuple[str, Optional[str], Optional[str]]]:
if not opts.pcr_private_keys:
return
@@ -757,6 +763,10 @@ def call_systemd_measure(uki: UKI, opts: UkifyConfig, profile_start: int = 0) ->
extra += [f'--certificate={pub_key}']
elif pub_key:
extra += [f'--public-key={pub_key}']
+
+ if opts.certificate_provider is not None:
+ extra += [f'--certificate-source=provider:{opts.certificate_provider}']
+
extra += [f'--phase={phase_path}' for phase_path in group or ()]
print('+', shell_join(cmd + extra)) # type: ignore
@@ -1007,34 +1017,30 @@ def make_uki(opts: UkifyConfig) -> None:
pcrpkey: Union[bytes, Path, None] = opts.pcrpkey
if pcrpkey is None:
+ measure_tool = find_tool('systemd-measure', '/usr/lib/systemd/systemd-measure')
+ cmd = [measure_tool, 'pcrpkey']
+
if opts.pcr_public_keys and len(opts.pcr_public_keys) == 1:
- pcrpkey = opts.pcr_public_keys[0]
- # If we are getting a certificate when using an engine or provider, we need to convert it to
- # public key format.
- if (opts.signing_engine or opts.signing_provider) and Path(pcrpkey).exists():
- from cryptography.hazmat.primitives import serialization
- from cryptography.x509 import load_pem_x509_certificate
+ # If we're using an engine or provider, the public key will be an X.509 certificate.
+ if opts.signing_engine or opts.signing_provider:
+ cmd += ['--certificate', opts.pcr_public_keys[0]]
+ if opts.certificate_provider:
+ cmd += ['--certificate-source', f'provider:{opts.certificate_provider}']
+ else:
+ cmd += ['--public-key', opts.pcr_public_keys[0]]
- try:
- cert = load_pem_x509_certificate(Path(pcrpkey).read_bytes())
- except ValueError:
- raise ValueError(f'{pcrpkey} must be an X.509 certificate when signing with an engine')
- else:
- pcrpkey = cert.public_key().public_bytes(
- encoding=serialization.Encoding.PEM,
- format=serialization.PublicFormat.SubjectPublicKeyInfo,
- )
+ print('+', shell_join(cmd))
+ pcrpkey = subprocess.check_output(cmd)
elif opts.pcr_private_keys and len(opts.pcr_private_keys) == 1:
- from cryptography.hazmat.primitives import serialization
+ cmd += ['--private-key', Path(opts.pcr_private_keys[0])]
- privkey = serialization.load_pem_private_key(
- Path(opts.pcr_private_keys[0]).read_bytes(),
- password=None,
- )
- pcrpkey = privkey.public_key().public_bytes(
- encoding=serialization.Encoding.PEM,
- format=serialization.PublicFormat.SubjectPublicKeyInfo,
- )
+ if opts.signing_engine:
+ cmd += ['--private-key-source', f'engine:{opts.signing_engine}']
+ if opts.signing_provider:
+ cmd += ['--private-key-source', f'provider:{opts.signing_provider}']
+
+ print('+', shell_join(cmd))
+ pcrpkey = subprocess.check_output(cmd)
sections = [
# name, content, measure?
@@ -1270,9 +1276,9 @@ def generate_keys(opts: UkifyConfig) -> None:
)
print(f'Writing SecureBoot private key to {opts.sb_key}')
with temporary_umask(0o077):
- opts.sb_key.write_bytes(key_pem)
+ Path(opts.sb_key).write_bytes(key_pem)
print(f'Writing SecureBoot certificate to {opts.sb_cert}')
- opts.sb_cert.write_bytes(cert_pem)
+ Path(opts.sb_cert).write_bytes(cert_pem)
work = True
@@ -1284,7 +1290,7 @@ def generate_keys(opts: UkifyConfig) -> None:
Path(priv_key).write_bytes(priv_key_pem)
if pub_key:
print(f'Writing public key for PCR signing to {pub_key}')
- pub_key.write_bytes(pub_key_pem)
+ Path(pub_key).write_bytes(pub_key_pem)
work = True
@@ -1674,6 +1680,12 @@ CONFIG_ITEMS = [
help='OpenSSL provider to use for signing',
config_key='UKI/SigningProvider',
),
+ ConfigItem(
+ '--certificate-provider',
+ metavar='PROVIDER',
+ help='OpenSSL provider to load certificate from',
+ config_key='UKI/CertificateProvider',
+ ),
ConfigItem(
'--signtool',
choices=('sbsign', 'pesign', 'systemd-sbsign'),
@@ -1746,7 +1758,6 @@ CONFIG_ITEMS = [
'--pcr-public-key',
dest='pcr_public_keys',
metavar='PATH',
- type=Path,
action='append',
help='public part of the keypair or engine/provider designation for signing PCR signatures',
config_key='PCRSignature:/PCRPublicKey',
@@ -1982,11 +1993,11 @@ def finalize_options(opts: argparse.Namespace) -> None:
if opts.signing_engine and opts.signing_provider:
raise ValueError('Only one of --signing-engine= and --signing-provider= may be specified')
- if opts.signing_engine is None and opts.signing_provider is None:
- if opts.sb_key:
- opts.sb_key = Path(opts.sb_key)
- if opts.sb_cert:
- opts.sb_cert = Path(opts.sb_cert)
+ if opts.signing_engine is None and opts.signing_provider is None and opts.sb_key:
+ opts.sb_key = Path(opts.sb_key)
+
+ if opts.certificate_provider is None and opts.sb_cert:
+ opts.sb_cert = Path(opts.sb_cert)
if bool(opts.sb_key) ^ bool(opts.sb_cert):
# one param only given, sbsign needs both
@@ -2012,6 +2023,9 @@ def finalize_options(opts: argparse.Namespace) -> None:
if opts.signing_provider and opts.signtool != SystemdSbSign:
raise ValueError('--signing-provider= can only be used with--signtool=systemd-sbsign')
+ if opts.certificate_provider and opts.signtool != SystemdSbSign:
+ raise ValueError('--certificate-provider= can only be used with--signtool=systemd-sbsign')
+
if opts.sign_kernel and not opts.sb_key and not opts.sb_cert_name:
raise ValueError(
'--sign-kernel requires either --secureboot-private-key= and --secureboot-certificate= (for sbsign) or --secureboot-certificate-name= (for pesign) to be specified' # noqa: E501