Merge branch 'master' into debian/bullseye

This commit is contained in:
Serge Schneider
2021-11-23 22:26:18 +00:00
13 changed files with 357 additions and 58 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -3,6 +3,10 @@
USB MSD boot also requires the firmware from Raspberry Pi OS 2020-08-20 or newer. 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 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 ## 2021-10-27 - Secure boot improvements - BETA
* Improve the error logging if a file is too large and truncated. * Improve the error logging if a file is too large and truncated.
* Increase the maximum size of the ramdisk to 96MB. * Increase the maximum size of the ramdisk to 96MB.

View File

@@ -8,6 +8,7 @@ import argparse
import atexit import atexit
import os import os
import subprocess import subprocess
import string
import struct import struct
import sys import sys
import tempfile import tempfile
@@ -15,7 +16,12 @@ import time
IMAGE_SIZE = 512 * 1024 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 # Each section starts with a magic number followed by a 32 bit offset to the
# next section (big-endian). # 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 # The last 4KB of the EEPROM image is reserved for internal use by the
# bootloader and may be overwritten during the update process. # bootloader and may be overwritten during the update process.
MAGIC = 0x55aaf00f MAGIC = 0x55aaf00f
PAD_MAGIC = 0x55aafeef
MAGIC_MASK = 0xfffff00f 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 FILE_HDR_LEN = 20
FILENAME_LEN = 12 FILENAME_LEN = 12
TEMP_DIR = None TEMP_DIR = None
DEBUG = False
def debug(s):
if DEBUG:
sys.stderr.write(s + '\n')
def rpi4(): def rpi4():
compatible_path = "/sys/firmware/devicetree/base/compatible" compatible_path = "/sys/firmware/devicetree/base/compatible"
if os.path.exists(compatible_path): if os.path.exists(compatible_path):
@@ -59,6 +71,25 @@ def create_tempdir():
if TEMP_DIR is None: if TEMP_DIR is None:
TEMP_DIR = tempfile.mkdtemp() 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): def exit_error(msg):
""" """
Trapped a fatal error, output message to stderr and exit with non-zero 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: else:
eeprom_image = get_latest_eeprom() eeprom_image = get_latest_eeprom()
create_tempdir() create_tempdir()
# Replace the contents of bootconf.txt with the contents of the config file
tmp_update = os.path.join(TEMP_DIR, 'pieeprom.upd') tmp_update = os.path.join(TEMP_DIR, 'pieeprom.upd')
image = BootloaderImage(eeprom_image, tmp_update) image = BootloaderImage(eeprom_image, tmp_update)
image.write(config) image.update_file(config, BOOTCONF_TXT)
image.write()
config_str = open(config).read() config_str = open(config).read()
if config_src is None: if config_src is None:
config_src = '' config_src = ''
sys.stdout.write("Updating bootloader EEPROM\n image: %s\nconfig_src: %s\nconfig: %s\n%s\n%s\n%s\n" % 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)) (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 # Ignore APT package checksums so that this doesn't fail when used
# with EEPROMs with configs delivered outside of APT. # with EEPROMs with configs delivered outside of APT.
# The checksums are really just a safety check for automatic updates. # 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): if os.path.exists(pending):
config_src = pending config_src = pending
image = BootloaderImage(pending) image = BootloaderImage(pending)
current_config = image.get_config().decode('utf-8') current_config = image.get_file(BOOTCONF_TXT).decode('utf-8')
else: else:
current_config, config_src = read_current_config() current_config, config_src = read_current_config()
@@ -178,6 +215,14 @@ def read_current_config():
return (shell_cmd(['vcgencmd', 'bootloader_config']), "vcgencmd bootloader_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): class BootloaderImage(object):
def __init__(self, filename, output=None): def __init__(self, filename, output=None):
""" """
@@ -185,6 +230,7 @@ class BootloaderImage(object):
and optionally an output filename. and optionally an output filename.
""" """
self._filename = filename self._filename = filename
self._sections = []
try: try:
self._bytes = bytearray(open(filename, 'rb').read()) self._bytes = bytearray(open(filename, 'rb').read())
except IOError as err: except IOError as err:
@@ -196,47 +242,112 @@ class BootloaderImage(object):
if len(self._bytes) != IMAGE_SIZE: if len(self._bytes) != IMAGE_SIZE:
exit_error("%s: Expected size %d bytes actual size %d bytes" % exit_error("%s: Expected size %d bytes actual size %d bytes" %
(filename, IMAGE_SIZE, len(self._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 offset = 0
magic = 0 magic = 0
found = False
while offset < IMAGE_SIZE: while offset < IMAGE_SIZE:
magic, length = struct.unpack_from('>LL', self._bytes, offset) magic, length = struct.unpack_from('>LL', self._bytes, offset)
if (magic & MAGIC_MASK) != MAGIC: if magic == 0x0 or magic == 0xffffffff:
raise Exception('EEPROM is corrupted') 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 if magic == FILE_MAGIC: # Found a file
name = self._bytes[offset + 8: offset + FILE_HDR_LEN] # Discard trailing null characters used to pad filename
if name.decode('utf-8') == 'bootconf.txt': filename = self._bytes[offset + 8: offset + FILE_HDR_LEN].decode('utf-8').replace('\0', '')
return (offset, length) self._sections.append(ImageSection(magic, offset, length, filename))
offset += 8 + length # length + type offset += 8 + length # length + type
offset = (offset + 7) & ~7 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): def update(self, src_bytes, dst_filename):
hdr_offset, length = self.find_config() """
new_config_bytes = open(new_config, 'rb').read() Replaces a modifiable file with specified byte array.
new_len = len(new_config_bytes) + FILENAME_LEN + 4 """
if len(new_config_bytes) > MAX_BOOTCONF_SIZE: hdr_offset, length, is_last = self.find_file(dst_filename)
raise Exception("Config is too large (%d bytes). The maximum size is %d bytes." if hdr_offset < 0:
% (len(new_config_bytes), MAX_BOOTCONF_SIZE)) raise Exception('Update target %s not found' % dst_filename)
if hdr_offset + len(new_config_bytes) + FILE_HDR_LEN > IMAGE_SIZE:
if hdr_offset + len(src_bytes) + FILE_HDR_LEN > IMAGE_SIZE:
raise Exception('EEPROM image size exceeded') 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('>L', self._bytes, hdr_offset + 4, new_len)
struct.pack_into(("%ds" % len(new_config_bytes)), self._bytes, struct.pack_into(("%ds" % len(src_bytes)), self._bytes,
hdr_offset + 4 + FILE_HDR_LEN, new_config_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) # 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 pad = 0
while pad < (length - len(new_config_bytes)): while pad < pad_bytes:
struct.pack_into('B', self._bytes, pad_start + pad, 0xff) struct.pack_into('B', self._bytes, pad_start + pad, 0xff)
pad = pad + 1 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: if self._out is not None:
self._out.write(self._bytes) self._out.write(self._bytes)
self._out.close() self._out.close()
@@ -246,14 +357,14 @@ class BootloaderImage(object):
else: else:
sys.stdout.write(self._bytes) sys.stdout.write(self._bytes)
def get_config(self): def get_file(self, filename):
hdr_offset, length = self.find_config() hdr_offset, length, is_last = self.find_file(filename)
offset = hdr_offset + 4 + FILE_HDR_LEN offset = hdr_offset + 4 + FILE_HDR_LEN
config_bytes = self._bytes[offset:offset+length-FILENAME_LEN-4] config_bytes = self._bytes[offset:offset+length-FILENAME_LEN-4]
return config_bytes return config_bytes
def read(self): def read(self):
config_bytes = self.get_config() config_bytes = self.get_file('bootconf.txt')
if self._out is not None: if self._out is not None:
self._out.write(config_bytes) self._out.write(config_bytes)
self._out.close() self._out.close()
@@ -320,8 +431,21 @@ Operating modes:
The default text editor is nano and may be overridden by setting the 'EDITOR' 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. environment variable and passing '-E' to 'sudo' to preserve the environment.
See 'rpi-eeprom-update -h' for more information about the available EEPROM 6. Signing the bootloader config file.
images. 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, parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
description=description) description=description)
@@ -331,6 +455,8 @@ images.
parser.add_argument('-c', '--config', help='Name of bootloader configuration file', required=False) 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('-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('-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') parser.add_argument('eeprom', nargs='?', help='Name of EEPROM file to use as input')
args = parser.parse_args() args = parser.parse_args()
@@ -351,7 +477,12 @@ images.
if args.config is not None: if args.config is not None:
if not os.path.exists(args.config): if not os.path.exists(args.config):
exit_error("config file '%s' not found" % 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: else:
image.read() image.read()
elif args.config is None and args.eeprom is None: elif args.config is None and args.eeprom is None:

108
rpi-eeprom-digest Executable file
View File

@@ -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 <<EOF
rpi-eeprom-digest [-k RSA_KEY] -i IMAGE -o OUTPUT
Creates a .sig file containing the sha256 digest of the IMAGE and an optional
RSA signature of that hash.
Options:
-i The source image.
-o The name of the digest/signature file.
-k Optional RSA private key.
RSA signing
If a private key in PEM format is supplied then the RSA signature of the
sha256 digest is included in the .sig file. Currently, the bootloader only
supports sha256 digests signed with a 2048bit RSA key.
The bootloader only verifies RSA signatures in signed boot mode
(not available yet) and only for the EEPROM config file and the signed image.
Examples:
# Generate RSA signature for the EEPROM config file.
rpi-eeprom-digest -k key.pem -i bootconf.txt -o bootconf.sig
# Generate the normal sha256 hash to guard against file-system corruption
rpi-eeprom-digest -i pieeprom.bin -o pieeprom.sig
rpi-eeprom-digest -i vl805.bin -o vl805.sig
EOF
exit 0
}
OUTPUT=""
while getopts i:k:ho: option; do
case "${option}" in
i) IMAGE="${OPTARG}"
;;
k) KEY="${OPTARG}"
;;
o) OUTPUT="${OPTARG}"
;;
h) usage
;;
*) echo "Unknown argument \"${option}\""
usage
;;
esac
done
[ -n "${IMAGE}" ] || usage
[ -n "${OUTPUT}" ] || usage
trap cleanup EXIT
checkDependencies
[ -f "${IMAGE}" ] || die "Source image \"${IMAGE}\" not found"
TMP_DIR=$(mktemp -d)
SIG_TMP="${TMP_DIR}/tmp.sig"
sha256sum "${IMAGE}" | awk '{print $1}' > "${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

