refactor and split class

move hardware manipulation functions into new class
This commit is contained in:
Rotzbua
2017-05-22 12:25:18 +02:00
parent 9c221e59ee
commit 0b258bfe0f
8 changed files with 299 additions and 242 deletions

View File

@@ -6,6 +6,7 @@
#include <Arduino.h>
#include "MFRC522.h"
#include "MFRC522Debug.h"
/////////////////////////////////////////////////////////////////////////////////////
// Functions for setting up the Arduino
@@ -1213,18 +1214,7 @@ MFRC522::StatusCode MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointe
*/
const __FlashStringHelper *MFRC522::GetStatusCodeName(MFRC522::StatusCode code ///< One of the StatusCode enums.
) {
switch (code) {
case STATUS_OK: return F("Success.");
case STATUS_ERROR: return F("Error in communication.");
case STATUS_COLLISION: return F("Collission detected.");
case STATUS_TIMEOUT: return F("Timeout in communication.");
case STATUS_NO_ROOM: return F("A buffer is not big enough.");
case STATUS_INTERNAL_ERROR: return F("Internal error in the code. Should not happen.");
case STATUS_INVALID: return F("Invalid argument.");
case STATUS_CRC_WRONG: return F("The CRC_A does not match.");
case STATUS_MIFARE_NACK: return F("A MIFARE PICC responded with NAK.");
default: return F("Unknown error");
}
return MFRC522Debug::GetStatusCodeName(code);
} // End GetStatusCodeName()
/**
@@ -1261,20 +1251,7 @@ MFRC522::PICC_Type MFRC522::PICC_GetType(byte sak ///< The SAK byte returned fr
*/
const __FlashStringHelper *MFRC522::PICC_GetTypeName(PICC_Type piccType ///< One of the PICC_Type enums.
) {
switch (piccType) {
case PICC_TYPE_ISO_14443_4: return F("PICC compliant with ISO/IEC 14443-4");
case PICC_TYPE_ISO_18092: return F("PICC compliant with ISO/IEC 18092 (NFC)");
case PICC_TYPE_MIFARE_MINI: return F("MIFARE Mini, 320 bytes");
case PICC_TYPE_MIFARE_1K: return F("MIFARE 1KB");
case PICC_TYPE_MIFARE_4K: return F("MIFARE 4KB");
case PICC_TYPE_MIFARE_UL: return F("MIFARE Ultralight or Ultralight C");
case PICC_TYPE_MIFARE_PLUS: return F("MIFARE Plus");
case PICC_TYPE_MIFARE_DESFIRE: return F("MIFARE DESFire");
case PICC_TYPE_TNP3XXX: return F("MIFARE TNP3XXX");
case PICC_TYPE_NOT_COMPLETE: return F("SAK indicates UID is not complete.");
case PICC_TYPE_UNKNOWN:
default: return F("Unknown type");
}
return MFRC522Debug::PICC_GetTypeName(piccType);
} // End PICC_GetTypeName()
/**
@@ -1628,207 +1605,6 @@ void MFRC522::MIFARE_SetAccessBits( byte *accessBitBuffer, ///< Pointer to byte
accessBitBuffer[2] = c3 << 4 | c2;
} // End MIFARE_SetAccessBits()
/**
* Performs the "magic sequence" needed to get Chinese UID changeable
* Mifare cards to allow writing to sector 0, where the card UID is stored.
*
* Note that you do not need to have selected the card through REQA or WUPA,
* this sequence works immediately when the card is in the reader vicinity.
* This means you can use this method even on "bricked" cards that your reader does
* not recognise anymore (see MFRC522::MIFARE_UnbrickUidSector).
*
* Of course with non-bricked devices, you're free to select them before calling this function.
*/
bool MFRC522::MIFARE_OpenUidBackdoor(bool logErrors) {
// Magic sequence:
// > 50 00 57 CD (HALT + CRC)
// > 40 (7 bits only)
// < A (4 bits only)
// > 43
// < A (4 bits only)
// Then you can write to sector 0 without authenticating
PICC_HaltA(); // 50 00 57 CD
byte cmd = 0x40;
byte validBits = 7; /* Our command is only 7 bits. After receiving card response,
this will contain amount of valid response bits. */
byte response[32]; // Card's response is written here
byte received;
MFRC522::StatusCode status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 40
if(status != STATUS_OK) {
if(logErrors) {
Serial.println(F("Card did not respond to 0x40 after HALT command. Are you sure it is a UID changeable one?"));
Serial.print(F("Error name: "));
Serial.println(GetStatusCodeName(status));
}
return false;
}
if (received != 1 || response[0] != 0x0A) {
if (logErrors) {
Serial.print(F("Got bad response on backdoor 0x40 command: "));
Serial.print(response[0], HEX);
Serial.print(F(" ("));
Serial.print(validBits);
Serial.print(F(" valid bits)\r\n"));
}
return false;
}
cmd = 0x43;
validBits = 8;
status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 43
if(status != STATUS_OK) {
if(logErrors) {
Serial.println(F("Error in communication at command 0x43, after successfully executing 0x40"));
Serial.print(F("Error name: "));
Serial.println(GetStatusCodeName(status));
}
return false;
}
if (received != 1 || response[0] != 0x0A) {
if (logErrors) {
Serial.print(F("Got bad response on backdoor 0x43 command: "));
Serial.print(response[0], HEX);
Serial.print(F(" ("));
Serial.print(validBits);
Serial.print(F(" valid bits)\r\n"));
}
return false;
}
// You can now write to sector 0 without authenticating!
return true;
} // End MIFARE_OpenUidBackdoor()
/**
* Reads entire block 0, including all manufacturer data, and overwrites
* that block with the new UID, a freshly calculated BCC, and the original
* manufacturer data.
*
* It assumes a default KEY A of 0xFFFFFFFFFFFF.
* Make sure to have selected the card before this function is called.
*/
bool MFRC522::MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors) {
// UID + BCC byte can not be larger than 16 together
if (!newUid || !uidSize || uidSize > 15) {
if (logErrors) {
Serial.println(F("New UID buffer empty, size 0, or size > 15 given"));
}
return false;
}
// Authenticate for reading
MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
MFRC522::StatusCode status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid);
if (status != STATUS_OK) {
if (status == STATUS_TIMEOUT) {
// We get a read timeout if no card is selected yet, so let's select one
// Wake the card up again if sleeping
// byte atqa_answer[2];
// byte atqa_size = 2;
// PICC_WakeupA(atqa_answer, &atqa_size);
if (!PICC_IsNewCardPresent() || !PICC_ReadCardSerial()) {
Serial.println(F("No card was previously selected, and none are available. Failed to set UID."));
return false;
}
status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid);
if (status != STATUS_OK) {
// We tried, time to give up
if (logErrors) {
Serial.println(F("Failed to authenticate to card for reading, could not set UID: "));
Serial.println(GetStatusCodeName(status));
}
return false;
}
}
else {
if (logErrors) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(GetStatusCodeName(status));
}
return false;
}
}
// Read block 0
byte block0_buffer[18];
byte byteCount = sizeof(block0_buffer);
status = MIFARE_Read((byte)0, block0_buffer, &byteCount);
if (status != STATUS_OK) {
if (logErrors) {
Serial.print(F("MIFARE_Read() failed: "));
Serial.println(GetStatusCodeName(status));
Serial.println(F("Are you sure your KEY A for sector 0 is 0xFFFFFFFFFFFF?"));
}
return false;
}
// Write new UID to the data we just read, and calculate BCC byte
byte bcc = 0;
for (uint8_t i = 0; i < uidSize; i++) {
block0_buffer[i] = newUid[i];
bcc ^= newUid[i];
}
// Write BCC byte to buffer
block0_buffer[uidSize] = bcc;
// Stop encrypted traffic so we can send raw bytes
PCD_StopCrypto1();
// Activate UID backdoor
if (!MIFARE_OpenUidBackdoor(logErrors)) {
if (logErrors) {
Serial.println(F("Activating the UID backdoor failed."));
}
return false;
}
// Write modified block 0 back to card
status = MIFARE_Write((byte)0, block0_buffer, (byte)16);
if (status != STATUS_OK) {
if (logErrors) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(GetStatusCodeName(status));
}
return false;
}
// Wake the card up again
byte atqa_answer[2];
byte atqa_size = 2;
PICC_WakeupA(atqa_answer, &atqa_size);
return true;
}
/**
* Resets entire sector 0 to zeroes, so the card can be read again by readers.
*/
bool MFRC522::MIFARE_UnbrickUidSector(bool logErrors) {
MIFARE_OpenUidBackdoor(logErrors);
byte block0_buffer[] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// Write modified block 0 back to card
MFRC522::StatusCode status = MIFARE_Write((byte)0, block0_buffer, (byte)16);
if (status != STATUS_OK) {
if (logErrors) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(GetStatusCodeName(status));
}
return false;
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////
// Convenience functions - does not add extra functionality
/////////////////////////////////////////////////////////////////////////////////////

View File

@@ -389,12 +389,10 @@ public:
// Support functions
/////////////////////////////////////////////////////////////////////////////////////
StatusCode PCD_MIFARE_Transceive(byte *sendData, byte sendLen, bool acceptTimeout = false);
// old function used too much memory, now name moved to flash; if you need char, copy from flash to memory
//const char *GetStatusCodeName(byte code);
static const __FlashStringHelper *GetStatusCodeName(StatusCode code);
static PICC_Type PICC_GetType(byte sak);
// old function used too much memory, now name moved to flash; if you need char, copy from flash to memory
//const char *PICC_GetTypeName(byte type);
// Support functions for debuging - proxy for MFRC522Debug to keep backwarts compatibility
static const __FlashStringHelper *GetStatusCodeName(StatusCode code);
static const __FlashStringHelper *PICC_GetTypeName(PICC_Type type);
// Support functions for debuging
@@ -407,12 +405,6 @@ public:
// Advanced functions for MIFARE
void MIFARE_SetAccessBits(byte *accessBitBuffer, byte g0, byte g1, byte g2, byte g3);
DEPRECATED_MSG("will move to extra class in next version")
bool MIFARE_OpenUidBackdoor(bool logErrors);
DEPRECATED_MSG("will move to extra class in next version")
bool MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors);
DEPRECATED_MSG("will move to extra class in next version")
bool MIFARE_UnbrickUidSector(bool logErrors);
/////////////////////////////////////////////////////////////////////////////////////
// Convenience functions - does not add extra functionality

