当使用Desfire本机包装的APDU与卡通信时,必须使用命令和响应的哪些部分来计算CMAC?
验证成功后,我有以下会话密钥:
Session Key: 7CCEBF73356F21C9191E87472F9D0EA2
然后当我发送GetKeyVersion命令时,卡会返回我正在尝试验证的以下CMAC:
<< 90 64 00 00 01 00 00
>> 00 3376289145DA8C27 9100
我已根据“NIST特刊800-38B”实施了CMAC算法并确保其正确无误。但我不知道必须使用命令和响应APDU的哪些部分来计算CMAC。
我正在使用TDES,因此MAC是8个字节。
答案 0 :(得分:1)
过去几天我一直在看同样的问题,我想我至少可以给你一些指示。得到一切&#39;就这样&#39;花了一些时间,恩智浦的文件(假设你有访问权限)在某些情况下有点难以解释。
因此,您可能知道,您需要在传输和接收时计算CMAC(并更新您的init vec)。每次将CMAC计算为下一次加密操作的初始化时(无论是CMAC还是加密等),都需要保存CMAC。
在为您的示例计算CMAC时,要输入CMAC算法的数据是INS字节(0x64
)和命令数据(0x00
)。当然这将由CMAC指定填充等。但是,请注意,不计算整个APDU包装中的CMAC(即90 64 00 00 01 00 00
),只使用INS字节和数据有效负载。
在接收时,您需要获取数据(0x00
)和第二个状态字节(也是0x00
)并计算CMAC。在这个例子中它并不重要,但顺序在这里很重要。您使用响应主体(不包括CMAC),然后使用SW2。
请注意,实际只发送了一半CMAC - CMAC应该产生16个字节,而卡正在发送前8个字节。
还有一些其他因素阻碍了我,包括:
我仍在努力正确计算Write Data命令的响应。命令成功但我无法验证CMAC。我知道写入数据没有填充CMAC填充但只是零 - 还不确定我还错过了什么。
最后,这是与我的日志中的卡通信的一个真实示例:
身份验证完成(AES),会话密钥确定为F92E48F9A6C34722A90EA29CFA0C3D12
; init vec是零
我将发送获取密钥版本命令(如您的示例所示),因此我计算CMAC超过6400
并获取1200551CA7E2F49514A1324B7E3428F1
(现在是我的初始版本)下一次计算)
将90640000010000
发送到卡并接收00C929939C467434A8
(状态为9100)。
计算CMAC超过00 00
并获取C929939C467434A8A29AB2C40B977B83
(并更新init vec以进行下一次计算)
步骤#4中我们CMAC的前半部分与步骤3中从卡片收到的8字节相匹配
答案 1 :(得分:0)
Sry for my English, - 太可怕了:)但它不是我的母语。我是俄罗斯人。
检查数组[0]的第一个MSB(7位),然后将其向左移动。如果MSB 7位= = 1,则XOR; 或者保存数组[0]的第一个MSB位,并在shiffting之后将该位放在数组[15]的末尾(LSB位)。
请在此证明: https://www.nxp.com/docs/en/application-note/AN10922.pdf
尝试这种方式:
Zeros&lt; - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
SessionKey&lt; - 00 01 02 03 E3 27 64 0C 0C 0D 0E 0F 5C 5D B9 D5
数据&lt; - 6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00
首先,您必须使用SesionKey加密16个字节(零);
enc_aes_128_ecb(Zeros);
你得到EncryptedData。
EncryptedData&lt; - 3D 08 A2 49 D9 71 58 EA 75 73 18 F2 FA 6A 27 AC
检查EncryptedData [0]的第7位[MSB - LSB] == 1?将我切换为真;
bool i = false;
if (EncryptedData[0] & 0x80){
i = true;
}
然后将所有EncryptedData Shiffting为1位&lt;&lt;。
ShiftLeft(EncryptedData,16);
现在,当i == true时 - XOR最后一个字节[15]用0x87
if (i){
ShiftedEncryptedData[15] ^= 0x87;
}
7A 11 44 93 B2 E2 B1 D4 EA E6 31 E5 F4 D4 4F 58
将其另存为KEY_1。
尝试ShiftedEncryptedData [0]的第7位[MSB - LSB] == 1?
i = false;
if (ShiftedEncryptedData[0] & 0x80){
i = true;
}
然后将所有ShiftedEncryptedData的Shiffting为1位&lt;&lt;。
ShiftLeft(ShiftedEncryptedData,16);
现在,当i == true时 - XOR最后一个字节[15]用0x87
if (i){
ShiftedEncryptedData[15] ^= 0x87;
}
F4 22 89 27 65 C5 63 A9 D5 CC 63 CB E9 A8 9E B0
将其另存为KEY_2。
现在我们获取数据(6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )
正如迈克尔所说 - 用0x80 0x00填充命令......
使用KEY_2的XOR数据 - 如果命令被填充,或者KEY_1如果不填写。 如果我们有更多像16个字节(例如32个),你必须XOR只是最后16个字节。
然后加密它:
enc_aes_128_ecb(Data);
现在你有一个CMAC。
CD C0 52 62 6D F6 60 CA 9B C1 09 FF EF 64 1A E3
Zeros&lt; - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
SessionKey&lt; - 00 01 02 03 E3 27 64 0C 0C 0D 0E 0F 5C 5D B9 D5
Key_1&lt; - 7A 11 44 93 B2 E2 B1 D4 EA E6 31 E5 F4 D4 4F 58
Key_2&lt; - F4 22 89 27 65 C5 63 A9 D5 CC 63 CB E9 A8 9E B0
数据&lt; - 6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00
CMAC&lt; - CD C0 52 62 6D F6 60 CA 9B C1 09 FF EF 64 1A E3
C / C ++函数:
void ShiftLeft(byte *data, byte dataLen){
for (int n = 0; n < dataLen - 1; n++) {
data[n] = ((data[n] << 1) | ((data[n+1] >> 7)&0x01));
}
data[dataLen - 1] <<= 1;
}
度过愉快的一天:)