Считыватель RC522 изменение ключа доступа на карточке?

leo_kg
Offline
Зарегистрирован: 20.05.2014

Здравствуйте!

Использую считываетль RC522  и библиотеку для него от сюда https://github.com/miguelbalboa/rfid

Подскажите как правильно изменить ключ доступа карточки с заводского FF FF FF FF FF FF на другой? В примерах это не показано а найти верную функцию в справочном файле не могу. Попробовал перезаписать ключ с помощю этого кода (с 48 строки) в 1 сектор 7 блок, после чего он перестал считыватся. Хотя по факту как я думаю записал опять же стандартынй ключ  FF FF FF FF FF FF 

Подскажите пожалуйста как это правильно делать?

 

// Библиотека RC522 взята отсюда: https://github.com/miguelbalboa/rfid
#include <SPI.h>
#include <MFRC522.h>

#define SS_PIN 10
#define RST_PIN 9
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance.
unsigned long uidDec, uidDecTemp;  // Для отображения номера карточки в десятичном формате.
MFRC522::MIFARE_Key keyA;



void setup() {
 Serial.begin(9600); // Initialize serial communications with the PC
 SPI.begin();   // Init SPI bus
 mfrc522.PCD_Init(); // Init MFRC522 card

  // Подготовьте ключ (используется как ключ A и как ключ B)
    // используя FFFFFFFFFFFFh, который используется по умолчанию при поставке чипа с завода
   byte Byte[6]= {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; //Ключ пароль на считывание карточки
  for (byte i = 0; i < 6; i++) {   //Закладываем в массив значения ключа который записан в массив Byte
    keyA.keyByte[i] = Byte[i];
   }
   
     
}

void loop() {

   
 if ( ! mfrc522.PICC_IsNewCardPresent()) {
  return;
 }

 // Select one of the cards
 if ( ! mfrc522.PICC_ReadCardSerial()) {
  return;
 }
 // Проверить совместимость
  MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
 Serial.println(mfrc522.PICC_GetTypeName(piccType));
    if (    piccType != MFRC522::PICC_TYPE_MIFARE_MINI
        &&  piccType != MFRC522::PICC_TYPE_MIFARE_1K
        &&  piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
        Serial.println(F("This sample only works with MIFARE Classic cards."));
        return;
    }
// Закладываем адреса сектора  блока куда будем писать данные
   
    byte sector         = 1;
    byte blockAddr      = 7;
    byte dataBlock[]    = {
        0x00, 0x00, 0x00, 0x00, //  Дамп заводского
        0x00, 0x00, 0xFF, 0x07, //  ключа
        0x80, 0x69, 0xFF, 0xFF, //  
        0xFF, 0xFF, 0xFF, 0xFF  // 
    };
    byte trailerBlock   = 7;
    MFRC522::StatusCode status;
    byte buffer[18];
    byte size = sizeof(buffer);

    // Аутентификация с использованием ключа A
   // Serial.println(F("Authenticating using key A..."));
    status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &keyA, &(mfrc522.uid));
    if (status != MFRC522::STATUS_OK) {
        Serial.print(F("PCD_Authenticate() failed: "));
        Serial.println(mfrc522.GetStatusCodeName(status));
        return;
    }

       // Аутентификация с использованием ключа B
  //  Serial.println(F("Authenticating again using key B..."));
    status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &keyA, &(mfrc522.uid));
    if (status != MFRC522::STATUS_OK) {
        Serial.print(F("PCD_Authenticate() failed: "));
        Serial.println(mfrc522.GetStatusCodeName(status));
        return;
    }

    // Записать данные в блок
 //   Serial.print(F("Writing data into block ")); Serial.print(blockAddr);
 //   Serial.println(F(" ..."));
 //   dump_byte_array(dataBlock, 16); Serial.println();
    status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, dataBlock, 16);
    if (status != MFRC522::STATUS_OK) {
        Serial.print(F("MIFARE_Write() failed: "));
        Serial.println(mfrc522.GetStatusCodeName(status));
    }
      Serial.println();

    // Чтение данных из блока (опять же, теперь должно быть то, что мы написали)
    Serial.print(F("Reading data from block ")); Serial.print(blockAddr);
    Serial.println(F(" ..."));
    status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
    if (status != MFRC522::STATUS_OK) {
        Serial.print(F("MIFARE_Read() failed: "));
        Serial.println(mfrc522.GetStatusCodeName(status));
    }
    Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":"));
    dump_byte_array(buffer, 16); Serial.println();
