<para>If PCR signing keys are provided via the
<varname>PCRPrivateKey=</varname>/<option>--pcr-private-key=</option> and
- <varname>PCRPublicKey=</varname>/<option>--pcr-public-key=</option> options, PCR values that will be seen
+ <varname>PCRPublicKey=</varname>/<option>--pcr-public-key=</option> or
+ <varname>PCRCertificate=</varname>/<option>--pcr-certificate=</option> options, PCR values that will be seen
after booting with the given kernel, initrd, and other sections, will be calculated, signed, and embedded
in the UKI.
<citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry> is
the <varname>Phases=</varname>/<option>--phases=</option> option. If not specified, the default provided
by <command>systemd-measure</command> is used. It is also possible to specify the
<varname>PCRPrivateKey=</varname>/<option>--pcr-private-key=</option>,
- <varname>PCRPublicKey=</varname>/<option>--pcr-public-key=</option>, and
+ <varname>PCRPublicKey=</varname>/<option>--pcr-public-key=</option> or
+ <varname>PCRCertificate=</varname>/<option>--pcr-certificate=</option>, and
<varname>Phases=</varname>/<option>--phases=</option> arguments more than once. Signatures will then be
performed with each of the specified keys. On the command line, when both <option>--phases=</option> and
<option>--pcr-private-key=</option> are used, they must be specified the same number of times, and then
<listitem><para>A path to a public key to embed in the <literal>.pcrpkey</literal> section. If not
specified, and there's exactly one
- <varname>PCRPublicKey=</varname>/<option>--pcr-public-key=</option> argument, that key will be used.
- Otherwise, the section will not be present.</para>
+ <varname>PCRPublicKey=</varname>/<option>--pcr-public-key=</option> or
+ <varname>PCRCertificate=</varname>/<option>--pcr-certificate=</option> argument, that key will be
+ used. Otherwise, the section will not be present.</para>
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry>
<para>On the command line, this option may be specified more than once, similarly to the
<option>--pcr-private-key=</option> option. If not present, the public keys will be extracted from
the private keys. On the command line, if present, this option must be specified the same number of
- times as the <option>--pcr-private-key=</option> option.</para>
+ times as the <option>--pcr-private-key=</option> option. Cannot be specified if
+ <option>--pcr-certificate=</option> is used.</para>
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>PCRCertificate=<replaceable>PATH</replaceable></varname></term>
+ <term><option>--pcr-certificate=<replaceable>PATH</replaceable></option></term>
+
+ <listitem><para>An X.509 certificate to use for signing PCR policies.</para>
+
+ <para>On the command line, this option may be specified more than once, similarly to the
+ <option>--pcr-private-key=</option> option. If not present, the public keys will be extracted from
+ the private keys. On the command line, if present, this option must be specified the same number of
+ times as the <option>--pcr-private-key=</option> option. Cannot be specified if
+ <option>--pcr-public-key=</option> is used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>Phases=<replaceable>LIST</replaceable></varname></term>
<term><option>--phases=<replaceable>LIST</replaceable></option></term>
pcr_banks: list[str]
pcr_private_keys: list[str]
pcr_public_keys: list[str]
+ pcr_certificates: list[str]
pcrpkey: Optional[Path]
pcrsig: Union[str, Path, None]
join_pcrsig: Optional[Path]
# Raise if any of the keys and certs are found on disk
paths: Iterator[Union[str, Path, None]] = itertools.chain(
(opts.sb_key, opts.sb_cert),
- *((priv_key, pub_key) for priv_key, pub_key, _ in key_path_groups(opts)),
+ *((priv_key, pub_key, cert) for priv_key, pub_key, cert, _ in key_path_groups(opts)),
)
for path in paths:
if path and Path(path).exists():
return json.dumps(combined)
-def key_path_groups(opts: UkifyConfig) -> Iterator[tuple[str, Optional[str], Optional[str]]]:
+def key_path_groups(opts: UkifyConfig) -> Iterator[tuple[str, Optional[str], Optional[str], Optional[str]]]:
if not opts.pcr_private_keys:
return
n_priv = len(opts.pcr_private_keys)
pub_keys = opts.pcr_public_keys or []
+ certs = opts.pcr_certificates or []
pp_groups = opts.phase_path_groups or []
yield from itertools.zip_longest(
opts.pcr_private_keys,
pub_keys[:n_priv],
+ certs[:n_priv],
pp_groups[:n_priv],
fillvalue=None,
)
]
# The JSON object will be used for offline signing, include the public key
- # so that the fingerprint is included too.
- if opts.policy_digest and opts.pcr_public_keys:
- cmd += [f'--public-key={opts.pcr_public_keys[0]}']
+ # so that the fingerprint is included too. In case a certificate is passed, use the
+ # right parameter so that systemd-measure can extract the public key from it.
+ if opts.policy_digest:
+ if opts.pcr_public_keys:
+ cmd += ['--public-key', opts.pcr_public_keys[0]]
+ elif opts.pcr_certificates:
+ cmd += ['--certificate', opts.pcr_certificates[0]]
+ if opts.certificate_provider:
+ cmd += ['--certificate-source', f'provider:{opts.certificate_provider}']
print('+', shell_join(cmd), file=sys.stderr)
output = subprocess.check_output(cmd, text=True) # type: ignore
*(f'--bank={bank}' for bank in banks),
]
- for priv_key, pub_key, group in key_path_groups(opts):
+ for priv_key, pub_key, cert, group in key_path_groups(opts):
extra = [f'--private-key={priv_key}']
if opts.signing_engine is not None:
- assert pub_key
- extra += [f'--private-key-source=engine:{opts.signing_engine}']
- extra += [f'--certificate={pub_key}']
+ assert pub_key or cert
+ # Backward compatibility, we used to pass the public key as the certificate
+ # as there was no --pcr-certificate= parameter
+ extra += [
+ f'--private-key-source=engine:{opts.signing_engine}',
+ f'--certificate={pub_key or cert}',
+ ]
elif opts.signing_provider is not None:
- assert pub_key
- extra += [f'--private-key-source=provider:{opts.signing_provider}']
- extra += [f'--certificate={pub_key}']
+ assert pub_key or cert
+ extra += [
+ f'--private-key-source=provider:{opts.signing_provider}',
+ f'--certificate={pub_key or cert}',
+ ]
+ elif cert:
+ extra += [f'--certificate={cert}']
elif pub_key:
extra += [f'--public-key={pub_key}']
pcrpkey = subprocess.check_output(cmd)
else:
pcrpkey = Path(opts.pcr_public_keys[0])
+ elif opts.pcr_certificates and len(opts.pcr_certificates) == 1:
+ cmd += ['--certificate', opts.pcr_certificates[0]]
+ if opts.certificate_provider:
+ cmd += ['--certificate-source', f'provider:{opts.certificate_provider}']
+
+ print('+', shell_join(cmd), file=sys.stderr)
+ pcrpkey = subprocess.check_output(cmd)
elif opts.pcr_private_keys and len(opts.pcr_private_keys) == 1:
cmd += ['--private-key', Path(opts.pcr_private_keys[0])]
work = True
- for priv_key, pub_key, _ in key_path_groups(opts):
+ for priv_key, pub_key, _, _ in key_path_groups(opts):
priv_key_pem, pub_key_pem = generate_priv_pub_key_pair()
print(f'Writing private key for PCR signing to {priv_key}')
config_key='PCRSignature:/PCRPublicKey',
config_push=ConfigItem.config_set_group,
),
+ ConfigItem(
+ '--pcr-certificate',
+ dest='pcr_certificates',
+ metavar='PATH',
+ action='append',
+ help='certificate part of the keypair or engine/provider designation for signing PCR signatures',
+ config_key='PCRSignature:/PCRCertificate',
+ config_push=ConfigItem.config_set_group,
+ ),
ConfigItem(
'--phases',
dest='phase_path_groups',
# Check that --pcr-public-key=, --pcr-private-key=, and --phases=
# have either the same number of arguments or are not specified at all.
+ # Also check that --pcr-public-key= and --pcr-certificate= are not set at the same time.
# But allow a single public key, for offline PCR signing, to pre-populate the JSON object
# with the certificate's fingerprint.
+ n_pcr_cert = None if opts.pcr_certificates is None else len(opts.pcr_certificates)
n_pcr_pub = None if opts.pcr_public_keys is None else len(opts.pcr_public_keys)
n_pcr_priv = None if opts.pcr_private_keys is None else len(opts.pcr_private_keys)
n_phase_path_groups = None if opts.phase_path_groups is None else len(opts.phase_path_groups)
if opts.policy_digest and n_pcr_priv is not None:
raise ValueError('--pcr-private-key= cannot be specified with --policy-digest')
- if opts.policy_digest and (n_pcr_pub is None or n_pcr_pub != 1):
- raise ValueError('--policy-digest requires exactly one --pcr-public-key=')
+ if (
+ opts.policy_digest
+ and (n_pcr_pub is None or n_pcr_pub != 1)
+ and (n_pcr_cert is None or n_pcr_cert != 1)
+ ):
+ raise ValueError('--policy-digest requires exactly one --pcr-public-key= or --pcr-certificate=')
if n_pcr_pub is not None and n_pcr_priv is not None and n_pcr_pub != n_pcr_priv:
raise ValueError('--pcr-public-key= specifications must match --pcr-private-key=')
+ if n_pcr_cert is not None and n_pcr_priv is not None and n_pcr_cert != n_pcr_priv:
+ raise ValueError('--pcr-certificate= specifications must match --pcr-private-key=')
+ if n_pcr_pub is not None and n_pcr_cert is not None:
+ raise ValueError('--pcr-public-key= and --pcr-certificate= cannot be used at the same time')
if n_phase_path_groups is not None and n_phase_path_groups != n_pcr_priv:
raise ValueError('--phases= specifications must match --pcr-private-key=')
or opts.devicetree_auto
or opts.pcr_private_keys
or opts.pcr_public_keys
+ or opts.pcr_certificates
):
raise ValueError('--pcrsig and --join-pcrsig cannot be used with other sections')
if opts.pcrsig: