diff --git a/man/systemd-measure.xml b/man/systemd-measure.xml
index c7e5a5e9e21..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 .
+
+
+
diff --git a/src/measure/measure.c b/src/measure/measure.c
index 979426c18fd..ac294d28b36 100644
--- a/src/measure/measure.c
+++ b/src/measure/measure.c
@@ -77,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"
@@ -1173,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 },
{}
};