View File

@@ -186,38 +186,15 @@ applyRecoveryUpdate()
findBootFS findBootFS
echo " BOOTFS: ${BOOTFS}" 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 if [ -n "${BOOTLOADER_UPDATE_IMAGE}" ]; then
[ -f "${BOOTLOADER_UPDATE_IMAGE}" ] || die "${BOOTLOADER_UPDATE_IMAGE} not found" [ -f "${BOOTLOADER_UPDATE_IMAGE}" ] || die "${BOOTLOADER_UPDATE_IMAGE} not found"
TMP_EEPROM_IMAGE="$(mktemp)" TMP_EEPROM_IMAGE="$(mktemp)"
prepareImage 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 # Generate a .sig file containing the sha256 hash of the EEPROM image
# During a self-update mode the bootloader examines the update-timestamp # and the current timestamp.
# and will only update itself if it is newer than the current update rpi-eeprom-digest -i "${TMP_EEPROM_IMAGE}" -o "${BOOTFS}/pieeprom.sig"
# 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"
cp -f "${TMP_EEPROM_IMAGE}" "${BOOTFS}/pieeprom.upd" \ cp -f "${TMP_EEPROM_IMAGE}" "${BOOTFS}/pieeprom.upd" \
|| die "Failed to copy ${TMP_EEPROM_IMAGE} to ${BOOTFS}" || die "Failed to copy ${TMP_EEPROM_IMAGE} to ${BOOTFS}"
@@ -228,8 +205,7 @@ applyRecoveryUpdate()
fi fi
if [ -n "${VL805_UPDATE_IMAGE}" ]; then if [ -n "${VL805_UPDATE_IMAGE}" ]; then
sha256sum "${VL805_UPDATE_IMAGE}" | awk '{print $1}' > "${BOOTFS}/vl805.sig" \ rpi-eeprom-digest -i "${VL805_UPDATE_IMAGE}" -o "${BOOTFS}/vl805.sig"
|| die "Failed to create ${BOOTFS}/vl805.sig"
cp -f "${VL805_UPDATE_IMAGE}" "${BOOTFS}/vl805.bin" \ cp -f "${VL805_UPDATE_IMAGE}" "${BOOTFS}/vl805.bin" \
|| die "Failed to copy ${VL805_UPDATE_IMAGE} to ${BOOTFS}/vl805.bin" || die "Failed to copy ${VL805_UPDATE_IMAGE} to ${BOOTFS}/vl805.bin"
@@ -342,6 +318,10 @@ checkDependencies() {
HAVE_VL805_EEPROM=0 HAVE_VL805_EEPROM=0
fi 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 if ! command -v lspci > /dev/null; then
die "lspci not found. Try installing the pciutils package." die "lspci not found. Try installing the pciutils package."
fi fi

3
test/bootconf.sig Normal file
View File

@@ -0,0 +1,3 @@
b5b917dc53a59c23035a89d4c58606211a07d4fb6e16bd00d74457a93ea5a264
ts: 1614092425
rsa2048: 284d3ef8b960ef9dd0bf5f320eacccb527623890b11136801fcfaf950ef3fb32c9b86ee48337d2cd21e00665a62215ff4d811b15d89867fb55d71b6372a4fa4b79af60cdda84c8a7d9b52d5f2b3026d3b088345d9424842cc2eb73e23f4577e12c74bc3bc1890bfbb6509fc8702cd3a202929bb6f9e5162ea53ec6e8503e08dda7b0c86a90bc21d33bd49535554383fe88e7d1d8e46e27c2bce60cf15b69c56ee27ccdd81ff8f47a616a2796cc2d943e17c6c01ac191f9b7dcda440238062e6c49df01f4bb8b97ce380a46aa29a03bf3fcbede3d76c13d862fc8b60b472a4d1017d5535f5188858a6e1103db2666aa63264e7c1f752b917405527094ed0da737

8
test/bootconf.txt Normal file
View File

@@ -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

View File

@@ -21,6 +21,7 @@ CONFIG="/etc/default/rpi-eeprom-update"
cp -rfv "${FIRMWARE_DIR}"/* /lib/firmware/raspberrypi/bootloader cp -rfv "${FIRMWARE_DIR}"/* /lib/firmware/raspberrypi/bootloader
cp -fv "${script_dir}/../rpi-eeprom-config" /usr/bin 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 cp -fv "${script_dir}/../rpi-eeprom-update" /usr/bin
rm -f /usr/bin/vl805 rm -f /usr/bin/vl805

BIN
test/pieeprom-signed.bin Normal file

Binary file not shown.

27
test/private.pem Normal file
View File

@@ -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-----

9
test/public.pem Normal file
View File

@@ -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-----

View File

@@ -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() check_loopback()
{ {
echo "check_loopback $1 $2" echo "check_loopback $1 $2"
@@ -148,6 +171,11 @@ for ver in ${versions}; do
cleanup cleanup
done 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" check_update "../firmware/old/beta/pieeprom-2019-07-15.bin" "pieeprom-2019-07-15-freeze.bin" "bootconf-2019-07-15-freeze.txt"
cleanup cleanup