summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--man/ukify.xml11
-rwxr-xr-xsrc/ukify/test/test_ukify.py19
-rwxr-xr-xsrc/ukify/ukify.py90
3 files changed, 72 insertions, 48 deletions
diff --git a/man/ukify.xml b/man/ukify.xml
index 6a697ee6e1..c42d6ae5c7 100644
--- a/man/ukify.xml
+++ b/man/ukify.xml
@@ -528,6 +528,17 @@
</varlistentry>
<varlistentry>
+ <term><varname>CertificateProvider=<replaceable>PROVIDER</replaceable></varname></term>
+ <term><option>--certificate-provider=<replaceable>PROVIDER</replaceable></option></term>
+
+ <listitem><para>An OpenSSL provider to be used for loading the certificate used to sign the
+ resulting binary and PCR measurements. This option can only be used when using
+ <command>systemd-sbsign</command> as the signing tool.</para>
+
+ <xi:include href="version-info.xml" xpointer="v257"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>SignKernel=<replaceable>BOOL</replaceable></varname></term>
<term><option>--sign-kernel</option></term>
<term><option>--no-sign-kernel</option></term>
diff --git a/src/ukify/test/test_ukify.py b/src/ukify/test/test_ukify.py
index 70e1a4e1d0..9eebf7eca1 100755
--- a/src/ukify/test/test_ukify.py
+++ b/src/ukify/test/test_ukify.py
@@ -138,7 +138,7 @@ def test_apply_config(tmp_path):
assert ns._groups == ['NAME']
assert ns.pcr_private_keys == ['some/path7']
- assert ns.pcr_public_keys == [pathlib.Path('some/path8')]
+ assert ns.pcr_public_keys == ['some/path8']
assert ns.phase_path_groups == [['enter-initrd:leave-initrd:sysinit:ready:shutdown:final']]
ukify.finalize_options(ns)
@@ -156,12 +156,12 @@ def test_apply_config(tmp_path):
assert ns.pcr_banks == ['sha512', 'sha1']
assert ns.signing_engine == 'engine1'
assert ns.sb_key == 'some/path5'
- assert ns.sb_cert == 'some/path6'
+ assert ns.sb_cert == pathlib.Path('some/path6')
assert ns.sign_kernel is False
assert ns._groups == ['NAME']
assert ns.pcr_private_keys == ['some/path7']
- assert ns.pcr_public_keys == [pathlib.Path('some/path8')]
+ assert ns.pcr_public_keys == ['some/path8']
assert ns.phase_path_groups == [['enter-initrd:leave-initrd:sysinit:ready:shutdown:final']]
def test_parse_args_minimal():
@@ -207,11 +207,11 @@ def test_parse_args_many_deprecated():
assert opts.uname == '1.2.3'
assert opts.stub == pathlib.Path('STUBPATH')
assert opts.pcr_private_keys == ['PKEY1']
- assert opts.pcr_public_keys == [pathlib.Path('PKEY2')]
+ assert opts.pcr_public_keys == ['PKEY2']
assert opts.pcr_banks == ['SHA1', 'SHA256']
assert opts.signing_engine == 'ENGINE'
assert opts.sb_key == 'SBKEY'
- assert opts.sb_cert == 'SBCERT'
+ assert opts.sb_cert == pathlib.Path('SBCERT')
assert opts.sign_kernel is False
assert opts.tools == [pathlib.Path('TOOLZ/')]
assert opts.output == pathlib.Path('OUTPUT')
@@ -253,11 +253,11 @@ def test_parse_args_many():
assert opts.uname == '1.2.3'
assert opts.stub == pathlib.Path('STUBPATH')
assert opts.pcr_private_keys == ['PKEY1']
- assert opts.pcr_public_keys == [pathlib.Path('PKEY2')]
+ assert opts.pcr_public_keys == ['PKEY2']
assert opts.pcr_banks == ['SHA1', 'SHA256']
assert opts.signing_engine == 'ENGINE'
assert opts.sb_key == 'SBKEY'
- assert opts.sb_cert == 'SBCERT'
+ assert opts.sb_cert == pathlib.Path('SBCERT')
assert opts.sign_kernel is False
assert opts.tools == [pathlib.Path('TOOLZ/')]
assert opts.output == pathlib.Path('OUTPUT')
@@ -360,13 +360,12 @@ def test_config_priority(tmp_path):
assert opts.uname == '1.2.3'
assert opts.stub == pathlib.Path('STUBPATH')
assert opts.pcr_private_keys == ['PKEY1', 'some/path7']
- assert opts.pcr_public_keys == [pathlib.Path('PKEY2'),
- pathlib.Path('some/path8')]
+ assert opts.pcr_public_keys == ['PKEY2', 'some/path8']
assert opts.pcr_banks == ['SHA1', 'SHA256']
assert opts.signing_engine == 'ENGINE'
assert opts.signtool == ukify.SbSign # from args
assert opts.sb_key == 'SBKEY' # from args
- assert opts.sb_cert == 'SBCERT' # from args
+ assert opts.sb_cert == pathlib.Path('SBCERT') # from args
assert opts.sb_certdir == 'some/path5' # from config
assert opts.sb_cert_name == 'some/name1' # from config
assert opts.sign_kernel is False
diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py
index ef4e9264c2..355e3f99f4 100755
--- a/src/ukify/ukify.py
+++ b/src/ukify/ukify.py
@@ -249,21 +249,22 @@ class UkifyConfig:
output: Optional[str]
pcr_banks: list[str]
pcr_private_keys: list[str]
- pcr_public_keys: list[Path]
+ pcr_public_keys: list[str]
pcrpkey: Optional[Path]
phase_path_groups: Optional[list[str]]
profile: Union[str, Path, None]
- sb_cert: Path
+ sb_cert: Union[str, Path, None]
sb_cert_name: Optional[str]
sb_cert_validity: int
sb_certdir: Path
- sb_key: Optional[Path]
+ sb_key: Union[str, Path, None]
sbat: Optional[list[str]]
sections: list['Section']
sections_by_name: dict[str, 'Section']
sign_kernel: bool
signing_engine: Optional[str]
signing_provider: Optional[str]
+ certificate_provider: Optional[str]
signtool: Optional[type['SignTool']]
splash: Optional[Path]
stub: Path
@@ -554,6 +555,11 @@ class SystemdSbSign(SignTool):
if opts.signing_provider is not None
else []
),
+ *(
+ ['--certificate-source', f'provider:{opts.certificate_provider}']
+ if opts.certificate_provider is not None
+ else []
+ ),
input_f,
'--output', output_f,
] # fmt: skip
@@ -666,7 +672,7 @@ def combine_signatures(pcrsigs: list[dict[str, str]]) -> str:
return json.dumps(combined)
-def key_path_groups(opts: UkifyConfig) -> Iterator[tuple[str, Optional[Path], Optional[str]]]:
+def key_path_groups(opts: UkifyConfig) -> Iterator[tuple[str, Optional[str], Optional[str]]]:
if not opts.pcr_private_keys:
return
@@ -757,6 +763,10 @@ def call_systemd_measure(uki: UKI, opts: UkifyConfig, profile_start: int = 0) ->
extra += [f'--certificate={pub_key}']
elif pub_key:
extra += [f'--public-key={pub_key}']
+
+ if opts.certificate_provider is not None:
+ extra += [f'--certificate-source=provider:{opts.certificate_provider}']
+
extra += [f'--phase={phase_path}' for phase_path in group or ()]
print('+', shell_join(cmd + extra)) # type: ignore
@@ -1007,34 +1017,30 @@ def make_uki(opts: UkifyConfig) -> None:
pcrpkey: Union[bytes, Path, None] = opts.pcrpkey
if pcrpkey is None:
+ measure_tool = find_tool('systemd-measure', '/usr/lib/systemd/systemd-measure')
+ cmd = [measure_tool, 'pcrpkey']
+
if opts.pcr_public_keys and len(opts.pcr_public_keys) == 1:
- pcrpkey = opts.pcr_public_keys[0]
- # If we are getting a certificate when using an engine or provider, we need to convert it to
- # public key format.
- if (opts.signing_engine or opts.signing_provider) and Path(pcrpkey).exists():
- from cryptography.hazmat.primitives import serialization
- from cryptography.x509 import load_pem_x509_certificate
-
- try:
- cert = load_pem_x509_certificate(Path(pcrpkey).read_bytes())
- except ValueError:
- raise ValueError(f'{pcrpkey} must be an X.509 certificate when signing with an engine')
- else:
- pcrpkey = cert.public_key().public_bytes(
- encoding=serialization.Encoding.PEM,
- format=serialization.PublicFormat.SubjectPublicKeyInfo,
- )
+ # If we're using an engine or provider, the public key will be an X.509 certificate.
+ if opts.signing_engine or opts.signing_provider:
+ cmd += ['--certificate', opts.pcr_public_keys[0]]
+ if opts.certificate_provider:
+ cmd += ['--certificate-source', f'provider:{opts.certificate_provider}']
+ else:
+ cmd += ['--public-key', opts.pcr_public_keys[0]]
+
+ print('+', shell_join(cmd))
+ pcrpkey = subprocess.check_output(cmd)
elif opts.pcr_private_keys and len(opts.pcr_private_keys) == 1:
- from cryptography.hazmat.primitives import serialization
+ cmd += ['--private-key', Path(opts.pcr_private_keys[0])]
- privkey = serialization.load_pem_private_key(
- Path(opts.pcr_private_keys[0]).read_bytes(),
- password=None,
- )
- pcrpkey = privkey.public_key().public_bytes(
- encoding=serialization.Encoding.PEM,
- format=serialization.PublicFormat.SubjectPublicKeyInfo,
- )
+ if opts.signing_engine:
+ cmd += ['--private-key-source', f'engine:{opts.signing_engine}']
+ if opts.signing_provider:
+ cmd += ['--private-key-source', f'provider:{opts.signing_provider}']
+
+ print('+', shell_join(cmd))
+ pcrpkey = subprocess.check_output(cmd)
sections = [
# name, content, measure?
@@ -1270,9 +1276,9 @@ def generate_keys(opts: UkifyConfig) -> None:
)
print(f'Writing SecureBoot private key to {opts.sb_key}')
with temporary_umask(0o077):
- opts.sb_key.write_bytes(key_pem)
+ Path(opts.sb_key).write_bytes(key_pem)
print(f'Writing SecureBoot certificate to {opts.sb_cert}')
- opts.sb_cert.write_bytes(cert_pem)
+ Path(opts.sb_cert).write_bytes(cert_pem)
work = True
@@ -1284,7 +1290,7 @@ def generate_keys(opts: UkifyConfig) -> None:
Path(priv_key).write_bytes(priv_key_pem)
if pub_key:
print(f'Writing public key for PCR signing to {pub_key}')
- pub_key.write_bytes(pub_key_pem)
+ Path(pub_key).write_bytes(pub_key_pem)
work = True
@@ -1675,6 +1681,12 @@ CONFIG_ITEMS = [
config_key='UKI/SigningProvider',
),
ConfigItem(
+ '--certificate-provider',
+ metavar='PROVIDER',
+ help='OpenSSL provider to load certificate from',
+ config_key='UKI/CertificateProvider',
+ ),
+ ConfigItem(
'--signtool',
choices=('sbsign', 'pesign', 'systemd-sbsign'),
action=SignToolAction,
@@ -1746,7 +1758,6 @@ CONFIG_ITEMS = [
'--pcr-public-key',
dest='pcr_public_keys',
metavar='PATH',
- type=Path,
action='append',
help='public part of the keypair or engine/provider designation for signing PCR signatures',
config_key='PCRSignature:/PCRPublicKey',
@@ -1982,11 +1993,11 @@ def finalize_options(opts: argparse.Namespace) -> None:
if opts.signing_engine and opts.signing_provider:
raise ValueError('Only one of --signing-engine= and --signing-provider= may be specified')
- if opts.signing_engine is None and opts.signing_provider is None:
- if opts.sb_key:
- opts.sb_key = Path(opts.sb_key)
- if opts.sb_cert:
- opts.sb_cert = Path(opts.sb_cert)
+ if opts.signing_engine is None and opts.signing_provider is None and opts.sb_key:
+ opts.sb_key = Path(opts.sb_key)
+
+ if opts.certificate_provider is None and opts.sb_cert:
+ opts.sb_cert = Path(opts.sb_cert)
if bool(opts.sb_key) ^ bool(opts.sb_cert):
# one param only given, sbsign needs both
@@ -2012,6 +2023,9 @@ def finalize_options(opts: argparse.Namespace) -> None:
if opts.signing_provider and opts.signtool != SystemdSbSign:
raise ValueError('--signing-provider= can only be used with--signtool=systemd-sbsign')
+ if opts.certificate_provider and opts.signtool != SystemdSbSign:
+ raise ValueError('--certificate-provider= can only be used with--signtool=systemd-sbsign')
+
if opts.sign_kernel and not opts.sb_key and not opts.sb_cert_name:
raise ValueError(
'--sign-kernel requires either --secureboot-private-key= and --secureboot-certificate= (for sbsign) or --secureboot-certificate-name= (for pesign) to be specified' # noqa: E501