物聯網安全之cortex m23/m33 MCU trustzone實操

前言

本文針對ARMv8m架構M23/M33 MCU安全特性使用進行介紹,以nxp LPC55xx系列和STM32L5xx系列爲例,爲大家闡述如何使用Trustzone技術提高物聯網設備安全性,適合有一定平臺安全基礎的物聯網設備開發人員、安全方案開發人員。

背景

爲了提升平臺安全性,ARM推出了ARMv8m架構,該架構引入了Trustzone安全擴展,該技術主要利用隔離技術將地址空間劃分安全和非安全區域,實現了空間隔離,這裏我們稱之爲安全世界和非安全世界,兩個世界的切換/交互通過指令集增加的幾條指令實現(SG/BXNX/BLXNX)。該架構主要包括兩個系列產品,以m23爲代表的baseline產品以及以m33以爲代表的mainline產品,前者可以認爲是m0的安全版本,後者是m3/m4的安全版本。因爲本文主要目的是實操,所以Trustzone具體技術知識不展開講述。

現狀

市面上已經有多家芯片廠商推出了m23/m33內核的MCU產品

廠商 型號
NXP 恩智浦 LPC55xx
ST 意法 STM32L5xx
Nuvoton 新塘 M2351
Microchip 微芯 SAM L10/L11
Renesas 瑞薩 RA2x
Nordic NRF5340
Dialog 戴濼格 DA1469x
ADI 亞德諾 ----
Silicon Labs 芯科zhan Gecko Series 2
紫光展銳(原RDA) 春藤v5663
GigaDevice 兆易創新 GD32E232

這氣勢不亞於當年的cortex m0/m3/m4,因爲大家知道安全是制約物聯網規模的重要原因之一,而armv8m中的trustzone能夠解決設備中大部分安全問題。

安全目標

利用這些芯片我們可以實現哪些安全目標?

安全目標 方法
密鑰安全 安全世界密鑰存儲區無法被非安全世界的非法軟件直接讀取
算法安全 關鍵算法可以放在安全世界來防止運行時篡改
外設保護 外設可以放進安全世界來防止數據的原始數據的篡改

實操LPC55xx

設置安全屬性單元SAU

/* SAU region boundaries */
#define REGION_0_BASE 0
#define REGION_0_END 0x0FFFFFFFU
#define REGION_1_BASE 0x20000000U
#define REGION_1_END 0xFFFFFFFFU
#define REGION_2_BASE 0x1000FE00U
#define REGION_2_END 0x1000FFFFU
    /* Set SAU Control register: Disable SAU and All Secure */
    SAU->CTRL = 0;

    /* Set SAU region number */
    SAU->RNR = 0;
    /* Region base address */
    SAU->RBAR = REGION_0_BASE & SAU_RBAR_BADDR_Msk;
    /* Region end address */
    SAU->RLAR = ((REGION_0_END & SAU_RLAR_LADDR_Msk) | ((0U << SAU_RLAR_NSC_Pos) & SAU_RLAR_NSC_Msk)) |
                ((1U << SAU_RLAR_ENABLE_Pos) & SAU_RLAR_ENABLE_Msk);

    /* Set SAU region number */
    SAU->RNR = 0x00000001U;
    /* Region base address */
    SAU->RBAR = REGION_1_BASE & SAU_RBAR_BADDR_Msk;
    /* Region end address */
    SAU->RLAR = ((REGION_1_END & SAU_RLAR_LADDR_Msk) | ((0U << SAU_RLAR_NSC_Pos) & SAU_RLAR_NSC_Msk)) |
                ((1U << SAU_RLAR_ENABLE_Pos) & SAU_RLAR_ENABLE_Msk);

    /* Set SAU region number */
    SAU->RNR = 0x00000002U;
    /* Region base address */
    SAU->RBAR = REGION_2_BASE & SAU_RBAR_BADDR_Msk;
    /* Region end address */
    SAU->RLAR = ((REGION_2_END & SAU_RLAR_LADDR_Msk) | ((1U << SAU_RLAR_NSC_Pos) & SAU_RLAR_NSC_Msk)) |
                ((1U << SAU_RLAR_ENABLE_Pos) & SAU_RLAR_ENABLE_Msk);

    /* Force memory writes before continuing */
    __DSB();
    /* Flush and refill pipeline with updated permissions */
    __ISB();
    /* Set SAU Control register: Enable SAU and All Secure (applied only if disabled) */
    SAU->CTRL = 0x00000001U;

