打印
[AURIX™]

英飞凌 TC3XX单片机HSM内核开发-Secure Boot(九)

[复制链接]
629|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2024-9-2 09:20 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
安全启动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

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2028

主题

15903

帖子

13

粉丝