KAuthID
KID

KEYAuthID 与 KID

更新一个内存槽(密钥,证书存储在内存槽中)时,外部实体必须知道知道合法的真实的另一个密钥,<Specification of Secure Hardware Extensions> 的 Table 4.5 中描述了, 需要更新的密钥和需要知道的密钥之间的对应关系。该流程图中 KEYAuthID 是需要上位机知道的密钥,KID 是需要被更新的密钥。其中的 ID 指的是 SHE 内存槽的编号。

例如:更新下表中对应 KEY_<n> 的位置的密钥时,因此外部实体应该知道现存的 MASTER_ECU_KEY,以及需要更新的 KEY_<n> 的密钥。


FID

每一个不同的内存槽类型所包含的信息不同,这里的信息通过每个内存槽的 FID 来表示,具体参考 Table 4.3。

SHE 内存槽更新流程


(1) K1 = KDF (KAuthID, KEY_UPDATE_ENC_C)
该公式是为了派生出中间密钥 K1,它是通过 KDF 的压缩函数对 KAuthID,KEY_UPDATE_ENC_C 进行压缩获得。规范中将 KDF (K, C) 压缩函数等同于 AES-MP (K | C),如下图所示。 因此 KDF (KAuthID, KEY_UPDATE_ENC_C) 实际上就等同于 AES-MP (KAuthID | KEY_UPDATE_ENC_C), KEY_UPDATE_ENC_C 是一个规范中已经给出的常数,为:0x01015348 45008000 00000000 000000B0。注意:这里的‘|’是串联的意思。

接下来我们可以先把 KDF 括号中的内容看作是 Message,在进行压缩前需要对 Message 进行填充,然后拆分成一个个 128 bit 的块。填充是在长度为 L 的消息 M 后面添加一个‘1’位, 和 k 个‘0’位,其中 k 是 L + 1 + k mod 128 与 88 mod 128 同余的最小非负解。拆分后的块为 X1,X2,...,Xn。以 Xi 作为明文,OUTi-1 作为密钥进行 AES-128 ECB 模式下的循环加密运算,每一次计算的结果会和本次的 OUTi-1 和 Xi 进行异或运算,最后的 OUTn 的值最为压缩的结果,即 K1 的值。(注:ECB 模式是没有初始向量的,这里的 OUT0 应该称之为初始密钥)。
(2) K2 = KDF (KAuthID, KEY_UPDATE_MAC_C)
K2 的获取与 K1 同理。

(3) M1 = UID' | ID | AuthID
M1 是 UID, ID 和 AuthID 串联后的值,其中 UID 通常是 HSM 芯片的唯一标识符,本规范中所有的 ' 指代的是新的意思,在这里指代就是,UID 不一定是被更新的 ECU 所对应的 UID,因为在 ECU 在当前状态下被更新的 ID 所对应的 FID 的 WILDCARD 为 0 的情况下,在这里上位机填写的 UID' 可以为 0。

