Skip to content

Commit

Permalink
workflows: Add workflow for DFU sanity check
Browse files Browse the repository at this point in the history
This patch adds a GitHub workflow that checks the DFU images for
correctness. For bootloader and application images, the workflow
verifies the signature of the image. Furthermore, the workflow
compares the partition table of the image with an old version.

Signed-off-by: Maximilian Deubel <[email protected]>
  • Loading branch information
maxd-nordic committed May 31, 2024
1 parent 9b494b7 commit 52cd0cd
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 0 deletions.
89 changes: 89 additions & 0 deletions .github/workflows/dfu_check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: DFU image compatibility check

on:
push:
branches:
- main
pull_request:

jobs:
build:
name: Build and analyze
runs-on: ubuntu-latest
container: ghcr.io/zephyrproject-rtos/ci:v0.26.12
env:
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory
CMAKE_PREFIX_PATH: /opt/toolchains
steps:
- name: Checkout
uses: actions/checkout@v3
with:
path: thingy91x-oob

- name: Initialize
working-directory: thingy91x-oob
run: |
west init -l .
west config manifest.group-filter +bsec
west update -o=--depth=1 -n
- name: Install dependencies
run: |
pip install -r nrf/scripts/requirements-build.txt
- name: Save paths
run: |
echo "CI_PROJECT_DIR=$(pwd)/thingy91x-oob" >> $GITHUB_ENV
echo "CI_NRF_DIR=$(pwd)/nrf" >> $GITHUB_ENV
echo "CI_ZEPHYR_DIR=$(pwd)/zephyr" >> $GITHUB_ENV
echo "CI_MCUBOOT_DIR=$(pwd)/bootloader/mcuboot" >> $GITHUB_ENV
- name: 'generate nsib verifying keys'
working-directory: thingy91x-oob
run: |
python3 ${CI_NRF_DIR}/scripts/bootloader/keygen.py --public --in ${CI_NRF_DIR}/boards/nordic/thingy91x/nsib_signing_key.pem --out verifying_key_nrf91.pem
python3 ${CI_NRF_DIR}/scripts/bootloader/keygen.py --public --in ${CI_NRF_DIR}/boards/nordic/thingy91x/nsib_signing_key_nrf5340.pem --out verifying_key_nrf53.pem
- name: Build OOB FW
working-directory: thingy91x-oob
run: |
west twister -T . -v -p thingy91x/nrf9151/ns --inline-logs
- name: 'nrf91: check partition layout'
working-directory: thingy91x-oob/twister-out/thingy91x_nrf9151_ns/app/app.build/
run: |
ninja partition_manager_report > partition_manager_report.txt
sed -i '1d' partition_manager_report.txt
diff partition_manager_report.txt ${CI_PROJECT_DIR}/scripts/pmr_nrf91.txt
- name: 'nrf91: check app image signature'
working-directory: thingy91x-oob
run: |
python3 ${CI_MCUBOOT_DIR}/scripts/imgtool.py verify -k ${CI_MCUBOOT_DIR}/root-ec-p256.pem twister-out/thingy91x_nrf9151_ns/app/app.build/zephyr/app_update.bin
- name: 'nrf91: check bootloader image signature'
working-directory: thingy91x-oob
run: |
python3 scripts/nsib_signature_check.py -i twister-out/thingy91x_nrf9151_ns/app/app.build/zephyr/signed_by_b0_s0_image.hex -p verifying_key_nrf91.pem -a 0x00008200
- name: Build Connectivity Bridge
working-directory: nrf/applications/connectivity_bridge
run: |
west twister -T ${CI_NRF_DIR}/applications/connectivity_bridge -v -p thingy91x/nrf5340/cpuapp --inline-logs
- name: 'nrf53: check partition layout'
working-directory: thingy91x-oob/twister-out/thingy91x_nrf5340_cpuapp/applications.connectivity_bridge/
run: |
ninja partition_manager_report > partition_manager_report.txt
sed -i '1d' partition_manager_report.txt
diff partition_manager_report.txt ${CI_PROJECT_DIR}/scripts/pmr_nrf53.txt
- name: 'nrf53: check app image signature'
working-directory: thingy91x-oob
run: |
python3 ${CI_MCUBOOT_DIR}/scripts/imgtool.py verify -k ${CI_MCUBOOT_DIR}/root-rsa-2048.pem twister-out/thingy91x_nrf5340_cpuapp/applications.connectivity_bridge/zephyr/app_update.bin
- name: 'nrf91: check bootloader image signature'
working-directory: thingy91x-oob
run: |
python3 scripts/nsib_signature_check.py -i twister-out/thingy91x_nrf5340_cpuapp/applications.connectivity_bridge/zephyr/signed_by_b0_s0_image.hex -p verifying_key_nrf53.pem -a 0x00008200
71 changes: 71 additions & 0 deletions scripts/nsib_signature_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause


