Add byhand script to perform code signing
authorBen Hutchings <ben@decadent.org.uk>
Mon, 27 Jun 2016 21:34:36 +0000 (23:34 +0200)
committerBen Hutchings <ben@decadent.org.uk>
Thu, 30 Jun 2016 19:39:56 +0000 (21:39 +0200)
It takes a tarball of code objects and generates a tarball of
corresponding detached PKCS#7 signatures (with '.sig' suffixes).

It will sign:
- EFI binaries (*.efi, vmlinuz-*) using pesign
- Linux kernel modules (*.ko) using sign-file from linux-kbuild-<version>

Currently it should work with private key files and certificates.  It
may be able to sign kernel modules with a key on a PKCS#11 device.
It definitely can't sign EFI binaries using a PKCS#11 device yet.

scripts/debian/byhand-code-sign [new file with mode: 0755]

diff --git a/scripts/debian/byhand-code-sign b/scripts/debian/byhand-code-sign
new file mode 100755 (executable)
index 0000000..8038813
--- /dev/null
@@ -0,0 +1,148 @@
+#!/bin/bash
+
+set -u
+set -e
+set -o pipefail
+
+if [ $# -lt 5 ]; then
+       echo "Usage: $0 filename version arch changes_file suite"
+       exit 1
+fi
+
+IN_TARBALL="$1"        # Tarball to read, compressed with xz
+VERSION="$2"
+ARCH="$3"
+CHANGES="$4"   # Changes file for the upload
+SUITE="$5"
+
+error() {
+       echo >&2 "E: $*"
+       exit 1
+}
+
+export OPENSSL_CONF=/dev/null
+
+# Read dak configuration for security or main archive
+case "$SUITE" in
+    *-security)
+       configdir="/srv/security-master.debian.org/dak/config/debian-security"
+       ;;
+    *)
+       configdir="/srv/ftp-master.debian.org/dak/config/debian"
+       ;;
+esac
+. "$configdir/vars"
+
+# Read and trivially validate our configuration
+. "$configdir/byhand-code-sign.conf"
+for var in EFI_BINARY_PRIVKEY EFI_BINARY_CERT \
+          LINUX_SIGNFILE LINUX_MODULE_PRIVKEY LINUX_MODULE_CERT; do
+       test -v $var || error "$var is not defined in configuration"
+       test -n "${!var}" || error "$var is empty in configuration"
+done
+
+TARGET="$ftpdir/dists/$SUITE/main/code-sign/"
+OUT_TARBALL="$TARGET/${IN_TARBALL##*/}"
+OUT_TARBALL="${OUT_TARBALL%.tar.xz}_sigs.tar.xz"
+
+# Check that this source/arch/version hasn't already been signed
+if [ -e "$OUT_TARBALL" ]; then
+       error "Signature tarball already exists: $OUT_TARBALL"
+fi
+
+# If we fail somewhere, cleanup the temporary directories
+IN_DIR=
+OUT_DIR=
+CERT_DIR=
+cleanup() {
+       for dir in "$IN_DIR" "$OUT_DIR" "$CERT_DIR"; do
+               test -z "$dir" || rm -rf "$dir"
+       done
+}
+trap cleanup EXIT
+
+# Extract the data into the input directory
+IN_DIR="$(mktemp -td byhand-code-sign-in.XXXXXX)"
+tar xaf "$IN_TARBALL" --directory="$IN_DIR"
+
+case "$EFI_BINARY_PRIVKEY" in
+    pkcs11:*)
+       # Translate from OpenSSL PKCS#11 enigne syntax to pesign parameters
+       # See: https://sources.debian.net/src/engine-pkcs11/0.2.2-1/src/engine_pkcs11.c
+       pkcs11_pin_value=
+       old_IFS="$IFS"
+       IFS=';'
+       for kv in ${EFI_BINARY_PRIVKEY#pkcs11:}; do
+               case "$kv" in
+                   token=*)
+                       pkcs11_token="${kv#*=}"
+                       ;;
+                   object=*)
+                       pkcs11_object="${kv#*=}"
+                       ;;
+                   pin-value=*)
+                       pkcs11_pin_value="${kv#*=}"
+                       ;;
+               esac
+       done
+       IFS="$old_IFS"
+       unset old_IFS
+       # TODO: unlock it
+       PESIGN_PARAMS=(-t "$pkcs11_token" -c "$pkcs11_object")
+       ;;
+    *)
+       # Create certificate store for pesign
+       CERT_DIR="$(mktemp -td byhand-code-sign-cert.XXXXXX)"
+       chmod 700 "$CERT_DIR"
+       mkdir "$CERT_DIR/store"
+       certutil -N --empty-password -d "$CERT_DIR/store"
+       openssl pkcs12 -export \
+               -inkey "$EFI_BINARY_PRIVKEY" -in "$EFI_BINARY_CERT" \
+               -out "$CERT_DIR/efi-image.p12" -passout pass: \
+               -name efi-image
+       pk12util -i "$CERT_DIR/efi-image.p12" -d "$CERT_DIR/store" -K '' -W ''
+       PESIGN_PARAMS=(-n "$CERT_DIR/store" -c efi-image)
+       ;;
+esac
+
+# Create hierarchy of detached signatures in parallel to the uploaded files
+OUT_DIR="$(mktemp -td byhand-code-sign-out.XXXXXX)"
+while read filename; do
+       mkdir -p "$OUT_DIR/${filename%/*}"
+       case "${filename##*/}" in
+           *.efi | vmlinuz-*)
+               pesign -i "$IN_DIR/$filename" \
+                      --export-signature "$OUT_DIR/$filename.sig" --sign \
+                      -d sha256 "${PESIGN_PARAMS[@]}"
+               ;;
+           *.ko)
+               "$LINUX_SIGNFILE" -d sha256 "$LINUX_MODULE_PRIVKEY" \
+                       "$LINUX_MODULE_CERT" "$IN_DIR/$filename"
+               mv "$IN_DIR/$filename.p7s" "$OUT_DIR/$filename.sig"
+               ;;
+           *)
+               echo >&2 "W: Not signing unrecognised file: $filename"
+               continue
+               ;;
+       esac
+       if [ ${#filename} -gt 60 ]; then
+               filename_trunc="...${filename:$((${#filename} - 57)):57}"
+       else
+               filename_trunc="$filename"
+       fi
+       printf 'I: Signed %-60s\r' "$filename_trunc"
+done < <(find "$IN_DIR" -type f -printf '%P\n')
+
+# Clear last progress message
+printf '%-70s\r' ''
+
+# Build tarball of signatures
+chmod -R a+rX "$OUT_DIR"
+mkdir -p "$TARGET"
+tar caf "$OUT_TARBALL" --directory="$OUT_DIR" .
+echo "I: Created $OUT_TARBALL"
+
+trap - EXIT
+cleanup
+
+exit 0