From 6f55a2f408e19bd0af2049e762198955cae967db Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 11 Jul 2023 10:36:08 +0200 Subject: [PATCH] Add org.osbuild.ostree.genkey stage 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. --- stages/org.osbuild.ostree.genkey | 92 ++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100755 stages/org.osbuild.ostree.genkey diff --git a/stages/org.osbuild.ostree.genkey b/stages/org.osbuild.ostree.genkey new file mode 100755 index 0000000000..d5ba046fd8 --- /dev/null +++ b/stages/org.osbuild.ostree.genkey @@ -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") as f: + f.write(pubkey_b64) + + with open(seckeyfile, "w") as f: + f.write(seckey_b64) + + +if __name__ == '__main__': + stage_args = osbuild.api.arguments() + r = main(stage_args, + stage_args["options"]) + sys.exit(r)