Skip to content

Commit

Permalink
tests: add tests for KMU and keys provisioning
Browse files Browse the repository at this point in the history
Add two test applications with test scenarios automated with pytest.
Keys are provisioned with the `west ncs-provision` command.
The tests verify if the applications boot and if the keys are correct.

Signed-off-by: Grzegorz Chwierut <[email protected]>
  • Loading branch information
gchwier authored and rlubos committed Nov 4, 2024
1 parent 829e4fa commit d3fc0cc
Show file tree
Hide file tree
Showing 20 changed files with 469 additions and 0 deletions.
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,7 @@
/tests/subsys/emds/ @balaklaka @nrfconnect/ncs-paladin
/tests/subsys/event_manager_proxy/ @nrfconnect/ncs-si-muffin
/tests/subsys/fw_info/ @nrfconnect/ncs-pluto
/tests/subsys/kmu/ @nrfconnect/ncs-pluto
/tests/subsys/mpsl/ @nrfconnect/ncs-dragoon
/tests/subsys/net/lib/aws_*/ @nrfconnect/ncs-cia
/tests/subsys/net/lib/azure_iot_hub/ @nrfconnect/ncs-cia
Expand Down
12 changes: 12 additions & 0 deletions tests/subsys/kmu/hello_for_kmu/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(hello_for_kmu)

target_sources(app PRIVATE src/main.c)
1 change: 1 addition & 0 deletions tests/subsys/kmu/hello_for_kmu/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# nothing here
6 changes: 6 additions & 0 deletions tests/subsys/kmu/hello_for_kmu/sb_secondary_key.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#
# Copyright (c) 2024 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="\${APPLICATION_CONFIG_DIR}/../keys/root-ed25519-2.pem"
6 changes: 6 additions & 0 deletions tests/subsys/kmu/hello_for_kmu/sb_wrong_key.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#
# Copyright (c) 2024 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="\${APPLICATION_CONFIG_DIR}/../keys/root-ed25519-w.pem"
14 changes: 14 additions & 0 deletions tests/subsys/kmu/hello_for_kmu/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA.
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/

#include <stdio.h>

int main(void)
{
printf("Hello World! %s\n", CONFIG_BOARD_TARGET);

return 0;
}
7 changes: 7 additions & 0 deletions tests/subsys/kmu/hello_for_kmu/sysbuild.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#
# Copyright (c) 2024 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
SB_CONFIG_BOOTLOADER_MCUBOOT=y
SB_CONFIG_BOOT_SIGNATURE_TYPE_ED25519=y
13 changes: 13 additions & 0 deletions tests/subsys/kmu/hello_for_kmu/sysbuild/mcuboot.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#
# Copyright (c) 2024 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
CONFIG_NRF_SECURITY=y
CONFIG_MBEDTLS=n
CONFIG_BOOT_ED25519_PSA=y
CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x10000
CONFIG_BOOT_SIGNATURE_USING_KMU=y

# can be removed after merging #18487
CONFIG_MBEDTLS_THREADING_C=n
19 changes: 19 additions & 0 deletions tests/subsys/kmu/hello_for_kmu/testcase.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
common:
sysbuild: true
timeout: 180
tags: pytest mcuboot kmu
platform_allow:
- nrf54l15dk/nrf54l15/cpuapp
harness: pytest
harness_config:
pytest_dut_scope: session
pytest_root:
- "../pytest/test_kmu_with_mcuboot.py"
tests:
mcuboot.kmu.west.provision.default_key: {}
mcuboot.kmu.west.provision.secondary_key:
extra_args:
- SB_EXTRA_CONF_FILE=sb_secondary_key.conf
mcuboot.kmu.west.provision.wrong_key:
extra_args:
- SB_EXTRA_CONF_FILE=sb_wrong_key.conf
3 changes: 3 additions & 0 deletions tests/subsys/kmu/keys/root-ed25519-1.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIG5zv1wuAZJttuHXngrRJfi1w536UDDKra71UXroQ5z/
-----END PRIVATE KEY-----
3 changes: 3 additions & 0 deletions tests/subsys/kmu/keys/root-ed25519-2.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEII9wFheJa4Lw7fAtmjp1GkonRMknzfJFEdZkTf94jyak
-----END PRIVATE KEY-----
3 changes: 3 additions & 0 deletions tests/subsys/kmu/keys/root-ed25519-w.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIAGMROMZRAwsLq7pWKOsumPPKOKVfEjAydgAhaaVOi7s
-----END PRIVATE KEY-----
67 changes: 67 additions & 0 deletions tests/subsys/kmu/pytest/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
from __future__ import annotations