SAU IDAU定義
根據代碼設置和SAU/IDAU規則可以看出,我們將4G空間按照256M大小以此劃分爲非安全/安全交替的地址,相鄰的256M空間映射到同一個物理器件,這種技術成爲alias技術,安全世界可以使用安全地址訪問硬件,而非安全世界可以使用對應的安全地址訪問硬件,驅動程序無需修改。硬件是否允許訪問,取決於MPC/PPC等設置。另外0x1000FE00U處預留了512字節的非安全可調用區域,用來存放跳板函數(veneer entry)。

設置存儲器保護控制器MPC
LPC55xx通過AHB Secure Controller來設置ROM/FLASH/SRAM安全屬性

	/*設置前64KB flash爲安全屬性*/
    AHB_SECURE_CTRL->SEC_CTRL_FLASH_ROM[0].SEC_CTRL_FLASH_MEM_RULE[0] = 0x00000033U;
    AHB_SECURE_CTRL->SEC_CTRL_FLASH_ROM[0].SEC_CTRL_FLASH_MEM_RULE[1] = 0;
    AHB_SECURE_CTRL->SEC_CTRL_FLASH_ROM[0].SEC_CTRL_FLASH_MEM_RULE[2] = 0;
    
    /*設置ROM爲非安全屬性*/
    AHB_SECURE_CTRL->SEC_CTRL_FLASH_ROM[0].SEC_CTRL_ROM_MEM_RULE[0]   = 0;
    AHB_SECURE_CTRL->SEC_CTRL_FLASH_ROM[0].SEC_CTRL_ROM_MEM_RULE[1]   = 0;
    AHB_SECURE_CTRL->SEC_CTRL_FLASH_ROM[0].SEC_CTRL_ROM_MEM_RULE[2]   = 0;
    AHB_SECURE_CTRL->SEC_CTRL_FLASH_ROM[0].SEC_CTRL_ROM_MEM_RULE[3]   = 0;

	/*設置前128KB SRAM爲安全屬性*/
    AHB_SECURE_CTRL->SEC_CTRL_RAMX[0].MEM_RULE[0]                     = 0;
    AHB_SECURE_CTRL->SEC_CTRL_RAM0[0].MEM_RULE[0]                     = 0x33333333U;
    AHB_SECURE_CTRL->SEC_CTRL_RAM0[0].MEM_RULE[1]                     = 0;
    AHB_SECURE_CTRL->SEC_CTRL_RAM1[0].MEM_RULE[0]                     = 0;
    AHB_SECURE_CTRL->SEC_CTRL_RAM1[0].MEM_RULE[1]                     = 0;
    AHB_SECURE_CTRL->SEC_CTRL_RAM2[0].MEM_RULE[0]                     = 0;
    AHB_SECURE_CTRL->SEC_CTRL_RAM2[0].MEM_RULE[1]                     = 0;
    AHB_SECURE_CTRL->SEC_CTRL_RAM3[0].MEM_RULE[0]                     = 0;
    AHB_SECURE_CTRL->SEC_CTRL_RAM3[0].MEM_RULE[1]                     = 0;
    AHB_SECURE_CTRL->SEC_CTRL_RAM4[0].MEM_RULE[0]                     = 0;
    AHB_SECURE_CTRL->SEC_CTRL_USB_HS[0].MEM_RULE[0]                   = 0;

這樣設置後,我們可以將安全代碼鏈接到0x1000 0000處,大小限制64KB,數據鏈接到0x3000 0000處,大小限制128KB;非安全代碼鏈接到0x0001 0000處,大小限制567KB.

設置外設保護控制器PPC

    //--- Security level configuration of peripherals --------------------
    AHB_SECURE_CTRL->SEC_CTRL_APB_BRIDGE[0].SEC_CTRL_APB_BRIDGE0_MEM_CTRL0 = 0x00000033U;
    AHB_SECURE_CTRL->SEC_CTRL_APB_BRIDGE[0].SEC_CTRL_APB_BRIDGE0_MEM_CTRL1 = 0;
    AHB_SECURE_CTRL->SEC_CTRL_APB_BRIDGE[0].SEC_CTRL_APB_BRIDGE0_MEM_CTRL2 = 0;
    AHB_SECURE_CTRL->SEC_CTRL_APB_BRIDGE[0].SEC_CTRL_APB_BRIDGE1_MEM_CTRL0 = 0;
    AHB_SECURE_CTRL->SEC_CTRL_APB_BRIDGE[0].SEC_CTRL_APB_BRIDGE1_MEM_CTRL1 = 0;
    AHB_SECURE_CTRL->SEC_CTRL_APB_BRIDGE[0].SEC_CTRL_APB_BRIDGE1_MEM_CTRL2 = 0;
    AHB_SECURE_CTRL->SEC_CTRL_APB_BRIDGE[0].SEC_CTRL_APB_BRIDGE1_MEM_CTRL3 = 0;
    AHB_SECURE_CTRL->SEC_CTRL_AHB0_0_SLAVE_RULE                            = 0x03000000U;
    AHB_SECURE_CTRL->SEC_CTRL_AHB0_1_SLAVE_RULE                            = 0;
    AHB_SECURE_CTRL->SEC_CTRL_AHB1_0_SLAVE_RULE                            = 0;
    AHB_SECURE_CTRL->SEC_CTRL_AHB1_1_SLAVE_RULE                            = 0;
    AHB_SECURE_CTRL->SEC_CTRL_AHB2[0].SEC_CTRL_AHB2_0_SLAVE_RULE           = 0;
    AHB_SECURE_CTRL->SEC_CTRL_AHB2[0].SEC_CTRL_AHB2_1_SLAVE_RULE           = 0;
    AHB_SECURE_CTRL->SEC_GPIO_MASK0 = 0xFFFFFFFFU;
    AHB_SECURE_CTRL->SEC_GPIO_MASK1 = 0xFFFFFFFFU;