from intelhex import IntelHex

import hashlib
import argparse
import struct
import ecdsa

def parse_args():
parser = argparse.ArgumentParser(
description='Check NSIB signature of a given file',
formatter_class=argparse.RawDescriptionHelpFormatter,
allow_abbrev=False)
parser.add_argument('-i', '--input', required=True, type=argparse.FileType('r', encoding='UTF-8'),
help='Input hex file.')
parser.add_argument('-p', '--public-key', required=True, type=argparse.FileType('r', encoding='UTF-8'),
help='Public key file (PEM).')
parser.add_argument('-a', '--start-address', required=False, type=str, default=None, help='Start address of the image (hex).')

args = parser.parse_args()
return args

def check_signature(hex_file, public_key, start_address):
public_key_bytes_expected = public_key.to_string()
image = IntelHex(hex_file)

signature_locations = []
offset = 0
while True:
offset = image.find(b'\xde\xe6\x1e\x28\x83\x84\x51\x86', start=offset+1)
if offset == -1:
break
signature_locations.append(offset)

for offset in signature_locations:
validation_info_version = image[offset + 8]
info_hardware_id = image[offset + 9]
validation_info_crypto_id = image[offset + 10]
info_magic_compatibility_id = image[offset + 11]
starting_address = struct.unpack('<I', image.tobinstr(start=offset + 12, size=4))[0]
hash_bytes = image.tobinstr(start=offset + 16, size=32)
public_key_bytes = image.tobinstr(start=offset + 48, size=64)
signature_bytes = image.tobinstr(start=offset + 112, size=64)

if start_address is not None and start_address != starting_address:
raise Exception(f"Signature found at address 0x{starting_address:08x} but expected 0x{start_address:08x}")

if public_key_bytes != public_key_bytes_expected:
print(f"Image has signature for different public key: {public_key_bytes.hex()}")
continue
public_key.verify(signature_bytes, hash_bytes, hashfunc=hashlib.sha256)
print(f"Signature verified for image starting at address 0x{starting_address:08x}")
return
raise Exception("No matching signature found in the image.")


def main():

args = parse_args()
public_key = ecdsa.VerifyingKey.from_pem(args.public_key.read())

check_signature(args.input, public_key, int(args.start_address, 16))

if __name__ == '__main__':
main()
57 changes: 57 additions & 0 deletions scripts/pmr_nrf53.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
flash_primary (0x100000 - 1024kB):
+--------------------------------------------------+
+---0x0: b0_container (0x8000 - 32kB)--------------+
| 0x0: b0 (0x8000 - 32kB) |
+---0x8000: s0 (0x14000 - 80kB)--------------------+
| 0x8000: s0_pad (0x200 - 512B) |
+---0x8200: s0_image (0x13e00 - 79kB)--------------+
| 0x8200: mcuboot (0x13e00 - 79kB) |
+---0x1c000: s1 (0x14000 - 80kB)-------------------+
| 0x1c000: s1_pad (0x200 - 512B) |
| 0x1c200: s1_image (0x13e00 - 79kB) |
+---0x30000: mcuboot_primary (0x64000 - 400kB)-----+
| 0x30000: mcuboot_pad (0x200 - 512B) |
+---0x30200: app_image (0x63e00 - 399kB)-----------+
+---0x30200: mcuboot_primary_app (0x63e00 - 399kB)-+
| 0x30200: app (0x63e00 - 399kB) |
+--------------------------------------------------+
| 0x94000: mcuboot_secondary (0x64000 - 400kB) |
| 0xf8000: EMPTY_1 (0x4000 - 16kB) |
| 0xfc000: settings_storage (0x2000 - 8kB) |
| 0xfe000: EMPTY_0 (0x2000 - 8kB) |
+--------------------------------------------------+