46
src/MFRC522Debug.cpp Normal file
View File

@@ -0,0 +1,46 @@
#include "MFRC522Debug.h"
/**
* Returns a __FlashStringHelper pointer to the PICC type name.
*
* @return const __FlashStringHelper *
*/
const __FlashStringHelper *MFRC522Debug::PICC_GetTypeName(MFRC522::PICC_Type piccType ///< One of the PICC_Type enums.
) {
switch (piccType) {
case MFRC522::PICC_TYPE_ISO_14443_4: return F("PICC compliant with ISO/IEC 14443-4");
case MFRC522::PICC_TYPE_ISO_18092: return F("PICC compliant with ISO/IEC 18092 (NFC)");
case MFRC522::PICC_TYPE_MIFARE_MINI: return F("MIFARE Mini, 320 bytes");
case MFRC522::PICC_TYPE_MIFARE_1K: return F("MIFARE 1KB");
case MFRC522::PICC_TYPE_MIFARE_4K: return F("MIFARE 4KB");
case MFRC522::PICC_TYPE_MIFARE_UL: return F("MIFARE Ultralight or Ultralight C");
case MFRC522::PICC_TYPE_MIFARE_PLUS: return F("MIFARE Plus");
case MFRC522::PICC_TYPE_MIFARE_DESFIRE: return F("MIFARE DESFire");
case MFRC522::PICC_TYPE_TNP3XXX: return F("MIFARE TNP3XXX");
case MFRC522::PICC_TYPE_NOT_COMPLETE: return F("SAK indicates UID is not complete.");
case MFRC522::PICC_TYPE_UNKNOWN:
default: return F("Unknown type");
}
} // End PICC_GetTypeName()
/**
* Returns a __FlashStringHelper pointer to a status code name.
*
* @return const __FlashStringHelper *
*/
const __FlashStringHelper *MFRC522Debug::GetStatusCodeName(MFRC522::StatusCode code ///< One of the StatusCode enums.
) {
switch (code) {
case MFRC522::STATUS_OK: return F("Success.");
case MFRC522::STATUS_ERROR: return F("Error in communication.");
case MFRC522::STATUS_COLLISION: return F("Collission detected.");
case MFRC522::STATUS_TIMEOUT: return F("Timeout in communication.");
case MFRC522::STATUS_NO_ROOM: return F("A buffer is not big enough.");
case MFRC522::STATUS_INTERNAL_ERROR: return F("Internal error in the code. Should not happen.");
case MFRC522::STATUS_INVALID: return F("Invalid argument.");
case MFRC522::STATUS_CRC_WRONG: return F("The CRC_A does not match.");
case MFRC522::STATUS_MIFARE_NACK: return F("A MIFARE PICC responded with NAK.");
default: return F("Unknown error");
}
} // End GetStatusCodeName()

