: Measure the components of the unified kernel image (UKI) using
`systemd-measure` and embed the PCR signature into the unified kernel
- image.
-
- This option requires the [`cryptography`](https://cryptography.io/)
- module.
+ image. This option takes a boolean value or the special value `auto`,
+ which is the default, which is equal to a true value if the
+ [`cryptography`](https://cryptography.io/) module is importable and
+ the `systemd-measure` binary is in `PATH`.
`CompressFs=`, `--compress-fs=`
# systemd-measure binary around, then also include a
# signature of expected PCR 11 values in the kernel image
if state.config.secure_boot and state.config.sign_expected_pcr:
- try:
- from cryptography import x509
- from cryptography.hazmat.primitives import serialization
- except ImportError:
- die("Couldn't import the cryptography Python module. This is needed for the --sign-expected-pcr option.")
-
- if not shutil.which('systemd-measure'):
- die("Couldn't find systemd-measure binary. It is needed for the --sign-expected-pcr option.")
+ from cryptography import x509
+ from cryptography.hazmat.primitives import serialization
with complete_step("Generating PCR 11 signature…"):
# Extract the public key from the SecureBoot certificate
super().__call__(parser, namespace, values, option_string)
+def parse_sign_expected_pcr(value: Union[bool, str]) -> bool:
+ if isinstance(value, bool):
+ return value
+
+ if value == "auto":
+ try:
+ # TODO: pyright stumbles over this with
+ # "import_module" is not a known member of module (reportGeneralTypeIssues)
+ # although importlib.import_module exists in Python 3.7
+ # a regular import trips pyflakes, though and I haven't found a way
+ # to silence that
+ importlib.import_module("cryptography") # type: ignore
+ return True if shutil.which('systemd-measure') else False
+ except ImportError:
+ return False
+
+ val = parse_boolean(value)
+ if val:
+ try:
+ importlib.import_module("cryptography") # type: ignore
+ except ImportError:
+ die("Couldn't import the cryptography Python module. This is needed for the --sign-expected-pcr option.")
+
+ if not shutil.which('systemd-measure'):
+ die("Couldn't find systemd-measure binary. It is needed for the --sign-expected-pcr option.")
+
+ return val
+
+
+class SignExpectedPcrAction(BooleanAction):
+ def __call__(
+ self,
+ parser: argparse.ArgumentParser,
+ namespace: argparse.Namespace,
+ values: Union[str, Sequence[Any], None, bool],
+ option_string: Optional[str] = None,
+ ) -> None:
+ if values is None:
+ parsed = False
+ elif isinstance(values, bool) or isinstance(values, str):
+ parsed = parse_sign_expected_pcr(values)
+ else:
+ raise argparse.ArgumentError(self, f"Invalid argument for {option_string}: {values}")
+ setattr(namespace, self.dest, parsed)
+
+
class CustomHelpFormatter(argparse.HelpFormatter):
def _format_action_invocation(self, action: argparse.Action) -> str:
if not action.option_strings or action.nargs == 0:
)
group.add_argument(
"--sign-expected-pcr",
- action=BooleanAction,
+ metavar="BOOL",
+ default="auto",
+ action=SignExpectedPcrAction,
+ type=parse_sign_expected_pcr,
help="Measure the components of the unified kernel image (UKI) and embed the PCR signature into the UKI",
)
group.add_argument(
import configparser
import contextlib
import copy
+import importlib
import os
+import shutil
from pathlib import Path
from typing import Any, Dict, Generator, List, Mapping, Optional
self.cli_arguments = []
self.reference_config = {}
+ def local_sign_expected_pcr_default(self) -> bool:
+ try:
+ importlib.import_module("cryptography")
+ return True if shutil.which('systemd-measure') else False
+ except ImportError:
+ return False
+
def add_reference_config(self, job_name: str = DEFAULT_JOB_NAME) -> None:
"""create one initial reference configuration
"bios_size": None,
"verb": Verb.build,
"verity": False,
- "sign_expected_pcr": False,
+ "sign_expected_pcr": self.local_sign_expected_pcr_default(),
"with_docs": False,
"with_network": False,
"with_tests": True,