keyutil: support adding content into PKCS#7 signature (#36663)

Support including the data that was signed inside the PKCS#7 signature.
This creates a self-contained file where the signature of the data can
be verified without any other information, since the file contains the
data, signature, and certificate (which contains the public key used for
the signing).

One use case of this is IPE which requires a PKCS#7 signature that is
not "detached", i.e. includes the IPE configuration that has been
signed.

This also slightly adjusts the test case to use the x509 certificate
inside the PKCS#7 signature instead of supplying it externally during
verification.
This commit is contained in:
Luca Boccassi
2025-03-08 01:42:20 +00:00
committed by GitHub
3 changed files with 63 additions and 6 deletions

View File

@@ -71,7 +71,8 @@
<listitem><para>This command embeds the PKCS#1 signature (RSA) provided with
<option>--signature=</option> in a PKCS#7 signature using the certificate given with
<option>--certificate=</option> and writes it to the file specified with <option>--output=</option>
in PKCS#7 format (p7s).</para>
in PKCS#7 format (p7s). If <option>--content=</option> is provided it is included in the p7s,
otherwise a "detached" signature is created.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
@@ -109,6 +110,15 @@
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--content=<replaceable>PATH</replaceable></option></term>
<listitem><para>Input data that corresponds to the PKCS#1 signature for the <command>pkcs7</command>
command, used for generating inline (i.e. non-"detached") PKCS#7 signatures.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--output=<replaceable>PATH</replaceable></option></term>

View File

@@ -23,6 +23,7 @@ static char *arg_certificate = NULL;
static char *arg_certificate_source = NULL;
static CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE;
static char *arg_signature = NULL;
static char *arg_content = NULL;
static char *arg_output = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
@@ -30,6 +31,7 @@ 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_signature, freep);
STATIC_DESTRUCTOR_REGISTER(arg_content, freep);
STATIC_DESTRUCTOR_REGISTER(arg_output, freep);
static int help(int argc, char *argv[], void *userdata) {
@@ -60,6 +62,7 @@ static int help(int argc, char *argv[], void *userdata) {
" Specify how to interpret the certificate from\n"
" --certificate=. Allows the certificate to be loaded\n"
" from an OpenSSL provider\n"
" --content=PATH Raw data content to embed in PKCS#7 signature\n"
" --signature=PATH PKCS#1 signature to embed in PKCS#7 signature\n"
" --output=PATH Where to write the PKCS#7 signature\n"
"\nSee the %2$s for details.\n",
@@ -81,6 +84,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_CERTIFICATE,
ARG_CERTIFICATE_SOURCE,
ARG_SIGNATURE,
ARG_CONTENT,
ARG_OUTPUT,
};
@@ -92,6 +96,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "certificate", required_argument, NULL, ARG_CERTIFICATE },
{ "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE },
{ "signature", required_argument, NULL, ARG_SIGNATURE },
{ "content", required_argument, NULL, ARG_CONTENT },
{ "output", required_argument, NULL, ARG_OUTPUT },
{}
};
@@ -150,6 +155,13 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_CONTENT:
r = parse_path_argument(optarg, /*suppress_root=*/ false, &arg_content);
if (r < 0)
return r;
break;
case ARG_OUTPUT:
r = parse_path_argument(optarg, /*suppress_root=*/ false, &arg_output);
if (r < 0)
@@ -345,12 +357,27 @@ static int verb_pkcs7(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to allocate PKCS#7 context: %m");
if (PKCS7_set_detached(pkcs7, true) == 0)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS#7 detached attribute: %s",
ERR_error_string(ERR_get_error(), NULL));
if (arg_content) {
_cleanup_free_ char *content = NULL;
size_t content_len = 0;
r = read_full_file(arg_content, &content, &content_len);
if (r < 0)
return log_error_errno(r, "Failed to read content file %s: %m", arg_content);
if (content_len == 0)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Content file %s is empty", arg_content);
if (!PKCS7_content_new(pkcs7, NID_pkcs7_data))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Error creating new PKCS7 content field");
ASN1_STRING_set0(pkcs7->d.sign->contents->d.data, TAKE_PTR(content), content_len);
} else
if (PKCS7_set_detached(pkcs7, true) == 0)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to set PKCS#7 detached attribute: %s",
ERR_error_string(ERR_get_error(), NULL));
/* Add PKCS1 signature to PKCS7_SIGNER_INFO */
ASN1_STRING_set0(signer_info->enc_digest, TAKE_PTR(pkcs1), pkcs1_len);
_cleanup_fclose_ FILE *output = NULL;

View File

@@ -49,9 +49,29 @@ testcase_public() {
testcase_pkcs7() {
echo -n "test" > /tmp/payload
# Generate PKCS#1 signature
openssl dgst -sha256 -sign /tmp/test.key -out /tmp/payload.sig /tmp/payload
# Generate PKCS#7 "detached" signature
/usr/lib/systemd/systemd-keyutil --certificate /tmp/test.crt --output /tmp/payload.p7s --signature /tmp/payload.sig pkcs7
openssl smime -verify -binary -inform der -in /tmp/payload.p7s -content /tmp/payload -certfile /tmp/test.crt -nointern -noverify > /dev/null
# Verify using internal x509 certificate
openssl smime -verify -binary -inform der -in /tmp/payload.p7s -content /tmp/payload -noverify > /dev/null
# Verify using external (original) x509 certificate
openssl smime -verify -binary -inform der -in /tmp/payload.p7s -content /tmp/payload -certificate /tmp/test.crt -nointern -noverify > /dev/null
rm -f /tmp/payload.p7s
# Generate PKCS#7 non-"detached" signature
/usr/lib/systemd/systemd-keyutil --certificate /tmp/test.crt --output /tmp/payload.p7s --signature /tmp/payload.sig --content /tmp/payload pkcs7
# Verify using internal x509 certificate
openssl smime -verify -binary -inform der -in /tmp/payload.p7s -noverify > /dev/null
# Verify using external (original) x509 certificate
openssl smime -verify -binary -inform der -in /tmp/payload.p7s -certificate /tmp/test.crt -nointern -noverify > /dev/null
}
run_testcases