14
src/MFRC522Debug.h Normal file
View File

@@ -0,0 +1,14 @@
#include "MFRC522.h"
#ifndef MFRC522Debug_h
#define MFRC522Debug_h
class MFRC522Debug {
private:
public:
// Get human readable code and type
static const __FlashStringHelper *PICC_GetTypeName(MFRC522::PICC_Type type);
static const __FlashStringHelper *GetStatusCodeName(MFRC522::StatusCode code);
};
#endif // MFRC522Debug_h

203
src/MFRC522Hack.cpp Normal file
View File

@@ -0,0 +1,203 @@
#include "MFRC522Hack.h"
/**
* Performs the "magic sequence" needed to get Chinese UID changeable
* Mifare cards to allow writing to sector 0, where the card UID is stored.
*
* Note that you do not need to have selected the card through REQA or WUPA,
* this sequence works immediately when the card is in the reader vicinity.
* This means you can use this method even on "bricked" cards that your reader does
* not recognise anymore (see MFRC522Hack::MIFARE_UnbrickUidSector).
*
* Of course with non-bricked devices, you're free to select them before calling this function.
*/
bool MFRC522Hack::MIFARE_OpenUidBackdoor(bool logErrors) {
// Magic sequence:
// > 50 00 57 CD (HALT + CRC)
// > 40 (7 bits only)
// < A (4 bits only)
// > 43
// < A (4 bits only)
// Then you can write to sector 0 without authenticating
_device->PICC_HaltA(); // 50 00 57 CD
byte cmd = 0x40;
byte validBits = 7; /* Our command is only 7 bits. After receiving card response,
this will contain amount of valid response bits. */
byte response[32]; // Card's response is written here
byte received;
MFRC522::StatusCode status = _device->PCD_TransceiveData(&cmd, (byte) 1, response, &received, &validBits, (byte) 0,
false); // 40
if (status != MFRC522::STATUS_OK) {
if (logErrors) {
Serial.println(
F("Card did not respond to 0x40 after HALT command. Are you sure it is a UID changeable one?"));
Serial.print(F("Error name: "));
Serial.println(MFRC522Debug::GetStatusCodeName(status));
}
return false;
}
if (received != 1 || response[0] != 0x0A) {
if (logErrors) {
Serial.print(F("Got bad response on backdoor 0x40 command: "));
Serial.print(response[0], HEX);
Serial.print(F(" ("));
Serial.print(validBits);
Serial.print(F(" valid bits)\r\n"));
}
return false;
}
cmd = 0x43;
validBits = 8;
status = _device->PCD_TransceiveData(&cmd, (byte) 1, response, &received, &validBits, (byte) 0, false); // 43
if (status != MFRC522::STATUS_OK) {
if (logErrors) {
Serial.println(F("Error in communication at command 0x43, after successfully executing 0x40"));
Serial.print(F("Error name: "));
Serial.println(MFRC522Debug::GetStatusCodeName(status));
}
return false;
}
if (received != 1 || response[0] != 0x0A) {
if (logErrors) {
Serial.print(F("Got bad response on backdoor 0x43 command: "));
Serial.print(response[0], HEX);
Serial.print(F(" ("));
Serial.print(validBits);
Serial.print(F(" valid bits)\r\n"));
}
return false;
}
// You can now write to sector 0 without authenticating!
return true;
} // End MIFARE_OpenUidBackdoor()
/**
* Reads entire block 0, including all manufacturer data, and overwrites
* that block with the new UID, a freshly calculated BCC, and the original
* manufacturer data.
*
* It assumes a default KEY A of 0xFFFFFFFFFFFF.
* Make sure to have selected the card before this function is called.
*/
bool MFRC522Hack::MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors) {
// UID + BCC byte can not be larger than 16 together
if (!newUid || !uidSize || uidSize > 15) {
if (logErrors) {
Serial.println(F("New UID buffer empty, size 0, or size > 15 given"));
}
return false;
}
// Authenticate for reading
MFRC522::MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
MFRC522::StatusCode status = _device->PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte) 1, &key, &(_device->uid));
if (status != MFRC522::STATUS_OK) {
if (status == MFRC522::STATUS_TIMEOUT) {
// We get a read timeout if no card is selected yet, so let's select one
// Wake the card up again if sleeping
// byte atqa_answer[2];
// byte atqa_size = 2;
// PICC_WakeupA(atqa_answer, &atqa_size);
if (!_device->PICC_IsNewCardPresent() || !_device->PICC_ReadCardSerial()) {
Serial.println(F("No card was previously selected, and none are available. Failed to set UID."));
return false;
}
status = _device->PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte) 1, &key, &(_device->uid));
if (status != MFRC522::STATUS_OK) {
// We tried, time to give up
if (logErrors) {
Serial.println(F("Failed to authenticate to card for reading, could not set UID: "));
Serial.println(MFRC522Debug::GetStatusCodeName(status));
}
return false;
}
} else {
if (logErrors) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(MFRC522Debug::GetStatusCodeName(status));
}
return false;
}
}
// Read block 0
byte block0_buffer[18];
byte byteCount = sizeof(block0_buffer);
status = _device->MIFARE_Read((byte) 0, block0_buffer, &byteCount);
if (status != MFRC522::STATUS_OK) {
if (logErrors) {
Serial.print(F("MIFARE_Read() failed: "));
Serial.println(MFRC522Debug::GetStatusCodeName(status));
Serial.println(F("Are you sure your KEY A for sector 0 is 0xFFFFFFFFFFFF?"));
}
return false;
}
// Write new UID to the data we just read, and calculate BCC byte
byte bcc = 0;
for (uint8_t i = 0; i < uidSize; i++) {
block0_buffer[i] = newUid[i];
bcc ^= newUid[i];
}
// Write BCC byte to buffer
block0_buffer[uidSize] = bcc;
// Stop encrypted traffic so we can send raw bytes
_device->PCD_StopCrypto1();
// Activate UID backdoor
if (!MIFARE_OpenUidBackdoor(logErrors)) {
if (logErrors) {
Serial.println(F("Activating the UID backdoor failed."));
}
return false;
}
// Write modified block 0 back to card
status = _device->MIFARE_Write((byte) 0, block0_buffer, (byte) 16);
if (status != MFRC522::STATUS_OK) {
if (logErrors) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(MFRC522Debug::GetStatusCodeName(status));
}
return false;
}
// Wake the card up again
byte atqa_answer[2];
byte atqa_size = 2;
_device->PICC_WakeupA(atqa_answer, &atqa_size);
return true;
}
/**
* Resets entire sector 0 to zeroes, so the card can be read again by readers.
*/
bool MFRC522Hack::MIFARE_UnbrickUidSector(bool logErrors) {
MIFARE_OpenUidBackdoor(logErrors);
byte block0_buffer[] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00};
// Write modified block 0 back to card
MFRC522::StatusCode status = _device->MIFARE_Write((byte) 0, block0_buffer, (byte) 16);
if (status != MFRC522::STATUS_OK) {
if (logErrors) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(MFRC522Debug::GetStatusCodeName(status));
}
return false;
}
return true;
}

22
src/MFRC522Hack.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef MFRC522HACK_H
#define MFRC522HACK_H
#include <Arduino.h>
#include "MFRC522.h"
#include "MFRC522Debug.h"
class MFRC522Hack {
private:
MFRC522 *const _device;
public:
MFRC522Hack(MFRC522 *device) : _device(device) {};
bool MIFARE_OpenUidBackdoor(bool logErrors);
bool MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors);
bool MIFARE_UnbrickUidSector(bool logErrors);
};
#endif //MFRC522HACK_H