Skip to content

Commit

Permalink
Add org.osbuild.ostree.genkey stage
Browse files Browse the repository at this point in the history
This stage allows you to create new (random) ed25519 keys as used by
`ostree sign`.

The primary usecase for this is composefs. You can generate a
transient key-pair during the build (unique to the build) that binds
the initrd to the userspace tree.

You put the public key in the initrd, sign the resulting commit with
the private key and then throw away the private key. During boot of a
(secureboot trusted) initrd, we use this public key to validate that
we're booting the right commit.

This is similar to how the transient kernel module signatures work.
It similarly generates a keypair during the kernel rpm build, sign the
modules, throw away the private key and embed the public key in the
kernel binary.

Of course, this stage can also be used to generate keys used for
persistant signatures.
  • Loading branch information
alexlarsson committed Nov 13, 2023
1 parent d52738d commit f938b8f
Showing 1 changed file with 92 additions and 0 deletions.
92 changes: 92 additions & 0 deletions stages/org.osbuild.ostree.genkey
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/python3
"""Generate ed25519 public/private keypair in format used by `ostree sign`.
This is used with the org.osbuild.ostree.sign stage, and these can be
used with composefs to tie an initrd and ostree commit together. See
https://ostreedev.github.io/ostree/composefs/#signatures for details
of how this works.
"""

import base64
import os
import subprocess
import sys
import tempfile

import osbuild.api

SCHEMA_2 = r"""
"options": {
"additionalProperties": false,
"required": ["publickey", "secretkey"],
"properties": {
"publickey": {
"description": "Path of generated public key",
"type": "string"
},
"secretkey": {
"description": "Path of generated secret key",
"type": "string"
}
}
}
"""


def openssl(*args):
args = list(args)
print("openssl " + " ".join(args), file=sys.stderr)
subprocess.run(["openssl"] + args,
encoding="utf8",
stdout=sys.stderr,
input=None,
check=True)


def openssl_stdout(*args):
args = list(args)
print("openssl " + " ".join(args), file=sys.stderr)
res = subprocess.run(["openssl"] + args,
stdout=subprocess.PIPE,
input=None,
check=True)

return res.stdout

# Based on gen_ed25519_keys() in https://github.com/ostreedev/ostree/blob/main/tests/libtest.sh


def main(args, options):
tree = args["tree"]
pubkeyfile = os.path.join(tree, options["publickey"].lstrip("/"))
seckeyfile = os.path.join(tree, options["secretkey"].lstrip("/"))

with tempfile.TemporaryDirectory(dir=tree) as tmpdir:
# Generate key
pemfile = os.path.join(tmpdir, "key.pem")
openssl("genpkey", "-algorithm", "ed25519", "-outform", "PEM", "-out", pemfile)

# Extract the seed/public parts from generated key (last 32 byte in PEM file)
pubkey = openssl_stdout("pkey", "-outform", "DER", "-pubout", "-in", pemfile)[-32:]
seed = openssl_stdout("pkey", "-outform", "DER", "-in", pemfile)[-32:]

# Private key is seed and public key joined
seckey = seed + pubkey

# Ostree stores keys in base64
pubkey_b64 = base64.b64encode(pubkey).decode("utf8")
seckey_b64 = base64.b64encode(seckey).decode("utf8")

with open(pubkeyfile, "w", encoding="utf8") as f:
f.write(pubkey_b64)

with open(seckeyfile, "w", encoding="utf8") as f:
f.write(seckey_b64)


if __name__ == '__main__':
stage_args = osbuild.api.arguments()
r = main(stage_args,
stage_args["options"])
sys.exit(r)

0 comments on commit f938b8f

Please sign in to comment.