Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions cmake/sysbuild/nrf54l_prot_ram_inv_slots.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright (c) 2025 Nordic Semiconductor ASA
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause

# This script defines a CMake target 'generate_prot_ram_inv_slots_json' to create prot_ram_inv_slots.json
# using 'west ncs-provision upload --dry-run'.

# --- Construct the list of commands and dependencies ---
set(kmu_json_commands "")
set(json_filename "prot_ram_inv_slots.json")

list(APPEND kmu_json_commands
COMMAND ${Python3_EXECUTABLE} -m west ncs-provision upload
--keyname PROT_RAM_INV_SLOTS
--build-dir ${CMAKE_BINARY_DIR}
--dry-run
)

add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/${json_filename}
${kmu_json_commands} # Expands to one or more COMMAND clauses
COMMENT "Generating/Updating KMU protected ram invalidation slots JSON (${CMAKE_BINARY_DIR}/${json_filename})"
VERBATIM
)

# --- Add custom target to trigger the generation ---
add_custom_target(
generate_prot_ram_inv_slot_json ALL
DEPENDS ${CMAKE_BINARY_DIR}/${json_filename}
)
74 changes: 56 additions & 18 deletions scripts/west_commands/ncs_provision.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@
"UROT_PUBKEY": [226, 228, 230],
"BL_PUBKEY": [242, 244, 246],
"APP_PUBKEY": [202, 204, 206],
"PROT_RAM_INV_SLOTS": [248],
}
POLICIES = ["revokable", "lock", "lock-last"]
NRF54L15_KEY_POLICIES: dict[str, str] = {
"revokable": psa_attr_generator.PsaKeyPersistence.PERSISTENCE_REVOKABLE,
"lock": psa_attr_generator.PsaKeyPersistence.PERSISTENCE_READ_ONLY,
"rotatable": psa_attr_generator.PsaKeyPersistence.PERSISTENCE_DEFAULT,
}


Expand All @@ -35,6 +37,7 @@ class SlotParams:
id: int
keyfile: str
lifetime: psa_attr_generator.PsaKeyPersistence
keytype: psa_attr_generator.PsaKeyType


class NrfutilWrapper:
Expand Down Expand Up @@ -65,26 +68,50 @@ def run_command(self):

def _make_json_file(self) -> str:
"""Generate PSA key attribute file, see generate_psa_key_attributes.py for details"""
output_file = (
Path(self.output_dir).joinpath("keyfile.json").resolve().expanduser()
)

for slot in self.slots:
attr = psa_attr_generator.PlatformKeyAttributes(
key_type=psa_attr_generator.PsaKeyType.ECC_PUBLIC_KEY_TWISTED_EDWARDS,
identifier=slot.id,
location=psa_attr_generator.PsaKeyLocation.LOCATION_CRACEN_KMU,
persistence=slot.lifetime,
key_usage=psa_attr_generator.PsaKeyUsage.VERIFY,
algorithm=psa_attr_generator.PsaAlgorithm.EDDSA_PURE,
key_bits=255,
cracen_usage=psa_attr_generator.PsaCracenUsageScheme.RAW,
)
if slot.keytype == psa_attr_generator.PsaKeyType.AES:
attr = psa_attr_generator.PlatformKeyAttributes(
key_type=psa_attr_generator.PsaKeyType.AES,
identifier=slot.id,
location=psa_attr_generator.PsaKeyLocation.LOCATION_CRACEN_KMU,
persistence=slot.lifetime,
key_usage=psa_attr_generator.PsaKeyUsage.ENCRYPT,
algorithm=psa_attr_generator.PsaAlgorithm.CTR,
key_bits=256,
cracen_usage=psa_attr_generator.PsaCracenUsageScheme.PROTECTED,
)

output_file = (
Path(self.output_dir).joinpath("prot_ram_inv_slots.json").resolve().expanduser()
)

with open(slot.keyfile, "rb") as key_file:
psa_attr_generator.generate_attr_file(
attributes=attr, key_file=output_file, key_from_file=key_file
attributes=attr, key_file=output_file, trng_key=True
)
elif slot.keytype == psa_attr_generator.PsaKeyType.ECC_PUBLIC_KEY_TWISTED_EDWARDS:
attr = psa_attr_generator.PlatformKeyAttributes(
key_type=psa_attr_generator.PsaKeyType.ECC_PUBLIC_KEY_TWISTED_EDWARDS,
identifier=slot.id,
location=psa_attr_generator.PsaKeyLocation.LOCATION_CRACEN_KMU,
persistence=slot.lifetime,
key_usage=psa_attr_generator.PsaKeyUsage.VERIFY,
algorithm=psa_attr_generator.PsaAlgorithm.EDDSA_PURE,
key_bits=255,
cracen_usage=psa_attr_generator.PsaCracenUsageScheme.RAW,
)

