#!/bin/bash
# shim-to-cert.tool - Extract OEM signing certificate public key (and full db, dbx if present) from GRUB shim file.
#
# Copyright (c) 2021-2023, Michael Beaton. All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
#
LIGHT_GREEN='\033[1;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Note: binutils can be placed last on path in macOS, to provide objcopy but avoid overwriting native tools.
command -v "${BINUTILS_PREFIX}"objcopy >/dev/null 2>&1 || { echo >&2 "objcopy not found - please install binutils package or set BINUTILS_PREFIX environment variable."; exit 1; }
command -v openssl >/dev/null 2>&1 || { echo >&2 "openssl not found - please install openssl package."; exit 1; }
if [ -z "$1" ]; then
echo "Usage: $(basename "$0") "
exit 1
fi
sectfile=$(mktemp) || exit 1
# make certain we have output file name, as objcopy will trash input file without it
if [ "x$sectfile" = "x" ]; then
echo >&2 "Error creating tempfile!"
exit 1
fi
# extract .vendor_cert section
"${BINUTILS_PREFIX}"objcopy -O binary -j .vendor_cert "$1" "$sectfile" || exit 1
if [ ! -s "$sectfile" ] ; then
echo >&2 "No .vendor_cert section in $1."
rm "$sectfile"
exit 1
fi
# xargs trims white space
vendor_authorized_size=$(dd if="$sectfile" ibs=1 skip=0 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm "$sectfile"; exit 1; }
vendor_deauthorized_size=$(dd if="$sectfile" ibs=1 skip=4 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm "$sectfile"; exit 1; }
vendor_authorized_offset=$(dd if="$sectfile" ibs=1 skip=8 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm "$sectfile"; exit 1; }
vendor_deauthorized_offset=$(dd if="$sectfile" ibs=1 skip=12 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm "$sectfile"; exit 1; }
# extract cert or db
certfile=$(mktemp) || { rm "$sectfile"; exit 1; }
# extract db
if [ "$vendor_authorized_size" -ne "0" ]; then
dd if="$sectfile" ibs=1 skip="$vendor_authorized_offset" count="$vendor_authorized_size" 2>/dev/null > "$certfile" || { rm "$sectfile"; rm "$certfile"; exit 1; }
fi
# extract dbx
if [ "$vendor_deauthorized_size" -ne "0" ]; then
dd if="$sectfile" ibs=1 skip="$vendor_deauthorized_offset" count="$vendor_deauthorized_size" 2>/dev/null > "vendor.dbx" || { rm "$sectfile"; rm "$certfile"; exit 1; }
echo -e " - Secure Boot revocation list saved as ${LIGHT_GREEN}vendor.dbx${NC}"
else
echo " - No secure Boot revocation list"
fi
rm "$sectfile"
if [ "$vendor_authorized_size" -eq "0" ]; then
echo "!!! Empty vendor_authorized section (no secure boot signing certificates present)."
rm "$certfile"
exit 1
fi
# valid as single cert?
openssl x509 -noout -inform der -in "$certfile" 2>/dev/null
if [ $? -ne 0 ]; then
cp "$certfile" vendor.db
echo -e " - Secure Boot signing list saved as ${LIGHT_GREEN}vendor.db${NC}"
else
# outfile name from cert CN
certname=$(openssl x509 -noout -subject -inform der -in "$certfile" | sed 's/^subject=.*CN *=[ \"]*//' | sed 's/[,\/].*//' | sed 's/ *//g') || { rm "$certfile"; exit 1; }
outfile="${certname}.der"
cp "$certfile" "$outfile" || { rm "$certfile"; exit 1; }
echo -e " - Secure Boot certificate saved as ${LIGHT_GREEN}${outfile}${NC}"
if ! command -v cert-to-efi-sig-list >/dev/null ; then
echo -e " o To convert certificate to vendor.db use:"
echo -e " ${YELLOW}" 'cert-to-efi-sig-list <(openssl x509 -in' "'${outfile}'" '-outform PEM) vendor.db' "${NC}"
else
cert-to-efi-sig-list <(openssl x509 -in "${outfile}" -outform PEM) vendor.db || { rm "$certfile"; exit 1; }
echo -e " o Certificate has also been saved as single cert signing list ${LIGHT_GREEN}vendor.db${NC}"
fi
fi
rm "$certfile"