/*
   
        for (byte i = 0; i < 4; i++) {
          if (uidCard[i] != mfrc522.uid.uidByte[i]){}
          //  return;           
        }
*/        
 
           // Выдача серийного номера карточки "UID".
  for (byte i = 0; i < mfrc522.uid.size; i++) 
  {
    uidDecTemp = mfrc522.uid.uidByte[i];
    uidDec = uidDec*256+uidDecTemp;  
  }  
  Serial.print ("UID DEC = ");
  Serial.println (uidDec);
 
    // Остановить PICC
    mfrc522.PICC_HaltA();
   // Остановить шифрование на PCD
    mfrc522.PCD_StopCrypto1();     
}
// Функция преобразования массива в шеснацетеричнвй вид
void dump_byte_array(byte *buffer, byte bufferSize) {
    for (byte i = 0; i < bufferSize; i++) {
        Serial.print(buffer[i] < 0x10 ? " 0" : " ");
        Serial.print(buffer[i], HEX);
    }
}

 

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

А как-же гугл?

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

Если есть желание разобраться с протоколом mifare classic, лучше обратиться к первоисточнику.

leo_kg
Offline
Зарегистрирован: 20.05.2014

mykaida пишет:

А как-же гугл?

Спасибо почитал. Я впринципе тот же код использую для записи данных и работет он хорошо, но стоит записать данные типа 

053         0x00, 0x00, 0x00, 0x00, //  Дамп заводского
054         0x00, 0x00, 0xFF, 0x07, //  ключа
055         0x80, 0x69, 0xFF, 0xFF, // 
056         0xFF, 0xFF, 0xFF, 0xFF  //

в блок 3, 7, 11, и т.д. как эти сектора перестают считыватся при помощи заводского ключа  FF FF FF FF FF FF. Хотя по логике я записываю в эти блоки то же самое что там было до этого, вот только после этого эти блоки перестают считыватся, отсюда и вопрос как правильно менять ключ доступа к секторам карточки?

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

Сейчас немножко прокомментирую что вы делаете.
Перезаписываете на заводском ключе (FFFFFF) в секторе трейлер, кроме ключа A и B в трейлере хранятся аксесс биты (те что FF 07 80) для доступа к блокам сектора и самого трейлера (т.е. какие операции разрешены read/write/increment/decrement и на каком ключе можно их выполнять на А или на B).
Для трейлера аксесс биты C1.3 C2.3 C3.3 у вас имеют значение 0 0 1 что означает:
Чтение ключа A в трейлере - недопустимо
Запись ключа A в трейлере - на ключе A
Чтение и запись аксесс битов и ключа B - на ключе A
Вы на загруженном заводском ключе A изменили ключ А и после на заводском ключе пытаетесь читать этот трейлер, не получится.

leo_kg
Offline
Зарегистрирован: 20.05.2014

Алексей у меня тоже по этому поводу были сомнения, получается что бы дать полное разрешение нужно вписать (0х00 0х00 0х08) это вроде как дефолтные настройки, поправте меня пожалуйста если не прав.

P.S. Сейчас еще раз посмотрел даташит по битам доступа, получается в полубайтах обязательно должны быть прописаны так же и инверсные занцения?

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

Ключи A и B используются для разграничения зон ответственности, например используем карту для проезда на транспорте и количество поездок хранятся в определенном блоке какого-то сектора, тогда для этого блока определим аксесс биты 1 1 0 что означает
read - ключ A|B
write - ключ B
increment - ключ B
decrement - ключ A|B
У того, кто "заряжает" карту есть ключи A и B, он может как читать так и записывать,
а валидатору дадим только ключ A, чтоб мог прочесть и списать поездку.

Мне пока не понятно в каких целях хотите использовать карту?
Что хотите делать и на каких ключах? Записывать болки на одном и читать на другом?
 

leo_kg
Offline
Зарегистрирован: 20.05.2014

Хочу разобратся как правильно работать с карточкой. Задача такая,хочу сделать так скажем не подделываемый ключ если так можно выразится. А точнее мысль такая вопервых поменять пароль на один из секторв с дефолтново на свой и затем писать в одну из ячеек этого сектора один рандомный байт который будет менятся после каждого поднесения карточки к считывателю и он же будет сохранятся в памяти контроллера, таким оброзом нелзя будет наделать дубликатов с этой карточки. 

leo_kg
Offline
Зарегистрирован: 20.05.2014

