Improved DESFire support and added emulation

- Added DESFire emulator in armsrc/desfiresim.c with all major commands
- Fixed value operations (credit/debit/limited credit) to work correctly in MAC mode
- Removed value operations from EV1D40TransmitMAC array to match real card behavior
- Added auto-detection fallback for MAC mode compatibility with older cards
- Added emulator with 28 applications (kinda EV1 standard)
- Enhanced test suite with comprehensive value operation and multi-app testing
- Added emulator commands: sim, ereset, eload, eview, test
- Consolidated user documentation in doc/desfire.md with emulation guide
- Value file operations work on both emulator and real DESFire cards
- Proper limit checking and transaction commitment for value operations
- Support for all file types: Standard, Backup, Value, Linear/Cyclic Record
- Authentication with DES, 2TDEA/3DES, 3TDEA, and AES keys
- ISO file ID support and access rights enforcement
This commit is contained in:
Mistial Developer 2025-07-18 22:19:46 -04:00
commit 4bfdaf3376
No known key found for this signature in database
15 changed files with 4738 additions and 71 deletions

View file

@ -37,7 +37,7 @@ APP_CFLAGS = $(PLATFORM_DEFS) \
SRC_LF = lfops.c lfsampling.c pcf7931.c lfdemod.c lfadc.c
SRC_HF = hfops.c
SRC_ISO15693 = iso15693.c iso15693tools.c
SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_common.c sam_mfc.c sam_seos.c
SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_common.c sam_mfc.c sam_seos.c desfiresim.c
#UNUSED: mifaresniff.c
SRC_ISO14443b = iso14443b.c

View file