設置flexcomm、iocon、syscon外設爲安全屬性,其他爲非安全屬性,flexcomm用來安全世界串口打印,iocon用來設置端口、syscon用來設置模塊上下電。

設置中斷安全屬性TZIC

    NVIC->ITNS[0] = 0;
    NVIC->ITNS[1] = 0;

設置所有IRQ中斷屬性爲非安全屬性。

非安全世界訪問安全世界函數

/* Non-secure callable (entry) function, calling a non-secure callback function */
__attribute__((cmse_nonsecure_entry)) uint32_t StringCompare_NSE(volatile callbackptr callback, char const *s1, char const *s2)
{
    callbackptr_NS callback_NS;
    size_t  string_length;
    int result;
 
    /* Input parameters check */
    /* Check whether function pointer is located in non-secure memory */
    callback_NS = (callbackptr_NS)cmse_nsfptr_create(callback);
    if (cmse_check_pointed_object((int *)callback_NS, CMSE_NONSECURE) == NULL)
    {
        PRINTF("The callback is not located in normal world!\r\n");
        abort();
    }
    /* Check whether string is properly terminated */
    string_length = strnlen(s1, MAX_STRING_LENGTH);
    if ((string_length == MAX_STRING_LENGTH) && (s1[string_length] != '\0'))
    {
        PRINTF("First string too long or invalid string termination!\r\n");
         abort();
    }
    /* Check whether string is properly terminated */
    string_length = strnlen(s2, MAX_STRING_LENGTH);
    if ((string_length == MAX_STRING_LENGTH) && (s2[string_length] != '\0'))
    {
        PRINTF("Second string too long or invalid string termination!\r\n");
         abort();
    }
    PRINTF("Comparing two string as a callback to normal world\r\n");
    PRINTF("String 1: ");
    PRINTF(s1);
    PRINTF("String 2: ");
    PRINTF(s2);
    result = callback_NS(s1, s2);
    return result;
}

這是一個在安全世界實現的字符串比較代碼,通過cmse_nonsecure_entry attribute屬性來提示編譯器在非安全可調用區域(上面SAU配置過)生成跳板,跳板也很簡單,每個跳板有兩條32位指令組成:

sg
bx StringCompare_NSE

上面預留了512個字節,能夠存放64個跳板函數,跳板相關的鏈接腳本如下:

#define  m_veneer_table_start          0x1000FE00U
#define  m_veneer_table_size           0x200
LR_m_veneer_table m_veneer_table_start m_veneer_table_size {
  ER_m_veneer_table m_veneer_table_start m_veneer_table_size {; veneer table
    *(Veneer$$CMSE)
  }
}

所以非安全世界要想訪問安全世界函數很簡單,只需要將函數設置爲cmse_nonsecure_entry 屬性即可。值得注意的是,由於跳板函數只能通過R0~R3傳遞數據(兩個世界的棧是獨立的),所以跳板函數參數不要超過4個。非安全世界只能通過跳板函數訪問安全世界提供的服務。

安全世界訪問非安全世界函數
安全世界可以訪問安全世界的資源(數據和代碼),但是不能直接執行非安全世界代碼,需要通過一下方式調用非安全世界函數:

typedef int (*callbackptr_NS)(char const * s1, char const * s2) 
__attribute__((cmse_nonsecure_call));

  callbackptr_NS callback_NS;
  callback_NS = (callbackptr_NS)cmse_nsfptr_create(callback);
  callback_NS(s1, s2)

callback是非安全世界函數地址,通過cmse_nsfptr_create函數將其轉換爲cmse_nonsecure_call屬性的函數,這樣編譯器會將調用指令有blx替換成blxnx,觸發安全世界的切換。

