Search code examples
stm32bootloaderstm32f4stm32cubeide

STM32 secure boot loader security enable fails consistently with "[SBOOT] Security issue : Execution stopped !"


I have my STM32F405RGT6 secure boot loader running with security flags disabled. So I try to introduce the security flags/options one by one. Independently of which flag I enable in app_sfu.h, the code fails in the first FLOW_CONTROL_CHECK in the SFU_BOOT_SM_VerifyUserFwSignature function in sfu_boot.c

I have added logging that shows exactly what happens:

  /* Double security check :
 - testing "static protections" twice will avoid basic hardware attack
 - flow control reached : dynamic protections checked
 - re-execute static then dynamic check
 - errors caught by FLOW_CONTROL ==> infinite loop */
  TRACE("= [SBOOT] FLOW_CONTROL_CHECK(%x, %x)\n", uFlowProtectValue, FLOW_CTRL_RUNTIME_PROTECT);
  FLOW_CONTROL_CHECK(uFlowProtectValue, FLOW_CTRL_RUNTIME_PROTECT);

Output from the trace shows this:

= [SBOOT] FLOW_CONTROL_CHECK(1554b, 30f1)

The FLOW_CONTROL_CHECK macro compares the two values. If they differ, the program fails.

As I understand the code, the uFlowProtectValue contains the run time protection values that are active at the actual execution time, while FLOW_CTRL_RUNTIME_PROTECT is a compile time #define that should be the same as what we're running with.

The core of the problem is that the run time protection value is what I expect it to be, while the compile time #define never differs from 0x30f1.

The #define comes to be in ST-provided code that your mother might not approve of, not in the least because it doesn't seem to work:

  /**
  * @brief  SFU_BOOT Flow Control : Control values static protections
  */
#define FLOW_CTRL_UBE (FLOW_CTRL_INIT_VALUE ^ FLOW_STEP_UBE)
#define FLOW_CTRL_WRP (FLOW_CTRL_UBE ^ FLOW_STEP_WRP)
#define FLOW_CTRL_PCROP (FLOW_CTRL_WRP ^ FLOW_STEP_PCROP)
#define FLOW_CTRL_SEC_MEM (FLOW_CTRL_PCROP ^ FLOW_STEP_SEC_MEM)
#define FLOW_CTRL_RDP (FLOW_CTRL_SEC_MEM ^ FLOW_STEP_RDP)
#define FLOW_CTRL_STATIC_PROTECT FLOW_CTRL_RDP

/**
  * @brief  SFU_BOOT Flow Control : Control values runtime protections
  */
#define FLOW_CTRL_TAMPER (FLOW_CTRL_STATIC_PROTECT ^ FLOW_STEP_TAMPER)
#define FLOW_CTRL_MPU (FLOW_CTRL_TAMPER ^ FLOW_STEP_MPU)
#define FLOW_CTRL_FWALL (FLOW_CTRL_MPU ^ FLOW_STEP_FWALL)
#define FLOW_CTRL_DMA (FLOW_CTRL_FWALL ^ FLOW_STEP_DMA)
#define FLOW_CTRL_IWDG (FLOW_CTRL_DMA ^ FLOW_STEP_IWDG)
#define FLOW_CTRL_DAP (FLOW_CTRL_IWDG ^ FLOW_STEP_DAP)
#define FLOW_CTRL_RUNTIME_PROTECT FLOW_CTRL_DAP

The hex numbers from my trace output above are from when I enable the internal watch dog, IWDG.

The values are XOR'ed from three involved bitmaps:

#define FLOW_CTRL_INIT_VALUE 0x00005776U         /*!< Init value definition */
#define FLOW_STEP_UBE 0x00006787U                /*!< Step UBE value */
#define FLOW_STEP_IWDG 0x000165baU               /*!< Step IWDG value */

The XOR of the two first is 0x30f1, and if you add FLOW_STEP_IWDG to that, you get 0x1554b.

So the run time value with IWDG enabled is correct, while the compile time value is wrong.

How can that be?


Solution

  • Ok, so this is just too silly: All the involved code is supplied by ST Microelectronics.

    The sfu_boot.h file which uses all the security definitions from app_sfu.h does not #include app_sfu.h and it has no built-in checks to verify that app_sfu.h has indeed been included somewhere in the include chains. So I added #include "app_sfu.h" to ST Microelectronic's provided sfu_boot.h and the problem goes away.

    Sorry for the inconvenience :-)