Authenticated Debug Access (APPROTECT)

We are preparing a product for production and have enabled authenticated debug access on the NRF52840. Because authenticated debug access is not described in any Nordic documentation or security notices, we are seeking review of our implementation to ensure that we have not defeated the security model of the NRF52840. 

Assumptions

Clarifications

Let's start with some statements made in the Rev 3 Errata listed above (4413_679 v1.3 / 2024-03-04)

Section 4.1 [249] DIF: Access port protection needs software interface configuration describes the need for SW intervention to enable the debugging access. We have taken this to mean that, with authentication, the application can securely enable/disable debugging via:

NRF_APPROTECT->DISABLE = APPROTECT_DISABLE_DISABLE_SwDisable; // enable debug
NRF_APPROTECT->DISABLE = ~APPROTECT_DISABLE_DISABLE_SwDisable; // disable debug

Thereby preventing unauthorized access to the integrated flash. Is this correct?


"Working with the nRF52 Series' improved APPROTECT" ( Working with the nRF52 Series' improved APPROTECT) does not describe a process for securely enabling debug access. Instead, the section "Unlocking: nrfjprog or CTRL-AP ERASEALL" describes a requirement to erase all flash in order to unlock debug access. Is this simply an incomplete statement and it is in fact possible to safely unlock the debug access using the NRF_APPROTECT register described above?


The KConfig option NRF_APPROTECT_USER_HANDLING is not available to NRF52: https://github.com/zephyrproject-rtos/zephyr/blob/c98f6bccc4b83fd801ba7a7f8ece320987ea19cf/soc/nordic/Kconfig#L147-L153

config NRF_APPROTECT_USER_HANDLING
	bool "Allow user handling"
	depends on !SOC_SERIES_NRF52X
	help
	  When this option is selected, the SystemInit() function does not
	  touch the APPROTECT mechanism, allowing the user code to handle it
	  at later stages, for example, to implement authenticated debug.

The help mentions that this option would be used to implement authenticated debug. Does this mean that authenticated debug is not possible on NRF52 or simply unimplemented and/or undocumented by Nordic? As stated, we feel that we have implemented authenticated debug by protecting access to alteration of NRF_APPROTECT, but statements like this lead to questions about whether we may inadvertently introduce some vulnerability.

Implementation

Next, I will describe our implementation. We are seeking feedback, keeping the assumptions listed above in mind.

We have followed the guidance provided by the NRF52 Reference Manual(4413_417 v1.11) p.68 "Access port protection controlled by hardware and software - This information refers to build codes Fxx and later.", but it does not provide guidance for enabling authenticated debug access, it only repeats that the protection can be disabled by ERASEALL, which does not meet our requirements for authenticated debug access with erasing flash.

Enable NRF_APPROTECT_USER_HANDLING

Patch the KConfig mentioned above:

diff --git a/soc/arm/nordic_nrf/Kconfig b/soc/arm/nordic_nrf/Kconfig
index c2129db64b3..e680f2e08e5 100644
--- a/soc/arm/nordic_nrf/Kconfig
+++ b/soc/arm/nordic_nrf/Kconfig
@@ -94,7 +94,6 @@ config NRF_APPROTECT_LOCK

 config NRF_APPROTECT_USER_HANDLING
 	bool "Allow user handling"
-	depends on !SOC_SERIES_NRF52X
 	help
 	  When this option is selected, the SystemInit() function does not
 	  touch the APPROTECT mechanism, allowing the user code to handle it

And patch mdk/system_nrf52_approtect.h:

diff --git a/nrfx/mdk/system_nrf52_approtect.h b/nrfx/mdk/system_nrf52_approtect.h
index 6b31402..267cb08 100644
--- a/nrfx/mdk/system_nrf52_approtect.h
+++ b/nrfx/mdk/system_nrf52_approtect.h
@@ -47,6 +47,10 @@ static inline void nrf52_handle_approtect(void)
                 /* Prevent processor from unlocking APPROTECT soft branch after this point. */
                 NRF_APPROTECT->FORCEPROTECT = APPROTECT_FORCEPROTECT_FORCEPROTECT_Force;
             }
+        #elif  defined (ENABLE_APPROTECT_USER_HANDLING)
+                //Implement USER_HANDLING mode as found in system_nrf53_appprotect.
+                //This mode leaves the APPROTECT configuration as is, and does not change it.
+
         #else
             if (nrf52_configuration_249())
             {

Full reference to these lines: https://github.com/NordicSemiconductor/nrfx/blob/11f57e578c7feea13f21c79ea0efab2630ac68c7/mdk/system_nrf52_approtect.h#L34-L59

Bootloader

CONFIG_NRF_APPROTECT_USER_HANDLING=y. With the patch, nrf52_handle_approtect() is a nop.

Application

CONFIG_NRF_APPROTECT_USER_HANDLING=y. With the patch, nrf52_handle_approtect() is a nop.

Our application implements authenticated access. Behind that, we allow an authenticated user to unlock the flash:

NRF_APPROTECT->DISABLE = APPROTECT_DISABLE_DISABLE_SwDisable; // enable debug
NRF_APPROTECT->DISABLE = ~APPROTECT_DISABLE_DISABLE_SwDisable; // disable debug

Testing has shown that this does enable/disable access, e.g. allowing nrfutil, nrfjprog, jlink, to connect after the NRF_APPROTECT->DISABLE is set to SwDisable.

Image

In order to ensure that UICR is written correctly at the factory, the data is written to a hex that is merged with our factory hex, with the intention of setting UICR to prevent unauthorized debug access to the product.

import sys
from intelhex import IntelHex
from enum import IntEnum, unique


@unique
class UICR_Addresses(IntEnum):
    APPROTECT = 0x10001208


@unique
class APPROTECT_Values(IntEnum):
    Disabled = 0xFF  # Hardware disable of access port protection for devices where access port protection is controlled by hardware.
    HwDisabled = 0x5A  # Hardware disable of access port protection for devices where access port protection is controlled by hardware and software.
    Enabled = 0x00


ih = IntelHex()
# NOTE: all writes must be 4-bytes and aligned
ih.puts(
    int(UICR_Addresses.APPROTECT), APPROTECT_Values.HwDisabled.to_bytes(4, "little")
)
ih.write_hex_file(sys.stdout)

Looking at this now, I am concerned that it's DISABLING the APPROTECT on startup rather than ENABLING it. I think this is important because it is what mitigates the vulnerability in previous revisions? Specifically, this value is fetched in hardware at startup, mitigating the vulnerability. If I am understanding correctly, then this should be "Disabled", not "Enabled" - or "HwDisabled"?

"Step 2: Setting UICR.APPROTECT" from  Working with the nRF52 Series' improved APPROTECT  suggests writing this in the app (and bootloader?) and I suppose we should do that to make sure UICR is always "correct" - though, the app does not touch UICR, presently.

Conclusion

Thanks for your review! I hope that this analysis of authenticated debug access on the NRF52 Rev 3+ will help other companies that require the same feature.

Cheers,

JP

Related