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)
该公式是为了派生出中间密钥 K
1,它是通过 KDF 的压缩函数对 K
AuthID,KEY_UPDATE_ENC_C 进行压缩获得。规范中将 KDF (K, C) 压缩函数等同于 AES-MP (K | C),如下图所示。
因此 KDF (K
AuthID, KEY_UPDATE_ENC_C) 实际上就等同于 AES-MP (K
AuthID | 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 同余的最小非负解。拆分后的块为 X
1,X
2,...,X
n。以 X
i 作为明文,OUT
i-1
作为密钥进行 AES-128 ECB 模式下的循环加密运算,每一次计算的结果会和本次的 OUT
i-1 和 X
i 进行异或运算,最后的 OUT
n 的值最为压缩的结果,即 K
1 的值。(注:ECB 模式是没有初始向量的,这里的 OUT
0 应该称之为初始密钥)。
(2) K2 = KDF (KAuthID, KEY_UPDATE_MAC_C)
K
2 的获取与 K
1 同理。
(3) M1 = UID' | ID | AuthID
M
1 是 UID, ID 和 AuthID 串联后的值,其中 UID 通常是 HSM 芯片的唯一标识符,本规范中所有的 ' 指代的是新的意思,在这里指代就是,UID 不一定是被更新的 ECU 所对应的 UID,因为在 ECU 在当前状态下被更新的 ID 所对应的 F
ID 的 WILDCARD 为 0 的情况下,在这里上位机填写的 UID' 可以为 0。
(4) M2 = ENCCBC,K1,IV=0 (CID' | FID' | ”0…0“95 | KID')
该公式含义为:以 K
1 为密钥,初始向量为 0,用 AES CBC 模式对数据 C
ID' | F
ID' | ”0…0“
95 | K
ID' 进行加密,其结果赋值给 M
2,其中的 ' 在这里指代的分别是需要更新的 C
ID、F
ID 和 KEY。
- CID:用于防止重放攻击的 Counter;
- FID:用于使能与不使能所对应 ID 的 kEY 的更新;
- ”0…0“95:95 个 bit 长度的 0;
- KID:是我们需要更新的目标 KEY。
(5) M3 = CMACK2 (M1 | M2)
该公式的含义是以 K
2 为密钥,以 AES 算法的 CBC 模式,计算出 M
1 和 M
2 串联后的消息消息认证码。
(6) CMD_LOAD_KEY
CMD_LOAD_KEY 是 SHE 接口函数。用于在更新时传递 M
1, M
2, M
3。在读取时传出 M
4, M
5 的值。
(7) Check write protection of KEYID
这里检查的是 ECU 内部的需要被更新的目标 KEY 的写保护的 Flog 是否使能(FID 中的其中一个 bit),如果使能返 ERC_WRITE_ERROR,停止更新。如果未使能,将 M
1, M
2, AuthID 传递到 SHE 的 Storage。
(8) Read (AuthID)
从第(7)步存储的位置读取 AuthID 所对应的 KEY
AuthID,并将其与 M
1, M
2 一并给到 KDF 压缩算法作为输入。
(9) KDF (KEYAuthID,KEY_UPDATE_MAC_C)
基于从ECU内部读取的密钥 KEY
AuthID,以及从外部传递的 M
1, M
2,再以 KDF 压缩算法算出的结果作为内部的 K
2。具体 KDF 的实现参考(1)。
(10) CMACK2 (M1 | M2)
拿到 K
2 后,以 M
1 | M
2 作为明文,得到内部的 M
3*,这里的 * 指的是内部计算的结果。
(11) Check (M3 = M3*)
对比外部传过来的 CMAC 值 M
3 和内部计算的 M
3*,其目的是为了判断 M
1(涵盖了 UID 等信息)和 M
2(涵盖了需要被更新的密钥)在传输的过程中有没有被篡改过。如果不相等,停止更新,并返回 ERC_KEY_UPDATE_ERROR。
(12) Check (UID' = 0)
检查输入的 UID' 是否为 0(直接从输入的 M
1 中提取)。
如果不为 0,从 Storage 相应的 ID 中读取 UID。并进行对比,若不相等,返回 ERC_KEY_UPDATE_ERROR。若相等读取 AuthID 所对应的 KEY
AuthID。
如果等于 0,需要对目标 ID 所对应的 WILDCARD 的标志位是否为 0。如果是 0,读取 AuthID 所对应的 KEY
AuthID。如果不是 0,说明通配符不被允许,即不允许 UID' 为 0 作为输入进行密钥的更新。
(13) KDF (KEYAuthID, KEY_UPDATE_ENC_C)
以 KDF 算法将 KEY
AuthID, KEY_UPDATE_ENC_C 进行压缩,得到 K
1(这里的算出来的 K
1 应该与外部用的 K
1 是一致的,因为在 KEY
AuthID 外部也是已知的,且与内部相同)。
(14) DECCBC,K1,IV=0 (M2)
以 AES CBC 的模式,算出的 K
1 为密钥,初始向量为 0,对外部给到的 M
2 进行解密,得到外部给到的 CID', FID', KID',对比外部 CID' 与本地 CID 的大小,如果外部的常数没有大于本地的常数,说明该消息是重放的,应返回 ERC_KEY_UPDATE_ERROR,如果是大于本地的常数,则将最新的常数、密钥、和标志位存储进相应的位置。