mirror of
https://github.com/systemd/systemd.git
synced 2025-03-14 04:58:28 +03:00
ukify: add --pcr-certificate= parameter
Public keys and certificates are not the same, as the latter embeds more information that the former, and other tools like sd-measure have distinct parameters for each of them. Add a new --pcr-certificate= parameter to ukify, and use it to pass certs down to sd-measure, as an alternative to --pcr-public-key=. Do not allow specifying both.
This commit is contained in:
parent
5dbd751ccc
commit
2ac8fcf656
@ -85,7 +85,8 @@
|
||||
|
||||
<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
|
||||
@ -95,7 +96,8 @@
|
||||
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
|
||||
@ -478,8 +480,9 @@
|
||||
|
||||
<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>
|
||||
@ -652,11 +655,27 @@
|
||||
<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>
|
||||
|
@ -274,6 +274,7 @@ class UkifyConfig:
|
||||
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]
|
||||
@ -683,7 +684,7 @@ def check_cert_and_keys_nonexistent(opts: UkifyConfig) -> None:
|
||||
# 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():
|
||||
@ -721,17 +722,19 @@ def combine_signatures(pcrsigs: list[dict[str, str]]) -> str:
|
||||
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,
|
||||
)
|
||||
@ -809,9 +812,15 @@ def call_systemd_measure(uki: UKI, opts: UkifyConfig, profile_start: int = 0) ->
|
||||
]
|
||||
|
||||
# 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
|
||||
@ -848,16 +857,24 @@ def call_systemd_measure(uki: UKI, opts: UkifyConfig, profile_start: int = 0) ->
|
||||
*(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}']
|
||||
|
||||
@ -1316,6 +1333,13 @@ def make_uki(opts: UkifyConfig) -> None:
|
||||
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])]
|
||||
|
||||
@ -1594,7 +1618,7 @@ def generate_keys(opts: UkifyConfig) -> None:
|
||||
|
||||
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}')
|
||||
@ -2099,6 +2123,15 @@ CONFIG_ITEMS = [
|
||||
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',
|
||||
@ -2298,17 +2331,27 @@ def finalize_options(opts: argparse.Namespace) -> None:
|
||||
|
||||
# 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=')
|
||||
|
||||
@ -2405,6 +2448,7 @@ def finalize_options(opts: argparse.Namespace) -> None:
|
||||
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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user