(4) M2 = ENCCBC,K1,IV=0 (CID' | FID' | ”0…0“95 | KID')
该公式含义为:以 K1 为密钥,初始向量为 0,用 AES CBC 模式对数据 CID' | FID' | ”0…0“95 | KID' 进行加密,其结果赋值给 M2,其中的 ' 在这里指代的分别是需要更新的 CID、FID 和 KEY。

- CID:用于防止重放攻击的 Counter;
- FID:用于使能与不使能所对应 ID 的 kEY 的更新;
- ”0…0“95:95 个 bit 长度的 0;
- KID:是我们需要更新的目标 KEY。

(5) M3 = CMACK2 (M1 | M2)
该公式的含义是以 K2 为密钥,以 AES 算法的 CBC 模式,计算出 M1 和 M2 串联后的消息消息认证码。

(6) CMD_LOAD_KEY
CMD_LOAD_KEY 是 SHE 接口函数。用于在更新时传递 M1, M2, M3。在读取时传出 M4, M5 的值。
(7) Check write protection of KEYID
这里检查的是 ECU 内部的需要被更新的目标 KEY 的写保护的 Flog 是否使能(FID 中的其中一个 bit),如果使能返 ERC_WRITE_ERROR,停止更新。如果未使能,将 M1, M2, AuthID 传递到 SHE 的 Storage。

(8) Read (AuthID)
从第(7)步存储的位置读取 AuthID 所对应的 KEYAuthID,并将其与 M1, M2 一并给到 KDF 压缩算法作为输入。

(9) KDF (KEYAuthID,KEY_UPDATE_MAC_C)
基于从ECU内部读取的密钥 KEYAuthID,以及从外部传递的 M1, M2,再以 KDF 压缩算法算出的结果作为内部的 K2。具体 KDF 的实现参考(1)。

(10) CMACK2 (M1 | M2)
拿到 K2 后,以 M1 | M2 作为明文,得到内部的 M3*,这里的 * 指的是内部计算的结果。

(11) Check (M3 = M3*)
对比外部传过来的 CMAC 值 M3 和内部计算的 M3*,其目的是为了判断 M1(涵盖了 UID 等信息)和 M2(涵盖了需要被更新的密钥)在传输的过程中有没有被篡改过。如果不相等,停止更新,并返回 ERC_KEY_UPDATE_ERROR。

(12) Check (UID' = 0)
检查输入的 UID' 是否为 0(直接从输入的 M1 中提取)。
如果不为 0,从 Storage 相应的 ID 中读取 UID。并进行对比,若不相等,返回 ERC_KEY_UPDATE_ERROR。若相等读取 AuthID 所对应的 KEYAuthID
如果等于 0,需要对目标 ID 所对应的 WILDCARD 的标志位是否为 0。如果是 0,读取 AuthID 所对应的 KEYAuthID。如果不是 0,说明通配符不被允许,即不允许 UID' 为 0 作为输入进行密钥的更新。

(13) KDF (KEYAuthID, KEY_UPDATE_ENC_C)
以 KDF 算法将 KEYAuthID, KEY_UPDATE_ENC_C 进行压缩,得到 K1(这里的算出来的 K1 应该与外部用的 K1 是一致的,因为在 KEYAuthID 外部也是已知的,且与内部相同)。

(14) DECCBC,K1,IV=0 (M2)
以 AES CBC 的模式,算出的 K1 为密钥,初始向量为 0,对外部给到的 M2 进行解密,得到外部给到的 CID', FID', KID',对比外部 CID' 与本地 CID 的大小,如果外部的常数没有大于本地的常数,说明该消息是重放的,应返回 ERC_KEY_UPDATE_ERROR,如果是大于本地的常数,则将最新的常数、密钥、和标志位存储进相应的位置。


生成验证消息流程


KDF (KEYID, KEY_UPDATE_ENC_C)
基于更新后的 Slot 位置的密钥和 KEY_UPDATE_ENC_C 常数,以 KDF 算法进行压缩,得到临时的密钥 K3

ENCECB, K3 (CID)
以 K3 为密钥,基于 ASE-128 的 ECB 模式对从更新后的 Slot 位置密钥的 Counter 参数进行加密,得到参数 M4*。此时 M4* 相当于涵盖了被更新的密钥的 Counter 参数的摘要信息。

M4 = UID | ID | AuthID | M4*
将 UID、被更新的密钥所对应的 ID、已知密钥的 ID 和参数 M4* 串联得到 M4。然后将 M4 的数据放入响应报文里传递给上位机,此时 M4 相当于涵盖了 UID、被更新的密钥的 ID、已知密钥的 ID、被更新的密钥的 Counter 参数的摘要信息,这些信息将在上位机中解析出来,并进行验证被刷写的 KEY 的各项信息是否正确。

KDF (KEYID, KEY_UPDATE_MAC_C)
基于更新后的 Slot 位置的密钥和 KEY_UPDATE_MAC_C 常数,以 KDF 算法进行压缩,得到临时的密钥 K4

CMACK4 (M5)
以 K4 为密钥,基于 ASE-128 的 CBC 模式对进行处理,生成消息认证码 M5,同样将 M5 的数据放入响应报文里传递给上位机。因为 CMAC 的目的是为了保证数据的完整性,因此这里就是为了保证 M4 在整个传递过程中没有被第三方篡改过。