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, а просто нулями - пока не уверен, что еще я пропустил.

Наконец, вот реальный пример общения с картой из моих журналов:

  1. Аутентификация завершена (AES), и ключ сеанса определяется как F92E48F9A6C34722A90EA29CFA0C3D12; init vec - это нули

  2. Я собираюсь отправить команду Get Key Version (как в вашем примере), чтобы вычислить CMAC по 6400 и получить 1200551CA7E2F49514A1324B7E3428F1 (который теперь является моим init vec для следующего расчета)

  3. послать 90640000010000 на карту и получить 00C929939C467434A8 (статус 9100).

  4. Рассчитать CMAC более 00 00 и получить C929939C467434A8A29AB2C40B977B83 (и обновите init vec для следующего расчета)

  5. Первая половина нашего CMAC из шага #4 соответствует 8 байту, полученному от карты в шаге #3

Другие вопросы по тегам