--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Second stage of build:
+# - signed hashes are in cpio archive in SOURCES/ together with artifacts from previous build
+# - if there are PCR policies to attach, do so and then prepare the hashes of the UKIs themselves
+# - if not, attach the signatures to the UKI(s) with pesign
+# - remove shasums of previous artifacts, given we are re-creating them after this step
+# - place artifacts from previous builds and signed UKI in output directory
+
+set -e
+
+if [ ! -f /usr/src/packages/SOURCES/hashes.cpio.rsasign.sig ]; then
+ exit 0
+fi
+
+echo "Signed files to be attached:"
+cpio -t </usr/src/packages/SOURCES/hashes.cpio.rsasign.sig
+cpio -t </usr/src/packages/SOURCES/hashes.cpio.rsasign
+mkdir -p hashes
+pushd hashes
+cpio -idm </usr/src/packages/SOURCES/hashes.cpio.rsasign.sig
+cpio -idm </usr/src/packages/SOURCES/hashes.cpio.rsasign
+popd
+
+OUTPUTDIR=/work/src/usr/src/packages/OTHER
+
+# OBS signs a hash without certificate information so it cannot simply be
+# attached to the PE binaries, certificate metadata has to be provided separately
+# so we need to create a certutil db and import the certificate manually
+rm -rf nss-db
+mkdir nss-db
+nss_db="$PWD/nss-db"
+certutil -N -d sql:"$nss_db" --empty-password
+certutil -A -d sql:"$nss_db" -n cert -t CT,CT,CT -i /usr/src/packages/SOURCES/_projectcert.crt
+
+cp -r /usr/src/packages/SOURCES/"$IMAGE_ID"* "$OUTPUTDIR"
+rm -f "$OUTPUTDIR/hashes.cpio.rsasign*" "$OUTPUTDIR"/*.sha*
+
+# First step: if there are UKI signatures, attach them
+while read -r SIG; do
+ infile="${SIG%.sig}"
+ sattrs="hashes/ukis/${SIG%.sig}"
+ test -s "$sattrs"
+
+ DEST="$OUTPUTDIR/${SIG#*/}"
+ DEST="${DEST%%.sig}"
+
+ # ensure the EFI hash matches before and after attaching the signature
+ old_hash=$(pesign -n sql:"$nss_db" -h -P -i "/usr/src/packages/SOURCES/$infile" | cut -d' ' -f1)
+
+ pesign -n sql:"$nss_db" --force -c cert -i "/usr/src/packages/SOURCES/$infile" -o "$DEST" -d sha256 -I "$sattrs" -R "hashes/ukis/$SIG"
+
+ new_hash=$(pesign -n sql:"$nss_db" -h -i "$DEST" | cut -d' ' -f1)
+ if [ "$old_hash" != "$new_hash" ]; then
+ echo "Pesign hash mismatch error: $old_hash $new_hash"
+ exit 1
+ fi
+
+ rm -f "$(basename "${infile}").sattrs" "$SIG" "$infile"
+done < <(find hashes/ukis hashes/kernels -type f \( -name '*efi.sig' -o -name 'vmlinu*.sig' \) -printf '%P\n')
+rm -rf nss-db "$OUTPUTDIR"/*.sig hashes/ukis
+
+# Second step: if there are PCR policy signatures, rebuild the JSON
+# blobs with the attached signatures
+while read -r SIG; do
+ uki="$OUTPUTDIR/$(basename "$(dirname "${SIG%.sig}")")"
+ pcrs="${uki%.efi}.pcrs"
+ pol="$(basename "${SIG%.sig}")"
+
+ test -f "${pcrs}"
+
+ jq -c --arg pol "$pol" --arg sig "$(base64 -w0 <"$SIG")" '
+ to_entries | map(
+ .value |= map(
+ if .pol == $pol then
+ .sig = $sig
+ else
+ .
+ end
+ )
+ ) | from_entries
+ ' "$pcrs" >"${pcrs}.new"
+ mv "${pcrs}.new" "$pcrs"
+ cp "$pcrs" "${pcrs}.sig"
+
+ rm -f "$SIG"
+done < <(find hashes/pcrs -type f -name '*.sig')
+rm -rf hashes/pcrs
+
+mkdir -p "$nss_db"
+certutil -N -d sql:"$nss_db" --empty-password
+
+# Third step: now that the JSON blob is rebuilt, merge it in the UKI
+while read -r PCRS; do
+ uki="${PCRS%.pcrs.sig}.efi"
+ ukify --json=short --pcrsig "@$PCRS" --join-pcrsig "$uki" --output "$uki.attached" build
+ mv "$uki.attached" "$uki"
+ mkdir -p hashes/ukis
+ pesign --force -n sql:"$nss_db" -i "$uki" -E "hashes/ukis/$(basename "$uki")"
+done < <(find "$OUTPUTDIR" -type f -name '*.pcrs.sig')
+rm -f "$OUTPUTDIR"/*.pcrs*
+
+# Fourth step: take hash of the UKIs after the signed JSON blobs have been merged
+# and prepare for the next iteration
+if [ -d hashes/ukis ]; then
+ pushd hashes
+ find . -type f | cpio -H newc -o >"$OUTPUTDIR/hashes.cpio.rsasign"
+ popd
+ cp /usr/src/packages/SOURCES/mkosi.conf "$OUTPUTDIR"
+ echo "Staging the following files for signing:"
+ cpio -t <"$OUTPUTDIR/hashes.cpio.rsasign"
+fi
+rm -rf hashes "$nss_db"
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# End of first stage of build:
+# - built UKI is in $OUTPUTDIR
+# - get PCR policy digests if any, or PE hash(es) with pesign
+# - pack them up in a cpio as OBS expects and store them in OTHER/
+# - create minimal recipe for second stage that will continue from here
+
+set -e
+
+declare -a UKIS
+UKIS=( "$(find "$OUTPUTDIR" -type f -name "*.efi" -printf '%P\n')" )
+declare -a KERNELS
+KERNELS=( "$(find "$OUTPUTDIR" -type f -name "vmlinu*" -printf '%P\n')" )
+
+if ((${#UKIS[@]} == 0)) && ((${#KERNELS[@]} == 0)); then
+ echo "No unsigned files found, exiting"
+ exit 0
+fi
+
+nss_db="$PWD/nss-db"
+# certutil will fail if it's called twice
+rm -rf "$nss_db"
+mkdir -p "$nss_db" hashes
+certutil -N -d sql:"$nss_db" --empty-password
+
+for f in "${UKIS[@]}"; do
+ test -f "${OUTPUTDIR}/${f}" || continue
+ if [ -f "${OUTPUTDIR}/${f%.efi}.pcrs" ]; then
+ mkdir -p "hashes/pcrs/$f"
+ while read -r pol; do
+ echo -n "$pol" | tr '[:lower:]' '[:upper:]' | basenc --base16 --decode >"hashes/pcrs/${f}/${pol}"
+ done < <(jq -r 'to_entries[] | .value[].pol' <"${OUTPUTDIR}/${f%.efi}.pcrs")
+ else
+ mkdir -p hashes/ukis
+ pesign --force -n sql:"$nss_db" -i "${OUTPUTDIR}/${f}" -E "hashes/ukis/$f"
+ fi
+done
+
+for f in "${KERNELS[@]}"; do
+ test -f "${OUTPUTDIR}/${f}" || continue
+ mkdir -p hashes/kernels
+ pesign --force -n sql:"$nss_db" -i "${OUTPUTDIR}/${f}" -E "hashes/kernels/$f"
+done
+
+# Pack everything into a CPIO archive and place it where OBS expects it
+pushd hashes
+find . -type f | cpio -H newc -o >"$OUTPUTDIR/hashes.cpio.rsasign"
+popd
+rm -rf hashes "$nss_db"
+
+echo "Staging the following files for signing:"
+cpio -t <"$OUTPUTDIR/hashes.cpio.rsasign"
+
+# The second stage will not do a full rebuild, but only attach signatures to the existing UKI
+cat >"$OUTPUTDIR/mkosi.conf" <<EOF
+[Distribution]
+Distribution=custom
+[Output]
+Format=none
+ImageId=$IMAGE_ID
+[Include]
+Include=mkosi-obs
+[Build]
+BuildSources=/usr/src/packages/OTHER:/usr/src/packages/OTHER
+EOF