import logging
import shlex
import subprocess

from pathlib import Path

logger = logging.getLogger(__name__)

APP_KEYS_FOR_KMU = Path(__file__).resolve().parent.parent / 'keys'


def run_command(command: list[str], timeout: int = 30):
logger.info(f"CMD: {shlex.join(command)}")
ret: subprocess.CompletedProcess = subprocess.run(
command, text=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, timeout=timeout)
if ret.returncode:
logger.error(f"Failed command: {shlex.join(command)}")
logger.error(ret.stdout)
raise subprocess.CalledProcessError(ret.returncode, command)


def erase_board(dev_id: str | None):
command = [
'nrfutil', 'device', 'erase'
]
if dev_id:
command.extend(['--serial-number', dev_id])
run_command(command)


def flash_board(build_dir: Path | str, dev_id: str | None, erase: bool = False):
logger.info("Flash the board.")
command = [
'west', 'flash', '--skip-rebuild',
'-d', str(build_dir)
]
if dev_id:
command.extend(['--dev-id', dev_id])
if erase:
command.extend(['--erase'])
run_command(command)


def provision_keys_for_kmu(dev_id: str | None, key1: str | Path,
key2: str | Path | None = None,
key3: str | Path | None = None):
logger.info("Provision keys using west command. Erase the board first")
erase_board(dev_id)
command = [
'west', 'ncs-provision', 'upload',
'--soc', 'nrf54l15',
'--key', str(key1)
]
if key2:
command.extend(['--key', str(key2)])
if key3:
command.extend(['--key', str(key3)])
if dev_id:
command.extend(['--dev-id', dev_id])
run_command(command)
logger.info("Keys provisioned successfully")
12 changes: 12 additions & 0 deletions tests/subsys/kmu/pytest/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
import pytest
import logging

logger = logging.getLogger(__name__)


@pytest.fixture(scope='function', autouse=True)
def test_log(request: pytest.FixtureRequest):
logging.info("========= Test '{}' STARTED".format(request.node.nodeid))
69 changes: 69 additions & 0 deletions tests/subsys/kmu/pytest/test_kmu_key_provision.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
from __future__ import annotations

import logging

from pathlib import Path
from twister_harness import DeviceAdapter
from twister_harness.helpers.utils import match_lines, find_in_config
from common import (
provision_keys_for_kmu,
flash_board,
APP_KEYS_FOR_KMU
)

logger = logging.getLogger(__name__)


def test_kmu_correct_keys_uploaded(dut: DeviceAdapter):
"""
Upload valid keys to DUT using west ncs-provission command
and verify it in application.
"""
zephyr_base = find_in_config(dut.device_config.build_dir / 'CMakeCache.txt', 'ZEPHYR_BASE:PATH')
default_key = Path(zephyr_base).parent / 'bootloader' / 'mcuboot' / 'root-ed25519.pem'
provision_keys_for_kmu(dut.device_config.id,
key1=default_key,
key2=APP_KEYS_FOR_KMU / 'root-ed25519-1.pem',
key3=APP_KEYS_FOR_KMU / 'root-ed25519-2.pem')

logger.info("Flash the board once again and check if keys are verified")
dut.clear_buffer()
flash_board(dut.device_config.build_dir, dut.device_config.id)

lines = dut.readlines_until(
regex='Key 2 failed|Key 2 verified|PSA crypto init failed',
print_output=True, timeout=20)

match_lines(lines, [
'Default key verified',
'Key 1 verified',
'Key 2 verified'
])


def test_kmu_wrong_keys_uploaded(dut: DeviceAdapter):
"""
Upload two wrong keys to DUT using west ncs-provission command
and verify it in application.
"""
provision_keys_for_kmu(dut.device_config.id,
key1=APP_KEYS_FOR_KMU / 'root-ed25519-w.pem',
key2=APP_KEYS_FOR_KMU / 'root-ed25519-1.pem',
key3=APP_KEYS_FOR_KMU / 'root-ed25519-w.pem')

logger.info("Flash the board once again and check if keys are verified")
dut.clear_buffer()
flash_board(dut.device_config.build_dir, dut.device_config.id)