И еще Алексей вопрос, получается этой строкой 

053         0x00, 0x00, 0x00, 0x00, //  Дамп заводского
054         0x00, 0x00, 0xFF, 0x07, //  ключа
055         0x80, 0x69, 0xFF, 0xFF, // 
056         0xFF, 0xFF, 0xFF, 0xFF  //

дефолтный ключА я сменил на 00 00 00 00 00 00 и естественно эти сектора перестали читатся дефолтным ключом?

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

leo_kg пишет:

Хочу разобратся как правильно работать с карточкой. Задача такая,хочу сделать так скажем не подделываемый ключ если так можно выразится. А точнее мысль такая вопервых поменять пароль на один из секторв с дефолтново на свой и затем писать в одну из ячеек этого сектора один рандомный байт который будет менятся после каждого поднесения карточки к считывателю и он же будет сохранятся в памяти контроллера, таким оброзом нелзя будет наделать дубликатов с этой карточки. 

Вы хотите это сделать на классике? 6-ти байтный ключ весьма условная защита, в своё время я повторяя сценарии атаки пытался оценить стойкость социальных карт, они тогда на классике 1к были, так атакуя 4-6 часов (на обычном юсб ридере, если он не зависал) можно было получать результаты.
Если вас это не смущает, меняйте ключ B и пишите/читайте на нем, а для трейлера аксесс биты поставьте 1 0 0 так чтоб и ключ B виден не был, трейлер после этого только на ключе B перезаписать можно будет, смотрите не потеряйте его.
 
leo_kg пишет:
дефолтный ключА я сменил на 00 00 00 00 00 00 и естественно эти сектора перестали читатся дефолтным ключом?
Я про это комментировал как раз выше ;-), только вы говорили что сам трейлер читаете ещё неудачно.

П.С.
Погорячился немножко для '1 0 0' только ключ то перезаписать сможете,
лучше '0 1 1' кроме ключа и аксесс биты изменять можно будет.
 

 
leo_kg
Offline
Зарегистрирован: 20.05.2014

Спасибо большое Алексей да класические карточки на 1 кб , потому и хочу добавить один рандомный байт что бы не могли размножать ключи. 

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

Тогда потребуется хранить этот рандом для каждой карты в контроллере и предусмотреть начальную инициализацию, когда контроллер первый раз включили и нужно для каждой карты инициализировать этот рандом.

leo_kg
Offline
Зарегистрирован: 20.05.2014

Да именно так, нужно в районе 300 ключей так что 300 лишних байт не очень моного, праблема даже в другом при быстром пронесении карточки не всегда успевате стабильно записать байт и возникает три состояния 1, байт на карточке остался старый 2, байт записался новый но выдало сообщение об ошибке записи и 3 состояние записалось непонятно что. Вот сейчас думаю как это разрулить максимально эфективно.

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

leo_kg пишет:
при быстром пронесении карточки не всегда успевате стабильно записать байт и возникает три состояния
;-))) это проблема не каты а оборудования, карта довольно шустрая. Typical ticketing transaction time of < 100 ms (including backup management)

leo_kg
Offline
Зарегистрирован: 20.05.2014

Понятно, спасибо за подсказку.

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

Сценарий у валидатора прост: горит красный - поднеси карту
Проверяем, пишем и проверяем что записали, включаем зеленый.
Если проверка не прошла - до свидания.
Если запись и проверка того, что записали не выполнена - до свидания, и сигнал в секюрити - "Возможно вторжение, карта скомпрометирована"
Или дисциплинировать или отказаться от карт.
 

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

leo_kg пишет:
нужно в районе 300 ключей так что 300 лишних байт не очень моного
Если 300 карт и раз 10 на день менять рандом (типа сотрудники по-маленькому выходят и заходят) в контроллере за пару месяцев в ее-проме или spi-флеше можно дырку протереть, нужно это учитывать.
Чтоб на пальцах биты не загибать, инверсные у меня не очень получаются, калькулятор для аксесс битов неожиданно нашёлся.

leo_kg
Offline
Зарегистрирован: 20.05.2014

Спасибо Алексей за калькулятор, масив ключей и рандомные байты к ним будут хранится в ОЗУ, ну а если сброс питания будет то по умолчанию будет в масив записаны нули в рандомые байты что будет означать повторную инициализацию рандомного байта на карточке. И еще вопрос, как я понял из даташита отдельно менять байт на карточке нельзя, пишется весь блок целиком так?