@ -55,6 +55,7 @@
#include "mifarecmd.h"
#include "mifaredesfire.h"
#include "mifaresim.h"
#include "desfiresim.h"
#include "emvsim.h"
#include "pcf7931.h"
#include "Standalone/standalone.h"
@ -2130,6 +2131,55 @@ static void PacketReceived(PacketCommandNG *packet) {
MifareSendCommand(packet->data.asBytes);
break;
}
case CMD_HF_DESFIRE_SIM_RESET: {
// Reset DESFire emulator to factory-fresh state
DesfireSimInit();
reply_ng(CMD_HF_DESFIRE_SIM_RESET, PM3_SUCCESS, NULL, 0);
break;
}
case CMD_HF_DESFIRE_EML_MEMCLR: {
DesfireEmlClear();
reply_ng(CMD_HF_DESFIRE_EML_MEMCLR, PM3_SUCCESS, NULL, 0);
break;
}
case CMD_HF_DESFIRE_EML_MEMSET: {
struct p {
uint32_t offset;
uint32_t length;
uint8_t data[PM3_CMD_DATA_SIZE - 8];
} *payload = (struct p *) packet->data.asBytes;
if (payload->length > PM3_CMD_DATA_SIZE - 8) {
reply_ng(CMD_HF_DESFIRE_EML_MEMSET, PM3_EMALLOC, NULL, 0);
return;
}
int res = DesfireEmlSet(payload->data, payload->offset, payload->length);
reply_ng(CMD_HF_DESFIRE_EML_MEMSET, res, NULL, 0);
break;
}
case CMD_HF_DESFIRE_EML_MEMGET: {
struct p {
uint32_t offset;
uint32_t length;
} *payload = (struct p *) packet->data.asBytes;
if (payload->length > PM3_CMD_DATA_SIZE) {
reply_ng(CMD_HF_DESFIRE_EML_MEMGET, PM3_EMALLOC, NULL, 0);
return;
}
uint8_t *buf = BigBuf_calloc(payload->length);
if (buf == NULL) {
reply_ng(CMD_HF_DESFIRE_EML_MEMGET, PM3_EMALLOC, NULL, 0);
return;
}
int res = DesfireEmlGet(buf, payload->offset, payload->length);
reply_ng(CMD_HF_DESFIRE_EML_MEMGET, res, buf, payload->length);
BigBuf_free_keep_EM();
break;
}
case CMD_HF_MIFARE_NACK_DETECT: {
DetectNACKbug();
break;

2243
armsrc/desfiresim.c Normal file

File diff suppressed because it is too large Load diff

270
armsrc/desfiresim.h Normal file
View file

@ -0,0 +1,270 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// DESFire emulation data structures and function prototypes
//-----------------------------------------------------------------------------
#ifndef __DESFIRESIM_H
#define __DESFIRESIM_H
#include "common.h"
#include "desfire.h"
#include "desfire_crypto.h"
// DESFire emulator memory layout (4KB BigBuf optimized for red team use)
#define DESFIRE_EMU_MEMORY_SIZE 4096
#define DESFIRE_CARD_HEADER_SIZE 32
#define DESFIRE_APP_DIR_SIZE 168 // 28 apps * 6 bytes each
#define DESFIRE_APP_DATA_SIZE 3896 // 4096 - 32 - 168 = 3896
#define DESFIRE_MAX_APPS 28 // EV1 standard limit
#define DESFIRE_MAX_FILES_PER_APP 16
#define DESFIRE_MAX_KEYS_PER_APP 14
#define DESFIRE_MAX_RESPONSE_SIZE 256 // Maximum response size for bounds checking
// Memory offsets
#define DESFIRE_CARD_HEADER_OFFSET 0x0000
#define DESFIRE_APP_DIR_OFFSET 0x0020
#define DESFIRE_APP_DATA_OFFSET 0x00C8 // 0x0020 + 168
// DESFire authentication constants
#define DESFIRE_NOT_YET_AUTHENTICATED 0xFF
// DESFire command constants (reuse existing from protocols.h where possible)
#ifndef MFDES_SELECT_APPLICATION
#define MFDES_SELECT_APPLICATION 0x5A
#endif
#ifndef MFDES_GET_APPLICATION_IDS
#define MFDES_GET_APPLICATION_IDS 0x6A
#endif
#ifndef MFDES_GET_FILE_IDS
#define MFDES_GET_FILE_IDS 0x6F
#endif
#define MFDES_GET_FILE_ISO_IDS 0x61
#define MFDES_AUTHENTICATE 0x0A
#define MFDES_AUTHENTICATE_3DES 0x1A
#define MFDES_AUTHENTICATE_AES 0xAA
#define MFDES_READ_DATA 0xBD
#define MFDES_WRITE_DATA 0x3D
#define MFDES_GET_FILE_SETTINGS 0xF5
#define MFDES_CREATE_APPLICATION 0xCA
#define MFDES_DELETE_APPLICATION 0xDA
#define MFDES_CREATE_STD_DATA_FILE 0xCD
#define MFDES_CREATE_BACKUP_DATA_FILE 0xCB
#define MFDES_CREATE_VALUE_FILE 0xCC
#define MFDES_CREATE_LINEAR_RECORD_FILE 0xC1
#define MFDES_CREATE_CYCLIC_RECORD_FILE 0xC0
#define MFDES_DELETE_FILE 0xDF
#define MFDES_GET_VALUE 0x6C
#define MFDES_CREDIT 0x0C
#define MFDES_DEBIT 0xDC
#define MFDES_LIMITED_CREDIT 0x1C
#define MFDES_WRITE_RECORD 0x3B
#define MFDES_READ_RECORDS 0xBB
#define MFDES_CLEAR_RECORD_FILE 0xEB
#define MFDES_COMMIT_TRANSACTION 0xC7
#define MFDES_ABORT_TRANSACTION 0xA7
#define MFDES_GET_KEY_SETTINGS 0x45
#define MFDES_GET_FREE_MEM 0x6E
#define MFDES_GET_DF_NAMES 0x6D
#define MFDES_GET_CARD_UID 0x51
#define MFDES_SET_CONFIGURATION 0x5C
#define MFDES_AUTHENTICATE_EV2_FIRST 0x71
#define MFDES_AUTHENTICATE_EV2_NONFIRST 0x77
#define MFDES_COMMIT_READER_ID 0xC8
// DESFire status codes
#define MFDES_OPERATION_OK 0x00
#define MFDES_AUTHENTICATION_ERROR 0xAE
#define MFDES_ADDITIONAL_FRAME 0xAF
#define MFDES_APPLICATION_NOT_FOUND 0xA0
#define MFDES_FILE_NOT_FOUND 0xF0
#define MFDES_PARAMETER_ERROR 0x9E
#define MFDES_COMMAND_ABORTED 0xCA
#define MFDES_DUPLICATE_ERROR 0x0E
// File types
typedef enum {
DESFIRE_FILE_TYPE_STANDARD = 0x00,
DESFIRE_FILE_TYPE_BACKUP = 0x01,
DESFIRE_FILE_TYPE_VALUE = 0x02,
DESFIRE_FILE_TYPE_LINEAR_RECORD = 0x03,
DESFIRE_FILE_TYPE_CYCLIC_RECORD = 0x04
} desfire_file_type_t;
// Authentication states
typedef enum {
DESFIRE_AUTH_NONE = 0,
DESFIRE_AUTH_CHALLENGE_SENT,
DESFIRE_AUTH_RESPONSE_RECEIVED,
DESFIRE_AUTH_AUTHENTICATED
} desfire_auth_state_t;
// DESFire card header (32 bytes at offset 0x0000)
typedef struct {
uint8_t version[8]; // DESFire version response
uint8_t uid[10]; // Card UID
uint8_t uidlen; // UID length
uint8_t num_apps; // Number of applications (max 2)
uint8_t master_key[16]; // Master key (up to AES-128)
uint8_t master_key_type; // Key type (0=DES, 1=3DES, 3=AES)
uint8_t key_settings; // Master key settings
uint8_t reserved[2]; // Reserved for future use
} PACKED desfire_card_t;
// Application directory entry (6 bytes each, max 2 entries)
typedef struct {
uint8_t aid[3]; // Application ID
uint16_t offset; // Offset in emulator memory
uint8_t auth_key; // Currently authenticated key (0xFF = none)
} PACKED desfire_app_dir_t;
// Application header in memory (8 bytes + variable data)
typedef struct {
uint8_t aid[3]; // Application ID
uint8_t key_settings; // Key settings
uint8_t num_keys; // Number of keys (1-14)
uint8_t num_files; // Number of files (0-16)
uint8_t auth_key; // Currently authenticated key
uint8_t reserved; // Reserved
// Followed by: keys array, file headers, file data
} PACKED desfire_app_t;
// File header (20 bytes - expanded for ISO support)
typedef struct {
uint8_t file_no; // File number (0-31)
uint8_t file_type; // File type (standard/backup/value/record)
uint8_t comm_settings; // Communication settings
uint8_t has_iso_id; // 1 if ISO file ID is present
uint16_t access_rights; // Access rights (4 nibbles)
uint16_t iso_file_id; // ISO file ID (if present)
union {
struct { // For standard/backup files
uint32_t size; // File size
} data;
struct { // For value files
int32_t lower_limit; // Lower limit
int32_t upper_limit; // Upper limit
int32_t value; // Current value
uint8_t limited_credit_enabled;
} value;
struct { // For record files
uint32_t record_size; // Size of one record
uint32_t max_records; // Maximum number of records
uint32_t current_records; // Current number of records
} record;
} settings;
uint16_t offset; // Offset to file data
} PACKED desfire_file_t;
// Enhanced runtime simulation state with full crypto support
typedef struct {
uint8_t selected_app[3]; // Currently selected AID (000000 = PICC level)
desfire_auth_state_t auth_state; // Authentication state
// Enhanced authentication context
struct desfire_tag *crypto_ctx; // Full DESFire crypto context
uint8_t auth_keyno; // Key number being authenticated
uint8_t auth_scheme; // Authentication scheme (DES/3DES/AES)
uint8_t current_auth_step; // Multi-step authentication tracking
// Challenge/response state for multi-step auth
uint8_t challenge[16]; // Current challenge data
uint8_t response[32]; // Response buffer for complex auth
uint8_t challenge_len; // Challenge length (8 for DES/3DES, 16 for AES)
uint8_t response_len; // Response length
// Session management
uint8_t session_active; // Boolean: is authenticated session active
uint8_t secure_channel; // Secure channel type (none/EV1/EV2/LRP)
uint8_t comm_mode; // Communication mode (plain/MAC/encrypted)
uint16_t cmd_counter; // Command counter for EV2/LRP
uint8_t transaction_id[4]; // Transaction identifier for EV2
uint8_t session_key[24]; // Session key storage
// Key management cache
uint8_t cached_key_type[DESFIRE_MAX_KEYS_PER_APP]; // Key types per app
uint8_t cached_key_data[DESFIRE_MAX_KEYS_PER_APP * 24]; // Key data cache
uint8_t cached_key_valid[DESFIRE_MAX_KEYS_PER_APP]; // Key validity flags
} desfire_sim_state_t;
// Function prototypes for DESFire emulation
// Memory management
void DesfireSimInit(void);
desfire_card_t *DesfireGetCard(void);
desfire_app_dir_t *DesfireGetAppDir(void);
desfire_app_t *DesfireFindApp(uint8_t *aid);
desfire_file_t *DesfireFindFile(desfire_app_t *app, uint8_t file_no);
// Command handlers
uint8_t HandleDesfireGetVersion(uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireSelectApp(uint8_t *aid, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireGetAppIDs(uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireGetFileIDs(uint8_t *response, uint8_t *response_len);
// Enhanced authentication handlers
uint8_t HandleDesfireAuthenticate(uint8_t keyno, uint8_t *data, uint8_t len, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireAuthenticateISO(uint8_t keyno, uint8_t *data, uint8_t len, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireAuthenticateAES(uint8_t keyno, uint8_t *data, uint8_t len, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireAdditionalFrame(uint8_t *data, uint8_t len, uint8_t *response, uint8_t *response_len);
// Authentication state management
void DesfireInitCryptoContext(void);
void DesfireClearSession(void);
bool DesfireIsAuthenticated(void);
uint8_t DesfireGetAuthKeyType(uint8_t *aid, uint8_t keyno);
void DesfireSetSessionKey(uint8_t *session_key, uint8_t key_type);
bool DesfireValidateMAC(uint8_t *data, uint8_t len, uint8_t *mac);
void DesfireCalculateMAC(uint8_t *data, uint8_t len, uint8_t *mac);
uint8_t HandleDesfireReadData(uint8_t file_no, uint32_t offset, uint32_t length, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireCreateApp(uint8_t *aid, uint8_t key_settings, uint8_t num_keys, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireDeleteApp(uint8_t *aid, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireCreateStdDataFile(uint8_t *data, uint8_t len, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireCreateBackupDataFile(uint8_t *data, uint8_t len, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireCreateValueFile(uint8_t *data, uint8_t len, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireCreateLinearRecordFile(uint8_t *data, uint8_t len, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireCreateCyclicRecordFile(uint8_t *data, uint8_t len, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireDeleteFile(uint8_t file_no, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireGetFileSettings(uint8_t file_no, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireWriteData(uint8_t file_no, uint32_t offset, uint32_t length, uint8_t *data, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireGetValue(uint8_t file_no, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireCredit(uint8_t file_no, int32_t value, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireDebit(uint8_t file_no, int32_t value, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireLimitedCredit(uint8_t file_no, int32_t value, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireWriteRecord(uint8_t file_no, uint32_t offset, uint32_t length, uint8_t *data, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireReadRecords(uint8_t file_no, uint32_t offset, uint32_t length, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireClearRecordFile(uint8_t file_no, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireGetKeySettings(uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireGetCardUID(uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireSetConfiguration(uint8_t option, uint8_t *data, uint8_t len, uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireGetDFNames(uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireGetFreeMem(uint8_t *response, uint8_t *response_len);
uint8_t HandleDesfireGetFileISOIDs(uint8_t *response, uint8_t *response_len);
// Utility functions
uint8_t DesfireGetCardVersion(void);
uint8_t DesfireGetKeySize(uint8_t key_type);
bool DesfireCheckAccess(desfire_file_t *file, uint8_t operation, uint8_t auth_key);
void DesfireGenerateChallenge(uint8_t *challenge, uint8_t len);
uint8_t DesfireGetKeyForAuth(uint8_t *aid, uint8_t keyno, uint8_t key_type, uint8_t *key_out);
// Global simulation state
extern desfire_sim_state_t g_desfire_state;
// DESFire emulator memory management functions
void DesfireEmlClear(void);
int DesfireEmlSet(const uint8_t *data, uint32_t offset, uint32_t length);
int DesfireEmlGet(uint8_t *data, uint32_t offset, uint32_t length);
#endif

View file

@ -37,6 +37,7 @@
#include "generator.h"
#include "desfire_crypto.h" // UL-C authentication helpers
#include "mifare.h" // for iso14a_polling_frame_t structure
#include "desfiresim.h" // DESFire emulation
#define MAX_ISO14A_TIMEOUT 524288
// this timeout is in MS
@ -1203,6 +1204,470 @@ static void Simulate_reread_ulc_key(uint8_t *ulc_key) {
reverse_array(ulc_key + 8, 4);
reverse_array(ulc_key + 12, 4);
}
//-----------------------------------------------------------------------------
// DESFire command dispatcher
//-----------------------------------------------------------------------------
uint8_t HandleDesfireCommand(uint8_t *cmd, uint8_t cmd_len, uint8_t *response, uint8_t *response_len) {
if (cmd_len < 1 || response == NULL || response_len == NULL) {
if (response != NULL && response_len != NULL) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
}
return MFDES_PARAMETER_ERROR;
}
// Check for ISO7816-wrapped commands (CLA=90 or 91, etc.)
if ((cmd[0] & 0xF0) == 0x90 && cmd_len >= 5) {
// ISO7816 wrapped command format: CLA INS P1 P2 [Lc Data] [Le]
uint8_t cla = cmd[0];
uint8_t ins = cmd[1];
uint8_t p1 = cmd[2];
uint8_t p2 = cmd[3];
uint8_t lc = 0;
uint8_t *data = NULL;
// Validate CLA - should be 0x90 for DESFire
if (cla != 0x90) {
response[0] = 0x6E; // Class not supported
response[1] = 0x00;
*response_len = 2;
return MFDES_COMMAND_ABORTED;
}
// Validate P1 - should be 0x00 for most DESFire commands
if (p1 != 0x00) {
response[0] = 0x6A; // Wrong P1-P2
response[1] = 0x86;
*response_len = 2;
return MFDES_PARAMETER_ERROR;
}
// Extract data length and pointer
if (cmd_len > 5) {
lc = cmd[4];
if (cmd_len >= 5 + lc) {
data = &cmd[5];
}
}
// Map ISO7816 INS to DESFire commands
uint8_t desfire_cmd;
switch (ins) {
case 0x5A: desfire_cmd = MFDES_SELECT_APPLICATION; break;
case 0x60: desfire_cmd = MFDES_GET_VERSION; break;
case 0x6A: desfire_cmd = MFDES_GET_APPLICATION_IDS; break;
case 0x6F: desfire_cmd = MFDES_GET_FILE_IDS; break;
case 0xF5: desfire_cmd = MFDES_GET_FILE_SETTINGS; break;
case 0x45: desfire_cmd = MFDES_GET_KEY_SETTINGS; break;
case 0xAA: desfire_cmd = MFDES_AUTHENTICATE_AES; break;
case 0x0A: desfire_cmd = MFDES_AUTHENTICATE; break;
case 0x1A: desfire_cmd = MFDES_AUTHENTICATE_3DES; break;
case 0xBD: desfire_cmd = MFDES_READ_DATA; break;
case 0x3D: desfire_cmd = MFDES_WRITE_DATA; break;
case 0xCA: desfire_cmd = MFDES_CREATE_APPLICATION; break;
case 0xDA: desfire_cmd = MFDES_DELETE_APPLICATION; break;
case 0xCD: desfire_cmd = MFDES_CREATE_STD_DATA_FILE; break;
case 0xCB: desfire_cmd = MFDES_CREATE_BACKUP_DATA_FILE; break;
case 0xCC: desfire_cmd = MFDES_CREATE_VALUE_FILE; break;
case 0xC1: desfire_cmd = MFDES_CREATE_LINEAR_RECORD_FILE; break;
case 0xC0: desfire_cmd = MFDES_CREATE_CYCLIC_RECORD_FILE; break;
case 0xDF: desfire_cmd = MFDES_DELETE_FILE; break;
case 0x6C: desfire_cmd = MFDES_GET_VALUE; break;
case 0x0C: desfire_cmd = MFDES_CREDIT; break;
case 0xDC: desfire_cmd = MFDES_DEBIT; break;
case 0x1C: desfire_cmd = MFDES_LIMITED_CREDIT; break;
case 0x3B: desfire_cmd = MFDES_WRITE_RECORD; break;
case 0xBB: desfire_cmd = MFDES_READ_RECORDS; break;
case 0xEB: desfire_cmd = MFDES_CLEAR_RECORD_FILE; break;
case 0x6E: desfire_cmd = MFDES_GET_FREE_MEM; break;
case 0x6D: desfire_cmd = MFDES_GET_DF_NAMES; break;
case 0x61: desfire_cmd = MFDES_GET_FILE_ISO_IDS; break;
case 0x51: desfire_cmd = MFDES_GET_CARD_UID; break;
case 0x5C: desfire_cmd = MFDES_SET_CONFIGURATION; break;
case 0x71: desfire_cmd = MFDES_AUTHENTICATE_EV2_FIRST; break;
case 0x77: desfire_cmd = MFDES_AUTHENTICATE_EV2_NONFIRST; break;
case 0xC8: desfire_cmd = MFDES_COMMIT_READER_ID; break;
case 0xAF: desfire_cmd = 0xAF; break; // Additional frame
default:
// Unknown ISO7816 command
response[0] = MFDES_COMMAND_ABORTED;
response[1] = 0x00; // SW2
*response_len = 2;
return MFDES_COMMAND_ABORTED;
}
// Handle the DESFire command
uint8_t status;
uint8_t desfire_response[DESFIRE_MAX_RESPONSE_SIZE];
uint8_t desfire_response_len = 0;
// Build native DESFire command
if (desfire_cmd == 0xAF) {
// Additional frame - just pass the data
if (data != NULL && lc > 0) {
status = HandleDesfireAuthenticate(0xFF, data, lc, desfire_response, &desfire_response_len);
} else {
status = MFDES_PARAMETER_ERROR;
}
} else if (desfire_cmd == MFDES_SELECT_APPLICATION && data != NULL && lc >= 3) {
status = HandleDesfireSelectApp(data, desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_GET_VERSION) {
status = HandleDesfireGetVersion(desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_GET_APPLICATION_IDS) {
status = HandleDesfireGetAppIDs(desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_GET_FILE_IDS) {
status = HandleDesfireGetFileIDs(desfire_response, &desfire_response_len);
} else if ((desfire_cmd == MFDES_AUTHENTICATE || desfire_cmd == MFDES_AUTHENTICATE_3DES ||
desfire_cmd == MFDES_AUTHENTICATE_AES) && lc >= 1) {
uint8_t keyno = (data != NULL && lc > 0) ? data[0] : p2;
uint8_t *auth_data = (lc > 1 && data != NULL) ? &data[1] : NULL;
uint8_t auth_len = (lc > 1) ? (lc - 1) : 0;
status = HandleDesfireAuthenticate(keyno, auth_data, auth_len, desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_READ_DATA && data != NULL && lc >= 7) {
uint8_t file_no = data[0];
uint32_t offset = (data[1] | (data[2] << 8) | (data[3] << 16));
uint32_t length = (data[4] | (data[5] << 8) | (data[6] << 16));
status = HandleDesfireReadData(file_no, offset, length, desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_WRITE_DATA && data != NULL && lc >= 7) {
uint8_t file_no = data[0];
uint32_t offset = (data[1] | (data[2] << 8) | (data[3] << 16));
uint32_t length = (data[4] | (data[5] << 8) | (data[6] << 16));
if (lc >= 7 + length) {
status = HandleDesfireWriteData(file_no, offset, length, &data[7], desfire_response, &desfire_response_len);
} else {
status = MFDES_PARAMETER_ERROR;
}
} else if (desfire_cmd == MFDES_CREATE_APPLICATION && data != NULL && lc >= 5) {
status = HandleDesfireCreateApp(data, data[3], data[4], desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_DELETE_APPLICATION && data != NULL && lc >= 3) {
status = HandleDesfireDeleteApp(data, desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_CREATE_STD_DATA_FILE && data != NULL && lc >= 7) {
status = HandleDesfireCreateStdDataFile(data, lc, desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_CREATE_BACKUP_DATA_FILE && data != NULL && lc >= 7) {
status = HandleDesfireCreateBackupDataFile(data, lc, desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_CREATE_VALUE_FILE && data != NULL && lc >= 17) {
status = HandleDesfireCreateValueFile(data, lc, desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_CREATE_LINEAR_RECORD_FILE && data != NULL && lc >= 10) {
status = HandleDesfireCreateLinearRecordFile(data, lc, desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_CREATE_CYCLIC_RECORD_FILE && data != NULL && lc >= 10) {
status = HandleDesfireCreateCyclicRecordFile(data, lc, desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_DELETE_FILE && data != NULL && lc >= 1) {
status = HandleDesfireDeleteFile(data[0], desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_GET_FILE_SETTINGS && data != NULL && lc >= 1) {
status = HandleDesfireGetFileSettings(data[0], desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_GET_KEY_SETTINGS) {
status = HandleDesfireGetKeySettings(desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_GET_VALUE && data != NULL && lc >= 1) {
status = HandleDesfireGetValue(data[0], desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_CREDIT && data != NULL && lc >= 5) {
uint8_t file_no = data[0];
int32_t value = (data[1] | (data[2] << 8) | (data[3] << 16) | (data[4] << 24));
status = HandleDesfireCredit(file_no, value, desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_DEBIT && data != NULL && lc >= 5) {
uint8_t file_no = data[0];
int32_t value = (data[1] | (data[2] << 8) | (data[3] << 16) | (data[4] << 24));
status = HandleDesfireDebit(file_no, value, desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_LIMITED_CREDIT && data != NULL && lc >= 5) {
uint8_t file_no = data[0];
int32_t value = (data[1] | (data[2] << 8) | (data[3] << 16) | (data[4] << 24));
status = HandleDesfireLimitedCredit(file_no, value, desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_WRITE_RECORD && data != NULL && lc >= 7) {
uint8_t file_no = data[0];
uint32_t offset = (data[1] | (data[2] << 8) | (data[3] << 16));
uint32_t length = (data[4] | (data[5] << 8) | (data[6] << 16));
if (lc >= 7 + length) {
status = HandleDesfireWriteRecord(file_no, offset, length, &data[7], desfire_response, &desfire_response_len);
} else {
status = MFDES_PARAMETER_ERROR;
}
} else if (desfire_cmd == MFDES_READ_RECORDS && data != NULL && lc >= 7) {
uint8_t file_no = data[0];
uint32_t offset = (data[1] | (data[2] << 8) | (data[3] << 16));
uint32_t length = (data[4] | (data[5] << 8) | (data[6] << 16));
status = HandleDesfireReadRecords(file_no, offset, length, desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_CLEAR_RECORD_FILE && data != NULL && lc >= 1) {
status = HandleDesfireClearRecordFile(data[0], desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_GET_FREE_MEM) {
status = HandleDesfireGetFreeMem(desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_GET_DF_NAMES) {
status = HandleDesfireGetDFNames(desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_GET_FILE_ISO_IDS) {
status = HandleDesfireGetFileISOIDs(desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_GET_CARD_UID) {
status = HandleDesfireGetCardUID(desfire_response, &desfire_response_len);
} else if (desfire_cmd == MFDES_SET_CONFIGURATION && data != NULL && lc >= 1) {
status = HandleDesfireSetConfiguration(data[0], (lc > 1) ? &data[1] : NULL, (lc > 1) ? lc - 1 : 0, desfire_response, &desfire_response_len);
} else {
status = MFDES_PARAMETER_ERROR;
}
// Format ISO7816 response
if (status == MFDES_OPERATION_OK || status == MFDES_ADDITIONAL_FRAME) {
// Copy response data
if (desfire_response_len > 0 && desfire_response_len <= DESFIRE_MAX_RESPONSE_SIZE - 2) {
memcpy(response, desfire_response, desfire_response_len);
*response_len = desfire_response_len;
}
// Add SW1 SW2 (90 00 for normal, 91 AF for additional frame)
if (status == MFDES_ADDITIONAL_FRAME) {
response[(*response_len)++] = 0x91;
response[(*response_len)++] = 0xAF;
} else {
response[(*response_len)++] = 0x91;
response[(*response_len)++] = 0x00;
}
} else {
// Error response with status code
response[0] = 0x91;
response[1] = status;
*response_len = 2;
}
return status;
}
// Native DESFire command handling
switch (cmd[0]) {
case MFDES_GET_VERSION:
return HandleDesfireGetVersion(response, response_len);
case MFDES_SELECT_APPLICATION:
if (cmd_len < 4) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireSelectApp(&cmd[1], response, response_len);
case MFDES_GET_APPLICATION_IDS:
return HandleDesfireGetAppIDs(response, response_len);
case MFDES_GET_FILE_IDS:
return HandleDesfireGetFileIDs(response, response_len);
case MFDES_AUTHENTICATE:
case MFDES_AUTHENTICATE_3DES:
case MFDES_AUTHENTICATE_AES:
if (cmd_len < 2) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireAuthenticate(cmd[1], &cmd[2], cmd_len - 2, response, response_len);
case MFDES_READ_DATA: {
if (cmd_len < 8) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
// Parse read data command: file_no, offset (3 bytes), length (3 bytes)
uint8_t file_no = cmd[1];
uint32_t offset = (cmd[2] | (cmd[3] << 8) | (cmd[4] << 16));
uint32_t length = (cmd[5] | (cmd[6] << 8) | (cmd[7] << 16));
return HandleDesfireReadData(file_no, offset, length, response, response_len);
}
case MFDES_CREATE_APPLICATION:
if (cmd_len < 6) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
// Parse create application command: AID (3 bytes), key_settings, num_keys
return HandleDesfireCreateApp(&cmd[1], cmd[4], cmd[5], response, response_len);
case MFDES_DELETE_APPLICATION:
if (cmd_len < 4) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireDeleteApp(&cmd[1], response, response_len);
case MFDES_WRITE_DATA: {
if (cmd_len < 8) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
uint8_t file_no = cmd[1];
uint32_t offset = (cmd[2] | (cmd[3] << 8) | (cmd[4] << 16));
uint32_t length = (cmd[5] | (cmd[6] << 8) | (cmd[7] << 16));
if (cmd_len < 8 + length) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireWriteData(file_no, offset, length, &cmd[8], response, response_len);
}
case MFDES_CREATE_STD_DATA_FILE:
if (cmd_len < 8) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireCreateStdDataFile(&cmd[1], cmd_len - 1, response, response_len);
case MFDES_CREATE_BACKUP_DATA_FILE:
if (cmd_len < 8) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireCreateBackupDataFile(&cmd[1], cmd_len - 1, response, response_len);
case MFDES_CREATE_VALUE_FILE:
if (cmd_len < 18) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireCreateValueFile(&cmd[1], cmd_len - 1, response, response_len);
case MFDES_CREATE_LINEAR_RECORD_FILE:
if (cmd_len < 11) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireCreateLinearRecordFile(&cmd[1], cmd_len - 1, response, response_len);
case MFDES_CREATE_CYCLIC_RECORD_FILE:
if (cmd_len < 11) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireCreateCyclicRecordFile(&cmd[1], cmd_len - 1, response, response_len);
case MFDES_DELETE_FILE:
if (cmd_len < 2) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireDeleteFile(cmd[1], response, response_len);
case MFDES_GET_FILE_SETTINGS:
if (cmd_len < 2) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireGetFileSettings(cmd[1], response, response_len);
case MFDES_GET_KEY_SETTINGS:
return HandleDesfireGetKeySettings(response, response_len);
case MFDES_GET_VALUE:
if (cmd_len < 2) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireGetValue(cmd[1], response, response_len);
case MFDES_CREDIT: {
if (cmd_len < 6) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
uint8_t file_no = cmd[1];
int32_t value = (cmd[2] | (cmd[3] << 8) | (cmd[4] << 16) | (cmd[5] << 24));
return HandleDesfireCredit(file_no, value, response, response_len);
}
case MFDES_DEBIT: {
if (cmd_len < 6) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
uint8_t file_no = cmd[1];
int32_t value = (cmd[2] | (cmd[3] << 8) | (cmd[4] << 16) | (cmd[5] << 24));
return HandleDesfireDebit(file_no, value, response, response_len);
}
case MFDES_LIMITED_CREDIT: {
if (cmd_len < 6) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
uint8_t file_no = cmd[1];
int32_t value = (cmd[2] | (cmd[3] << 8) | (cmd[4] << 16) | (cmd[5] << 24));
return HandleDesfireLimitedCredit(file_no, value, response, response_len);
}
case MFDES_WRITE_RECORD: {
if (cmd_len < 8) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
uint8_t file_no = cmd[1];
uint32_t offset = (cmd[2] | (cmd[3] << 8) | (cmd[4] << 16));
uint32_t length = (cmd[5] | (cmd[6] << 8) | (cmd[7] << 16));
if (cmd_len < 8 + length) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireWriteRecord(file_no, offset, length, &cmd[8], response, response_len);
}
case MFDES_READ_RECORDS: {
if (cmd_len < 8) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
uint8_t file_no = cmd[1];
uint32_t offset = (cmd[2] | (cmd[3] << 8) | (cmd[4] << 16));
uint32_t length = (cmd[5] | (cmd[6] << 8) | (cmd[7] << 16));
return HandleDesfireReadRecords(file_no, offset, length, response, response_len);
}
case MFDES_CLEAR_RECORD_FILE:
if (cmd_len < 2) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireClearRecordFile(cmd[1], response, response_len);
case MFDES_GET_FREE_MEM:
return HandleDesfireGetFreeMem(response, response_len);
case MFDES_GET_DF_NAMES:
return HandleDesfireGetDFNames(response, response_len);
case MFDES_GET_FILE_ISO_IDS:
return HandleDesfireGetFileISOIDs(response, response_len);
case MFDES_GET_CARD_UID:
return HandleDesfireGetCardUID(response, response_len);
case MFDES_SET_CONFIGURATION:
if (cmd_len < 2) {
response[0] = MFDES_PARAMETER_ERROR;
*response_len = 1;
return MFDES_PARAMETER_ERROR;
}
return HandleDesfireSetConfiguration(cmd[1], (cmd_len > 2) ? &cmd[2] : NULL, (cmd_len > 2) ? cmd_len - 2 : 0, response, response_len);
default:
// Unknown command
response[0] = MFDES_COMMAND_ABORTED;
*response_len = 1;
return MFDES_COMMAND_ABORTED;
}
}
bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
uint8_t *ats, size_t ats_len, tag_response_info_t **responses,
uint32_t *cuid, uint8_t *pages, uint8_t *ulc_key) {
@ -1279,6 +1744,19 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
sak = 0x20;
memcpy(rATS, "\x06\x75\x77\x81\x02\x80\x00\x00", 8);
rATS_len = 8; // including CRC
// Initialize DESFire simulation
DesfireSimInit();
// Set version response from emulator memory card header
desfire_card_t *card = DesfireGetCard();
if (card != NULL && card->version[0] != 0x00) {
memcpy(rVERSION, card->version, 8);
} else {
// Default DESFire EV1 version (proper EV1 response)
memcpy(rVERSION, "\x04\x01\x01\x01\x00\x1A\x05\x91", 8);
}
AddCrc14A(rVERSION, sizeof(rVERSION) - 2);
break;
}
case 4: { // ISO/IEC 14443-4 - javacard (JCOP)
@ -1983,6 +2461,33 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin
p_response = &responses[RESP_INDEX_VERSION];
} else if (receivedCmd[0] == MFDES_GET_VERSION && len == 4 && (tagType == 3)) {
p_response = &responses[RESP_INDEX_VERSION];
} else if (tagType == 3) { // DESFire emulation - handle all DESFire commands
uint8_t response_len = 0;
uint8_t status = HandleDesfireCommand(receivedCmd, len, dynamic_response_info.response, &response_len);
if (status == MFDES_OPERATION_OK) {
dynamic_response_info.response_n = response_len;
prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE);
p_response = &dynamic_response_info;
} else if (status == MFDES_ADDITIONAL_FRAME) {
// Authentication in progress - send challenge
dynamic_response_info.response_n = response_len;
prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE);
p_response = &dynamic_response_info;
} else {
// Error - DESFire sends error status in response, not NACK
// The error status is already in the response buffer
// Clear authentication state on certain errors
if (status == MFDES_AUTHENTICATION_ERROR || status == MFDES_APPLICATION_NOT_FOUND) {
// Reset authentication state on critical errors
// Note: g_desfire_state is already declared extern in desfiresim.h
g_desfire_state.auth_state = DESFIRE_AUTH_NONE;
g_desfire_state.auth_keyno = 0xFF;
}
dynamic_response_info.response_n = response_len;
prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE);
p_response = &dynamic_response_info;
}
} else if ((receivedCmd[0] == MIFARE_AUTH_KEYA || receivedCmd[0] == MIFARE_AUTH_KEYB) && len == 4 && tagType != 2 && tagType != 7 && tagType != 13) { // Received an authentication request
cardAUTHKEY = receivedCmd[0] - 0x60;
cardAUTHSC = receivedCmd[1] / 4; // received block num
@ -2144,6 +2649,101 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin
dynamic_response_info.response[2] = 0x00;
dynamic_response_info.response_n = 3;
}
} else if (tagType == 3) {
// DESFire ISO14443-4 command handling
// Check for ISO 14443A-4 I-Block
if ((receivedCmd[0] & 0xC0) == 0x00 || (receivedCmd[0] & 0xC0) == 0x40) { // I-Block
uint8_t pcb = receivedCmd[0];
uint8_t block_num = pcb & 0x01;
uint8_t chain_bit = (pcb & 0x10) >> 4;
(void)chain_bit; // Mark as intentionally unused for now
// Extract the ISO7816 APDU from the I-Block
uint8_t apdu_offset = 1; // Skip PCB
if (pcb & 0x08) apdu_offset++; // Skip CID if present
if (pcb & 0x04) apdu_offset++; // Skip NAD if present
if (len > apdu_offset + 2) { // Must have at least PCB + APDU + CRC
uint8_t apdu_len = len - apdu_offset - 2; // Remove CRC
uint8_t response_len = 0;
uint8_t desfire_response[DESFIRE_MAX_RESPONSE_SIZE];
// Process the DESFire command
uint8_t status = HandleDesfireCommand(&receivedCmd[apdu_offset], apdu_len, desfire_response, &response_len);
// Build I-Block response
dynamic_response_info.response[0] = pcb & 0x0B; // Keep CID/NAD bits, clear chain bit
dynamic_response_info.response[0] |= block_num; // Echo block number
uint8_t resp_offset = 1;
if (pcb & 0x08) { // Echo CID if present
dynamic_response_info.response[resp_offset++] = receivedCmd[1];
}
// Copy DESFire response
if (response_len > 0) {
memcpy(&dynamic_response_info.response[resp_offset], desfire_response, response_len);
dynamic_response_info.response_n = resp_offset + response_len;
} else {
// Empty response with just status
dynamic_response_info.response[resp_offset] = 0x91;
dynamic_response_info.response[resp_offset + 1] = status;
dynamic_response_info.response_n = resp_offset + 2;
}
} else {
// Invalid I-Block
dynamic_response_info.response[0] = 0x92; // R-Block NACK
dynamic_response_info.response_n = 1;
}
} else if ((receivedCmd[0] & 0xC0) == 0xC0) { // S-Block
// Handle S-Block (DESELECT, WTX)
dynamic_response_info.response[0] = receivedCmd[0];
dynamic_response_info.response_n = 1;
} else if ((receivedCmd[0] & 0xC0) == 0x80) { // R-Block
// Handle R-Block (ACK/NAK)
dynamic_response_info.response[0] = receivedCmd[0];
dynamic_response_info.response_n = 1;
} else if ((receivedCmd[0] & 0xF0) != 0x90) {
// Not an ISO7816 wrapped command, might be native DESFire
// Native DESFire commands in I-Block: PCB + native command
uint8_t pcb = receivedCmd[0];
uint8_t apdu_offset = 1;
if (pcb & 0x08) apdu_offset++; // Skip CID if present
if (pcb & 0x04) apdu_offset++; // Skip NAD if present
if (len > apdu_offset + 2) { // Must have at least PCB + cmd + CRC
uint8_t cmd_len = len - apdu_offset - 2; // Remove CRC
uint8_t response_len = 0;
uint8_t desfire_response[DESFIRE_MAX_RESPONSE_SIZE];
// Process native DESFire command
uint8_t status = HandleDesfireCommand(&receivedCmd[apdu_offset], cmd_len, desfire_response, &response_len);
// Build I-Block response with native DESFire format
dynamic_response_info.response[0] = pcb & 0x0B; // Keep CID/NAD bits
dynamic_response_info.response[0] |= (pcb & 0x01); // Echo block number
uint8_t resp_offset = 1;
if (pcb & 0x08) { // Echo CID if present
dynamic_response_info.response[resp_offset++] = receivedCmd[1];
}
// For native DESFire, status byte comes first
dynamic_response_info.response[resp_offset++] = status;
// Then data if any
if (response_len > 0 && status == MFDES_OPERATION_OK) {
memcpy(&dynamic_response_info.response[resp_offset], desfire_response, response_len);
dynamic_response_info.response_n = resp_offset + response_len;
} else {
dynamic_response_info.response_n = resp_offset;
}
}
} else {
// Unknown format
dynamic_response_info.response[0] = 0x92; // R-Block NACK
dynamic_response_info.response_n = 1;
}
} else {
// Check for ISO 14443A-4 compliant commands, look at left nibble

View file

@ -194,6 +194,9 @@ void DetectNACKbug(void);
bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t rec_maxlen, uint8_t *received_len);
// DESFire emulation command dispatcher
uint8_t HandleDesfireCommand(uint8_t *cmd, uint8_t cmd_len, uint8_t *response, uint8_t *response_len);
extern iso14a_polling_parameters_t WUPA_POLLING_PARAMETERS;
extern iso14a_polling_parameters_t REQA_POLLING_PARAMETERS;

View file

@ -647,6 +647,7 @@ SRCS = mifare/aiddesfire.c \
cmdhflto.c \
cmdhfmf.c \
cmdhfmfdes.c \
cmdhfmfdessim.c \
cmdhfmfhard.c \
cmdhfmfu.c \
cmdhfmfp.c \

View file

@ -27,6 +27,7 @@
#include "cmdhf14a.h"
#include "aes.h"
#include "crypto/libpcrypto.h"
#include "cmdhfmfdessim.h"
#include "protocols.h"
#include "cmdtrace.h"
#include "cliparser.h"
@ -463,7 +464,7 @@ static void swap24(uint8_t *data) {
// default parameters
static uint8_t defaultKeyNum = 0;
static DesfireCryptoAlgorithm defaultAlgoId = T_DES;
static DesfireCryptoAlgorithm defaultAlgoId = T_3DES; // Real DESFire cards use 2TDEA by default
static uint8_t defaultKey[DESFIRE_MAX_KEY_SIZE] = {0};
static int defaultKdfAlgo = MFDES_KDF_ALGO_NONE;
static int defaultKdfInputLen = 0;
@ -604,8 +605,8 @@ static int CmdHF14ADesDefault(const char *Cmd) {
void *argtable[] = {
arg_param_begin,
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -1511,8 +1512,8 @@ static int CmdHF14aDesDetect(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -1828,8 +1829,8 @@ static int CmdHF14aDesMAD(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -2002,8 +2003,8 @@ static int CmdHF14ADesSelectApp(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -2171,7 +2172,25 @@ static int CmdHF14ADesBruteApps(const char *Cmd) {
return res;
}
// TODO: We need to check the tag version, EV1 should stop after 26 apps are found
// Get card version to check if it's EV1 (limited to 26 apps)
mfdes_info_res_t info;
int version_res = mfdes_get_info(&info);
bool is_ev1 = false;
if (version_res == PM3_SUCCESS) {
// Check if it's DESFire EV1 (version 1.x.x)
if (info.versionHW[1] == 0x01) {
is_ev1 = true;
PrintAndLogEx(INFO, "DESFire EV1 detected - will limit search to 26 applications");
}
}
// re-select PICC after getting version
res = DesfireSelectAIDHex(&dctx, 0x000000, false, 0);
if (res != PM3_SUCCESS) {
DropField();
PrintAndLogEx(FAILED, "Desfire PICC level select " _RED_("failed") " after version check.");
return res;
}
if (mad) {
idIncrement = 0x10;
startAid[0] = 0xF0;
@ -2192,12 +2211,19 @@ static int CmdHF14ADesBruteApps(const char *Cmd) {
PrintAndLogEx(INFO, "Bruteforce from " _YELLOW_("%06x") " to " _YELLOW_("%06x"), idStart, idEnd);
PrintAndLogEx(INFO, "Enumerating through all AIDs manually, this will take a while!");
int app_count = 0;
for (uint32_t id = idStart; id <= idEnd && id >= idStart; id += idIncrement) {
if (kbd_enter_pressed()) {
break;
}
// EV1 is limited to 26 applications
if (is_ev1 && app_count >= 26) {
PrintAndLogEx(INFO, "\nDESFire EV1 card limited to 26 applications - stopping search");
break;
}
float progress = ((id - idStart) / (idEnd - idStart));
PrintAndLogEx(INPLACE, "Progress " _YELLOW_("%0.1f") " %% current AID: %06X", progress, id);
@ -2207,6 +2233,7 @@ static int CmdHF14ADesBruteApps(const char *Cmd) {
if (res == PM3_SUCCESS) {
printf("\33[2K\r"); // clear current line before printing
PrintAndLogEx(SUCCESS, "Got new APPID " _GREEN_("%06X"), id);
app_count++;
}
}
@ -2228,9 +2255,10 @@ static int CmdHF14ADesAuth(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mfdes auth",
"Select application on the card. It selects app if it is a valid one or returns an error.",
"hf mfdes auth -n 0 -t des -k 0000000000000000 --kdf none -> select PICC level and authenticate with key num=0, key type=des, key=00..00 and key derivation = none\n"
"hf mfdes auth -n 0 -t aes -k 00000000000000000000000000000000 -> select PICC level and authenticate with key num=0, key type=aes, key=00..00 and key derivation = none\n"
"hf mfdes auth -n 0 -t des -k 0000000000000000 --save -> select PICC level and authenticate and in case of successful authentication - save channel parameters to defaults\n"
"hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none -> select PICC level and authenticate with key num=0, key type=2tdea (default for factory cards), key=00..00\n"
"hf mfdes auth -n 0 -t des -k 0000000000000000 --kdf none -> select PICC level and authenticate with key num=0, key type=des (single DES, rarely used), key=00..00\n"
"hf mfdes auth -n 0 -t aes -k 00000000000000000000000000000000 -> select PICC level and authenticate with key num=0, key type=aes, key=00..00\n"
"hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --save -> authenticate and save channel parameters to defaults\n"
"hf mfdes auth --aid 123456 -> select application 123456 and authenticate via parameters from `default` command");
void *argtable[] = {
@ -2238,8 +2266,8 @@ static int CmdHF14ADesAuth(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -2606,8 +2634,8 @@ static int CmdHF14ADesCreateApp(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -2778,8 +2806,8 @@ static int CmdHF14ADesDeleteApp(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -2841,8 +2869,8 @@ static int CmdHF14ADesGetUID(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -2921,8 +2949,8 @@ static int CmdHF14ADesFormatPICC(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -2978,8 +3006,8 @@ static int CmdHF14ADesGetFreeMem(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -3040,8 +3068,8 @@ static int CmdHF14ADesChKeySettings(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -3113,8 +3141,8 @@ static int CmdHF14ADesGetKeyVersions(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number for authentication"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -3215,8 +3243,8 @@ static int CmdHF14ADesGetKeySettings(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -3293,8 +3321,8 @@ static int CmdHF14ADesGetAIDs(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -3365,8 +3393,8 @@ static int CmdHF14ADesGetAppNames(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -3437,8 +3465,8 @@ static int CmdHF14ADesGetFileIDs(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -3511,8 +3539,8 @@ static int CmdHF14ADesGetFileISOIDs(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -3584,8 +3612,8 @@ static int CmdHF14ADesGetFileSettings(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -3753,8 +3781,8 @@ static int CmdHF14ADesChFileSettings(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -3896,8 +3924,8 @@ static int CmdHF14ADesCreateFile(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -4034,8 +4062,8 @@ static int CmdHF14ADesCreateValueFile(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -4160,8 +4188,8 @@ static int CmdHF14ADesCreateRecordFile(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -4396,8 +4424,8 @@ static int CmdHF14ADesDeleteFile(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -4474,8 +4502,8 @@ static int CmdHF14ADesValueOperations(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -4646,8 +4674,8 @@ static int CmdHF14ADesClearRecordFile(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -5051,8 +5079,8 @@ static int CmdHF14ADesReadData(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -5222,8 +5250,8 @@ static int CmdHF14ADesWriteData(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -5561,8 +5589,8 @@ static int CmdHF14ADesLsFiles(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -5634,8 +5662,8 @@ static int CmdHF14ADesLsApp(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -5699,8 +5727,8 @@ static int CmdHF14ADesDump(const char *Cmd) {
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose output"),
arg_int0("n", "keyno", "<dec>", "Key number"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Crypt algo"),
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "Key type (default: 2TDEA for factory cards)"),
arg_str0("k", "key", "<hex>", "Key (hex): 8 bytes (DES), 16 bytes (2TDEA/AES), 24 bytes (3TDEA)"),
arg_str0(NULL, "kdf", "<none|AN10922|gallagher>", "Key Derivation Function (KDF)"),
arg_str0("i", "kdfi", "<hex>", "KDF input (1-31 hex bytes)"),
arg_str0("m", "cmode", "<plain|mac|encrypt>", "Communicaton mode"),
@ -5853,6 +5881,8 @@ static command_t CommandTable[] = {
{"write", CmdHF14ADesWriteData, IfPm3Iso14443a, "Write data to standard/backup/record/value file"},
{"value", CmdHF14ADesValueOperations, IfPm3Iso14443a, "Operations with value file (get/credit/limited credit/debit/clear)"},
{"clearrecfile", CmdHF14ADesClearRecordFile, IfPm3Iso14443a, "Clear record File"},
{"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("Simulation") " --------------------"},
{"sim", CmdHFMFDesSim, IfPm3Iso14443a, "Simulate DESFire EV1 card"},
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("System") " -----------------------"},
{"test", CmdHF14ADesTest, AlwaysAvailable, "Regression crypto tests"},
{NULL, NULL, NULL, NULL}

1141
client/src/cmdhfmfdessim.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,26 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// DESFire simulation commands
//-----------------------------------------------------------------------------
#ifndef CMDHFMFDESSIM_H__
#define CMDHFMFDESSIM_H__
#include "common.h"
int CmdHFMFDesSim(const char *Cmd);
#endif

View file

@ -2240,6 +2240,20 @@ int DesfireValueFileOperations(DesfireContext_t *dctx, uint8_t fid, uint8_t oper
int res = DesfireCommand(dctx, operation, data, datalen, resp, &resplen, -1);
// Auto-detection fallback: if MAC mode fails with length error, retry with plain mode
if ((res == 0x7E || res == -20) && dctx->commMode == DCMMACed) {
PrintAndLogEx(INFO, "MAC mode failed with length error, retrying with plain mode");
DesfireCommunicationMode original_mode = dctx->commMode;
dctx->commMode = DCMPlain;
memset(resp, 0, sizeof(resp));
resplen = 0;
res = DesfireCommand(dctx, operation, data, datalen, resp, &resplen, -1);
// Restore original mode for future commands
dctx->commMode = original_mode;
}
if (resplen == 4 && value) {
*value = MemLeToUint4byte(resp);
}

View file

@ -219,9 +219,6 @@ static uint8_t DesfireGetCmdHeaderLen(uint8_t cmd) {
static const uint8_t EV1D40TransmitMAC[] = {
MFDES_WRITE_DATA,
MFDES_CREDIT,
MFDES_LIMITED_CREDIT,
MFDES_DEBIT,
MFDES_WRITE_RECORD,
MFDES_UPDATE_RECORD,
MFDES_COMMIT_READER_ID,

View file

@ -23,8 +23,14 @@
- [How to create files](#how-to-create-files)
- [How to delete files](#how-to-delete-files)
- [How to read/write files](#how-to-readwrite-files)
- [How to work with value files](#how-to-work-with-value-files)
- [How to work with transaction mac](#how-to-work-with-transaction-mac)
- [How to switch DESFire Light to LRP mode](#how-to-switch-desfire-light-to-lrp-mode)
- [How to get File ISO IDs](#how-to-get-file-iso-ids)
- [How to use DESFire emulation](#how-to-use-desfire-emulation)
- [Emulation limitations](#emulation-limitations)
- [Testing](#testing)
- [Real DESFire card notes](#real-desfire-card-notes)
## Documentation
@ -162,6 +168,29 @@ FCI sends from card to reader after selecting the application (df01 by default)
If it needs to have more space for FCI - just change the ID of one of the bigger files to 0x1f (and the current ID to something else) via SetConfiguration command.
### DESFire Light File Access Rights
Access rights are defined as 2 bytes (16 bits) per file:
- **Bits 15-12**: Read access condition
- **Bits 11-8**: Write access condition
- **Bits 7-4**: ReadWrite access condition
- **Bits 3-0**: Change access condition
Access condition values:
- **0x0 to 0x4**: Key number requiring authentication
- **0xE**: Free access (no authentication)
- **0xF**: No access allowed
Default file access rights:
| File | Type | Read | Write | RW | Change |
|------|------|------|-------|-------|--------|
| 0x1F | StandardData | 0xE | 0xF | 0x3 | 0x0 |
| 0x00 | StandardData | 0x1 | 0xF | 0x3 | 0x0 |
| 0x04 | StandardData | 0x1 | 0x2 | 0x3 | 0x0 |
| 0x03 | Value | 0x1 | 0x2 | 0x3 | 0x0 |
| 0x01 | CyclicRecord | 0x1 | 0x2 | 0x3 | 0x0 |
| 0x0F | TransactionMAC | 0x1 | 0xF | 0x1 | 0x0 |
## How to
@ -314,6 +343,33 @@ For more detailed samples look at the next howto.
`hf mfdes write --aid 123456 --fid 01 -d 01020304 --readerid 010203` write data to the file with CommitReaderID command before and CommitTransaction after write
### How to work with value files
^[Top](#top)
Value files provide secure counter functionality with automatic transaction support.
*create value file:*
`hf mfdes createvaluefile --aid 123456 --fid 02 --lower 00000000 --upper 000003E8 --value 00000064 --rrights free --wrights free --rwrights free --chrights key0` - create value file with limits 0-1000, initial value 100
*value operations:*
`hf mfdes value --aid 123456 --fid 02 --op get -m plain` - read current value in plain mode
`hf mfdes value --aid 123456 --fid 02 --op get -m mac` - read current value in MAC mode
`hf mfdes value --aid 123456 --fid 02 --op credit -d 00000032 -m plain` - add 50 to value in plain mode
`hf mfdes value --aid 123456 --fid 02 --op credit -d 00000032 -m mac` - add 50 to value in MAC mode
`hf mfdes value --aid 123456 --fid 02 --op debit -d 00000014 -m mac` - subtract 20 from value in MAC mode
`hf mfdes value --aid 123456 --fid 02 --op limcredit -d 0000000A -m mac` - limited credit operation (if enabled)
*note on MAC mode:*
Value operations now work correctly in MAC mode on all DESFire variants. If you encounter issues with older cards, the system automatically falls back to plain mode for compatibility.
### How to work with transaction mac
^[Top](#top)
@ -389,3 +445,124 @@ Switch LRP mode on
`hf mfdes setconfig --appisoid df01 -t aes -s ev2 --param 05 --data 00000000010000000000`
### How to get File ISO IDs
^[Top](#top)
DESFire EV1+ supports ISO file IDs that can be used for ISO 7816-4 compatible access.
`hf mfdes getfileisoids --aid 123456` -- Get ISO file IDs for a specific application
`hf mfdes getfileisoids --no-auth` -- Get ISO file IDs without authentication
### How to use DESFire emulation
^[Top](#top)
The DESFire emulator supports simulating DESFire EV1/EV2/EV3 cards with most features:
Start emulation:
`hf mfdes sim` -- Start emulation with default card
`hf mfdes sim -u 04112233445566` -- Start emulation with custom 7-byte UID
Reset emulator to factory state:
`hf mfdes sim ereset` -- Reset to factory DESFire card (2TDEA master key)
Load a card dump:
`hf mfdes sim eload -f mydump.bin` -- Load card data from binary file
`hf mfdes sim eload -j mydump.json` -- Load card data from JSON file
View emulator state:
`hf mfdes sim eview` -- Display current card structure
`hf mfdes sim eview --detailed` -- Display detailed card information
Test emulator functionality:
`hf mfdes sim test` -- Run comprehensive emulator tests
Example workflow:
```
# Reset emulator to factory state
hf mfdes sim ereset
# Start emulation
hf mfdes sim
# In another terminal, interact with the emulated card
hf mfdes info
hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none
hf mfdes createapp --aid 112233
hf mfdes selectapp --aid 112233
hf mfdes createfile --fid 01 --size 20 --isofid 1001
hf mfdes write --fid 01 -d 48656c6c6f20576f726c64
```
The emulator supports:
- All authentication modes (DES, 2TDEA/3DES, 3TDEA, AES)
- All file types (Standard, Backup, Value, Linear/Cyclic Record)
- Value file operations (get, credit, debit, limited credit) with proper limit checking
- Access rights enforcement
- ISO file IDs (EV1+ feature)
- GetCardUID with proper encryption
- SetConfiguration command
- GetDFNames for application enumeration
- Transaction MAC with CommitReaderID
- Automatic transaction commitment for value operations
### Emulation limitations
The DESFire emulator supports:
- Maximum 28 applications (EV1 standard limit)
- Up to 16 files per application
- Up to 14 keys per application
- 4KB total memory for data storage
## Testing
^[Top](#top)
### Built-in Tests
Run comprehensive DESFire tests with:
`./tools/pm3_tests.sh --desfire` - run DESFire-specific tests including emulator validation
`./tools/pm3_tests.sh --long` - run all tests including DESFire tests
`hf mfdes test` - run offline cryptographic and core protocol tests
`hf mfdes sim test` - run basic emulator functionality tests
`hf mfdes sim test --all` - run extended emulator validation with stress testing
### Manual Testing
Test value operations on both emulator and real cards:
```bash
# Test with emulator (requires two terminals)
# Terminal 1: Start emulator
hf mfdes sim ereset
hf mfdes sim
# Terminal 2: Test value operations with multiple applications
hf mfdes createapp --aid 123456 --ks1 0F --ks2 0E --numkeys 1
hf mfdes createapp --aid 789ABC --ks1 0F --ks2 0E --numkeys 1
hf mfdes getaids
hf mfdes selectapp --aid 123456
hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none
hf mfdes createvaluefile --fid 02 --lower 00000000 --upper 000003E8 --value 00000064 --rrights free --wrights free --rwrights free --chrights key0
hf mfdes value --fid 02 --op get -m plain
hf mfdes value --fid 02 --op credit -d 00000032 -m mac
hf mfdes value --fid 02 --op debit -d 00000014 -m mac
hf mfdes value --fid 02 --op get -m mac
# Test with real card (place DESFire card on reader)
# Same commands as above
```
### Real DESFire card notes
When working with real DESFire cards:
- Factory cards use 2TDEA (16-byte 3DES) as the default master key, not DES or AES
- The default master key is all zeros (16 bytes: 00000000000000000000000000000000)
- EV1 cards are limited to 26 applications maximum
- EV3 cards report version 03.xx.xx in hardware version field

View file

@ -758,6 +758,10 @@ typedef struct {
#define CMD_HF_DESFIRE_READER 0x072c
#define CMD_HF_DESFIRE_INFO 0x072d
#define CMD_HF_DESFIRE_COMMAND 0x072e
#define CMD_HF_DESFIRE_SIM_RESET 0x072f
#define CMD_HF_DESFIRE_EML_MEMCLR 0x0750
#define CMD_HF_DESFIRE_EML_MEMSET 0x0751
#define CMD_HF_DESFIRE_EML_MEMGET 0x0752
#define CMD_HF_MIFARE_NACK_DETECT 0x0730
#define CMD_HF_MIFARE_STATIC_NONCE 0x0731

View file

@ -11,6 +11,7 @@ RESOURCEPATH="./client/resources"
SLOWTESTS=false
OPENCLTESTS=false
TESTDESFIRE=false
TESTALL=true
TESTMFKEY=false
TESTSTATICNESTED=false
@ -32,9 +33,10 @@ while (( "$#" )); do
case "$1" in
-h|--help)
echo """
Usage: $0 [--long] [--opencl] [--clientbin /path/to/proxmark3] [mfkey|nonce2key|mf_nonce_brute|staticnested|mfd_aes_brute|cryptorf|fpga_compress|bootrom|armsrc|client|recovery|common]
Usage: $0 [--long] [--opencl] [--desfire] [--clientbin /path/to/proxmark3] [mfkey|nonce2key|mf_nonce_brute|staticnested|mfd_aes_brute|cryptorf|fpga_compress|bootrom|armsrc|client|recovery|common|desfire]
--long: Enable slow tests
--opencl: Enable tests requiring OpenCL (preferably a Nvidia GPU)
--desfire: Run comprehensive DESFire emulator vs real card tests
--clientbin ...: Specify path to proxmark3 binary to test
If no target given, all targets will be tested
"""
@ -48,6 +50,11 @@ Usage: $0 [--long] [--opencl] [--clientbin /path/to/proxmark3] [mfkey|nonce2key|
OPENCLTESTS=true
shift
;;
--desfire)
TESTDESFIRE=true
TESTALL=false
shift
;;
--clientbin)
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
CLIENTBIN=$2
@ -122,6 +129,11 @@ Usage: $0 [--long] [--opencl] [--clientbin /path/to/proxmark3] [mfkey|nonce2key|
TESTCOMMON=true
shift
;;
desfire)
TESTALL=false
TESTDESFIRE=true
shift
;;
-*|--*=) # unsupported flags
echo "Error: Unsupported flag $1" >&2
exit 1
@ -578,8 +590,107 @@ while true; do
if ! CheckExecute "emv test" "$CLIENTBIN -c 'emv test'" "Tests \( ok"; then break; fi
if ! CheckExecute "hf cipurse test" "$CLIENTBIN -c 'hf cipurse test'" "Tests \( ok"; then break; fi
if ! CheckExecute "hf mfdes test" "$CLIENTBIN -c 'hf mfdes test'" "Tests \( ok"; then break; fi
if ! CheckExecute "hf mfdes sim test" "$CLIENTBIN -c 'hf mfdes sim test'" "Tests \( ok"; then break; fi
if ! CheckExecute "hf mfdes sim test --all" "$CLIENTBIN -c 'hf mfdes sim test --all'" "Tests \( ok"; then break; fi
# DESFire Real Card Validation Tests (requires real DESFire card on reader)
echo -e "\n${C_BLUE}DESFire Real Card Tests (place DESFire card on reader):${C_NC}"
read -p "Place a factory DESFire card on reader and press Enter (or Ctrl+C to skip)..."
if ! CheckExecute "hf mfdes info" "$CLIENTBIN -c 'hf mfdes info'" "Hardware.*version"; then
echo " Warning: DESFire card not detected, skipping real card tests"
else
if ! CheckExecute "hf mfdes factory auth" "$CLIENTBIN -c 'hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none'" "Authentication.*ok\|Authenticated"; then break; fi
if ! CheckExecute "hf mfdes getaids" "$CLIENTBIN -c 'hf mfdes getaids'" "000000"; then break; fi
if ! CheckExecute "hf mfdes getappnames" "$CLIENTBIN -c 'hf mfdes getappnames'" "Application"; then break; fi
if ! CheckExecute "hf mfdes getuid" "$CLIENTBIN -c 'hf mfdes getuid'" "UID"; then break; fi
if ! CheckExecute "hf mfdes freemem" "$CLIENTBIN -c 'hf mfdes freemem'" "free.*memory\|Free.*mem"; then break; fi
if ! CheckExecute "hf mfdes create test app" "$CLIENTBIN -c 'hf mfdes createapp --aid 112233 --ks 0F --numkeys 1'" "ok\|success"; then break; fi
if ! CheckExecute "hf mfdes select test app" "$CLIENTBIN -c 'hf mfdes selectapp --aid 112233'" "ok\|success"; then break; fi
if ! CheckExecute "hf mfdes auth test app" "$CLIENTBIN -c 'hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none'" "Authentication.*ok\|Authenticated"; then break; fi
if ! CheckExecute "hf mfdes create test file" "$CLIENTBIN -c 'hf mfdes createfile --aid 112233 --fid 01 --size 32'" "ok\|success"; then break; fi
if ! CheckExecute "hf mfdes write test data" "$CLIENTBIN -c 'hf mfdes write --aid 112233 --fid 01 --offset 0 -d 48656c6c6f20576f726c64'" "ok\|success"; then break; fi
if ! CheckExecute "hf mfdes read test data" "$CLIENTBIN -c 'hf mfdes read --aid 112233 --fid 01 --offset 0 --length 12'" "Hello.*World\|48.*65.*6c.*6c.*6f"; then break; fi
if ! CheckExecute "hf mfdes getfileids" "$CLIENTBIN -c 'hf mfdes getfileids --aid 112233'" "01"; then break; fi
if ! CheckExecute "hf mfdes getfilesettings" "$CLIENTBIN -c 'hf mfdes getfilesettings --aid 112233 --fid 01'" "File.*settings\|Size.*32"; then break; fi
if ! CheckExecute "hf mfdes cleanup test" "$CLIENTBIN -c 'hf mfdes selectapp --aid 000000 && hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none && hf mfdes deleteapp --aid 112233'" "ok\|success"; then break; fi
echo " Real card tests completed successfully!"
fi
if ! CheckExecute "hf waveshare load" "$CLIENTBIN -c 'hf waveshare load -m 6 -f tools/lena.bmp -s dither.bmp' && echo '34ff55fe7257876acf30dae00eb0e439 dither.bmp' | md5sum -c -" "dither.bmp: OK"; then break; fi
fi
# Dedicated DESFire emulator vs real card comparison tests
if $TESTDESFIRE; then
echo -e "\n${C_BLUE}Testing DESFire Emulator vs Real Card Validation:${C_NC}"
# Phase 1: Emulator tests
echo -e "\n${C_BLUE}Phase 1: Testing DESFire Emulator:${C_NC}"
if ! CheckExecute "desfire emulator reset" "$CLIENTBIN -c 'hf mfdes sim ereset'" "ok\|success\|Reset\|reset\|Resetting"; then break; fi
echo "Starting DESFire emulator (run 'hf mfdes sim' in another terminal)..."
read -p "Press Enter when emulator is running..."
if ! CheckExecute "emulator info test" "$CLIENTBIN -c 'hf mfdes info'" "DESFire\|Hardware.*version"; then break; fi
if ! CheckExecute "emulator auth test" "$CLIENTBIN -c 'hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none'" "Authentication.*ok\|Authenticated"; then break; fi
if ! CheckExecute "emulator getaids test" "$CLIENTBIN -c 'hf mfdes getaids'" "000000"; then break; fi
if ! CheckExecute "emulator app creation test" "$CLIENTBIN -c 'hf mfdes createapp --aid 123456 --ks1 0F --ks2 0E --numkeys 1'" "ok\|success"; then break; fi
if ! CheckExecute "emulator second app creation" "$CLIENTBIN -c 'hf mfdes createapp --aid 789ABC --ks1 0F --ks2 0E --numkeys 1'" "ok\|success"; then break; fi
if ! CheckExecute "emulator file creation test" "$CLIENTBIN -c 'hf mfdes selectapp --aid 123456 && hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none && hf mfdes createfile --fid 01 --size 16'" "ok\|success"; then break; fi
if ! CheckExecute "emulator write test" "$CLIENTBIN -c 'hf mfdes write --aid 123456 --fid 01 --offset 0 -d 48656c6c6f'" "ok\|success"; then break; fi
if ! CheckExecute "emulator read test" "$CLIENTBIN -c 'hf mfdes read --aid 123456 --fid 01 --offset 0 --length 5'" "Hello\|48.*65.*6c.*6c.*6f"; then break; fi
if ! CheckExecute "emulator multi-app test" "$CLIENTBIN -c 'hf mfdes getaids'" "123456.*789ABC\|789ABC.*123456"; then break; fi
if ! CheckExecute "emulator EV1+ getuid test" "$CLIENTBIN -c 'hf mfdes selectapp --aid 000000 && hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none && hf mfdes getuid'" "UID"; then break; fi
if ! CheckExecute "emulator EV1+ freemem test" "$CLIENTBIN -c 'hf mfdes freemem'" "free.*memory\|Free.*mem"; then break; fi
# Value file operation tests
echo " Testing value file operations..."
if ! CheckExecute "emulator value file creation" "$CLIENTBIN -c 'hf mfdes selectapp --aid 123456 && hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none && hf mfdes createvaluefile --fid 02 --lowlimit 0 --highlimit 1000 --value 100 --settings 0 --rrights 0 --wrights 0 --rwrights 0 --chrights 0'" "ok\|success"; then break; fi
if ! CheckExecute "emulator value get plain" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m plain'" "Value.*100\|0x00000064"; then break; fi
if ! CheckExecute "emulator value get mac" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m mac'" "Value.*100\|0x00000064"; then break; fi
if ! CheckExecute "emulator value credit plain" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op credit -d 00000032 -m plain'" "Value.*changed\|ok\|success"; then break; fi
if ! CheckExecute "emulator value get after credit" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m plain'" "Value.*150\|0x00000096"; then break; fi
if ! CheckExecute "emulator value credit mac" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op credit -d 0000000A -m mac'" "Value.*changed\|ok\|success"; then break; fi
if ! CheckExecute "emulator value debit plain" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op debit -d 00000014 -m plain'" "Value.*changed\|ok\|success"; then break; fi
if ! CheckExecute "emulator value debit mac" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op debit -d 00000014 -m mac'" "Value.*changed\|ok\|success"; then break; fi
if ! CheckExecute "emulator value final check" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m mac'" "Value.*132\|0x00000084"; then break; fi
if ! CheckExecute "emulator cleanup" "$CLIENTBIN -c 'hf mfdes selectapp --aid 000000 && hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none && hf mfdes deleteapp --aid 123456 && hf mfdes deleteapp --aid 789ABC'" "ok\|success"; then break; fi
echo " Emulator tests completed successfully!"
# Phase 2: Real card tests (if available)
echo -e "\n${C_BLUE}Phase 2: Testing Real DESFire Card (optional):${C_NC}"
echo "Place a factory DESFire card on reader for comparison tests..."
read -p "Press Enter to test real card (or Ctrl+C to skip)..."
if ! CheckExecute "real card info test" "$CLIENTBIN -c 'hf mfdes info'" "DESFire\|Hardware.*version"; then
echo " Real card not detected, skipping comparison tests"
else
echo " Real card detected, running comparison tests..."
if ! CheckExecute "real card auth test" "$CLIENTBIN -c 'hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none'" "Authentication.*ok\|Authenticated"; then break; fi
if ! CheckExecute "real card getaids test" "$CLIENTBIN -c 'hf mfdes getaids'" "000000"; then break; fi
if ! CheckExecute "real card app creation test" "$CLIENTBIN -c 'hf mfdes createapp --aid 123456 --ks1 0F --ks2 0E --numkeys 1'" "ok\|success"; then break; fi
if ! CheckExecute "real card second app creation" "$CLIENTBIN -c 'hf mfdes createapp --aid 789ABC --ks1 0F --ks2 0E --numkeys 1'" "ok\|success"; then break; fi
if ! CheckExecute "real card file creation test" "$CLIENTBIN -c 'hf mfdes selectapp --aid 123456 && hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none && hf mfdes createfile --fid 01 --size 16'" "ok\|success"; then break; fi
if ! CheckExecute "real card write test" "$CLIENTBIN -c 'hf mfdes write --aid 123456 --fid 01 --offset 0 -d 48656c6c6f'" "ok\|success"; then break; fi
if ! CheckExecute "real card read test" "$CLIENTBIN -c 'hf mfdes read --aid 123456 --fid 01 --offset 0 --length 5'" "Hello\|48.*65.*6c.*6c.*6f"; then break; fi
if ! CheckExecute "real card multi-app test" "$CLIENTBIN -c 'hf mfdes getaids'" "123456.*789ABC\|789ABC.*123456"; then break; fi
if ! CheckExecute "real card EV1+ getuid test" "$CLIENTBIN -c 'hf mfdes selectapp --aid 000000 && hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none && hf mfdes getuid'" "UID"; then break; fi
if ! CheckExecute "real card EV1+ freemem test" "$CLIENTBIN -c 'hf mfdes freemem'" "free.*memory\|Free.*mem"; then break; fi
# Value file operation tests on real card
echo " Testing value file operations on real card..."
if ! CheckExecute "real card value file creation" "$CLIENTBIN -c 'hf mfdes selectapp --aid 123456 && hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none && hf mfdes createvaluefile --fid 02 --lowlimit 0 --highlimit 1000 --value 100 --settings 0 --rrights 0 --wrights 0 --rwrights 0 --chrights 0'" "ok\|success"; then break; fi
if ! CheckExecute "real card value get plain" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m plain'" "Value.*100\|0x00000064"; then break; fi
if ! CheckExecute "real card value get mac" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m mac'" "Value.*100\|0x00000064"; then break; fi
if ! CheckExecute "real card value credit plain" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op credit -d 00000032 -m plain'" "Value.*changed\|ok\|success"; then break; fi
if ! CheckExecute "real card value get after credit" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m plain'" "Value.*150\|0x00000096"; then break; fi
if ! CheckExecute "real card value credit mac" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op credit -d 0000000A -m mac'" "Value.*changed\|ok\|success"; then break; fi
if ! CheckExecute "real card value debit plain" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op debit -d 00000014 -m plain'" "Value.*changed\|ok\|success"; then break; fi
if ! CheckExecute "real card value debit mac" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op debit -d 00000014 -m mac'" "Value.*changed\|ok\|success"; then break; fi
if ! CheckExecute "real card value final check" "$CLIENTBIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m mac'" "Value.*132\|0x00000084"; then break; fi
if ! CheckExecute "real card cleanup" "$CLIENTBIN -c 'hf mfdes selectapp --aid 000000 && hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none && hf mfdes deleteapp --aid 123456 && hf mfdes deleteapp --aid 789ABC'" "ok\|success"; then break; fi
echo " Real card tests completed successfully!"
echo " Both emulator and real card behave identically!"
fi
echo -e "\n${C_GREEN}DESFire validation tests completed successfully!${C_NC}"
fi
echo -e "\n------------------------------------------------------------"
echo -e "Tests [ ${C_GREEN}OK${C_NC} ] ${C_OK}\n"
exit 0