Mifare Desfire Wrapped Mode: Как рассчитать CMAC?
При использовании собственных APDU Desfire для связи с картой какие части команды и ответа должны использоваться для расчета CMAC?
После успешной аутентификации у меня есть следующий сеансовый ключ:
Session Key: 7CCEBF73356F21C9191E87472F9D0EA2
Затем, когда я отправляю команду GetKeyVersion, карта возвращает следующий CMAC, который я пытаюсь проверить:
<< 90 64 00 00 01 00 00
>> 00 3376289145DA8C27 9100
Я реализовал алгоритм CMAC в соответствии со специальным изданием NIST 800-38B и убедился, что он правильный. Но я не знаю, какие части APDU команд и ответов должны использоваться для расчета CMAC.
Я использую TDES, поэтому MAC составляет 8 байтов.
2 ответа
Извините за мой английский - это ужасно:) но это не мой родной язык. Я русский.
Сначала проверьте MSB (7 - бит) массива [0], а затем сдвиньте его влево. И затем XOR, если MSB 7 бит был == 1; Или сохраните первый бит MSB массива [0] и после сдвига поместите этот бит в конец массива [15] в конце (бит LSB).
Просто докажите, что это здесь: https://www.nxp.com/docs/en/application-note/AN10922.pdf
Попробуйте так:
Нули <- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
SessionKey <- 00 01 02 03 E3 27 64 0C 0C 0D 0E 0F 5C 5D B9 D5
Данные <- 6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Сначала нужно зашифровать 16 байтов (нулей) с помощью SesionKey;
enc_aes_128_ecb(Zeros);
И вы получите EncryptedData.
EncryptedData <- 3D 08 A2 49 D9 71 58 EA 75 73 18 F2 FA 6A 27 AC
Проверьте бит 7 [MSB - LSB] EncryptedData[0] == 1? переключите я в истину;
bool i = false;
if (EncryptedData[0] & 0x80){
i = true;
}
Затем выполните сдвиг всех EncryptedData на 1 бит <<.
ShiftLeft(EncryptedData,16);
А теперь, когда я == 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.
Попробуйте бит 7 [MSB - LSB] из ShiftedEncryptedData[0] == 1?
i = false;
if (ShiftedEncryptedData[0] & 0x80){
i = true;
}
Затем выполните сдвиг всех ShiftedEncryptedData на 1 бит <<.
ShiftLeft(ShiftedEncryptedData,16);
А теперь, когда я == 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)
Как говорит Майкл - команда pad с 0x80 0x00...
Данные XOR с KEY_2 - если команда была дополнена, или 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
Нули <- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
SessionKey <- 00 01 02 03 E3 27 64 0C 0C 0D 0E 0F 5C 5D B9 D5
Ключ_1 <- 7A 11 44 93 B2 E2 B1 D4 EA E6 31 E5 F4 D4 4F 58
Ключ_2 <- F4 22 89 27 65 C5 63 A9 D5 CC 63 CB E9 A8 9E B0
Данные <- 6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00
CMAC <- 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;
}
Хорошего дня:)
Последние несколько дней я смотрю на точно такую же проблему и думаю, что могу хотя бы дать вам несколько советов. Получение всего "просто так" заняло некоторое время, и документация от NXP (при условии, что у вас есть доступ) немного трудна для интерпретации в некоторых случаях.
Итак, как вы, вероятно, знаете, вам нужно рассчитать CMAC (и обновить ваш init vec) при передаче, а также при приеме. Вам нужно сохранять CMAC каждый раз, когда вы рассчитываете его как init vec для следующей операции шифрования (будь то CMAC или шифрование и т. Д.).
При расчете CMAC для вашего примера данные для подачи в алгоритм CMAC представляют собой байт INS (0x64
) и данные команды (0x00
). Конечно, это будет дополнено и т.д., как указано CMAC. Тем не менее, обратите внимание, что вы не рассчитываете CMAC для всей упаковки APDU (т.е. 90 64 00 00 01 00 00
) используется только байт INS и данные.
При получении необходимо принять данные (0x00
) и второй байт состояния (также 0x00
) и рассчитать CMAC по этому. Это не важно в этом примере, но порядок важен здесь. Вы используете тело ответа (исключая CMAC) затем SW2.
Обратите внимание, что фактически отправляется только половина CMAC - CMAC должен выдать 16 байтов, а карта отправляет первые 8 байтов.
Было несколько других вещей, которые меня поддерживали, в том числе:
- Я неправильно вычислял сессионный ключ - стоит проверить это дважды, если все пойдет не так, как вы ожидаете
- Я интерпретировал документацию, чтобы сказать, что вся структура APDU используется для вычисления CMAC (трудно прочитать их любым другим способом).
Я все еще работаю над правильным вычислением ответа от команды записи данных. Команда выполнена успешно, но я не могу проверить CMAC. Я знаю, что запись данных не дополняется заполнением CMAC, а просто нулями - пока не уверен, что еще я пропустил.
Наконец, вот реальный пример общения с картой из моих журналов:
Аутентификация завершена (AES), и ключ сеанса определяется как
F92E48F9A6C34722A90EA29CFA0C3D12
; init vec - это нулиЯ собираюсь отправить команду Get Key Version (как в вашем примере), чтобы вычислить CMAC по
6400
и получить1200551CA7E2F49514A1324B7E3428F1
(который теперь является моим init vec для следующего расчета)послать
90640000010000
на карту и получить00C929939C467434A8
(статус 9100).Рассчитать CMAC более
00 00
и получитьC929939C467434A8A29AB2C40B977B83
(и обновите init vec для следующего расчета)Первая половина нашего CMAC из шага #4 соответствует 8 байту, полученному от карты в шаге #3