lines = dut.readlines_until(
regex='Key 2 failed|Key 2 verified|PSA crypto init failed',
print_output=True, timeout=20)

match_lines(lines, [
'Default key failed',
'Key 1 verified',
'Key 2 failed'
])
72 changes: 72 additions & 0 deletions tests/subsys/kmu/pytest/test_kmu_with_mcuboot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
from __future__ import annotations

import logging

from pathlib import Path
from twister_harness import DeviceAdapter
from twister_harness.helpers.utils import match_lines, find_in_config
from common import (
provision_keys_for_kmu,
flash_board,
APP_KEYS_FOR_KMU
)

logger = logging.getLogger(__name__)


def test_kmu_use_key_from_config(dut: DeviceAdapter):
"""
Upload proper key using west ncs-provision command,
verify that the application boots successfully.
"""
logger.info("Provision same key that was used during building")
signature_key_file = find_in_config(Path(dut.device_config.build_dir) / 'mcuboot' / 'zephyr' / '.config',
'CONFIG_BOOT_SIGNATURE_KEY_FILE')
provision_keys_for_kmu(dut.device_config.id, key1=signature_key_file)

dut.clear_buffer()
flash_board(dut.device_config.build_dir, dut.device_config.id)

lines = dut.readlines_until(
regex='Unable to find bootable image|Jumping to the first image slot',
print_output=True, timeout=20)

match_lines(lines, ['Jumping to the first image slot'])
logger.info("Passed: Booted succesvully after provisioning the same key that was used during building")


def test_kmu_use_predefined_keys(dut: DeviceAdapter):
"""
Upload keys using west ncs-provision command,
verify that the application boots successfully if the keys are correct,
and does not boot if the keys are incorrect.
"""
signature_key_file = find_in_config(Path(dut.device_config.build_dir) / 'mcuboot' / 'zephyr' / '.config',
'CONFIG_BOOT_SIGNATURE_KEY_FILE')
zephyr_base = find_in_config(dut.device_config.build_dir / 'CMakeCache.txt', 'ZEPHYR_BASE:PATH')
default_key = Path(zephyr_base).parent / 'bootloader' / 'mcuboot' / 'root-ed25519.pem'
provision_keys_for_kmu(dut.device_config.id,
key1=default_key,
key2=APP_KEYS_FOR_KMU / 'root-ed25519-1.pem',
key3=APP_KEYS_FOR_KMU / 'root-ed25519-2.pem')

dut.clear_buffer()
flash_board(dut.device_config.build_dir, dut.device_config.id)

lines = dut.readlines_until(
regex='Unable to find bootable image|Jumping to the first image slot',
print_output=True, timeout=20)

if 'root-ed25519-w.pem' in signature_key_file:
match_lines(lines, [
'ED25519 signature verification failed',
'Image in the primary slot is not valid',
'Unable to find bootable image'
])
logger.info("Passed: Not booted when used wrong keys")
else:
match_lines(lines, ['Jumping to the first image slot'])
logger.info("Passed: Booted with correct keys")
12 changes: 12 additions & 0 deletions tests/subsys/kmu/verify_west_ncs_provision/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(verify_west_ncs_provision)

target_sources(app PRIVATE src/main.c)
20 changes: 20 additions & 0 deletions tests/subsys/kmu/verify_west_ncs_provision/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
CONFIG_NRF_SECURITY=y
CONFIG_PSA_WANT_ALG_PURE_EDDSA=y
CONFIG_PSA_WANT_ALG_SHA_512=y
CONFIG_PSA_WANT_ECC_TWISTED_EDWARDS_255=y
CONFIG_PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT=y
CONFIG_MBEDTLS=n
CONFIG_MBEDTLS_ENABLE_HEAP=y
CONFIG_MBEDTLS_HEAP_SIZE=2048
CONFIG_PSA_WANT_ALG_GCM=y
CONFIG_PSA_WANT_KEY_TYPE_AES=y
CONFIG_PSA_WANT_AES_KEY_SIZE_256=y
CONFIG_PSA_WANT_ALG_SP800_108_COUNTER_CMAC=y
CONFIG_PSA_WANT_ALG_CMAC=y
CONFIG_PSA_WANT_ALG_ECB_NO_PADDING=y
CONFIG_MAIN_STACK_SIZE=8192
Loading

0 comments on commit d3fc0cc

Please sign in to comment.