ukify: add support for engine signing of PCR signatures

This commit is contained in:
Luca Boccassi 2024-02-11 00:34:19 +00:00
parent ed896a5b85
commit 419b25ddca
3 changed files with 23 additions and 17 deletions

View file

@ -99,7 +99,10 @@
the n-th boot phase path set will be signed by the n-th key. This can be used to build different trust
policies for different phases of the boot. In the config file, <varname>PCRPrivateKey=</varname>,
<varname>PCRPublicKey=</varname>, and <varname>Phases=</varname> are grouped into separate sections,
describing separate boot phases.</para>
describing separate boot phases. If <varname>SigningEngine=</varname>/<option>--signing-engine=</option>
is specified, then the private keys arguments will be passed verbatim to OpenSSL as URIs, and the public
key arguments will be loaded as X.509 certificates, so that signing can be perfomed with an OpenSSL
engine.</para>
<para>If a SecureBoot signing key is provided via the
<varname>SecureBootPrivateKey=</varname>/<option>--secureboot-private-key=</option> option, the resulting

View file

@ -120,7 +120,7 @@ def test_apply_config(tmp_path):
assert ns.sign_kernel is False
assert ns._groups == ['NAME']
assert ns.pcr_private_keys == [pathlib.Path('some/path7')]
assert ns.pcr_private_keys == ['some/path7']
assert ns.pcr_public_keys == [pathlib.Path('some/path8')]
assert ns.phase_path_groups == [['enter-initrd:leave-initrd:sysinit:ready:shutdown:final']]
@ -143,7 +143,7 @@ def test_apply_config(tmp_path):
assert ns.sign_kernel is False
assert ns._groups == ['NAME']
assert ns.pcr_private_keys == [pathlib.Path('some/path7')]
assert ns.pcr_private_keys == ['some/path7']
assert ns.pcr_public_keys == [pathlib.Path('some/path8')]
assert ns.phase_path_groups == [['enter-initrd:leave-initrd:sysinit:ready:shutdown:final']]
@ -189,7 +189,7 @@ def test_parse_args_many_deprecated():
assert opts.pcrpkey == pathlib.Path('PATH')
assert opts.uname == '1.2.3'
assert opts.stub == pathlib.Path('STUBPATH')
assert opts.pcr_private_keys == [pathlib.Path('PKEY1')]
assert opts.pcr_private_keys == ['PKEY1']
assert opts.pcr_public_keys == [pathlib.Path('PKEY2')]
assert opts.pcr_banks == ['SHA1', 'SHA256']
assert opts.signing_engine == 'ENGINE'
@ -235,7 +235,7 @@ def test_parse_args_many():
assert opts.pcrpkey == pathlib.Path('PATH')
assert opts.uname == '1.2.3'
assert opts.stub == pathlib.Path('STUBPATH')
assert opts.pcr_private_keys == [pathlib.Path('PKEY1')]
assert opts.pcr_private_keys == ['PKEY1']
assert opts.pcr_public_keys == [pathlib.Path('PKEY2')]
assert opts.pcr_banks == ['SHA1', 'SHA256']
assert opts.signing_engine == 'ENGINE'
@ -342,8 +342,7 @@ def test_config_priority(tmp_path):
assert opts.pcrpkey == pathlib.Path('PATH')
assert opts.uname == '1.2.3'
assert opts.stub == pathlib.Path('STUBPATH')
assert opts.pcr_private_keys == [pathlib.Path('PKEY1'),
pathlib.Path('some/path7')]
assert opts.pcr_private_keys == ['PKEY1', 'some/path7']
assert opts.pcr_public_keys == [pathlib.Path('PKEY2'),
pathlib.Path('some/path8')]
assert opts.pcr_banks == ['SHA1', 'SHA256']

View file

@ -449,7 +449,7 @@ def check_cert_and_keys_nonexistent(opts):
*((priv_key, pub_key)
for priv_key, pub_key, _ in key_path_groups(opts)))
for path in paths:
if path and path.exists():
if path and pathlib.Path(path).exists():
raise ValueError(f'{path} is present')
@ -539,7 +539,11 @@ def call_systemd_measure(uki, linux, opts):
for priv_key, pub_key, group in key_path_groups(opts):
extra = [f'--private-key={priv_key}']
if pub_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}']
elif pub_key:
extra += [f'--public-key={pub_key}']
extra += [f'--phase={phase_path}' for phase_path in group or ()]
@ -728,11 +732,13 @@ def sbsign_sign(sbsign_tool, input_f, output_f, opts=None):
sbsign_tool,
'--key', opts.sb_key,
'--cert', opts.sb_cert,
input_f,
'--output', output_f,
]
if opts.signing_engine is not None:
sign_invocation += ['--engine', opts.signing_engine]
sign_invocation += [
input_f,
'--output', output_f,
]
signer_sign(sign_invocation)
def find_pesign(opts=None):
@ -820,7 +826,7 @@ def make_uki(opts):
pcrpkey = opts.pcr_public_keys[0]
elif opts.pcr_private_keys and len(opts.pcr_private_keys) == 1:
from cryptography.hazmat.primitives import serialization
privkey = serialization.load_pem_private_key(opts.pcr_private_keys[0].read_bytes(), password=None)
privkey = serialization.load_pem_private_key(pathlib.Path(opts.pcr_private_keys[0]).read_bytes(), password=None)
pcrpkey = privkey.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
@ -1002,7 +1008,7 @@ def generate_keys(opts):
print(f'Writing private key for PCR signing to {priv_key}')
with temporary_umask(0o077):
priv_key.write_bytes(priv_key_pem)
pathlib.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)
@ -1405,10 +1411,8 @@ CONFIG_ITEMS = [
ConfigItem(
'--pcr-private-key',
dest = 'pcr_private_keys',
metavar = 'PATH',
type = pathlib.Path,
action = 'append',
help = 'private part of the keypair for signing PCR signatures',
help = 'private part of the keypair or engine-specific designation for signing PCR signatures',
config_key = 'PCRSignature:/PCRPrivateKey',
config_push = ConfigItem.config_set_group,
),
@ -1418,7 +1422,7 @@ CONFIG_ITEMS = [
metavar = 'PATH',
type = pathlib.Path,
action = 'append',
help = 'public part of the keypair for signing PCR signatures',
help = 'public part of the keypair or engine-specific designation for signing PCR signatures',
config_key = 'PCRSignature:/PCRPublicKey',
config_push = ConfigItem.config_set_group,
),