Skip to content

Commit d7d3625

Browse files
DaanDeMeyerbluca
authored andcommitted
ukify: Derive public key from private key if not specified
1 parent 85fe60b commit d7d3625

File tree

2 files changed

+46
-38
lines changed

2 files changed

+46
-38
lines changed

src/ukify/test/test_ukify.py

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -609,46 +609,48 @@ def test_pcr_signing(kernel_initrd, tmpdir):
609609
'--cmdline=ARG1 ARG2 ARG3',
610610
'--os-release=ID=foobar\n',
611611
'--pcr-banks=sha1', # use sha1 because it doesn't really matter
612-
f'--pcrpkey={pub.name}',
613-
f'--pcr-public-key={pub.name}',
614612
f'--pcr-private-key={priv.name}',
615613
])
616614

617-
try:
618-
ukify.check_inputs(opts)
619-
except OSError as e:
620-
pytest.skip(str(e))
621-
622-
ukify.make_uki(opts)
623-
624-
# let's check that objdump likes the resulting file
625-
dump = subprocess.check_output(['objdump', '-h', output], text=True)
626-
627-
for sect in 'text osrel cmdline linux initrd uname pcrsig'.split():
628-
assert re.search(fr'^\s*\d+\s+.{sect}\s+0', dump, re.MULTILINE)
629-
630-
# objcopy fails when called without an output argument (EPERM).
631-
# It also fails when called with /dev/null (file truncated).
632-
# It also fails when called with /dev/zero (because it reads the
633-
# output file, infinitely in this case.)
634-
# So let's just call it with a dummy output argument.
635-
subprocess.check_call([
636-
'objcopy',
637-
*(f'--dump-section=.{n}={tmpdir}/out.{n}' for n in (
638-
'pcrpkey', 'pcrsig', 'osrel', 'uname', 'cmdline')),
639-
output,
640-
tmpdir / 'dummy',
641-
],
642-
text=True)
643-
644-
assert open(tmpdir / 'out.pcrpkey').read() == open(pub.name).read()
645-
assert open(tmpdir / 'out.osrel').read() == 'ID=foobar\n'
646-
assert open(tmpdir / 'out.uname').read() == '1.2.3'
647-
assert open(tmpdir / 'out.cmdline').read() == 'ARG1 ARG2 ARG3'
648-
sig = open(tmpdir / 'out.pcrsig').read()
649-
sig = json.loads(sig)
650-
assert list(sig.keys()) == ['sha1']
651-
assert len(sig['sha1']) == 4 # four items for four phases
615+
# If the public key is not explicitly specified, it is derived automatically. Let's make sure everything
616+
# works as expected both when the public keys is specified explicitly and when it is derived from the
617+
# private key.
618+
for extra in ([f'--pcrpkey={pub.name}', f'--pcr-public-key={pub.name}'], []):
619+
try:
620+
ukify.check_inputs(opts + extra)
621+
except OSError as e:
622+
pytest.skip(str(e))
623+
624+
ukify.make_uki(opts + extra)
625+
626+
# let's check that objdump likes the resulting file
627+
dump = subprocess.check_output(['objdump', '-h', output], text=True)
628+
629+
for sect in 'text osrel cmdline linux initrd uname pcrsig'.split():
630+
assert re.search(fr'^\s*\d+\s+.{sect}\s+0', dump, re.MULTILINE)
631+
632+
# objcopy fails when called without an output argument (EPERM).
633+
# It also fails when called with /dev/null (file truncated).
634+
# It also fails when called with /dev/zero (because it reads the
635+
# output file, infinitely in this case.)
636+
# So let's just call it with a dummy output argument.
637+
subprocess.check_call([
638+
'objcopy',
639+
*(f'--dump-section=.{n}={tmpdir}/out.{n}' for n in (
640+
'pcrpkey', 'pcrsig', 'osrel', 'uname', 'cmdline')),
641+
output,
642+
tmpdir / 'dummy',
643+
],
644+
text=True)
645+
646+
assert open(tmpdir / 'out.pcrpkey').read() == open(pub.name).read()
647+
assert open(tmpdir / 'out.osrel').read() == 'ID=foobar\n'
648+
assert open(tmpdir / 'out.uname').read() == '1.2.3'
649+
assert open(tmpdir / 'out.cmdline').read() == 'ARG1 ARG2 ARG3'
650+
sig = open(tmpdir / 'out.pcrsig').read()
651+
sig = json.loads(sig)
652+
assert list(sig.keys()) == ['sha1']
653+
assert len(sig['sha1']) == 4 # four items for four phases
652654

653655
def test_pcr_signing2(kernel_initrd, tmpdir):
654656
if kernel_initrd is None:

src/ukify/ukify.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,11 +729,17 @@ def make_uki(opts):
729729
uki = UKI(opts.stub)
730730
initrd = join_initrds(opts.initrd)
731731

732-
# TODO: derive public key from opts.pcr_private_keys?
733732
pcrpkey = opts.pcrpkey
734733
if pcrpkey is None:
735734
if opts.pcr_public_keys and len(opts.pcr_public_keys) == 1:
736735
pcrpkey = opts.pcr_public_keys[0]
736+
elif opts.pcr_private_keys and len(opts.pcr_private_keys) == 1:
737+
import cryptography.hazmat.primitives.serialization as serialization
738+
privkey = serialization.load_pem_private_key(opts.pcr_private_keys[0].read_bytes(), password=None)
739+
pcrpkey = privkey.public_key().public_bytes(
740+
encoding=serialization.Encoding.PEM,
741+
format=serialization.PublicFormat.SubjectPublicKeyInfo,
742+
)
737743

738744
sections = [
739745
# name, content, measure?

0 commit comments

Comments
 (0)