diff --git a/firmware/beta/pieeprom-2021-11-22.bin b/firmware/beta/pieeprom-2021-11-22.bin new file mode 100755 index 0000000..6f21e0c Binary files /dev/null and b/firmware/beta/pieeprom-2021-11-22.bin differ diff --git a/firmware/beta/recovery.bin b/firmware/beta/recovery.bin index e2be08b..755e406 100644 Binary files a/firmware/beta/recovery.bin and b/firmware/beta/recovery.bin differ diff --git a/firmware/release-notes.md b/firmware/release-notes.md index f0204bf..3ce01a8 100644 --- a/firmware/release-notes.md +++ b/firmware/release-notes.md @@ -3,6 +3,10 @@ USB MSD boot also requires the firmware from Raspberry Pi OS 2020-08-20 or newer. https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#raspberry-pi-4-bootloader-configuration +## 2021-11-22 - Fix for Sabrent rocket Nano NVMe reboot issue - BETA + * Fixes issue with Sabrent rocket Nano NVMe disk after a reboot. + Run pcie initialisation again if there's an error. + ## 2021-10-27 - Secure boot improvements - BETA * Improve the error logging if a file is too large and truncated. * Increase the maximum size of the ramdisk to 96MB. diff --git a/rpi-eeprom-config b/rpi-eeprom-config index b8c03e6..2eaf252 100755 --- a/rpi-eeprom-config +++ b/rpi-eeprom-config @@ -8,6 +8,7 @@ import argparse import atexit import os import subprocess +import string import struct import sys import tempfile @@ -15,7 +16,12 @@ import time IMAGE_SIZE = 512 * 1024 -MAX_BOOTCONF_SIZE = 2024 +# Larger files won't with with "vcgencmd bootloader_config" +MAX_FILE_SIZE = 2024 +ALIGN_SIZE = 4096 +BOOTCONF_TXT = 'bootconf.txt' +BOOTCONF_SIG = 'bootconf.sig' +PUBKEY_BIN = 'pubkey.bin' # Each section starts with a magic number followed by a 32 bit offset to the # next section (big-endian). @@ -26,12 +32,18 @@ MAX_BOOTCONF_SIZE = 2024 # The last 4KB of the EEPROM image is reserved for internal use by the # bootloader and may be overwritten during the update process. MAGIC = 0x55aaf00f +PAD_MAGIC = 0x55aafeef MAGIC_MASK = 0xfffff00f -FILE_MAGIC = 0x55aaf11f # id for modifiable file, currently only bootconf.txt +FILE_MAGIC = 0x55aaf11f # id for modifiable files FILE_HDR_LEN = 20 FILENAME_LEN = 12 TEMP_DIR = None +DEBUG = False +def debug(s): + if DEBUG: + sys.stderr.write(s + '\n') + def rpi4(): compatible_path = "/sys/firmware/devicetree/base/compatible" if os.path.exists(compatible_path): @@ -59,6 +71,25 @@ def create_tempdir(): if TEMP_DIR is None: TEMP_DIR = tempfile.mkdtemp() +def pemtobin(infile): + """ + Converts an RSA public key into the format expected by the bootloader. + """ + # Import the package here to make this a weak dependency. + from Cryptodome.PublicKey import RSA + + arr = bytearray() + f = open(infile,'r') + key = RSA.importKey(f.read()) + + if key.size_in_bits() != 2048: + raise Exception("RSA key size must be 2048") + + # Export N and E in little endian format + arr.extend(key.n.to_bytes(256, byteorder='little')) + arr.extend(key.e.to_bytes(8, byteorder='little')) + return arr + def exit_error(msg): """ Trapped a fatal error, output message to stderr and exit with non-zero @@ -109,15 +140,21 @@ def apply_update(config, eeprom=None, config_src=None): else: eeprom_image = get_latest_eeprom() create_tempdir() + + # Replace the contents of bootconf.txt with the contents of the config file tmp_update = os.path.join(TEMP_DIR, 'pieeprom.upd') image = BootloaderImage(eeprom_image, tmp_update) - image.write(config) + image.update_file(config, BOOTCONF_TXT) + image.write() + config_str = open(config).read() if config_src is None: config_src = '' sys.stdout.write("Updating bootloader EEPROM\n image: %s\nconfig_src: %s\nconfig: %s\n%s\n%s\n%s\n" % (eeprom_image, config_src, config, '#' * 80, config_str, '#' * 80)) + sys.stdout.write("\n*** To cancel this update run 'sudo rpi-eeprom-update -r' ***\n\n") + # Ignore APT package checksums so that this doesn't fail when used # with EEPROMs with configs delivered outside of APT. # The checksums are really just a safety check for automatic updates. @@ -143,7 +180,7 @@ def edit_config(eeprom=None): if os.path.exists(pending): config_src = pending image = BootloaderImage(pending) - current_config = image.get_config().decode('utf-8') + current_config = image.get_file(BOOTCONF_TXT).decode('utf-8') else: current_config, config_src = read_current_config() @@ -178,6 +215,14 @@ def read_current_config(): return (shell_cmd(['vcgencmd', 'bootloader_config']), "vcgencmd bootloader_config") +class ImageSection: + def __init__(self, magic, offset, length, filename=''): + self.magic = magic + self.offset = offset + self.length = length + self.filename = filename + debug("ImageSection %x %x %x %s" % (magic, offset, length, filename)) + class BootloaderImage(object): def __init__(self, filename, output=None): """ @@ -185,6 +230,7 @@ class BootloaderImage(object): and optionally an output filename. """ self._filename = filename + self._sections = [] try: self._bytes = bytearray(open(filename, 'rb').read()) except IOError as err: @@ -196,47 +242,112 @@ class BootloaderImage(object): if len(self._bytes) != IMAGE_SIZE: exit_error("%s: Expected size %d bytes actual size %d bytes" % (filename, IMAGE_SIZE, len(self._bytes))) + self.parse() - def find_config(self): + def parse(self): + """ + Builds a table of offsets to the different sections in the EEPROM. + """ offset = 0 magic = 0 + found = False while offset < IMAGE_SIZE: magic, length = struct.unpack_from('>LL', self._bytes, offset) - if (magic & MAGIC_MASK) != MAGIC: - raise Exception('EEPROM is corrupted') + if magic == 0x0 or magic == 0xffffffff: + break # EOF + elif (magic & MAGIC_MASK) != MAGIC: + raise Exception('EEPROM is corrupted %x %x %x' % (magic, magic & MAGIC_MASK, MAGIC)) + filename = '' if magic == FILE_MAGIC: # Found a file - name = self._bytes[offset + 8: offset + FILE_HDR_LEN] - if name.decode('utf-8') == 'bootconf.txt': - return (offset, length) + # Discard trailing null characters used to pad filename + filename = self._bytes[offset + 8: offset + FILE_HDR_LEN].decode('utf-8').replace('\0', '') + self._sections.append(ImageSection(magic, offset, length, filename)) offset += 8 + length # length + type offset = (offset + 7) & ~7 - raise Exception('EEPROM parse error: Bootloader config not found') + def find_file(self, filename): + """ + Returns the offset, length and whether this is the last section in the + EEPROM for a modifiable file within the image. + """ + ret = (-1, -1, False) + for i in range(0, len(self._sections)): + s = self._sections[i] + if s.magic == FILE_MAGIC and s.filename == filename: + is_last = (i == len(self._sections) - 1) + ret = (s.offset, s.length, is_last) + break + debug('%s offset %d length %d last %s' % (filename, ret[0], ret[1], ret[2])) + return ret - def write(self, new_config): - hdr_offset, length = self.find_config() - new_config_bytes = open(new_config, 'rb').read() - new_len = len(new_config_bytes) + FILENAME_LEN + 4 - if len(new_config_bytes) > MAX_BOOTCONF_SIZE: - raise Exception("Config is too large (%d bytes). The maximum size is %d bytes." - % (len(new_config_bytes), MAX_BOOTCONF_SIZE)) - if hdr_offset + len(new_config_bytes) + FILE_HDR_LEN > IMAGE_SIZE: + def update(self, src_bytes, dst_filename): + """ + Replaces a modifiable file with specified byte array. + """ + hdr_offset, length, is_last = self.find_file(dst_filename) + if hdr_offset < 0: + raise Exception('Update target %s not found' % dst_filename) + + if hdr_offset + len(src_bytes) + FILE_HDR_LEN > IMAGE_SIZE: raise Exception('EEPROM image size exceeded') + new_len = len(src_bytes) + FILENAME_LEN + 4 struct.pack_into('>L', self._bytes, hdr_offset + 4, new_len) - struct.pack_into(("%ds" % len(new_config_bytes)), self._bytes, - hdr_offset + 4 + FILE_HDR_LEN, new_config_bytes) + struct.pack_into(("%ds" % len(src_bytes)), self._bytes, + hdr_offset + 4 + FILE_HDR_LEN, src_bytes) - # If the new config is smaller than the old config then set any old + # If the new file is smaller than the old file then set any old # data which is now unused to all ones (erase value) - pad_start = hdr_offset + 4 + FILE_HDR_LEN + len(new_config_bytes) + pad_start = hdr_offset + 4 + FILE_HDR_LEN + len(src_bytes) + + # Add padding up to 8-byte boundary + while pad_start % 8 != 0: + struct.pack_into('B', self._bytes, pad_start, 0xff) + pad_start += 1 + + # Create a padding section unless the padding size is smaller than the + # size of a section head. Padding is allowed in the last section but + # by convention bootconf.txt is the last section and there's no need to + # pad to the end of the sector. This also ensures that the loopback + # config read/write tests produce identical binaries. + pad_bytes = ALIGN_SIZE - (pad_start % ALIGN_SIZE) + if pad_bytes > 8 and not is_last: + pad_bytes -= 8 + struct.pack_into('>i', self._bytes, pad_start, PAD_MAGIC) + pad_start += 4 + struct.pack_into('>i', self._bytes, pad_start, pad_bytes) + pad_start += 4 + + debug("pad %d" % pad_bytes) pad = 0 - while pad < (length - len(new_config_bytes)): + while pad < pad_bytes: struct.pack_into('B', self._bytes, pad_start + pad, 0xff) pad = pad + 1 + def update_key(self, src_pem, dst_filename): + """ + Replaces the specified public key entry with the public key values extracted + from the source PEM file. + """ + pubkey_bytes = pemtobin(src_pem) + self.update(pubkey_bytes, dst_filename) + + def update_file(self, src_filename, dst_filename): + """ + Replaces the contents of dst_filename in the EEPROM with the contents of src_file. + """ + src_bytes = open(src_filename, 'rb').read() + if len(src_bytes) > MAX_FILE_SIZE: + raise Exception("src file %s is too large (%d bytes). The maximum size is %d bytes." + % (src_filename, len(src_bytes), MAX_FILE_SIZE)) + self.update(src_bytes, dst_filename) + + def write(self): + """ + Writes the updated EEPROM image to stdout or the specified output file. + """ if self._out is not None: self._out.write(self._bytes) self._out.close() @@ -246,14 +357,14 @@ class BootloaderImage(object): else: sys.stdout.write(self._bytes) - def get_config(self): - hdr_offset, length = self.find_config() + def get_file(self, filename): + hdr_offset, length, is_last = self.find_file(filename) offset = hdr_offset + 4 + FILE_HDR_LEN config_bytes = self._bytes[offset:offset+length-FILENAME_LEN-4] return config_bytes def read(self): - config_bytes = self.get_config() + config_bytes = self.get_file('bootconf.txt') if self._out is not None: self._out.write(config_bytes) self._out.close() @@ -320,8 +431,21 @@ Operating modes: The default text editor is nano and may be overridden by setting the 'EDITOR' environment variable and passing '-E' to 'sudo' to preserve the environment. -See 'rpi-eeprom-update -h' for more information about the available EEPROM -images. +6. Signing the bootloader config file. + Updates an EEPROM binary with a signed config file (created by rpi-eeprom-digest) plus + the corresponding RSA public key. + + Requires Python Cryptodomex libraries and OpenSSL. To install on Raspberry Pi OS run:- + sudo apt install openssl python-pip + sudo python3 -m pip install cryptodomex + + rpi-eeprom-digest -k private.pem -i bootconf.txt -o bootconf.sig + rpi-eeprom-config --config bootconf.txt --digest bootconf.sig --pubkey public.pem --out pieeprom-signed.bin pieeprom.bin + + Currently, the signing process is a separate step so can't be used with the --edit or --apply modes. + + +See 'rpi-eeprom-update -h' for more information about the available EEPROM images. """ parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=description) @@ -331,6 +455,8 @@ images. parser.add_argument('-c', '--config', help='Name of bootloader configuration file', required=False) parser.add_argument('-e', '--edit', action='store_true', default=False, help='Edit the current EEPROM config') parser.add_argument('-o', '--out', help='Name of output file', required=False) + parser.add_argument('-d', '--digest', help='Signed boot only. The name of the .sig file generated by rpi-eeprom-dgst for config.txt ', required=False) + parser.add_argument('-p', '--pubkey', help='Signed boot only. The name of the RSA public key file to store in the EEPROM', required=False) parser.add_argument('eeprom', nargs='?', help='Name of EEPROM file to use as input') args = parser.parse_args() @@ -351,7 +477,12 @@ images. if args.config is not None: if not os.path.exists(args.config): exit_error("config file '%s' not found" % args.config) - image.write(args.config) + image.update_file(args.config, BOOTCONF_TXT) + if args.digest is not None: + image.update_file(args.digest, BOOTCONF_SIG) + if args.pubkey is not None: + image.update_key(args.pubkey, PUBKEY_BIN) + image.write() else: image.read() elif args.config is None and args.eeprom is None: diff --git a/rpi-eeprom-digest b/rpi-eeprom-digest new file mode 100755 index 0000000..58b4843 --- /dev/null +++ b/rpi-eeprom-digest @@ -0,0 +1,108 @@ +#!/bin/sh + +# Helper script to generate .sig files for use with the Raspberry Pi bootloader. + +# This has been implemented in a separate script in order to have avoid having +# a hard dependency on OpenSSL. + +set -e + +OPENSSL=${OPENSSl:-openssl} + +die() { + echo "$@" >&2 + exit 1 +} + +TMP_DIR="" +cleanup() { + if [ -f "${TMP_DIR}" ]; then + rm -rf "${TMP_DIR}" + fi +} + +checkDependencies() { + if ! command -v sha256sum > /dev/null; then + die "sha256sum not found. Try installing the coreutilities package." + fi + + if ! command -v openssl > /dev/null; then + die "openssl not found. Try installing the openssl package." + fi + + if ! command -v xxd > /dev/null; then + die "xxd not found. Try installing the xxd package." + fi +} + +usage() { +cat < "${OUTPUT}" + +# Include the update-timestamp +echo "ts: $(date -u +%s)" >> "${OUTPUT}" + +if [ -n "${KEY}" ]; then + [ -f "${KEY}" ] || die "RSA private \"${KEY}\" not found" + + "${OPENSSL}" dgst -sign "${KEY}" -keyform PEM -sha256 -out "${SIG_TMP}" "${IMAGE}" + echo "rsa2048: $(xxd -c 4096 -p < "${SIG_TMP}")" >> "${OUTPUT}" +fi diff --git a/rpi-eeprom-update b/rpi-eeprom-update index 6866ef5..7b92a5e 100755 --- a/rpi-eeprom-update +++ b/rpi-eeprom-update @@ -186,38 +186,15 @@ applyRecoveryUpdate() findBootFS echo " BOOTFS: ${BOOTFS}" - # A '.sig' file is created so that recovery.bin can check that the - # EEPROM image has not been corrupted (e.g. SD card corruption). - # Format of the .sig file. - # -- - # SHA256\n - # ts: UPDATE-TIMESTAMP\n - # -- - # SHA256 is a 64 character hex string - # UPDATE-TIMESTAMP is an unsigned decimal. - # - # The 'filename' output from sha256 MUST be omitted. if [ -n "${BOOTLOADER_UPDATE_IMAGE}" ]; then [ -f "${BOOTLOADER_UPDATE_IMAGE}" ] || die "${BOOTLOADER_UPDATE_IMAGE} not found" TMP_EEPROM_IMAGE="$(mktemp)" prepareImage - # If recovery.bin encounters pieeprom.upd then it will select it in - # preference to pieeprom.bin. The .upd file also causes recovery.bin - # to rename itself to recovery.000 and reboot if the update is successful. - # The rename causes the ROM to ignore this file and use the newly flashed - # EEPROM image instead. - sha256sum "${TMP_EEPROM_IMAGE}" | awk '{print $1}' > "${BOOTFS}/pieeprom.sig" \ - || die "Failed to create ${BOOTFS}/pieeprom.sig" - # Appends the update creation timestamp on a newline in pieeprom.sig - # During a self-update mode the bootloader examines the update-timestamp - # and will only update itself if it is newer than the current update - # timestamp. - # - # The update-timestamp is independent of the bootloader version and - # does not have to be timestamp. - echo "ts: $(date -u +%s)" >> "${BOOTFS}/pieeprom.sig" + # Generate a .sig file containing the sha256 hash of the EEPROM image + # and the current timestamp. + rpi-eeprom-digest -i "${TMP_EEPROM_IMAGE}" -o "${BOOTFS}/pieeprom.sig" cp -f "${TMP_EEPROM_IMAGE}" "${BOOTFS}/pieeprom.upd" \ || die "Failed to copy ${TMP_EEPROM_IMAGE} to ${BOOTFS}" @@ -228,8 +205,7 @@ applyRecoveryUpdate() fi if [ -n "${VL805_UPDATE_IMAGE}" ]; then - sha256sum "${VL805_UPDATE_IMAGE}" | awk '{print $1}' > "${BOOTFS}/vl805.sig" \ - || die "Failed to create ${BOOTFS}/vl805.sig" + rpi-eeprom-digest -i "${VL805_UPDATE_IMAGE}" -o "${BOOTFS}/vl805.sig" cp -f "${VL805_UPDATE_IMAGE}" "${BOOTFS}/vl805.bin" \ || die "Failed to copy ${VL805_UPDATE_IMAGE} to ${BOOTFS}/vl805.bin" @@ -342,6 +318,10 @@ checkDependencies() { HAVE_VL805_EEPROM=0 fi + if ! command -v rpi-eeprom-digest > /dev/null; then + die "rpi-eeprom-digest not found. Try re-installing the rpi-eeprom package" + fi + if ! command -v lspci > /dev/null; then die "lspci not found. Try installing the pciutils package." fi diff --git a/test/bootconf.sig b/test/bootconf.sig new file mode 100644 index 0000000..05ac78a --- /dev/null +++ b/test/bootconf.sig @@ -0,0 +1,3 @@ +b5b917dc53a59c23035a89d4c58606211a07d4fb6e16bd00d74457a93ea5a264 +ts: 1614092425 +rsa2048: 284d3ef8b960ef9dd0bf5f320eacccb527623890b11136801fcfaf950ef3fb32c9b86ee48337d2cd21e00665a62215ff4d811b15d89867fb55d71b6372a4fa4b79af60cdda84c8a7d9b52d5f2b3026d3b088345d9424842cc2eb73e23f4577e12c74bc3bc1890bfbb6509fc8702cd3a202929bb6f9e5162ea53ec6e8503e08dda7b0c86a90bc21d33bd49535554383fe88e7d1d8e46e27c2bce60cf15b69c56ee27ccdd81ff8f47a616a2796cc2d943e17c6c01ac191f9b7dcda440238062e6c49df01f4bb8b97ce380a46aa29a03bf3fcbede3d76c13d862fc8b60b472a4d1017d5535f5188858a6e1103db2666aa63264e7c1f752b917405527094ed0da737 diff --git a/test/bootconf.txt b/test/bootconf.txt new file mode 100644 index 0000000..b802df6 --- /dev/null +++ b/test/bootconf.txt @@ -0,0 +1,8 @@ +[all] +BOOT_UART=1 +WAKE_ON_GPIO=1 +POWER_OFF_ON_HALT=0 +HDMI_DELAY=0 +# Load firmware and kernel from signed boot.img file +SIGNED_BOOT=1 + diff --git a/test/install b/test/install index 176598a..752d38a 100755 --- a/test/install +++ b/test/install @@ -21,6 +21,7 @@ CONFIG="/etc/default/rpi-eeprom-update" cp -rfv "${FIRMWARE_DIR}"/* /lib/firmware/raspberrypi/bootloader cp -fv "${script_dir}/../rpi-eeprom-config" /usr/bin +cp -fv "${script_dir}/../rpi-eeprom-digest" /usr/bin cp -fv "${script_dir}/../rpi-eeprom-update" /usr/bin rm -f /usr/bin/vl805 diff --git a/test/pieeprom-signed.bin b/test/pieeprom-signed.bin new file mode 100644 index 0000000..6866b20 Binary files /dev/null and b/test/pieeprom-signed.bin differ diff --git a/test/private.pem b/test/private.pem new file mode 100644 index 0000000..3f88c5c --- /dev/null +++ b/test/private.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA+l3E+h/QNjrIR1cG6NpzP0fBwp2UDpuQAafXDS5yryrfCPDY +TO9DvzAfOk9Dz/putDfHV0RTOFXv1tmc4nqOgU6nKx7tTdsjTiY4CgG3vXRMuAmD +GX5ssJFCVmljGuILt1INlCmtun7Ow35VTxOcRDDfrBDKnSitzOTf6KTR7xJhqFFh +dMpIg8hW4bDBKMavyt38pRvDaO1o01qaQT/GgAPmJm27y5RKNAe6iVTqsm4TMAhK +C6P4XyRAbe6OMdFZyEWEk7Asexuc7uZlVHsUI6pebSW/07O+5l/U7/3k6r//hO/H +DFOBUUW55EjzzC1BhTlWHWfZNI+5+NdN8o323QIDAQABAoIBAByQGZKSkhG5w5MV +++ERWQARaurNyPAgsb1qnUdw8t8GlFLkDT07t74mWo2vsNQXpU0Upv6O+jKNZVMc +2P/ijQL2Cu7JtLeC5mR6Sj7kAscPr1f4p9b+/B3puIh8tfSBcOY9a3Spi5sg7+xQ +K6HdoiCKdd4evUrQMwHS47OaKCQuuibm46LWbXO1nk9QkymUy6zyaT5IuNpfKYKD +UdFqV1FNwZ9A2Yb89rweBgU4DWdbjgVqBc23vS9l913rqd2LHN/4+XDBOGrovu5r +mJy4WsyXuT0twuqi7FzhtbCdN/zhLo2od1XK6uA65EKdA9rrRMkNeGvxts6q3fPE +i6tj7OECgYEA/YbIR8n8Vvb5XPAav/aAon4qjXyhkUTjnJfVT0yA+6T1AJwvQ+O4 +AhYgN4ld7msKRDJLcJs0EU8CmWUKJRt5Ai+JsOCbPuBNo+VGEFSsdG0mrSjFZf2e +Bjm41lnvAEWReGwr9MVIf/prDE2/3aUl9irkNdu5q6NpG9M0N7AhzGECgYEA/M8Y +Ew9Nv+XqEVKvOzxKRZBa6yzlOUj5PQ3cD7jl1aUNK4rTucvr3sJZAsgm5j+0XG99 +AJ447zdDEdcQbsOSaBR69pccdHYEaRSiIxWaCAir2BBS5DxYtgB6BLrIfBd1cKHv +qB6u4M6FRJ5BcQa6VYlizAfG2yXoJv0xFrlQ2/0CgYEAwq0Alb+QOOckzCzDHayX +Ui83VbXiCr6vWMtuTJoeYR1l1LYZxTPTVCbRTlP5AN7I310PeMR00uWsxUVE6QGT +hg4i2ONf0oRCmhuwFVIvqqc2D7lC+vIoqfcg69fbIoZJEgNeLXJgHYWZNbVuIzBx +WfnNi13R0O6GA4vGiQyCp4ECgYB1ZTG3wBaJsxlDnBLVPgT7UrJ1nO6A8HsUt/fl +sSXBVRjNjHUPRTutwLAW050EtLZrajYw8EheBVp20VjHJrg47rG/CqLjDd60cSlt +g114t5YdCk+DvuYu9f+zbI0m2rnlaL1iY4UvzZcjKx4Wf1pN2DNxrXbRU0P/vvlp +pPqAfQKBgDZnxWuvRsT9rztGrEottifchfrStZx7u/2+iBtjFeFXr7L4MI14fNm2 +HkoThCpfFXCJFpRxy+kYi6xbPK/Om/hFNs3J5xqheTW8hFx7KN/zPg7jc0MlZ2R/ +uuOgZU9kkzLOamDyP85Doah7kAyA2PnLUno2k4IirbNVoH3aV++G +-----END RSA PRIVATE KEY----- diff --git a/test/public.pem b/test/public.pem new file mode 100644 index 0000000..ea6d4dc --- /dev/null +++ b/test/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+l3E+h/QNjrIR1cG6Npz +P0fBwp2UDpuQAafXDS5yryrfCPDYTO9DvzAfOk9Dz/putDfHV0RTOFXv1tmc4nqO +gU6nKx7tTdsjTiY4CgG3vXRMuAmDGX5ssJFCVmljGuILt1INlCmtun7Ow35VTxOc +RDDfrBDKnSitzOTf6KTR7xJhqFFhdMpIg8hW4bDBKMavyt38pRvDaO1o01qaQT/G +gAPmJm27y5RKNAe6iVTqsm4TMAhKC6P4XyRAbe6OMdFZyEWEk7Asexuc7uZlVHsU +I6pebSW/07O+5l/U7/3k6r//hO/HDFOBUUW55EjzzC1BhTlWHWfZNI+5+NdN8o32 +3QIDAQAB +-----END PUBLIC KEY----- diff --git a/test/test-rpi-eeprom-config b/test/test-rpi-eeprom-config index f758ebe..25acdd7 100755 --- a/test/test-rpi-eeprom-config +++ b/test/test-rpi-eeprom-config @@ -59,6 +59,29 @@ check_reduce_size() } +check_signed_loopback() +{ + echo "check_signed $1 $2" + + image="${script_dir}/$1" + conf="${script_dir}/$2" + digest="${script_dir}/$3" + pubkey="${script_dir}/$4" + + # Replace the config, config.sig and pubkey and verify that the output is the same + TMP_EEPROM="$(mktemp)" + "${script_dir}/../rpi-eeprom-config" \ + "${image}" \ + --config "${conf}" \ + --digest "${digest}" \ + --pubkey "${pubkey}" \ + --out "${TMP_EEPROM}" + + expected_md5="$(md5sum "${image}" | awk '{print $1}')" + actual_md5="$(md5sum "${TMP_EEPROM}" | awk '{print $1}')" + [ "${actual_md5}" = "${expected_md5}" ] || die "EEPROM signed-loopback: checksum mismatch" +} + check_loopback() { echo "check_loopback $1 $2" @@ -148,6 +171,11 @@ for ver in ${versions}; do cleanup done +echo "Test lookback with a signed EEPROM image" +check_loopback pieeprom-signed.bin bootconf.txt +check_signed_loopback pieeprom-signed.bin bootconf.txt bootconf.sig public.pem +cleanup + check_update "../firmware/old/beta/pieeprom-2019-07-15.bin" "pieeprom-2019-07-15-freeze.bin" "bootconf-2019-07-15-freeze.txt" cleanup