otp (0x2fc - 764B):
+------------------------------------+
| 0xff8100: provision (0x280 - 640B) |
| 0xff8380: otp (0x7c - 124B) |
+------------------------------------+

ram_flash (0x40000 - 256kB):
+------------------------------------------+
| 0x0: mcuboot_primary_1 (0x40000 - 256kB) |
| 0x40000: ram_flash (0x0 - 0B) |
+------------------------------------------+

sram_primary (0x80000 - 512kB):
+-----------------------------------------------+
| 0x20000000: pcd_sram (0x2000 - 8kB) |
| 0x20002000: sram_primary (0x6dc00 - 439kB) |
| 0x2006fc00: rpmsg_nrf53_sram (0x10000 - 64kB) |
| 0x2007fc00: sram_retained_mem (0x400 - 1kB) |
+-----------------------------------------------+

CPUNET flash_primary (0x40000 - 256kB):
+--------------------------------------------+
+---0x1000000: b0n_container (0x8800 - 34kB)-+
| 0x1000000: b0n (0x8580 - 33kB) |
| 0x1008580: provision (0x280 - 640B) |
+---0x1008800: app (0x37800 - 222kB)---------+
| 0x1008800: hci_ipc (0x37800 - 222kB) |
+--------------------------------------------+

CPUNET sram_primary (0x10000 - 64kB):
+-------------------------------------------+
| 0x21000000: sram_primary (0x10000 - 64kB) |
+-------------------------------------------+

49 changes: 49 additions & 0 deletions scripts/pmr_nrf91.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
external_flash (0x2000000 - 32768kB):
+---------------------------------------------+
+---0x0: external_flash (0x2000000 - 32768kB)-+
| 0x0: mcuboot_secondary (0xd0000 - 832kB) |
+---0xd0000: nonsecure_storage (0x2000 - 8kB)-+
| 0xd0000: settings_storage (0x2000 - 8kB) |
+---------------------------------------------+

flash_primary (0x100000 - 1024kB):
+--------------------------------------------------+
+---0x0: b0_container (0x8000 - 32kB)--------------+
| 0x0: b0 (0x8000 - 32kB) |
+---0x8000: s0 (0x14000 - 80kB)--------------------+
| 0x8000: s0_pad (0x200 - 512B) |
+---0x8200: s0_image (0x13e00 - 79kB)--------------+
| 0x8200: mcuboot (0x13e00 - 79kB) |
+---0x1c000: s1 (0x14000 - 80kB)-------------------+
| 0x1c000: s1_pad (0x200 - 512B) |
| 0x1c200: s1_image (0x13e00 - 79kB) |
+---0x30000: mcuboot_primary (0xd0000 - 832kB)-----+
+---0x30000: tfm_secure (0x8000 - 32kB)------------+
| 0x30000: mcuboot_pad (0x200 - 512B) |
+---0x30200: app_image (0xcfe00 - 831kB)-----------+
+---0x30200: mcuboot_primary_app (0xcfe00 - 831kB)-+
| 0x30200: tfm (0x7e00 - 31kB) |
+---0x38000: tfm_nonsecure (0xc8000 - 800kB)-------+
| 0x38000: app (0xc8000 - 800kB) |
+--------------------------------------------------+

otp (0x2f4 - 756B):
+------------------------------------+
| 0xff8108: provision (0x280 - 640B) |
| 0xff8388: otp (0x74 - 116B) |
+------------------------------------+

sram_primary (0x40000 - 256kB):
+-------------------------------------------------+
+---0x20000000: mcuboot_sram (0xa000 - 40kB)------+
+---0x20000000: sram_secure (0xa000 - 40kB)-------+
| 0x20000000: tfm_sram (0xa000 - 40kB) |
+---0x2000a000: sram_nonsecure (0x36000 - 216kB)--+
+---0x2000a000: nrf_modem_lib_sram (0x24e8 - 9kB)-+
| 0x2000a000: nrf_modem_lib_ctrl (0x4e8 - 1kB) |
| 0x2000a4e8: nrf_modem_lib_tx (0x1000 - 4kB) |
| 0x2000b4e8: nrf_modem_lib_rx (0x1000 - 4kB) |
+-------------------------------------------------+
| 0x2000c4e8: sram_primary (0x33b18 - 206kB) |
+-------------------------------------------------+

0 comments on commit 52cd0cd

Please sign in to comment.