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?
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 :-)