值得注意的問題

  1. A0版本芯片不要開secure boot,不要寫prince key
  2. A0版本PFR驅動和ROM不一致
  3. Hashcrypt設置爲安全後需要lock才能生效
  4. 使用flash驅動時需要將ROM對應區域劃分爲安全
  5. 使用最新的sdk(目前爲2.7.1,對應keil DFP 12.1.1)

以上是LPC55xx平臺安全屬性配置以及兩個世界的交互介紹,這些只是我們開發過程中比較簡單的一部分,更深層次的使用問題可以在留言區留言

實操STM32L5

SAU設置

/*NSC FLASH 8KB*/
#define SAU_INIT_START0     0x0C03E000      
#define SAU_INIT_END0       0x0C03FFFF    
 
/*NS FLASH 256KB*/ 
#define SAU_INIT_START1     0x08040000
#define SAU_INIT_END1       0x0807FFFF

/*NS SRAM 160KB*/
#define SAU_INIT_START2     0x20018000      
#define SAU_INIT_END2       0x2003FFFF   

/*Peripheral NSalias*/   
#define SAU_INIT_START3     0x40000000       
#define SAU_INIT_END3       0x4FFFFFFF    

/*FMC&OCTOSPI NS*/  
#define SAU_INIT_START4     0x60000000      
#define SAU_INIT_END4       0x9FFFFFFF   

/*SYSTEM MEMORY NS*/    
#define SAU_INIT_START5     0x0BF90000       
#define SAU_INIT_END5       0x0BFA8FFF       

配置寄存器和LPC平臺相同,只是區域不同,和下圖IDAU配合,完成安全屬性配置
在這裏插入圖片描述
設置存儲器保護控制器MPC
STM32L5通過MPCBB設置MPC

  MPCBB_desc.AttributeConfig.MPCBB_SecConfig_array[12] = 0x00000000U;
  MPCBB_desc.AttributeConfig.MPCBB_SecConfig_array[13] = 0x00000000U;
  MPCBB_desc.AttributeConfig.MPCBB_SecConfig_array[14] = 0x00000000U;
  MPCBB_desc.AttributeConfig.MPCBB_SecConfig_array[15] = 0x00000000U;
  MPCBB_desc.AttributeConfig.MPCBB_SecConfig_array[16] = 0x00000000U;
  MPCBB_desc.AttributeConfig.MPCBB_SecConfig_array[17] = 0x00000000U;
  MPCBB_desc.AttributeConfig.MPCBB_SecConfig_array[18] = 0x00000000U;
  MPCBB_desc.AttributeConfig.MPCBB_SecConfig_array[19] = 0x00000000U;
  MPCBB_desc.AttributeConfig.MPCBB_SecConfig_array[20] = 0x00000000U;
  MPCBB_desc.AttributeConfig.MPCBB_SecConfig_array[21] = 0x00000000U;
  MPCBB_desc.AttributeConfig.MPCBB_SecConfig_array[22] = 0x00000000U;
  MPCBB_desc.AttributeConfig.MPCBB_SecConfig_array[23] = 0x00000000U;

  if (HAL_GTZC_MPCBB_ConfigMem(SRAM1_BASE, &MPCBB_desc) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }

設置0x2001 8000開始,160KB SRAM爲非安全,MPCBB_SecConfig_array每個bit表示256Byte的安全屬性,1表示安全,0表示非安全。

FLASH安全屬性設置可以通過

HAL_GTZC_TZSC_MPCWM_ConfigMemAttributes()

設置,整個bank的安全屬性也可以通過工具設置option bytes的SECWM2_PSTRT > SECWM2_PEND

設置外設保護控制器PPC

   if (HAL_GTZC_TZSC_ConfigPeriphAttributes(GTZC_PERIPH_USART3, GTZC_TZSC_PERIPH_PRIV | GTZC_TZSC_PERIPH_NSEC) != HAL_OK)
   {
    /* Initialization error */
     while(1);
   }

  HAL_GPIO_ConfigPinAttributes(BUTTON_USER_GPIO_PORT, BUTTON_USER_PIN, GPIO_PIN_NSEC);

通過上面接口,實現了外設和端口安全屬性的設置。

設置中斷屬性控制器TZIC
與LPC55xx相同

安全世界和非安全世界交互
與LPC55xx相同

值得注意的問題

  1. STM32L5 iCache是一個讀cache,寫flash不走icache,所以如果讀取了flash,然後再寫,會導致cache和flash內容不一致,需要invalidate
  2. RCC_CR priv位,手冊上說在TZEN=1,SECCFGR爲非0時,非安全世界寫該位會導致IAL異常,實際並不會,只是silent failure

以上是STM32L5xx平臺安全屬性配置以及兩個世界的交互介紹,這些只是我們開發過程中比較簡單的一部分,更深層次的使用問題可以在留言區留言

持續更新中,感興趣請關注收藏

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章