Mifare Desfire Wrapped Mode:如何计算CMAC?

时间:2016-11-14 07:21:09

标签: mifare des apdu message-authentication-code

当使用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个字节。

2 个答案:

答案 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个字节。

还有一些其他因素阻碍了我,包括:

  • 我正在错误地计算会话密钥 - 如果事情没有按照您的期望出现,值得仔细检查
  • 我解释说文档说整个APDU结构用于计算CMAC(很难以任何其他方式读取它们)

我仍在努力正确计算Write Data命令的响应。命令成功但我无法验证CMAC。我知道写入数据没有填充CMAC填充但只是零 - 还不确定我还错过了什么。

最后,这是与我的日志中的卡通信的一个真实示例:

  1. 身份验证完成(AES),会话密钥确定为F92E48F9A6C34722A90EA29CFA0C3D12; init vec是零

  2. 我将发送获取密钥版本命令(如您的示例所示),因此我计算CMAC超过6400并获取1200551CA7E2F49514A1324B7E3428F1(现在是我的初始版本)下一次计算)

  3. 90640000010000发送到卡并接收00C929939C467434A8(状态为9100)。

  4. 计算CMAC超过00 00并获取C929939C467434A8A29AB2C40B977B83(并更新init vec以进行下一次计算)

  5. 步骤#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;
}   

度过愉快的一天:)