mirror of
https://gitlab.com/fabinfra/fabhardware/fabreader3.git
synced 2025-03-12 22:51:42 +01:00
971 lines
31 KiB
C++
971 lines
31 KiB
C++
#include <Desfire.h>
|
||
|
||
MFRC522::StatusCode DESFire::PICC_RequestATS(byte *atsBuffer, byte *atsLength)
|
||
{
|
||
MFRC522::StatusCode result;
|
||
|
||
// Build command buffer
|
||
atsBuffer[0] = 0xE0; //PICC_CMD_RATS;
|
||
atsBuffer[1] = 0x50; // FSD=64, CID=0
|
||
|
||
// Calculate CRC_A
|
||
result = PCD_CalculateCRC(atsBuffer, 2, &atsBuffer[2]);
|
||
if (result != STATUS_OK) {
|
||
return result;
|
||
}
|
||
|
||
// Transmit the buffer and receive the response, validate CRC_A.
|
||
result = PCD_TransceiveData(atsBuffer, 4, atsBuffer, atsLength, NULL, 0, true);
|
||
if (result != STATUS_OK) {
|
||
PICC_HaltA();
|
||
Serial.println("WTF???");
|
||
return result;
|
||
}
|
||
|
||
return result;
|
||
} // End PICC_RequestATS()
|
||
|
||
/**
|
||
* Transmits Protocol and Parameter Selection Request (PPS)
|
||
*
|
||
* @return STATUS_OK on success, STATUS_??? otherwise.
|
||
*/
|
||
MFRC522::StatusCode DESFire::PICC_ProtocolAndParameterSelection(byte cid, ///< The lower nibble indicates the CID of the selected PICC in the range of 0x00 and 0x0E
|
||
byte pps0, ///< PPS0
|
||
byte pps1 ///< PPS1
|
||
) {
|
||
MFRC522::StatusCode result;
|
||
|
||
byte ppsBuffer[5];
|
||
byte ppsBufferSize = 5;
|
||
ppsBuffer[0] = 0xD0 | (cid & 0x0F);
|
||
ppsBuffer[1] = pps0;
|
||
ppsBuffer[2] = pps1;
|
||
|
||
// Calculate CRC_A
|
||
result = PCD_CalculateCRC(ppsBuffer, 3, &ppsBuffer[3]);
|
||
if (result != STATUS_OK) {
|
||
return result;
|
||
}
|
||
|
||
// Transmit the buffer and receive the response, validate CRC_A.
|
||
result = PCD_TransceiveData(ppsBuffer, 5, ppsBuffer, &ppsBufferSize, NULL, 0, true);
|
||
if (result == STATUS_OK) {
|
||
// This is how my MFRC522 is by default.
|
||
// Reading https://www.nxp.com/documents/data_sheet/MFRC522.pdf it seems CRC generation can only be disabled in this mode.
|
||
if (pps1 == 0x00) {
|
||
PCD_WriteRegister(TxModeReg, 0x00);
|
||
PCD_WriteRegister(RxModeReg, 0x00);
|
||
}
|
||
}
|
||
|
||
return result;
|
||
} // End PICC_ProtocolAndParameterSelection()
|
||
|
||
/**
|
||
* @see MIFARE_BlockExchangeWithData()
|
||
*/
|
||
DESFire::StatusCode DESFire::MIFARE_BlockExchange(mifare_desfire_tag *tag, byte cmd, byte *backData, byte *backLen)
|
||
{
|
||
return MIFARE_BlockExchangeWithData(tag, cmd, NULL, NULL, backData, backLen);
|
||
} // End MIFARE_BlockExchange()
|
||
|
||
/**
|
||
*
|
||
* Frame Format for DESFire APDUs
|
||
* ==============================
|
||
*
|
||
* The frame format for DESFire APDUs is based on only the ISO 14443-4 specifications for block formats.
|
||
* This is the format used by the example firmware, and seen in Figure 3.
|
||
* - PCB <20> Protocol Control Byte, this byte is used to transfer format information about each PDU block.
|
||
* - CID <20> Card Identifier field, this byte is used to identify specific tags. It contains a 4 bit CID value as well
|
||
* as information on the signal strength between the reader and the tag.
|
||
* - NAD <20> Node Address field, the example firmware does not support the use of NAD.
|
||
* - DESFire Command Code <20> This is discussed in the next section.
|
||
* - Data Bytes <20> This field contains all of the Data Bytes for the command
|
||
*
|
||
* |-----|-----|-----|---------|------|----------|
|
||
* | PCB | CID | NAD | Command | Data | Checksum |
|
||
* |-----|-----|-----|---------|------|----------|
|
||
*
|
||
* Documentation: http://read.pudn.com/downloads64/ebook/225463/M305_DESFireISO14443.pdf
|
||
* http://www.ti.com.cn/cn/lit/an/sloa213/sloa213.pdf
|
||
*/
|
||
DESFire::StatusCode DESFire::MIFARE_BlockExchangeWithData(mifare_desfire_tag *tag, byte cmd, byte *sendData, byte *sendLen, byte *backData, byte *backLen)
|
||
{
|
||
StatusCode result;
|
||
|
||
byte buffer[64];
|
||
byte bufferSize = 64;
|
||
byte sendSize = 3;
|
||
|
||
buffer[0] = tag->pcb;
|
||
buffer[1] = tag->cid;
|
||
buffer[2] = cmd;
|
||
|
||
// Append data if available
|
||
if (sendData != NULL && sendLen != NULL) {
|
||
if (*sendLen > 0) {
|
||
memcpy(&buffer[3], sendData, *sendLen);
|
||
sendSize = sendSize + *sendLen;
|
||
}
|
||
}
|
||
|
||
// Update the PCB
|
||
if (tag->pcb == 0x0A)
|
||
tag->pcb = 0x0B;
|
||
else
|
||
tag->pcb = 0x0A;
|
||
|
||
// Calculate CRC_A
|
||
result.mfrc522 = PCD_CalculateCRC(buffer, sendSize, &buffer[sendSize]);
|
||
if (result.mfrc522 != STATUS_OK) {
|
||
return result;
|
||
}
|
||
|
||
result.mfrc522 = PCD_TransceiveData(buffer, sendSize + 2, buffer, &bufferSize);
|
||
if (result.mfrc522 != STATUS_OK) {
|
||
return result;
|
||
}
|
||
|
||
// Set the DESFire status code
|
||
result.desfire = (DesfireStatusCode)(buffer[2]);
|
||
|
||
// Copy data to backData and backLen
|
||
if (backData != NULL && backLen != NULL) {
|
||
memcpy(backData, &buffer[3], bufferSize - 5);
|
||
*backLen = bufferSize - 5;
|
||
}
|
||
|
||
return result;
|
||
} // End MIFARE_BlockExchangeWithData()
|
||
|
||
DESFire::StatusCode DESFire::MIFARE_DESFIRE_GetVersion(mifare_desfire_tag *tag, MIFARE_DESFIRE_Version_t *versionInfo)
|
||
{
|
||
StatusCode result;
|
||
byte versionBuffer[64];
|
||
byte versionBufferSize = 64;
|
||
|
||
result = MIFARE_BlockExchange(tag, 0x60, versionBuffer, &versionBufferSize);
|
||
if (result.mfrc522 == STATUS_OK) {
|
||
byte hardwareVersion[2];
|
||
byte storageSize;
|
||
|
||
versionInfo->hardware.vendor_id = versionBuffer[0];
|
||
versionInfo->hardware.type = versionBuffer[1];
|
||
versionInfo->hardware.subtype = versionBuffer[2];
|
||
versionInfo->hardware.version_major = versionBuffer[3];
|
||
versionInfo->hardware.version_minor = versionBuffer[4];
|
||
versionInfo->hardware.storage_size = versionBuffer[5];
|
||
versionInfo->hardware.protocol = versionBuffer[6];
|
||
|
||
if (result.desfire == MF_ADDITIONAL_FRAME) {
|
||
result = MIFARE_BlockExchange(tag, 0xAF, versionBuffer, &versionBufferSize);
|
||
if (result.mfrc522 == STATUS_OK) {
|
||
versionInfo->software.vendor_id = versionBuffer[0];
|
||
versionInfo->software.type = versionBuffer[1];
|
||
versionInfo->software.subtype = versionBuffer[2];
|
||
versionInfo->software.version_major = versionBuffer[3];
|
||
versionInfo->software.version_minor = versionBuffer[4];
|
||
versionInfo->software.storage_size = versionBuffer[5];
|
||
versionInfo->software.protocol = versionBuffer[6];
|
||
} else {
|
||
Serial.print("Failed to send AF: ");
|
||
Serial.println(GetStatusCodeName(result));
|
||
}
|
||
|
||
if (result.desfire == MF_ADDITIONAL_FRAME) {
|
||
byte nad = 0x60;
|
||
result = MIFARE_BlockExchange(tag, 0xAF, versionBuffer, &versionBufferSize);
|
||
if (result.mfrc522 == STATUS_OK) {
|
||
memcpy(versionInfo->uid, &versionBuffer[0], 7);
|
||
memcpy(versionInfo->batch_number, &versionBuffer[7], 5);
|
||
versionInfo->production_week = versionBuffer[12];
|
||
versionInfo->production_year = versionBuffer[13];
|
||
} else {
|
||
Serial.print("Failed to send AF: ");
|
||
Serial.println(GetStatusCodeName(result));
|
||
}
|
||
}
|
||
|
||
if (result.desfire == MF_ADDITIONAL_FRAME) {
|
||
Serial.println("GetVersion(): More data???");
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
Serial.println("Version(): Failure.");
|
||
}
|
||
|
||
return result;
|
||
} // End MIFARE_DESFIRE_GetVersion
|
||
|
||
DESFire::StatusCode DESFire::MIFARE_DESFIRE_SelectApplication(mifare_desfire_tag *tag, mifare_desfire_aid_t *aid)
|
||
{
|
||
StatusCode result;
|
||
|
||
byte buffer[64];
|
||
byte bufferSize = MIFARE_AID_SIZE;
|
||
|
||
for (byte i = 0; i < MIFARE_AID_SIZE; i++) {
|
||
buffer[i] = aid->data[i];
|
||
}
|
||
|
||
result = MIFARE_BlockExchangeWithData(tag, 0x5A, buffer, &bufferSize, buffer, &bufferSize);
|
||
if (IsStatusCodeOK(result)) {
|
||
// keep track of the application
|
||
memcpy(tag->selected_application, aid->data, MIFARE_AID_SIZE);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
DESFire::StatusCode DESFire::MIFARE_DESFIRE_GetFileIDs(mifare_desfire_tag *tag, byte *files, byte *filesCount)
|
||
{
|
||
StatusCode result;
|
||
|
||
byte bufferSize = MIFARE_MAX_FILE_COUNT + 5;
|
||
byte buffer[bufferSize];
|
||
|
||
result = MIFARE_BlockExchange(tag, 0x6F, buffer, &bufferSize);
|
||
if (IsStatusCodeOK(result)) {
|
||
*filesCount = bufferSize;
|
||
memcpy(files, &buffer, *filesCount);
|
||
}
|
||
|
||
return result;
|
||
} // End MIFARE_DESFIRE_GetFileIDs
|
||
|
||
DESFire::StatusCode DESFire::MIFARE_DESFIRE_GetFileSettings(mifare_desfire_tag *tag, byte *file, mifare_desfire_file_settings_t *fileSettings)
|
||
{
|
||
StatusCode result;
|
||
|
||
byte buffer[21];
|
||
byte bufferSize = 21;
|
||
byte sendLen = 1;
|
||
|
||
buffer[0] = *file;
|
||
|
||
result = MIFARE_BlockExchangeWithData(tag, 0xF5, buffer, &sendLen, buffer, &bufferSize);
|
||
if (IsStatusCodeOK(result)) {
|
||
fileSettings->file_type = buffer[0];
|
||
fileSettings->communication_settings = buffer[1];
|
||
fileSettings->access_rights = ((uint16_t)(buffer[2]) << 8) | (buffer[3]);
|
||
|
||
switch (buffer[0]) {
|
||
case MDFT_STANDARD_DATA_FILE:
|
||
case MDFT_BACKUP_DATA_FILE:
|
||
fileSettings->settings.standard_file.file_size = ((uint32_t)(buffer[4])) | ((uint32_t)(buffer[5]) << 8) | ((uint32_t)(buffer[6]) << 16);
|
||
break;
|
||
|
||
case MDFT_VALUE_FILE_WITH_BACKUP:
|
||
fileSettings->settings.value_file.lower_limit = ((uint32_t)(buffer[4])) | ((uint32_t)(buffer[5]) << 8) | ((uint32_t)(buffer[6]) << 16) | ((uint32_t)(buffer[7]) << 24);
|
||
fileSettings->settings.value_file.upper_limit = ((uint32_t)(buffer[8])) | ((uint32_t)(buffer[9]) << 8) | ((uint32_t)(buffer[10]) << 16) | ((uint32_t)(buffer[11]) << 24);
|
||
fileSettings->settings.value_file.limited_credit_value = ((uint32_t)(buffer[12])) | ((uint32_t)(buffer[13]) << 8) | ((uint32_t)(buffer[14]) << 16) | ((uint32_t)(buffer[15]) << 24);
|
||
fileSettings->settings.value_file.limited_credit_enabled = buffer[16];
|
||
break;
|
||
|
||
case MDFT_LINEAR_RECORD_FILE_WITH_BACKUP:
|
||
case MDFT_CYCLIC_RECORD_FILE_WITH_BACKUP:
|
||
fileSettings->settings.record_file.record_size = ((uint32_t)(buffer[4])) | ((uint32_t)(buffer[5]) << 8) | ((uint32_t)(buffer[6]) << 16);
|
||
fileSettings->settings.record_file.max_number_of_records = ((uint32_t)(buffer[7])) | ((uint32_t)(buffer[8]) << 8) | ((uint32_t)(buffer[9]) << 16);
|
||
fileSettings->settings.record_file.current_number_of_records = ((uint32_t)(buffer[10])) | ((uint32_t)(buffer[11]) << 8) | ((uint32_t)(buffer[12]) << 16);
|
||
break;
|
||
|
||
default:
|
||
//return FAIL;
|
||
result.mfrc522 = STATUS_ERROR;
|
||
return result;
|
||
}
|
||
}
|
||
|
||
return result;
|
||
} // End MIFARE_DESFIRE_GetFileSettings
|
||
|
||
DESFire::StatusCode DESFire::MIFARE_DESFIRE_GetKeySettings(mifare_desfire_tag *tag, byte *settings, byte *maxKeys)
|
||
{
|
||
StatusCode result;
|
||
|
||
byte buffer[7];
|
||
byte bufferSize = 7;
|
||
|
||
result = MIFARE_BlockExchange(tag, 0x45, buffer, &bufferSize);
|
||
if (IsStatusCodeOK(result)) {
|
||
*settings = buffer[0];
|
||
*maxKeys = buffer[1];
|
||
}
|
||
|
||
return result;
|
||
} // End MIFARE_DESFIRE_GetKeySettings()
|
||
|
||
DESFire::StatusCode DESFire::MIFARE_DESFIRE_GetKeyVersion(mifare_desfire_tag *tag, byte key, byte *version)
|
||
{
|
||
StatusCode result;
|
||
|
||
byte buffer[6];
|
||
byte bufferSize = 6;
|
||
byte sendLen = 1;
|
||
|
||
buffer[0] = key;
|
||
|
||
result = MIFARE_BlockExchangeWithData(tag, 0x64, buffer, &sendLen, buffer, &bufferSize);
|
||
if (IsStatusCodeOK(result)) {
|
||
*version = buffer[0];
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
DESFire::StatusCode DESFire::MIFARE_DESFIRE_ReadData(mifare_desfire_tag *tag, byte fid, uint32_t offset, uint32_t length, byte *backData, size_t *backLen)
|
||
{
|
||
StatusCode result;
|
||
|
||
byte buffer[64];
|
||
byte bufferSize = 64;
|
||
byte sendLen = 7;
|
||
size_t outSize = 0;
|
||
|
||
// file ID
|
||
buffer[0] = fid;
|
||
// offset
|
||
buffer[1] = (offset & 0x00000F);
|
||
buffer[2] = (offset & 0x00FF00) >> 8;
|
||
buffer[3] = (offset & 0xFF0000) >> 16;
|
||
// length
|
||
buffer[4] = (length & 0x0000FF);
|
||
buffer[5] = (length & 0x00FF00) >> 8;
|
||
buffer[6] = (length & 0xFF0000) >> 16;
|
||
|
||
result = MIFARE_BlockExchangeWithData(tag, 0xBD, buffer, &sendLen, buffer, &bufferSize);
|
||
if (result.mfrc522 == STATUS_OK) {
|
||
do {
|
||
// Copy the data
|
||
memcpy(backData + outSize, buffer, bufferSize);
|
||
outSize += bufferSize;
|
||
*backLen = outSize;
|
||
|
||
if (result.desfire == MF_ADDITIONAL_FRAME) {
|
||
result = MIFARE_BlockExchange(tag, 0xAF, buffer, &bufferSize);
|
||
}
|
||
} while (result.mfrc522 == STATUS_OK && result.desfire == MF_ADDITIONAL_FRAME);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
DESFire::StatusCode DESFire::MIFARE_DESFIRE_GetValue(mifare_desfire_tag *tag, byte fid, int32_t *value)
|
||
{
|
||
StatusCode result;
|
||
|
||
byte buffer[MFRC522::FIFO_SIZE];
|
||
byte bufferSize = MFRC522::FIFO_SIZE;
|
||
byte sendLen = 1;
|
||
size_t outSize = 0;
|
||
|
||
buffer[0] = fid;
|
||
|
||
result = MIFARE_BlockExchangeWithData(tag, 0x6C, buffer, &sendLen, buffer, &bufferSize);
|
||
if (IsStatusCodeOK(result)) {
|
||
*value = ((uint32_t)buffer[0] | ((uint32_t)buffer[1] << 8) | ((uint32_t)buffer[2] << 16) | ((uint32_t)buffer[3] << 24));
|
||
}
|
||
|
||
return result;
|
||
} // End MIFARE_DESFIRE_GetValue()
|
||
|
||
DESFire::StatusCode DESFire::MIFARE_DESFIRE_GetApplicationIds(mifare_desfire_tag *tag, mifare_desfire_aid_t *aids, byte *applicationCount)
|
||
{
|
||
StatusCode result;
|
||
|
||
// MIFARE_MAX_APPLICATION_COUNT * MIFARE_AID_SIZE + PCB (1 byte) + CID (1 byte) + Checksum (2 bytes)
|
||
// I also add an extra byte in case NAD is needed
|
||
byte bufferSize = (MIFARE_MAX_APPLICATION_COUNT * MIFARE_AID_SIZE) + 5;
|
||
byte buffer[bufferSize];
|
||
byte aidBuffer[MIFARE_MAX_APPLICATION_COUNT * MIFARE_AID_SIZE];
|
||
byte aidBufferSize = 0;
|
||
|
||
result = MIFARE_BlockExchange(tag, 0x6A, buffer, &bufferSize);
|
||
if (result.mfrc522 != STATUS_OK)
|
||
return result;
|
||
|
||
// MIFARE_MAX_APPLICATION_COUNT (28) * MIFARE_AID_SIZE + PCB (1) + CID (1) + Checksum (2) = 88
|
||
// Even if the NAD byte is not present we could GET a 0xAF response.
|
||
if (result.desfire == MF_OPERATION_OK && bufferSize == 0x00) {
|
||
// Empty application list
|
||
*applicationCount = 0;
|
||
return result;
|
||
}
|
||
|
||
memcpy(aidBuffer, buffer, bufferSize);
|
||
aidBufferSize = bufferSize;
|
||
|
||
while (result.desfire == MF_ADDITIONAL_FRAME) {
|
||
bufferSize = (MIFARE_MAX_APPLICATION_COUNT * MIFARE_AID_SIZE) + 5;
|
||
result = MIFARE_BlockExchange(tag, 0xAF, buffer, &bufferSize);
|
||
if (result.mfrc522 != STATUS_OK)
|
||
return result;
|
||
|
||
// Make sure we have space (Just in case)
|
||
if ((aidBufferSize + bufferSize) > (MIFARE_MAX_APPLICATION_COUNT * MIFARE_AID_SIZE)) {
|
||
result.mfrc522 = STATUS_NO_ROOM;
|
||
return result;
|
||
}
|
||
|
||
// Append the new data
|
||
memcpy(aidBuffer + aidBufferSize, buffer, bufferSize);
|
||
}
|
||
|
||
|
||
// Applications are identified with a 3 byte application identifier(AID)
|
||
// we also received the status byte:
|
||
if ((aidBufferSize % 3) != 0) {
|
||
Serial.println(F("MIFARE_DESFIRE_GetApplicationIds(): Data is not a modulus of 3."));
|
||
// TODO: Some kind of failure
|
||
result.mfrc522 = STATUS_ERROR;
|
||
return result;
|
||
}
|
||
|
||
*applicationCount = aidBufferSize / 3;
|
||
|
||
for (byte i = 0; i < *applicationCount; i++) {
|
||
aids[i].data[0] = aidBuffer[(i * 3)];
|
||
aids[i].data[1] = aidBuffer[1 + (i * 3)];
|
||
aids[i].data[2] = aidBuffer[2 + (i * 3)];
|
||
}
|
||
|
||
return result;
|
||
} // End MIFARE_DESFIRE_GetApplicationIds()
|
||
|
||
/**
|
||
* Returns a __FlashStringHelper pointer to a status code name.
|
||
*
|
||
* @return const __FlashStringHelper *
|
||
*/
|
||
const __FlashStringHelper *DESFire::GetStatusCodeName(StatusCode code)
|
||
{
|
||
if (code.mfrc522 != MFRC522::STATUS_OK) {
|
||
return MFRC522::GetStatusCodeName(code.mfrc522);
|
||
}
|
||
|
||
switch (code.desfire) {
|
||
case MF_OPERATION_OK: return F("Successful operation.");
|
||
case MF_NO_CHANGES: return F("No changes done to backup files.");
|
||
case MF_OUT_OF_EEPROM_ERROR: return F("Insufficient NV-Mem. to complete cmd.");
|
||
case MF_ILLEGAL_COMMAND_CODE: return F("Command code not supported.");
|
||
case MF_INTEGRITY_ERROR: return F("CRC or MAC does not match data.");
|
||
case MF_NO_SUCH_KEY: return F("Invalid key number specified.");
|
||
case MF_LENGTH_ERROR: return F("Length of command string invalid.");
|
||
case MF_PERMISSION_ERROR: return F("Curr conf/status doesnt allow cmd.");
|
||
case MF_PARAMETER_ERROR: return F("Value of the parameter(s) invalid.");
|
||
case MF_APPLICATION_NOT_FOUND: return F("Requested AID not present on PICC.");
|
||
case MF_APPL_INTEGRITY_ERROR: return F("Unrecoverable err within app.");
|
||
case MF_AUTHENTICATION_ERROR: return F("Current authentication status doesn't allow requested command.");
|
||
case MF_ADDITIONAL_FRAME: return F("Additional data frame to be sent.");
|
||
case MF_BOUNDARY_ERROR: return F("Attempt to read/write beyond limits.");
|
||
case MF_PICC_INTEGRITY_ERROR: return F("Unrecoverable error within PICC.");
|
||
case MF_COMMAND_ABORTED: return F("Previous command not fully completed.");
|
||
case MF_PICC_DISABLED_ERROR: return F("PICC disabled by unrecoverable error.");
|
||
case MF_COUNT_ERROR: return F("Cant create more apps, already @ 28.");
|
||
case MF_DUPLICATE_ERROR: return F("Cant create dup. file/app.");
|
||
case MF_EEPROM_ERROR: return F("Couldnt complete NV-write operation.");
|
||
case MF_FILE_NOT_FOUND: return F("Specified file number doesnt exist.");
|
||
case MF_FILE_INTEGRITY_ERROR: return F("Unrecoverable error within file.");
|
||
default: return F("Unknown error");
|
||
}
|
||
} // End GetStatusCodeName()
|
||
|
||
const __FlashStringHelper *DESFire::GetFileTypeName(mifare_desfire_file_types fileType)
|
||
{
|
||
switch (fileType) {
|
||
case MDFT_STANDARD_DATA_FILE: return F("Standard data file.");
|
||
case MDFT_BACKUP_DATA_FILE: return F("Backup data file.");
|
||
case MDFT_VALUE_FILE_WITH_BACKUP: return F("Value file with backup.");
|
||
case MDFT_LINEAR_RECORD_FILE_WITH_BACKUP: return F("Linear record file with backup.");
|
||
case MDFT_CYCLIC_RECORD_FILE_WITH_BACKUP: return F("Cyclic record file with backup.");
|
||
default: return F("Unknown file type.");
|
||
}
|
||
} // End GetFileTypeName()
|
||
|
||
const __FlashStringHelper *DESFire::GetCommunicationModeName(mifare_desfire_communication_modes communicationMode)
|
||
{
|
||
switch (communicationMode) {
|
||
case MDCM_PLAIN: return(F("Plain Communication."));
|
||
case MDCM_MACED: return(F("Plain Comm secured by DES/3DES MACing."));
|
||
case MDCM_ENCIPHERED: return(F("Fully DES/3DES enciphered comm."));
|
||
default: return F("Unknown communication mode.");
|
||
}
|
||
} // End GetCommunicationModeName()
|
||
|
||
bool DESFire::IsStatusCodeOK(StatusCode code)
|
||
{
|
||
if (code.mfrc522 != STATUS_OK)
|
||
return false;
|
||
if (code.desfire != MF_OPERATION_OK)
|
||
return false;
|
||
|
||
return true;
|
||
} // End IsStatusCodeOK();
|
||
|
||
void DESFire::PICC_DumpMifareDesfireMasterKey(mifare_desfire_tag *tag)
|
||
{
|
||
StatusCode response;
|
||
mifare_desfire_aid_t aid;
|
||
|
||
aid.data[0] = 0x00;
|
||
aid.data[1] = 0x00;
|
||
aid.data[2] = 0x00;
|
||
|
||
Serial.println(F("-- Desfire Master Key ---------------------------------------"));
|
||
Serial.println(F("-------------------------------------------------------------"));
|
||
// Select the current application.
|
||
response = MIFARE_DESFIRE_SelectApplication(tag, &aid);
|
||
if (!IsStatusCodeOK(response)) {
|
||
Serial.println(F("Error: Failed to select application."));
|
||
Serial.println(GetStatusCodeName(response));
|
||
Serial.println(F("-------------------------------------------------------------"));
|
||
return;
|
||
}
|
||
|
||
// Get Key settings
|
||
byte keySettings;
|
||
byte keyCount = 0;
|
||
byte keyVersion;
|
||
|
||
response = MIFARE_DESFIRE_GetKeySettings(tag, &keySettings, &keyCount);
|
||
if (IsStatusCodeOK(response)) {
|
||
Serial.print(F(" Key settings : 0x"));
|
||
if (keySettings < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.println(keySettings, HEX);
|
||
|
||
Serial.print(F(" Max num keys : "));
|
||
Serial.println(keyCount);
|
||
|
||
// Output key versions
|
||
if (keyCount > 0) {
|
||
Serial.println(F(" ----------------------------------------------------------"));
|
||
Serial.println(F(" Key Versions"));
|
||
|
||
// Get key versions (No output will be outputed later)
|
||
for (byte ixKey = 0; ixKey < keyCount; ixKey++) {
|
||
response = MIFARE_DESFIRE_GetKeyVersion(tag, ixKey, &keyVersion);
|
||
Serial.print(F(" Key 0x"));
|
||
if (ixKey < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.print(ixKey, HEX);
|
||
Serial.print(F(" : "));
|
||
|
||
if (IsStatusCodeOK(response)) {
|
||
Serial.print(F("0x"));
|
||
if (keyVersion < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.println(keyVersion, HEX);
|
||
} else {
|
||
Serial.println(GetStatusCodeName(response));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
Serial.println(F(" Error: Failed to get application key settings."));
|
||
// Just to be sure..
|
||
keyCount = 0;
|
||
}
|
||
|
||
Serial.println(F("-------------------------------------------------------------"));
|
||
} // End PICC_DumpMifareDesfireMasterKey()
|
||
|
||
void DESFire::PICC_DumpMifareDesfireVersion(mifare_desfire_tag *tag, MIFARE_DESFIRE_Version_t *versionInfo)
|
||
{
|
||
Serial.println(F("-- Desfire Information --------------------------------------"));
|
||
Serial.println(F("-------------------------------------------------------------"));
|
||
switch (versionInfo->hardware.version_major) {
|
||
case 0x00:
|
||
Serial.println(F(" Card type : MIFARE DESFire (MF3ICD40)"));
|
||
switch (versionInfo->hardware.storage_size) {
|
||
case 0x16:
|
||
Serial.print(F(" 2K"));
|
||
break;
|
||
case 0x18:
|
||
Serial.print(F(" 4K"));
|
||
break;
|
||
case 0x1A:
|
||
Serial.print(F(" 8K"));
|
||
break;
|
||
}
|
||
Serial.println();
|
||
break;
|
||
case 0x01:
|
||
Serial.print(F(" Card type : MIFARE DESFire EV1"));
|
||
switch (versionInfo->hardware.storage_size) {
|
||
case 0x16:
|
||
Serial.print(F(" 2K"));
|
||
break;
|
||
case 0x18:
|
||
Serial.print(F(" 4K"));
|
||
break;
|
||
case 0x1A:
|
||
Serial.print(F(" 8K"));
|
||
break;
|
||
}
|
||
Serial.println();
|
||
break;
|
||
case 0x12:
|
||
Serial.print(F(" Card type : MIFARE DESFire EV2"));
|
||
switch (versionInfo->hardware.storage_size) {
|
||
case 0x16:
|
||
Serial.print(F(" 2K"));
|
||
break;
|
||
case 0x18:
|
||
Serial.print(F(" 4K"));
|
||
break;
|
||
case 0x1A:
|
||
Serial.print(F(" 8K"));
|
||
break;
|
||
}
|
||
Serial.println();
|
||
break;
|
||
}
|
||
|
||
// UID
|
||
Serial.print(F(" UID :"));
|
||
for (byte i = 0; i < 7; i++) {
|
||
if (versionInfo->uid[i] < 0x10)
|
||
Serial.print(F(" 0"));
|
||
else
|
||
Serial.print(F(" "));
|
||
Serial.print(versionInfo->uid[i], HEX);
|
||
}
|
||
Serial.println();
|
||
|
||
// Batch
|
||
Serial.print(F(" Batch number :"));
|
||
for (byte i = 0; i < 5; i++) {
|
||
if (versionInfo->batch_number[i] < 0x10)
|
||
Serial.print(F(" 0"));
|
||
else
|
||
Serial.print(F(" "));
|
||
Serial.print(versionInfo->batch_number[i], HEX);
|
||
}
|
||
Serial.println();
|
||
|
||
Serial.print(F(" Production week : 0x"));
|
||
if (versionInfo->production_week < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.println(versionInfo->production_week, HEX);
|
||
|
||
Serial.print(F(" Production year : 0x"));
|
||
if (versionInfo->production_year < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.println(versionInfo->production_year, HEX);
|
||
|
||
Serial.println(F(" ----------------------------------------------------------"));
|
||
Serial.println(F(" Hardware Information"));
|
||
Serial.print(F(" Vendor ID : 0x"));
|
||
if (versionInfo->hardware.vendor_id < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.print(versionInfo->hardware.vendor_id, HEX);
|
||
if (versionInfo->hardware.vendor_id == 0x04)
|
||
Serial.print(F(" (NXP)"));
|
||
Serial.println();
|
||
|
||
Serial.print(F(" Type : 0x"));
|
||
if (versionInfo->hardware.type < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.println(versionInfo->hardware.type, HEX);
|
||
|
||
Serial.print(F(" Subtype : 0x"));
|
||
if (versionInfo->hardware.subtype < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.println(versionInfo->hardware.subtype, HEX);
|
||
|
||
Serial.print(F(" Version : "));
|
||
Serial.print(versionInfo->hardware.version_major);
|
||
Serial.print(F("."));
|
||
Serial.println(versionInfo->hardware.version_minor);
|
||
|
||
Serial.print(F(" Storage size : 0x"));
|
||
if (versionInfo->hardware.storage_size < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.print(versionInfo->hardware.storage_size, HEX);
|
||
switch (versionInfo->hardware.storage_size) {
|
||
case 0x16:
|
||
Serial.print(F(" (2048 bytes)"));
|
||
break;
|
||
case 0x18:
|
||
Serial.print(F(" (4096 bytes)"));
|
||
break;
|
||
case 0x1A:
|
||
Serial.print(F(" (8192 bytes)"));
|
||
break;
|
||
}
|
||
Serial.println();
|
||
|
||
Serial.print(F(" Protocol : 0x"));
|
||
if (versionInfo->hardware.protocol < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.println(versionInfo->hardware.protocol, HEX);
|
||
|
||
Serial.println(F(" ----------------------------------------------------------"));
|
||
Serial.println(F(" Software Information"));
|
||
Serial.print(F(" Vendor ID : 0x"));
|
||
if (versionInfo->software.vendor_id < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.print(versionInfo->software.vendor_id, HEX);
|
||
if (versionInfo->software.vendor_id == 0x04)
|
||
Serial.print(F(" (NXP)"));
|
||
Serial.println();
|
||
|
||
Serial.print(F(" Type : 0x"));
|
||
if (versionInfo->software.type < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.println(versionInfo->software.type, HEX);
|
||
|
||
Serial.print(F(" Subtype : 0x"));
|
||
if (versionInfo->software.subtype < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.println(versionInfo->software.subtype, HEX);
|
||
|
||
Serial.print(F(" Version : "));
|
||
Serial.print(versionInfo->software.version_major);
|
||
Serial.print(F("."));
|
||
Serial.println(versionInfo->software.version_minor);
|
||
|
||
Serial.print(F(" Storage size : 0x"));
|
||
if (versionInfo->software.storage_size < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.print(versionInfo->software.storage_size, HEX);
|
||
switch (versionInfo->software.storage_size) {
|
||
case 0x16:
|
||
Serial.print(F(" (2048 bytes)"));
|
||
break;
|
||
case 0x18:
|
||
Serial.print(F(" (4096 bytes)"));
|
||
break;
|
||
case 0x1A:
|
||
Serial.print(F(" (8192 bytes)"));
|
||
break;
|
||
}
|
||
Serial.println();
|
||
|
||
Serial.print(F(" Protocol : 0x"));
|
||
if (versionInfo->software.protocol < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.println(versionInfo->software.protocol, HEX);
|
||
|
||
Serial.println(F("-------------------------------------------------------------"));
|
||
}
|
||
|
||
void DESFire::PICC_DumpMifareDesfireApplication(mifare_desfire_tag *tag, mifare_desfire_aid_t *aid)
|
||
{
|
||
StatusCode response;
|
||
|
||
Serial.println(F("-- Desfire Application --------------------------------------"));
|
||
Serial.println(F("-------------------------------------------------------------"));
|
||
Serial.print(F(" AID :"));
|
||
for (byte i = 0; i < 3; i++) {
|
||
if (aid->data[i] < 0x10)
|
||
Serial.print(F(" 0"));
|
||
else
|
||
Serial.print(F(" "));
|
||
Serial.print(aid->data[i], HEX);
|
||
}
|
||
Serial.println();
|
||
|
||
// Select the current application.
|
||
response = MIFARE_DESFIRE_SelectApplication(tag, aid);
|
||
if (!IsStatusCodeOK(response)) {
|
||
Serial.println(F("Error: Failed to select application."));
|
||
Serial.println(GetStatusCodeName(response));
|
||
Serial.println(F("-------------------------------------------------------------"));
|
||
return;
|
||
}
|
||
|
||
// Get Key settings
|
||
byte keySettings;
|
||
byte keyCount = 0;
|
||
byte keyVersion[16];
|
||
|
||
response = MIFARE_DESFIRE_GetKeySettings(tag, &keySettings, &keyCount);
|
||
if (IsStatusCodeOK(response)) {
|
||
Serial.print(F(" Key settings : 0x"));
|
||
if (keySettings < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.println(keySettings, HEX);
|
||
|
||
Serial.print(F(" Max num keys : "));
|
||
Serial.println(keyCount);
|
||
|
||
// Get key versions (No output will be outputed later)
|
||
for (byte ixKey = 0; ixKey < keyCount; ixKey++) {
|
||
response = MIFARE_DESFIRE_GetKeyVersion(tag, ixKey, &(keyVersion[ixKey]));
|
||
if (!IsStatusCodeOK(response))
|
||
keyVersion[ixKey] = 0x00;
|
||
}
|
||
|
||
} else {
|
||
Serial.println(F(" Error: Failed to get application key settings."));
|
||
// Just to be sure..
|
||
keyCount = 0;
|
||
}
|
||
|
||
// Get the files
|
||
byte files[MIFARE_MAX_FILE_COUNT];
|
||
byte filesCount = 0;
|
||
response = MIFARE_DESFIRE_GetFileIDs(tag, files, &filesCount);
|
||
if (!IsStatusCodeOK(response)) {
|
||
Serial.println(F(" Error: Failed to get application file IDs."));
|
||
Serial.print(F(" "));
|
||
Serial.println(GetStatusCodeName(response));
|
||
Serial.println(F("-------------------------------------------------------------"));
|
||
return;
|
||
}
|
||
|
||
// Number of files
|
||
Serial.print(F(" Num. Files : "));
|
||
Serial.println(filesCount);
|
||
|
||
// Output key versions
|
||
if (keyCount > 0) {
|
||
Serial.println(F(" ----------------------------------------------------------"));
|
||
Serial.println(F(" Key Versions"));
|
||
for (byte ixKey = 0; ixKey < keyCount; ixKey++) {
|
||
Serial.print(F(" Key 0x"));
|
||
if (ixKey < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.print(ixKey, HEX);
|
||
Serial.print(F(" : 0x"));
|
||
if (keyVersion[ixKey] < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.println(keyVersion[ixKey], HEX);
|
||
}
|
||
}
|
||
|
||
for (byte i = 0; i < filesCount; i++) {
|
||
Serial.println(F(" ----------------------------------------------------------"));
|
||
Serial.println(F(" File Information"));
|
||
Serial.print(F(" File ID : 0x"));
|
||
if (files[i] < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.println(files[i], HEX);
|
||
|
||
// Get file settings
|
||
mifare_desfire_file_settings_t fileSettings;
|
||
|
||
response = MIFARE_DESFIRE_GetFileSettings(tag, &(files[i]), &fileSettings);
|
||
if (IsStatusCodeOK(response)) {
|
||
Serial.print(F(" File Type : 0x"));
|
||
if (fileSettings.file_type < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.print(fileSettings.file_type, HEX);
|
||
Serial.print(F(" ("));
|
||
Serial.print(GetFileTypeName((mifare_desfire_file_types)fileSettings.file_type));
|
||
Serial.println(F(")"));
|
||
|
||
Serial.print(F(" Communication : 0x"));
|
||
if (fileSettings.communication_settings < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.print(fileSettings.communication_settings, HEX);
|
||
Serial.print(F(" ("));
|
||
Serial.print(GetCommunicationModeName((mifare_desfire_communication_modes)fileSettings.communication_settings));
|
||
Serial.println(F(")"));
|
||
|
||
Serial.print(F(" Access rights : 0x"));
|
||
Serial.println(fileSettings.access_rights, HEX);
|
||
|
||
switch (fileSettings.file_type) {
|
||
case MDFT_STANDARD_DATA_FILE:
|
||
case MDFT_BACKUP_DATA_FILE:
|
||
Serial.print(F(" File Size : "));
|
||
Serial.print(fileSettings.settings.standard_file.file_size);
|
||
Serial.println(F(" bytes"));
|
||
break;
|
||
case MDFT_VALUE_FILE_WITH_BACKUP:
|
||
Serial.print(F(" Lower Limit : "));
|
||
Serial.println(fileSettings.settings.value_file.lower_limit);
|
||
Serial.print(F(" Upper Limit : "));
|
||
Serial.println(fileSettings.settings.value_file.upper_limit);
|
||
Serial.print(F(" Limited credit : "));
|
||
Serial.println(fileSettings.settings.value_file.limited_credit_value);
|
||
Serial.print(F(" Limited credit : "));
|
||
|
||
if (fileSettings.settings.value_file.limited_credit_enabled == 0x00)
|
||
Serial.print(F("Disabled ("));
|
||
else
|
||
Serial.print(F("Enabled (0x"));
|
||
if (fileSettings.settings.value_file.limited_credit_enabled < 0x10)
|
||
Serial.print(F("0"));
|
||
Serial.print(fileSettings.settings.value_file.limited_credit_enabled, HEX);
|
||
Serial.println(F(")"));
|
||
|
||
break;
|
||
|
||
case MDFT_LINEAR_RECORD_FILE_WITH_BACKUP:
|
||
case MDFT_CYCLIC_RECORD_FILE_WITH_BACKUP:
|
||
Serial.print(F(" Record size : "));
|
||
Serial.println(fileSettings.settings.record_file.record_size);
|
||
Serial.print(F(" max num records: "));
|
||
Serial.println(fileSettings.settings.record_file.max_number_of_records);
|
||
Serial.print(F(" num records : "));
|
||
Serial.println(fileSettings.settings.record_file.current_number_of_records);
|
||
break;
|
||
}
|
||
|
||
switch (fileSettings.file_type) {
|
||
case MDFT_STANDARD_DATA_FILE:
|
||
case MDFT_BACKUP_DATA_FILE:
|
||
{
|
||
// Get file data
|
||
byte fileContent[fileSettings.settings.standard_file.file_size];
|
||
size_t fileContentLength = fileSettings.settings.standard_file.file_size;
|
||
response = MIFARE_DESFIRE_ReadData(tag, files[i], 0, fileSettings.settings.standard_file.file_size, fileContent, &fileContentLength);
|
||
if (response.mfrc522 == STATUS_OK) {
|
||
Serial.println(F(" ------------------------------------------------------"));
|
||
Serial.println(F(" Data"));
|
||
|
||
if (response.desfire == MF_OPERATION_OK || response.desfire == MF_ADDITIONAL_FRAME) {
|
||
for (unsigned int iByte = 0; iByte < fileContentLength; iByte++) {
|
||
if ((iByte % 16) == 0) {
|
||
if (iByte != 0)
|
||
Serial.println();
|
||
Serial.print(F(" "));
|
||
}
|
||
if (fileContent[iByte] < 0x10)
|
||
Serial.print(F(" 0"));
|
||
else
|
||
Serial.print(F(" "));
|
||
Serial.print(fileContent[iByte], HEX);
|
||
}
|
||
Serial.println();
|
||
}
|
||
else {
|
||
Serial.print(F(" "));
|
||
Serial.println(GetStatusCodeName(response));
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
case MDFT_VALUE_FILE_WITH_BACKUP:
|
||
{
|
||
// Get value
|
||
int32_t fileValue;
|
||
response = MIFARE_DESFIRE_GetValue(tag, files[i], &fileValue);
|
||
Serial.print(F(" Value : "));
|
||
if (IsStatusCodeOK(response)) {
|
||
Serial.println(fileValue);
|
||
} else {
|
||
Serial.println(GetStatusCodeName(response));
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
} else {
|
||
Serial.println(F(" Error: Failed to get file settings."));
|
||
Serial.print(F(" "));
|
||
Serial.println(GetStatusCodeName(response));
|
||
}
|
||
}
|
||
|
||
|
||
Serial.println(F("-------------------------------------------------------------"));
|
||
} |