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

安全启动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模块的寄存器地址。
  • AESIN0AESIN3 是AES模块的输入寄存器,用于加载32位的数据块。
  • AESCTRL 是AES模块的控制寄存器,用于设置解密操作的参数。AES_CTRL 宏设置了操作模式为ECB(电子密码本模式),使用密钥编号0,和常量向量编号0。
  • AESSTAT 是AES模块的状态寄存器,其中的 BSY 位表示AES模块是否忙碌。
  • AESOUT0AESOUT3 是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解密得到的配置数据。
  • BootMarkerhsmCfgData 结构体中的一个字段,用作验证解密数据是否有效的标记。
  • 如果 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[i] != Cmac_boot.w[i])
            {
                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 的实际值返回。
  • 20
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

美好生活丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值