output_file = (
Path(self.output_dir).joinpath("keyfile.json").resolve().expanduser()
)

with open(slot.keyfile, "rb") as key_file:
psa_attr_generator.generate_attr_file(
attributes=attr, key_file=output_file, key_from_file=key_file
)
else:
sys.exit(f"Unsupported key type: {slot.keytype}")

return str(output_file)

def _build_command(self) -> list[str]:
Expand Down Expand Up @@ -129,7 +156,7 @@ def do_add_parser(self, parser_adder):
"""),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
group = upload_parser.add_mutually_exclusive_group(required=True)
group = upload_parser.add_mutually_exclusive_group()
group.add_argument(
"-i", "--input", metavar="PATH", help="Upload keys from YAML file"
)
Expand Down Expand Up @@ -187,8 +214,16 @@ def do_run(self, args, unknown_args):
self._upload_keys(args)

def _upload_keys(self, args: argparse.Namespace) -> None:
if args.keys is None and args.input is None and args.keyname != "PROT_RAM_INV_SLOTS":
sys.exit("error: one of the arguments -i/--input -k/--key is required")

slots: list[SlotParams] = []
if args.input:
data: list[dict[str, Any]] = []

if args.keyname == "PROT_RAM_INV_SLOTS":
for id in KEY_SLOTS["PROT_RAM_INV_SLOTS"]:
slots.append(SlotParams(id=id, keyfile=None, lifetime=NRF54L15_KEY_POLICIES["rotatable"], keytype=psa_attr_generator.PsaKeyType.AES))
elif args.input:
data = self._read_keys_params_from_file(args.input)
else:
data = self._read_keys_params_from_args(args)
Expand Down Expand Up @@ -221,6 +256,7 @@ def _read_keys_params_from_file(self, filename: str) -> list[dict[str, Any]]:

def _generate_slots(self, keyname: str, keys: str, policy: str) -> list[SlotParams]:
"""Return list of SlotParams for given keys."""

if len(keys) > len(KEY_SLOTS[keyname]):
sys.exit(
"Error: requested upload of more keys than there are designated slots."
Expand All @@ -234,8 +270,10 @@ def _generate_slots(self, keyname: str, keys: str, policy: str) -> list[SlotPara
key_policy = NRF54L15_KEY_POLICIES["revokable"]
else:
key_policy = NRF54L15_KEY_POLICIES[policy]

key_type = psa_attr_generator.PsaKeyType.ECC_PUBLIC_KEY_TWISTED_EDWARDS
slot_id = KEY_SLOTS[keyname][slot_idx]
slot = SlotParams(id=slot_id, keyfile=keyfile, lifetime=key_policy)
slot = SlotParams(id=slot_id, keyfile=keyfile, lifetime=key_policy, keytype=key_type)
slots.append(slot)
return slots

Expand Down
4 changes: 4 additions & 0 deletions sysbuild/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,10 @@ function(${SYSBUILD_CURRENT_MODULE_NAME}_post_cmake)
include(${ZEPHYR_NRF_MODULE_DIR}/cmake/sysbuild/generate_default_keyfile.cmake)
endif()

if(SB_CONFIG_NRF54L_INVALIDATE_PROTECTED_RAM_SLOTS)
include(${ZEPHYR_NRF_MODULE_DIR}/cmake/sysbuild/nrf54l_prot_ram_inv_slots.cmake)
endif()

if(SB_CONFIG_MATTER_OTA)
include(${ZEPHYR_CONNECTEDHOMEIP_MODULE_DIR}/config/zephyr/ota-image_sysbuild.cmake)
if(SB_CONFIG_DFU_MULTI_IMAGE_PACKAGE_BUILD)
Expand Down
8 changes: 8 additions & 0 deletions sysbuild/Kconfig.nrf54l_kmu
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) 2025 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause

config NRF54L_INVALIDATE_PROTECTED_RAM_SLOTS
bool
default y
depends on SOC_SERIES_NRF54LX
1 change: 1 addition & 0 deletions sysbuild/Kconfig.sysbuild
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,4 @@ rsource "Kconfig.lwm2m_carrier"
rsource "Kconfig.cracen"
rsource "Kconfig.tfm"
rsource "Kconfig.firmware_loader"
rsource "Kconfig.nrf54l_kmu"
2 changes: 1 addition & 1 deletion west.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ manifest:
# https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/guides/modules.html
- name: zephyr
repo-path: sdk-zephyr
revision: 29aa21b8ac8e738fa6854e2127833c0e1613e101
revision: pull/3394/head
import:
# In addition to the zephyr repository itself, NCS also
# imports the contents of zephyr/west.yml at the above
Expand Down