From 1a76ee21ce5119d182464d35aef21c815df7f5bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Sj=C3=B6strom?= <lars@radicore.se> Date: Thu, 23 Jan 2025 12:11:57 +0100 Subject: [PATCH] feat: initial secure boot --- .woodpecker/ci.yaml | 13 +++- flake.nix | 3 + keys/DB.auth | Bin 0 -> 2092 bytes keys/KEK.auth | Bin 0 -> 2091 bytes keys/PK.auth | Bin 0 -> 2089 bytes modules/image/builder.nix | 1 + scripts/sbkeys | 154 ++++++++++++++++++++++++++++++++++++++ scripts/sign-release.sh | 19 +++++ 8 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 keys/DB.auth create mode 100644 keys/KEK.auth create mode 100644 keys/PK.auth create mode 100755 scripts/sbkeys create mode 100755 scripts/sign-release.sh diff --git a/.woodpecker/ci.yaml b/.woodpecker/ci.yaml index 3099d84..606a477 100644 --- a/.woodpecker/ci.yaml +++ b/.woodpecker/ci.yaml @@ -6,6 +6,17 @@ when: steps: check: - image: docker.io/nixpkgs/nix-flakes:nixos-24.05 + image: docker.io/nixpkgs/nix-flakes:nixos-25.05 commands: - nix flake check + + sign: + image: docker.io/nixpkgs/nix-flakes:nixos-25.05 + environment: + DB_KEY: + from_secret: secure_boot_key + DB_CRT: + from_secret: secure_boot_crt + commands: + - ./scripts/sign-release.sh + diff --git a/flake.nix b/flake.nix index 7648b8b..e5f4787 100644 --- a/flake.nix +++ b/flake.nix @@ -80,8 +80,11 @@ devShells.${system}.default = pkgs.mkShell { buildInputs = with pkgs; [ + efitools erofs-utils just + openssl + sbsigntool self.packages.${system}.qemu-uefi-tpm squashfs-tools-ng ]; diff --git a/keys/DB.auth b/keys/DB.auth new file mode 100644 index 0000000000000000000000000000000000000000..d8ce304ab5dcd9eccabe35c4093e7498daa916b0 GIT binary patch literal 2092 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 literal 0 HcmV?d00001 diff --git a/keys/KEK.auth b/keys/KEK.auth new file mode 100644 index 0000000000000000000000000000000000000000..1e01cd38364e5af65e068ae38f9f71bc8975cc28 GIT binary patch literal 2091 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 literal 0 HcmV?d00001 diff --git a/keys/PK.auth b/keys/PK.auth new file mode 100644 index 0000000000000000000000000000000000000000..77ce10ffeb80f9ff109269b36782368b8064f20d GIT binary patch literal 2089 zcmaFK&M3yuWXb>or&t&m7??it&AqhV>wd;N&#sjwwdP@|%MF@X)-f?M8uA<PvT-J~ zc`&9jvoJBTG8k~PacZ@Bw0-AgWM^E^#H?)4#4K;n#KgXUnTe5!NhI~;iPure=W7ze z#U~xse{#6Z;PYmX5l}sh+^h@+@`kbo(rnD3EX+J20f{C4!3x2t$)!c93Qqa?B?<xF z26E!OMy3XaMn;Av21cevQR2MDAg%$FOW1%WMkQoR8Ce;an;7{S44N3Zn3@<F8P?U^ zF&9uul+4)J6u+T&TfocklM5~DVotN3UGs|RcX4Lp_5&B6M@dI++0hXzb?F4>#j9(# zABx;^WX_aXrx<OXMyy)0rqb_I<+ZCS-ySZyd?HK1^32|sEFv>?&bIAadEn!B-@d*u z`8YB8Jdv14d3C3k7Hfph$D9vYUmp3>qW`dDmtTgh-Kre|i!Q&Ew_wgFkBR;lbJa9F zO<JOCNzt{`mOhaw{h{Yw;+GVCSe4HFV8W3V`|K`!s#?!6>C@|9b)Rl@AMjys77I}n z;@R+vr|!kitqEJKE0+8}xNGrIcejVjPcX)n>)7Y!KaGDEFhy%!o%@l0DmnWs+Z7dJ zi_*DU4`$~wF*7nSE)F&bG>~Ou4wdC&5n~b2*l=<4O!<3rcQGvzo+;fladD{sZUcFc zv@(l?fmj1}1^gfd!i<dnSy&C285#d02Rk=7*cln-y|~6Q^WgWsbtju%yyEloI2Zf1 zP&@f^r7ZIfRi2`W?zRHP@?S3boVI^=MRmr*<3^|V&z-ZRJI`R<buV=T##=gPl264Y zu3%MqyIB6zgY8et3fJc^TDK&<OyJAM2_Nl$f1Yh;dM|&@l<#FW7tSc1?|8km-;UAv zZvE~0xwAb|vy%Qsm$q%a^}?}!cg0clioors?`ZPQDJ#BvboZT`ch20{6Yl6@@j2(y zj2Y#84_xo>v?&$4zbs8o;npfnqqcd`9}Kt7l#!S#{-fQ*pJD2p3f4Efu00B9o4Dq0 z&z!7>?|<bB6c>Hy^I6mTMJ2}Pu0Y2=!NNOVSMhbSu9zQLGdrxNrccqZi80Bbi7}3e zk<q}MVB*D{hmf-&FFYGEGO{wTG%+$T-P*d;TKrI$+R{&NJ+kh+wPxY}Z!T=prOcxK zvRU(X1LMu?t%ufy1PD#IbM0Zz$I_1qhhp=(B$VrI@>a8{E{)gJVTrvc#x_qj{G6!; z|Dx8XQ(p=kTRHdNu}I@hT&gEN8YsJ`nroaCc(F>zI8&u~rDA*Rl<)_u#hUDVR>?Tn zM{-KCtUuFv`2JJ1{}G$~Zz<guY+A7S{sD&E=|A51%k2M?aXqqOBX9rSeTHGFmz}+q zZ#e%T{I+F8(`+LJS*@8zQ#84~G^(xD@18#SR!KVYs=y`JxL@HOKkhs2;|g5w;#TQg zV4#?u(RAF&{KJ<y;ct)J6KJ<7;1j$ptnc2kQMNo_VdR0OPo^yQYTvqAG)DVFkv}t} lj5B6tU=aKrx-8ks&v|*i68E7;Dea?$)@Y$MT4>=dv;bYNUCjUh literal 0 HcmV?d00001 diff --git a/modules/image/builder.nix b/modules/image/builder.nix index f510fe7..65dc08a 100644 --- a/modules/image/builder.nix +++ b/modules/image/builder.nix @@ -76,6 +76,7 @@ let contents = { "/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI".source = "${pkgs.systemdUkify}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi"; "/EFI/Linux/${config.system.boot.loader.ukiFile}".source = "${config.system.build.uki}/${config.system.boot.loader.ukiFile}"; + "/EFI/loader/keys/patos".source = ../../keys; "/EFI/memtest86/memtest86.efi".source = "${pkgs.memtest86plus}/memtest.efi"; "/loader/entries/patos-factory-reset.conf".source = pkgs.writeText "patos-factory-reset.conf" '' title Patos Factory Reset diff --git a/scripts/sbkeys b/scripts/sbkeys new file mode 100755 index 0000000..a24e215 --- /dev/null +++ b/scripts/sbkeys @@ -0,0 +1,154 @@ +#!/usr/bin/env bash +# Copyright (c) 2015 by Roderick W. Smith +# Copyright (c) 2020 Corey Hinshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +[ -n "${DEBUG}" ] && set -x +set -e + +usage() { + cat <<EOF +Usage: sbkeys [OPTION]... +Generate secure boot keys + +Options: + -h Print this help text + -m Generate signature database entries for Microsoft certificates +EOF +} + +generate_keys() { + # Do not create new keys if key files already exist + KEYS=( + PK.key PK.crt PK.cer PK.esl PK.auth + KEK.key KEK.crt KEK.cer KEK.esl KEK.auth + DB.key DB.crt DB.cer DB.esl DB.auth + noPK.esl noPK.auth + myGUID.txt + ) + for file in ${KEYS[@]}; do + if [ -f ${file} ]; then + echo "Skipping key generation: keys already exist in $(pwd)" + return + fi + done + + echo -n "Enter a Common Name to embed in the keys: " + read NAME + + # Platform key + openssl req -new -x509 \ + -subj "/CN=${NAME} PK/" -days 3650 -nodes \ + -newkey rsa:2048 -sha256 \ + -keyout PK.key -out PK.crt + openssl x509 -in PK.crt -out PK.cer -outform DER + + # Key exchange key + openssl req -new -x509 \ + -subj "/CN=${NAME} KEK/" -days 3650 -nodes \ + -newkey rsa:2048 -sha256 \ + -keyout KEK.key -out KEK.crt + openssl x509 -in KEK.crt -out KEK.cer -outform DER + + # Signature database + openssl req -new -x509 \ + -subj "/CN=${NAME} DB/" -days 3650 -nodes \ + -newkey rsa:2048 -sha256 \ + -keyout DB.key -out DB.crt + openssl x509 -in DB.crt -out DB.cer -outform DER + + GUID="$(uuidgen -r)" + echo ${GUID} > myGUID.txt + + cert-to-efi-sig-list -g ${GUID} PK.crt PK.esl + cert-to-efi-sig-list -g ${GUID} KEK.crt KEK.esl + cert-to-efi-sig-list -g ${GUID} DB.crt DB.esl + rm -f noPK.esl + touch noPK.esl + + sign-efi-sig-list \ + -t "$(date --date='1 second' +'%Y-%m-%d %H:%M:%S')" \ + -k PK.key -c PK.crt \ + PK PK.esl PK.auth + sign-efi-sig-list \ + -t "$(date --date='1 second' +'%Y-%m-%d %H:%M:%S')" \ + -k PK.key -c PK.crt \ + PK noPK.esl noPK.auth + sign-efi-sig-list \ + -t "$(date --date='1 second' +'%Y-%m-%d %H:%M:%S')" \ + -k PK.key -c PK.crt \ + KEK KEK.esl KEK.auth + sign-efi-sig-list \ + -t "$(date --date='1 second' +'%Y-%m-%d %H:%M:%S')" \ + -k KEK.key -c KEK.crt \ + DB DB.esl DB.auth + + chmod 0600 *.key +} + +generate_ms_db() { + msguid=77fa9abd-0359-4d32-bd60-28f4e78f784b + + msdb="MS_db.esl add_MS_db.auth" + for file in $msdb; do + if [ -f $file ]; then + echo "Microsoft signature lists already exist in $(pwd)" + return + fi + done + + wget --user-agent="Mozilla" https://www.microsoft.com/pkiops/certs/MicWinProPCA2011_2011-10-19.crt + wget --user-agent="Mozilla" https://www.microsoft.com/pkiops/certs/MicCorUEFCA2011_2011-06-27.crt + + sbsiglist --owner "$msguid" --type x509 --output MS_Win_db.esl MicWinProPCA2011_2011-10-19.crt + sbsiglist --owner "$msguid" --type x509 --output MS_UEFI_db.esl MicCorUEFCA2011_2011-06-27.crt + cat MS_Win_db.esl MS_UEFI_db.esl > MS_db.esl + sign-efi-sig-list -a -g "$msguid" -k KEK.key -c KEK.crt DB MS_db.esl add_MS_db.auth + + rm MS_Win_db.esl MS_UEFI_db.esl MicWinProPCA2011_2011-10-19.crt MicCorUEFCA2011_2011-06-27.crt +} + +mskeys=0 + +while getopts ":hm" opt; do + case $opt in + h) + usage + cat <<EOF + +For use with KeyTool, copy the *.auth and *.esl files to a FAT USB +flash drive or to your EFI System Partition (ESP). +For use with most UEFIs' built-in key managers, copy the *.cer files. + +To add Microsoft's certificates use KeyTool or UEFI to append +add_MS_db.auth to the signature database. +EOF + exit 0 + ;; + m) + mskeys=1 + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + usage >&2 + exit 1 + ;; + esac +done + +generate_keys +if [ $mskeys -eq 1 ]; then + generate_ms_db +fi diff --git a/scripts/sign-release.sh b/scripts/sign-release.sh new file mode 100755 index 0000000..0de9aed --- /dev/null +++ b/scripts/sign-release.sh @@ -0,0 +1,19 @@ +#! /usr/bin/env nix-shell +#! nix-shell -i bash -p efitools + +set -eux + +mkdir signed +cp -L result/* signed/ + +loopdev=$(sudo losetup -f) +sudo losetup -P "$loopdev" signed/*.img +sudo mount "${loopdev}p1" /mnt -t vfat + +sudo find signed/ /mnt/ -name "*.efi" -type f -exec sbsign --key <(echo "$DB_KEY") --cert <(echo "$DB_CRT") --output {} {} \; + +sudo mkdir -p /mnt/loader/keys/patos +sudo cp keys/*.auth /mnt/loader/keys/patos/ + +sudo umount /mnt +sudo losetup -d "$loopdev"