From e1598cd620a7c08471c7574cb9356da6c62fc8c1 Mon Sep 17 00:00:00 2001 From: Mistial Developer Date: Sat, 19 Jul 2025 08:39:47 -0400 Subject: [PATCH 1/2] DESFire: fix value file operations and improve MAC mode compatibility - Add auto-detection fallback for MAC mode in value operations When MAC mode fails with length errors, automatically retry with plain mode for better compatibility across different card types - Fix MAC transmission behavior for value operations Remove CREDIT, LIMITED_CREDIT, and DEBIT from EV1D40TransmitMAC array to match real card behavior and prevent authentication issues - Change default algorithm from DES to 2TDEA Real DESFire cards seem to use 2TDEA by default, improving out-of-the-box compatibility with factory cards - Update help text for value commands to follow client patterns Standardize "Crypt algo (deft: 2TDEA)" format for consistency - Add online test suite for DESFire value operations New pm3_online_tests.sh script validates value file creation, credit/debit operations in both plain and MAC modes with real cards --- client/src/cmdhfmfdes.c | 6 +- client/src/mifare/desfirecore.c | 14 +++ client/src/mifare/desfiresecurechan.c | 3 - tools/pm3_online_tests.sh | 148 ++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 6 deletions(-) create mode 100755 tools/pm3_online_tests.sh diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index eddba910f..d2160104c 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -463,7 +463,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 seem to use 2TDEA by default static uint8_t defaultKey[DESFIRE_MAX_KEY_SIZE] = {0}; static int defaultKdfAlgo = MFDES_KDF_ALGO_NONE; static int defaultKdfInputLen = 0; @@ -4034,7 +4034,7 @@ 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", "", "Key number"), - arg_str0("t", "algo", "", "Crypt algo"), + arg_str0("t", "algo", "", "Crypt algo (deft: 2TDEA)"), arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), arg_str0(NULL, "kdf", "", "Key Derivation Function (KDF)"), arg_str0("i", "kdfi", "", "KDF input (1-31 hex bytes)"), @@ -4474,7 +4474,7 @@ 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", "", "Key number"), - arg_str0("t", "algo", "", "Crypt algo"), + arg_str0("t", "algo", "", "Crypt algo (deft: 2TDEA)"), arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), arg_str0(NULL, "kdf", "", "Key Derivation Function (KDF)"), arg_str0("i", "kdfi", "", "KDF input (1-31 hex bytes)"), diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index f6ec34e17..97fcbc184 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -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); } diff --git a/client/src/mifare/desfiresecurechan.c b/client/src/mifare/desfiresecurechan.c index 2b439b1b4..d9506d48b 100644 --- a/client/src/mifare/desfiresecurechan.c +++ b/client/src/mifare/desfiresecurechan.c @@ -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, diff --git a/tools/pm3_online_tests.sh b/tools/pm3_online_tests.sh new file mode 100755 index 000000000..998122f62 --- /dev/null +++ b/tools/pm3_online_tests.sh @@ -0,0 +1,148 @@ +#!/usr/bin/env bash + +# Online tests that require actual PM3 device connection +# This is used to make sure that the language for the functions is english instead of the system default language. +LANG=C + +PM3PATH="$(dirname "$0")/.." +cd "$PM3PATH" || exit 1 + +TESTALL=false +TESTDESFIREVALUE=false + +# https://medium.com/@Drew_Stokes/bash-argument-parsing-54f3b81a6a8f +PARAMS="" +while (( "$#" )); do + case "$1" in + -h|--help) + echo """ +Usage: $0 [--pm3bin /path/to/pm3] [desfire_value] + --pm3bin ...: Specify path to pm3 binary to test + desfire_value: Test DESFire value operations with card + You must specify a test target - no default 'all' for online tests +""" + exit 0 + ;; + --pm3bin) + if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then + PM3BIN=$2 + shift 2 + else + echo "Error: Argument for $1 is missing" >&2 + exit 1 + fi + ;; + desfire_value) + TESTALL=false + TESTDESFIREVALUE=true + shift + ;; + -*|--*=) # unsupported flags + echo "Error: Unsupported flag $1" >&2 + exit 1 + ;; + *) # preserve positional arguments + PARAMS="$PARAMS $1" + shift + ;; + esac +done +# set positional arguments in their proper place +eval set -- "$PARAMS" + +C_RED='\033[0;31m' +C_GREEN='\033[0;32m' +C_YELLOW='\033[0;33m' +C_BLUE='\033[0;34m' +C_NC='\033[0m' # No Color +C_OK='\xe2\x9c\x94\xef\xb8\x8f' +C_FAIL='\xe2\x9d\x8c' + +# Check if file exists +function CheckFileExist() { + printf "%-40s" "$1 " + if [ -f "$2" ]; then + echo -e "[ ${C_GREEN}OK${C_NC} ] ${C_OK}" + return 0 + fi + if ls "$2" 1> /dev/null 2>&1; then + echo -e "[ ${C_GREEN}OK${C_NC} ] ${C_OK}" + return 0 + fi + echo -e "[ ${C_RED}FAIL${C_NC} ] ${C_FAIL}" + return 1 +} + +# Execute command and check result +function CheckExecute() { + printf "%-40s" "$1 " + + start=$(date +%s) + TIMEINFO="" + RES=$(eval "$2") + end=$(date +%s) + delta=$(expr $end - $start) + if [ $delta -gt 2 ]; then + TIMEINFO=" ($delta s)" + fi + if echo "$RES" | grep -E -q "$3"; then + echo -e "[ ${C_GREEN}OK${C_NC} ] ${C_OK} $TIMEINFO" + return 0 + fi + echo -e "[ ${C_RED}FAIL${C_NC} ] ${C_FAIL} $TIMEINFO" + echo "Execution trace:" + echo "$RES" + return 1 +} + +echo -e "${C_BLUE}Iceman Proxmark3 online test tool${C_NC}" +echo "" +echo "work directory: $(pwd)" + +if command -v git >/dev/null && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo -n "git branch: " + git describe --all + echo -n "git sha: " + git rev-parse HEAD + echo "" +fi + +# Check that user specified a test +if [ "$TESTDESFIREVALUE" = false ]; then + echo "Error: You must specify a test target. Use -h for help." + exit 1 +fi + +while true; do + # DESFire value tests + if $TESTDESFIREVALUE; then + echo -e "\n${C_BLUE}Testing DESFire card value operations${C_NC} ${PM3BIN:=./pm3}" + echo " PLACE A FACTORY DESFIRE CARD ON THE READER NOW" + if ! CheckFileExist "pm3 exists" "$PM3BIN"; then break; fi + + echo " Formatting card to clean state..." + if ! CheckExecute "format card" "$PM3BIN -c 'hf mfdes formatpicc'" "done"; then break; fi + + echo " Running value operation tests..." + if ! CheckExecute "card auth test" "$PM3BIN -c 'hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none'" "authenticated.*succes"; then break; fi + if ! CheckExecute "card app creation" "$PM3BIN -c 'hf mfdes createapp --aid 123456 --ks1 0F --ks2 0E --numkeys 1'" "successfully created"; then break; fi + if ! CheckExecute "card value file creation" "$PM3BIN -c 'hf mfdes createvaluefile --aid 123456 --fid 02 --lower 00000000 --upper 000003E8 --value 00000064'" "created successfully"; then break; fi + if ! CheckExecute "card value get plain" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m plain'" "Value.*100"; then break; fi + if ! CheckExecute "card value get mac" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m mac'" "Value.*100"; then break; fi + if ! CheckExecute "card value credit plain" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op credit -d 00000032 -m plain'" "Value.*changed"; then break; fi + if ! CheckExecute "card value get after credit" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m plain'" "Value.*150"; then break; fi + if ! CheckExecute "card value credit mac" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op credit -d 0000000A -m mac'" "Value.*changed"; then break; fi + if ! CheckExecute "card value debit plain" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op debit -d 00000014 -m plain'" "Value.*changed"; then break; fi + if ! CheckExecute "card value debit mac" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op debit -d 00000014 -m mac'" "Value.*changed"; then break; fi + if ! CheckExecute "card value final check" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m mac'" "Value.*120"; then break; fi + if ! CheckExecute "card cleanup" "$PM3BIN -c 'hf mfdes selectapp --aid 000000; hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none; hf mfdes deleteapp --aid 123456'" "application.*deleted"; then break; fi + echo " card value operation tests completed successfully!" + fi + + echo -e "\n------------------------------------------------------------" + echo -e "Tests [ ${C_GREEN}OK${C_NC} ] ${C_OK}\n" + exit 0 +done +echo -e "\n------------------------------------------------------------" +echo -e "\nTests [ ${C_RED}FAIL${C_NC} ] ${C_FAIL}\n" +exit 1 \ No newline at end of file From b3b24855d02727ea1f25affce016e1e3aedce684 Mon Sep 17 00:00:00 2001 From: Mistial Developer Date: Sat, 19 Jul 2025 09:52:30 -0400 Subject: [PATCH 2/2] DESFire: add detailed documentation for value file operations - Introduced a new "How to work with value files" section - Updated command examples with links to the new section - Clarified current/deprecated value operation commands - Expanded examples with practical use cases, security modes, and error handling scenarios --- doc/desfire.md | 191 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 3 deletions(-) diff --git a/doc/desfire.md b/doc/desfire.md index 50bddc9d5..65f1af748 100644 --- a/doc/desfire.md +++ b/doc/desfire.md @@ -23,6 +23,7 @@ - [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) @@ -254,7 +255,7 @@ Create standard file with mac access mode and specified access settings. access `hf mfdes createfile --aid 123456 --fid 01 --isofid 0001 --size 000010 --amode mac --rrights free --wrights free --rwrights free --chrights key0` -`hf mfdes createvaluefile --aid 123456 --fid 01 --isofid 0001 --lower 00000010 --upper 00010000 --value 00000100` - create value file +`hf mfdes createvaluefile --aid 123456 --fid 01 --isofid 0001 --lower 00000010 --upper 00010000 --value 00000100` - create value file (see [How to work with value files](#how-to-work-with-value-files) for detailed examples) `hf mfdes createrecordfile --aid 123456 --fid 01 --isofid 0001 --size 000010 --maxrecord 000010` - create linear record file @@ -294,9 +295,11 @@ Here it is needed to specify the type of the file because there is no `hf mfdes `hf mfdes write --aid 123456 --fid 01 --type data -d 01020304 --commit` - write backup data file and commit -`hf mfdes write --aid 123456 --fid 01 --type value -d 00000001` increment value file +`hf mfdes write --aid 123456 --fid 01 --type value -d 00000001` increment value file (deprecated, use `hf mfdes value` command) -`hf mfdes write --aid 123456 --fid 01 --type value -d 00000001 --debit` decrement value file +`hf mfdes write --aid 123456 --fid 01 --type value -d 00000001 --debit` decrement value file (deprecated, use `hf mfdes value` command) + +For modern value file operations, see [How to work with value files](#how-to-work-with-value-files) `hf mfdes write --aid 123456 --fid 01 --type record -d 01020304` write data to a record file @@ -314,6 +317,188 @@ 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 are specialized files designed for storing and manipulating monetary values or counters. They provide atomic operations for incrementing (credit) and decrementing (debit) values with built-in limits and security features. + +**Key Features:** +- 32-bit value storage (represented internally as unsigned) +- Lower and upper limits to prevent underflow/overflow +- Atomic operations with automatic transaction commit +- Transaction logging support +- Secure communication modes (plain, MAC, encrypted) + +**Value File Structure:** +- Current value: 32-bit value +- Lower limit: minimum allowed value (prevents underflow) +- Upper limit: maximum allowed value (prevents overflow) + +**Access Rights:** +Value files use four access right categories: +- **Read**: Required to get the current value (`hf mfdes value --op get`) +- **Write**: Required for debit operations (`hf mfdes value --op debit`) +- **Read/Write**: Required for credit operations (`hf mfdes value --op credit`) +- **Change**: Required to modify file settings or delete the file + +Access rights can be set to: +- `key0` through `keyE`: Requires authentication with the specified key +- `free`: No authentication required +- `deny`: Operation is forbidden + +*Create value file:* + +Creating a Bitcoin wallet on your DESFire card: +``` +pm3 --> hf mfdes createapp --aid 425443 --ks1 0B --ks2 0E +[+] Desfire application 425443 successfully created + +pm3 --> hf mfdes createvaluefile --aid 425443 --fid 01 --lower 00000000 --upper 01406F40 --value 00000032 +[=] ---- Create file settings ---- +[+] File type : Value +[+] File number : 0x01 (1) +[+] File comm mode : Plain +[+] Additional access: No +[+] Access rights : EEEE +[+] read......... free +[+] write........ free +[+] read/write... free +[+] change....... free +[=] Lower limit... 0 / 0x00000000 +[=] Upper limit... 21000000 / 0x01406F40 +[=] Value............ 50 / 0x00000032 +[=] Limited credit... 0 - disabled +[=] GetValue access... Not Free +[+] Value file 01 in the app 425443 created successfully +``` +This creates a DESFire Bitcoin wallet with: +- Application ID 0x425443 (ASCII "BTC") +- File ID 0x01 for the wallet +- Lower limit: 0 BTC (no overdrafts in crypto) +- Upper limit: 21,000,000 BTC (respecting Satoshi's vision) +- Initial value: 50 BTC (the original block reward) + +Creating the infamous Pizza Day wallet: +``` +pm3 --> hf mfdes createvaluefile --aid 425443 --fid 02 --lower 00000000 --upper 01406F40 --value 00002710 +[=] ---- Create file settings ---- +[+] File type : Value +[+] File number : 0x02 (2) +[+] File comm mode : Plain +[+] Additional access: No +[+] Access rights : EEEE +[+] read......... free +[+] write........ free +[+] read/write... free +[+] change....... free +[=] Lower limit... 0 / 0x00000000 +[=] Upper limit... 21000000 / 0x01406F40 +[=] Value............ 10000 / 0x00002710 +[=] Limited credit... 0 - disabled +[=] GetValue access... Not Free +[+] Value file 02 in the app 425443 created successfully +``` +This creates a wallet pre-loaded with 10,000 BTC (historical exchange rate: 2 pizzas) + +*Value file operations:* + +Check your Bitcoin balance: +``` +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 50 (0x00000032) + +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get -m mac +[+] Value: 50 (0x00000032) +``` + +Loading Bitcoin IOUs onto your card: +``` +pm3 --> hf mfdes value --aid 425443 --fid 01 --op credit -d 00000019 +[+] Value changed successfully + +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 75 (0x0000004b) +``` +Card now holds 75 BTC in IOUs ($9,000,000 in debt obligations) + +Buying coffee with Bitcoin IOUs: +``` +pm3 --> hf mfdes value --aid 425443 --fid 01 --op debit -d 00000001 +[+] Value changed successfully # You now owe the coffee shop $120,000 + +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 74 (0x0000004a) # Remaining debt capacity +``` + +The legendary Pizza Day recreation: +``` +pm3 --> hf mfdes value --aid 425443 --fid 02 --op debit -d 00002710 +[+] Value changed successfully # You now owe Papa John's $1.2 billion + +pm3 --> hf mfdes value --aid 425443 --fid 02 --op get +[+] Value: 0 (0x00000000) # Card empty, bankruptcy imminent +``` + +*Communication modes:* + +Value files support different communication modes for security: + +Plain mode (no encryption): +``` +pm3 --> hf mfdes value --aid 123456 --fid 02 --op get -m plain +[+] Value: 125 (0x0000007d) +``` + +MAC mode (message authentication): +``` +pm3 --> hf mfdes value --aid 123456 --fid 02 --op credit -d 00000032 -m mac +[+] Value changed successfully +``` + +Encrypted mode (full encryption): +``` +pm3 --> hf mfdes value --aid 123456 --fid 02 --op debit -d 00000014 -m encrypted +[+] Value changed successfully +``` + +*Error handling and compatibility:* + +The Proxmark3 implementation includes automatic fallback for compatibility: +- If MAC mode fails with a length error (-20), it automatically retries in plain mode +- This ensures compatibility across different DESFire card generations +- Original communication mode is restored after fallback + +*Transaction behavior:* + +Value operations are atomic with automatic commit: +- The `hf mfdes value` command automatically issues CommitTransaction after credit/debit operations +- Get operations do not require a commit +- Operations either complete fully (including commit) or fail completely +- No manual transaction management required when using the `hf mfdes value` command +- Transaction MAC files can log all value operations for audit trails + +*Practical examples:* + +Daily Bitcoin IOU catastrophes: +``` +# Check morning IOU balance +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 50 (0x00000032) # $6 million in IOUs + +# Friend sends you more IOUs via NFC bump +pm3 --> hf mfdes value --aid 425443 --fid 01 --op credit -d 000000C8 +[+] Value changed successfully # +200 BTC IOUs ($24M more debt) + +# Buy a Tesla (tap payment) +pm3 --> hf mfdes value --aid 425443 --fid 01 --op debit -d 00000001 +[+] Value changed successfully + +# Check remaining IOU capacity +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 273 (0x00000111) # $32.76M in transferable debt +``` + + ### How to work with transaction mac ^[Top](#top)