| 
 
| 安全启动CMAC计算过程 获取DFLASH存储秘钥
 AesDecryptDf1Block 函数,其作用是读取HSM(High Security Module,高安全模块)数据闪存中加密的数据,然后使用AES(Advanced Encryption Standard,高级加密标准)算法进行解密,并将解密后的明文数据加载到HSM RAM结构体中。
 
 /**
 * @brief 该函数将从HSM数据闪存中读取加密数据,然后进行解密,并将明文数据加载到HSM RAM结构体中
 * @param rounds: 要执行的解密循环次数
 * @return 无返回值
 *
 *****************************************************************************/
 void AesDecryptDf1Block(uint32_t rounds)
 {
 /* 使用密钥编号0 */
 in = (uint32_t*)&hsmCfgDataRom;  // 指向加密数据的指针
 out = (uint32_t*)&hsmCfgData;   // 指向HSM RAM结构体的指针
 
 if (rounds > 0)
 {
 /* 加载第一个密文数据块 */
 HSM_AES->AESIN0.U = *in++;
 HSM_AES->AESIN1.U = *in++;
 HSM_AES->AESIN2.U = *in++;
 HSM_AES->AESIN3.U = *in++;
 /* 解密数据块 */
 HSM_AES->AESCTRL.U = AES_CTRL(OPC_ECB_DEC,KEYNR_0,CVNR_0);  // 使用ECB模式和密钥0进行解密
 
 while(--rounds)
 {
 /* 等待上一个解密操作完成 */
 while (HSM_AES->AESSTAT.B.BSY == true)
 ;
 /* 存储明文数据块 */
 *out++ = HSM_AES->AESOUT0.U;
 *out++ = HSM_AES->AESOUT1.U;
 *out++ = HSM_AES->AESOUT2.U;
 *out++ = HSM_AES->AESOUT3.U;
 
 /* 加载下一个密文数据块 */
 HSM_AES->AESIN0.U = *in++;
 HSM_AES->AESIN1.U = *in++;
 HSM_AES->AESIN2.U = *in++;
 HSM_AES->AESIN3.U = *in++;
 /* 解密数据块 */
 HSM_AES->AESCTRL.U = AES_CTRL(OPC_ECB_DEC,KEYNR_0,CVNR_0);
 }
 
 /* 写入最后一个明文数据到HSM RAM结构体 */
 *out++ = HSM_AES->AESOUT0.U;
 *out++ = HSM_AES->AESOUT1.U;
 *out++ = HSM_AES->AESOUT2.U;
 *out++ = HSM_AES->AESOUT3.U;
 }
 }
 
 
 
 rounds 参数指定了要执行的解密循环次数。
 in 指针指向一个结构体,该结构体包含了从HSM数据闪存中读取的加密数据。
 out 指针指向HSM RAM中的一个结构体,用于存储解密后的明文数据。
 HSM_AES 是HSM中AES模块的寄存器地址。
 AESIN0 到 AESIN3 是AES模块的输入寄存器,用于加载32位的数据块。
 AESCTRL 是AES模块的控制寄存器,用于设置解密操作的参数。AES_CTRL 宏设置了操作模式为ECB(电子密码本模式),使用密钥编号0,和常量向量编号0。
 AESSTAT 是AES模块的状态寄存器,其中的 BSY 位表示AES模块是否忙碌。
 AESOUT0 到 AESOUT3 是AES模块的输出寄存器,用于存储解密后的数据。
 hsmCfgDataRom 按照自定义的格式存储在DF1的固定位置,只有HSM可以进行访问。
 函数首先检查 rounds 是否大于0,然后开始解密循环。在每次循环中,它都会从 in 指针处读取加密的数据块,使用AES模块进行解密,并将结果写入 out 指针所指向的RAM结构体中。循环结束后,它还会处理最后一个数据块,并将结果写入RAM结构体。
 
 判断秘钥有效性并加载秘钥
 这段代码定义了一个名为 cryptoInitBootParameters 的函数,其作用是在系统启动时初始化加密参数,特别是用于CMAC(Cipher-based Message Authentication Code,基于密码的消息认证码)的AES(Advanced Encryption Standard,高级加密标准)密钥。以下是函数的中文解释和注释:
 
 /**
 * @brief 初始化启动参数的加密参数。
 *
 * @param None
 * @return 无返回值
 *
 * @ingroup
 *****************************************************************************/
 uint8_t cryptoInitBootParameters(void)
 {
 uint8_t cryptoStatus;
 
 /* 使用ECB模式和密钥槽0解密DF1数据块,然后将它复制到RAM */
 AesDecryptDf1Block(HSM_CONFIG_DATA_ROUNDS);
 
 if(hsmCfgData.BootMarker == 0xAA550000)
 {
 /* 加载用于CMAC的AES密钥到密钥槽2 */
 HSM_AES->AESIN0.U = hsmCfgData.Cmac.Key.w[0];
 HSM_AES->AESIN1.U = hsmCfgData.Cmac.Key.w[1];
 HSM_AES->AESIN2.U = hsmCfgData.Cmac.Key.w[2];
 HSM_AES->AESIN3.U = hsmCfgData.Cmac.Key.w[3];
 /* 将CMAC密钥加载到槽2 */
 HSM_AES->AESCTRL.U = AES_CTRL(OPC_WK, KEYNR_2, CVNR_0);
 /* 将所有零的IV写入上下文0 */
 HSM_AES->AESCTRL.U = AES_CTRL(OPC_WCV, KEYNR_2, CVNR_0);
 cryptoStatus = E_CRYPTO_OK;  // 设置加密状态为成功
 }
 else
 {
 /* 该设备未用有效数据初始化,或DF1中有故障 */
 hsmCfgData.BootMarker = 0x0;
 cryptoStatus = E_CRYPTO_NOT_OK;  // 设置加密状态为失败
 }
 return cryptoStatus;  // 返回加密状态
 }
 
 
 
 cryptoStatus 变量用于存储加密操作的状态。
 AesDecryptDf1Block 函数用于解密DF1数据块,并将解密后的明文数据复制到RAM中。HSM_CONFIG_DATA_ROUNDS 宏定义了要执行的解密循环次数。
 hsmCfgData 结构体存储了从DF1解密得到的配置数据。
 BootMarker 是 hsmCfgData 结构体中的一个字段,用作验证解密数据是否有效的标记。
 如果 BootMarker 等于 0xAA550000,则认为数据有效,函数将加载用于CMAC的AES密钥到AES模块的密钥槽2,并设置相应的控制寄存器。
 OPC_WK 宏定义了写入密钥的操作,KEYNR_2 指定了密钥槽2,CVNR_0 指定了常量向量编号0。
 OPC_WCV 宏定义了写入初始化向量(IV)的操作。
 如果 BootMarker 不等于 0xAA550000,则认为设备未用有效数据初始化或DF1中有故障,函数将 BootMarker 设置为0并返回失败状态。
 计算CMAC并输出结果
 CmacOnBoot 函数,其作用是在系统启动时计算CMAC(Cipher-based Message Authentication Code,基于密码的消息认证码)。
 
 /**
 * @brief 在系统启动时计算CMAC。
 *
 * @param None
 * @return 加密操作成功或失败的状态。
 *
 * @ingroup
 *****************************************************************************/
 uint8_t CmacOnBoot(void)
 {
 uint32_t rounds = 0;
 uint8_t result = E_CRYPTO_NOT_OK;
 
 #if ((DEBUG & 0x01) == 0x01)
 P00_OMR.U = PORT_SET_P0;  // 调试标志,设置P00.0引脚
 #endif
 
 if(hsmCfgData.BootMarker == 0xAA550000)
 {
 /* 从命令结构体获取输入信息 */
 AES_DATA_Type *in = (AES_DATA_Type *)(hsmCfgData.BootMacStartAddress);
 
 /* 计算最后一个块和要执行的轮数 */
 rounds = AesCMAC_CalculateLastBlock(hsmCfgData.BootMacSize, in);
 HSM_AES->AESCTRL.U = AES_CTRL(OPC_WK, KEYNR_0, CVNR_0);  /* 清除输入值 */
 HSM_AES->AESCTRL.U = AES_CTRL(OPC_WCV, KEYNR_2, CVNR_0); /* 写入IV */
 
 /* 以128位块的形式读取闪存数据 */
 if (rounds > 1)
 {
 AesCmacMove((uint32_t *)in, rounds-1, AES_CTRL(OPC_CBC_ENC, KEYNR_2, CVNR_0));
 }
 
 /* 总是执行最后一个块 */
 HSM_AES->AESIN0.U = M_last.w[0];
 HSM_AES->AESIN1.U = M_last.w[1];
 HSM_AES->AESIN2.U = M_last.w[2];
 HSM_AES->AESIN3.U = M_last.w[3];
 HSM_AES->AESCTRL.U = AES_CTRL(OPC_CBC_ENC, KEYNR_2, CVNR_0);
 
 while (HSM_AES->AESSTAT.B.BSY == true)
 ;  // 等待AES模块完成操作
 
 #if 0
 /* 将计算出的CMAC与存储的值进行比较 */
 AesGetResult((uint32_t *)&Cmac_boot);
 
 for(uint32_t i=0; i < 4; i++)
 {
 if (Boot_MAC != Cmac_boot.w)
 {
 result = E_CRYPTO_NOT_OK;
 break;
 }
 else
 {
 result = E_CRYPTO_OK;
 }
 }
 #else
 /* 需要计算SAHMEM窗口,以便将数据写回主机内存 */
 HSM_BRIDGE->SAHBASE.U = (uint32_t)(Boot_ResponseAddress);
 out = (uint32_t*)(((uint32_t)(Boot_ResponseAddress) & 0x0000FFFFu) | 0xF0050000u);
 AesGetResult(out);
 out += 8;
 *out++ = hsmCfgData.BootMacSize;
 *out = hsmCfgData.BootMacStartAddress;
 result = E_CRYPTO_OK;
 #endif
 }
 
 #if ((DEBUG & 0x01) == 0x01)
 P00_OMR.U = PORT_CLR_P0;  // 调试标志,清除P00.0引脚
 #endif
 
 return result;
 }
 
 
 
 rounds 变量用于存储要执行的CMAC计算轮数。
 result 变量用于存储CMAC计算的结果状态。
 hsmCfgData 结构体包含了启动时的配置信息,包括启动标记(BootMarker)、启动MAC起始地址(BootMacStartAddress)和大小(BootMacSize)。
 如果 BootMarker 等于 0xAA550000,则认为设备已用有效数据初始化,函数将继续执行CMAC计算。
 AesCMAC_CalculateLastBlock 函数用于计算最后一个CMAC数据块和轮数。
 AES_DATA_Type 类型定义了要进行CMAC操作的数据指针。
 AesCmacMove 函数用于执行CMAC计算的中间轮。
 M_last 变量存储了最后一个CMAC数据块。
 HSM_BRIDGE 用于设置SAHMEM(Secure Access to Host Memory)基地址,以便将CMAC结果写回主机内存。
 AesGetResult 函数用于从AES模块获取CMAC计算的结果。
 返回流程
 /*!
 * @fn uint8_t CheckHashApplication(void)
 * @brief 计算 PFLASH 上的 HASH 值
 * @param None
 * @return 成功或失败的状态
 */
 uint8_t CheckHashApplication(void)
 {
 uint8_t cryptoCheck = E_CRYPTO_NOT_OK;
 /* */
 Boot_ResponseAddress = Boot_ResponseAddressRom;
 /* 加载 CMAC 密钥并获取子密钥 */
 cryptoCheck = cryptoInitBootParameters();
 
 /* 我们是否正确地解码了安全启动参数? */
 if (E_CRYPTO_OK == cryptoCheck)
 {
 /* 安全启动是否应该立即释放 TriCore 还是在检查后释放? */
 if ((0x01 & hsmCfgData.BootOptions) == 1)
 {
 /* 检查时钟控制单元是否从默认设置更改过 */
 if (true == clockWasSet)
 {
 /* 降低使用内部 100 MHz 振荡器的 PLL0 */
 CCU_RampDownSystemPll();
 /* 表示我们已经重置了时钟 */
 clockWasSet = false;
 }
 /* 释放 AURIX CPU0 */
 HSM_BRIDGE->HSM2HTF.U = 1;
 }
 /* 对 AURIX 代码运行 CMAC */
 cryptoCheck = CmacOnBoot();
 }
 
 /* 检查时钟控制单元是否从默认设置更改过 */
 if (true == clockWasSet)
 {
 /* 降低使用内部 100 MHz 振荡器的 PLL0 */
 CCU_RampDownSystemPll();
 }
 
 /* 返回结果代码,对于演示代码总是为真,对于真正的应用,这应该返回 CMAC 验证的实际状态 */
 return E_CRYPTO_OK;
 }
 
 
 
 cryptoCheck 变量用于存储加密操作的状态。
 Boot_ResponseAddress 被设置为 Boot_ResponseAddressRom,一个用于存储启动响应的地址。
 cryptoInitBootParameters 函数用于加载 CMAC(Cipher-based Message Authentication Code,基于密码的消息认证码)密钥和子密钥。
 hsmCfgData.BootOptions 包含了启动选项,如果设置了立即释放 TriCore 标志,则会检查时钟控制单元是否更改过默认设置,并执行相应的操作。
 clockWasSet 变量用于指示时钟是否已经被更改过。如果是,将调用 CCU_RampDownSystemPll 函数降低 PLL0 的频率。
 HSM_BRIDGE->HSM2HTF.U = 1; 这行代码释放了 AURIX CPU0,允许它执行用户代码。
 CmacOnBoot 函数用于对启动代码执行 CMAC 操作,以验证其完整性。
 最后,函数返回 E_CRYPTO_OK 表示成功,但在实际应用中,应根据 cryptoCheck 的实际值返回。
 ————————————————
 
 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
 
 原文链接:https://blog.csdn.net/qq_36750998/article/details/140863977
 
 
 | 
 |