Merge branch 'master' into cherry_pick_emv

Signed-off-by: Iceman <iceman@iuse.se>
This commit is contained in:
Iceman 2025-01-14 16:26:35 +01:00 committed by GitHub
commit 76ad5a5b51
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
149 changed files with 7795 additions and 2976 deletions

View file

@ -12,6 +12,7 @@
name: "CodeQL"
on:
workflow_dispatch:
push:
branches: [ master ]
pull_request:

View file

@ -2,6 +2,7 @@ on: pull_request_target
name: Changelog Reminder
jobs:
remind:
if: github.repository_owner = 'RfidResearchGroup'
name: Changelog Reminder
runs-on: ubuntu-latest
steps:

View file

@ -18,5 +18,5 @@ jobs:
- name: check unique keys in dic files
shell: bash
run: |
find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | sed 's/ *#.*//;/^$/d' | sort | uniq -i -d -c | sort -n -r "
if [[ $(find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | sed 's/ *#.*//;/^$/d' | sort | uniq -i -d -c | sort -n -r " | grep -v '^\./' | wc -l) -gt 0 ]]; then exit 1; fi
find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | sed 's/ *#.*//;/^$/d' | sed 's/\(.*\)/\U\1/' | sort | uniq -i -d -c | sort -n -r "
if [[ $(find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | sed 's/ *#.*//;/^$/d' | sed 's/\(.*\)/\U\1/' | sort | uniq -i -d -c | sort -n -r " | grep -v '^\./' | wc -l) -gt 0 ]]; then exit 1; fi

View file

@ -3,20 +3,51 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Fixed symlink name in `mem spiffs tree` (@ANTodorov)
- Fixed reported file/link names when `mem spiffs wipe` (ANTodorov)
- Changed `hf mf info` - now differentiates between full USCUID and cut down ZUID chips (@nvx)
- Changed `lf hitag chk` - added key counter, client side abort and minor delay (@iceman1001)
- Added `hf seos sam` - Added support for HID SAM SEOS communications (@jkramarz)
- Changed (extended) area accessible by spiffs into last page of FLASH (@piotrva)
- Changed flash-stored key dictionaries (Mifare, iClass, T55XX) and T55XX configurations to SPIFFS files (@piotrva)
- Changed `lf em 410x sim` to use default gap value of 0 and extended help (@piotrva)
- Changed `hf 14a info` - now identifies MIAFRE Duox (@iceman1001)
- Added `hf iclass trbl` to perform tear-off attacks on iClass (@antiklesys)
- Added support for connection to host device in all Docker envs (@doegox)
- Changed `hf 15 info` to show all type matches and check ST25TVxC signature (@doegox)
- Added initial support for ST25TN and its signature verification (@doegox)
- Changed originality checks handling to refactor code and pk data (@doegox)
- Changed `uniq.yaml` workflow to be case-insensitive (@iceman1001)
- Fixed `mem load --mfc` not erasing all SPI flash blocks after extending to 4095 keys (@piotrva)
- Changed extended area for Mifare keys in SPI flash to hold 4095 keys (@piotrva)
- Fixed DESFire D40 secure channel crypto (@nvx)
- Fixed `hf mfp info` fix signature check on 4b UID cards (@doegox)
- Automatically set maximum read/write block when using predefined types in `hf_mf_ultimatecard` script (@piotrva)
- Changed SPI flash detection to calculate the size instead of table lookup, updated spi_flash_decode.py script with more ICs (@ANTodorov)
- Fixed `hf/lf tune` segfault when called from script (@doegox)
- Added option to set and get maximum read/write block number using `hf_mf_ultimatecard` script (@piotrva)
- Added JEDEC information for SPI flash W25Q64JV (@ANTodorov)
- Added special iclass legacy config cards in `hf iclass configcard` (@antiklesys)
- Added simulation function to `hf iclass legrec` (@antiklesys)
- Added keys from Momentum firmware projects. (@onovy)
- Added Dutch Statistics Agency default key (@eagle00789)
- Fixed Wiegand decode with hex input dropping the first bit (@emilyastranova)
- Changed `hf mf autopwn` - now allows for custom suffix (@zxkmm)
## [Orca.4.19552][2024-11-22]
- Fixed `hf_legic.lua` - removed bit32 commands from the script (@diorch1968)
- Fixed `mem spiffs tree` - now show correct symlink name (@ANTodorov)
- Fixed `mem spiffs wipe` - reported file/link names is now correct (@ANTodorov)
- Updated atrs list (@iceman1001)
- Added support for a new KDF (@iceman1001)
- Added Inner range aid and mad entries (@iceman1001)
- Changed `mem spiffs` - Use all available space in SPI flash (@ANTodorov)
- Fixed wrong size check in MifareSim (@iceman1001)
- Fixed `hf mf sim` - wrong size check in MifareSim (@iceman1001)
- Fixed `hf mf sim` not to respond to authentication attempts for sectors out of bound for selected Mifare type (@piotrva)
- Added option to build against non-default python3 with CMake as well (@doegox)
- Added option to build against non-default python3 with Makefile (@ANTodorov)
- Changed `hf 14a info` `hf mf info` - now detects FM1216-137 CPU cards (@iceman1001)
- Changed `hf iclass configcard` expanding the list of available options and functionalities (@antiklesys)
- Changed `hf iclass configcard` - expanding the list of available options and functionalities (@antiklesys)
- Fixed `intertic.py` - missing comma in array (@iceman1001)
- Added improved algorithm for `hf iclass legrec` leveraging reduced entropy from hash0 constraints (@antiklesys)
- Changed `hf iclass legrec` - improved algorithm leveraging reduced entropy from hash0 constraints (@antiklesys)
- Fixed `hf iclass configcard` when generating elite or keyroll elite configcards for Rev.C legacy readers (@antiklesys)
- Changed `hf mf c*` - now accepts a --gdm flag to write using uscuid/gdm 20/23 alt magic wakeup (@nvx)
- Changed `pm3_console()` - Python/Lua/C: replace `passthru` by `capture` and `quiet` (@doegox)
@ -26,12 +57,12 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Changed `hf iclass legrec` - updated script implementation to ensure functionality (@antiklesys)
- Added recovered iclass custom key to dictionary (@antiklesys)
- Added support for all Hitag S response protocol mode (@douniwan5788)
- Fixed 'hf_young.c' - flags declaration was missing a semicolon (@jakkpotts)
- Fixed `hf_young` - flags declaration was missing a semicolon (@jakkpotts)
- Changed `hf mf sim` - add option to allow key b to be used even if readable (@doegox)
- Changed `data num` - outputed binary strings are now properly zero padded (@iceman1001)
- Changed `hf iclass info` - now tries default keys and decode if legacy (@iceman1001)
- Changed `hf iclass chk` - now loads dictionary file by default (@iceman1001)
- Added an Makefile variable `DONT_BUILD_NATIVE` in mfd_aes_brute Makefile to easify downstream package
- Added Makefile variable `DONT_BUILD_NATIVE` in mfd_aes_brute Makefile to easify downstream package (@Cryolitia)
- Auto detect whether compile option `march=native` is supported for mfd_aes_brute Makefile
- Changed `hf mf sim` - support data-first and nested reader attacks (@doegox)
- Fixed `lf search` and `lf em 4x50 rdbl -b <blk>` does not coredump reading EM4450 tag (@ANTodorov)
@ -46,13 +77,13 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Changed `hf 14b info` - now detect Tiananxin (@iceman1001)
- Fixed `lf em 410x brute` - better filehandling and memory handling (@iceman1001)
- Changed split PacketResponseNG status into status and reason (@douniwan5788)
- add a helper script to decode JEDEC data `script run spi_flash_decode` (@ANTodorov)
- show SPI flash JEDEC Manufacturer ID and Device ID in `hw status` output (@ANTodorov)
- Improved `hf iclass configcards` to support generating config cards using a different key than the default k0 as the card's key (@antiklesys)
- Added `spi_flash_decode.py` - helper script to decode JEDEC data (@ANTodorov)
- Changed `hw status` - now show SPI flash JEDEC Manufacturer ID and Device ID in output (@ANTodorov)
- Changed `hf iclass configcards` to support generating config cards using a different key than the default k0 as the card's key (@antiklesys)
- Added maur keys (@iceman1001)
- Fixed `hf mfu pwdgen` for the 7 byte UID (@ANTodorov)
- Added `hf iclass unhash` command to reverse an iclass diversified key to hash0 pre-images (@antiklesys)
- Added crypto1 support to `hf 14a raw` (@doegox)
- Changed `hf 14a raw` - now supports crypto (@doegox)
- Changed `hw version` command to print LUA and Python versions (@jmichelp)
- Updated LUA to v5.4.7 which adds utf-8 support (@jmichelp)
- Moved `lf hitag sim --hts` -> `lf hitag hts sim` (@douniwan5788)
@ -70,11 +101,11 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Added detection for FM11NT021 (@iceman1001)
- Added detection of a magic NTAG 215 (@iceman1001)
- Fixed hardnested on AVX512F #2410 (@xianglin1998)
- Added `hf 14a aidsim` - simulates a PICC (like `14a sim`), and allows you to respond to specific AIDs and getData responses (@evildaemond)
- Added `hf 14a aidsim` - simulates a PICC and allows you to respond to specific AIDs and getData responses (@evildaemond)
- Fixed arguments for `SimulateIso14443aTag` and `SimulateIso14443aInit` in `hf_young.c`, `hf_aveful.c`, `hf_msdsal.c`, `hf_cardhopper.c`, `hf_reblay.c`, `hf_tcprst.c` and `hf_craftbyte.c` (@archi)
- Added `mf_backdoor_dump.py` script that dumps FM11RF08S and similar (Mifare Classic 1k) tag data that can be directly read by known backdoor keys. (@Aptimex)
- Added keys for Metro Q transit cards in Huston, TX. (@Anarchothulhu)
- Add new Mifare Classic keys from MifareClassicTool and Flipper projects. (@onovy)
- Added keys from MifareClassicTool and Flipper projects. (@onovy)
## [Backdoor.4.18994][2024-09-10]
- Changed flashing messages to be less scary (@iceman1001)

View file

@ -37,7 +37,8 @@ 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_mfc.c sam_seos.c emvsim.c
SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_common.c sam_mfc.c sam_seos.c emvsim.c
#UNUSED: mifaresniff.c
SRC_ISO14443b = iso14443b.c
SRC_FELICA = felica.c

View file

@ -441,7 +441,41 @@ static void SendStatus(uint32_t wait) {
ModInfo();
#ifdef WITH_FLASH
Flashmem_print_info();
DbpString(_CYAN_("Flash memory dictionary loaded"));
uint32_t num = 0;
if (exists_in_spiffs(MF_KEYS_FILE)) {
num = size_in_spiffs(MF_KEYS_FILE) / MF_KEY_LENGTH;
} else {
num = 0;
}
if (num > 0) {
Dbprintf(" Mifare.................. "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, MF_KEYS_FILE);
} else {
Dbprintf(" Mifare.................. "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, MF_KEYS_FILE);
}
if (exists_in_spiffs(T55XX_KEYS_FILE)) {
num = size_in_spiffs(T55XX_KEYS_FILE) / T55XX_KEY_LENGTH;
} else {
num = 0;
}
if (num > 0) {
Dbprintf(" T55xx................... "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, T55XX_KEYS_FILE);
} else {
Dbprintf(" T55xx................... "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, T55XX_KEYS_FILE);
}
if (exists_in_spiffs(ICLASS_KEYS_FILE)) {
num = size_in_spiffs(ICLASS_KEYS_FILE) / ICLASS_KEY_LENGTH;
} else {
num = 0;
}
if (num > 0) {
Dbprintf(" iClass.................. "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, ICLASS_KEYS_FILE);
} else {
Dbprintf(" iClass.................. "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, ICLASS_KEYS_FILE);
}
#endif
DbpString("");
reply_ng(CMD_STATUS, PM3_SUCCESS, NULL, 0);
@ -1792,7 +1826,7 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES: {
MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes, true);
MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes, true, packet->oldarg[1], packet->oldarg[2]);
break;
}
case CMD_HF_MIFARE_ACQ_NONCES: {
@ -2226,7 +2260,7 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_HF_SAM_SEOS: {
// sam_seos_get_pacs();
sam_seos_get_pacs(packet);
break;
}
@ -2762,28 +2796,10 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
if (payload->startidx == DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k)) {
if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k)) {
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(3, 0xC);
} else if (payload->startidx == DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k)) {
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(3, 0x8);
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(3, 0x9);
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(3, 0xA);
} else if (payload->startidx == DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages64k)) {
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(3, 0xB);
} else if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k)) {
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(3, 0xF);
Flash_Erase4k(spi_flash_pages64k - 1, 0xF);
}
uint16_t res = Flash_Write(payload->startidx, payload->data, payload->len);

View file

@ -37,6 +37,7 @@
#include "protocols.h"
#include "pmflash.h"
#include "flashmem.h" // persistence on flash
#include "spiffs.h" // spiffs
#include "appmain.h" // print stack
/*
@ -324,31 +325,7 @@ void setT55xxConfig(uint8_t arg0, const t55xx_configurations_t *c) {
return;
}
if (!FlashInit()) {
BigBuf_free();
return;
}
uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN);
Flash_CheckBusy(BUSY_TIMEOUT);
uint16_t res = Flash_ReadDataCont(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN);
if (res == 0) {
FlashStop();
BigBuf_free();
return;
}
memcpy(buf, &T55xx_Timing, T55XX_CONFIG_LEN);
// delete old configuration
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(3, 0xD);
// write new
res = Flash_Write(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN);
if (res == T55XX_CONFIG_LEN && g_dbglevel > 1) {
if (SPIFFS_OK == rdv40_spiffs_write(T55XX_CONFIG_FILE, (uint8_t *)&T55xx_Timing, T55XX_CONFIG_LEN, RDV40_SPIFFS_SAFETY_SAFE)) {
DbpString("T55XX Config save " _GREEN_("success"));
}
@ -363,15 +340,23 @@ t55xx_configurations_t *getT55xxConfig(void) {
void loadT55xxConfig(void) {
#ifdef WITH_FLASH
if (!FlashInit()) {
uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN);
uint32_t size = 0;
if (exists_in_spiffs(T55XX_CONFIG_FILE)) {
size = size_in_spiffs(T55XX_CONFIG_FILE);
}
if (size == 0) {
Dbprintf("Spiffs file: %s does not exists or empty.", T55XX_CONFIG_FILE);
BigBuf_free();
return;
}
uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN);
Flash_CheckBusy(BUSY_TIMEOUT);
uint16_t isok = Flash_ReadDataCont(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN);
FlashStop();
if (SPIFFS_OK != rdv40_spiffs_read(T55XX_CONFIG_FILE, buf, T55XX_CONFIG_LEN, RDV40_SPIFFS_SAFETY_SAFE)) {
Dbprintf("Spiffs file: %s cannot be read.", T55XX_CONFIG_FILE);
BigBuf_free();
return;
}
// verify read mem is actual data.
uint8_t cntA = T55XX_CONFIG_LEN, cntB = T55XX_CONFIG_LEN;
@ -380,6 +365,7 @@ void loadT55xxConfig(void) {
if (buf[i] == 0x00) cntB--;
}
if (!cntA || !cntB) {
Dbprintf("Spiffs file: %s does not malformed or empty.", T55XX_CONFIG_FILE);
BigBuf_free();
return;
}
@ -387,7 +373,7 @@ void loadT55xxConfig(void) {
if (buf[0] != 0xFF) // if not set for clear
memcpy((uint8_t *)&T55xx_Timing, buf, T55XX_CONFIG_LEN);
if (isok == T55XX_CONFIG_LEN) {
if (size == T55XX_CONFIG_LEN) {
if (g_dbglevel > 1) DbpString("T55XX Config load success");
}
@ -2146,29 +2132,34 @@ void T55xx_ChkPwds(uint8_t flags, bool ledcontrol) {
#ifdef WITH_FLASH
BigBuf_Clear_EM();
uint16_t isok = 0;
uint8_t counter[2] = {0x00, 0x00};
isok = Flash_ReadData(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k), counter, sizeof(counter));
if (isok != sizeof(counter))
goto OUT;
uint32_t size = 0;
pwd_count = (uint16_t)(counter[1] << 8 | counter[0]);
if (exists_in_spiffs(T55XX_KEYS_FILE)) {
size = size_in_spiffs(T55XX_KEYS_FILE);
}
if (size == 0) {
Dbprintf("Spiffs file: %s does not exists or empty.", T55XX_KEYS_FILE);
goto OUT;
}
pwd_count = size / T55XX_KEY_LENGTH;
if (pwd_count == 0)
goto OUT;
// since flash can report way too many pwds, we need to limit it.
// bigbuff EM size is determined by CARD_MEMORY_SIZE
// a password is 4bytes.
uint16_t pwd_size_available = MIN(CARD_MEMORY_SIZE, pwd_count * 4);
uint16_t pwd_size_available = MIN(CARD_MEMORY_SIZE, pwd_count * T55XX_KEY_LENGTH);
// adjust available pwd_count
pwd_count = pwd_size_available / 4;
pwd_count = pwd_size_available / T55XX_KEY_LENGTH;
isok = Flash_ReadData(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k) + 2, pwds, pwd_size_available);
if (isok != pwd_size_available)
if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(T55XX_KEYS_FILE, pwds, pwd_size_available, RDV40_SPIFFS_SAFETY_SAFE)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u passwords from spiffs file: %s", pwd_count, T55XX_KEYS_FILE);
} else {
Dbprintf("Spiffs file: %s cannot be read.", T55XX_KEYS_FILE);
goto OUT;
Dbprintf("Password dictionary count " _YELLOW_("%d"), pwd_count);
}
#endif

View file

@ -1036,7 +1036,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
// acquire static encrypted nonces in order to perform the attack described in
// Philippe Teuwen, "MIFARE Classic: exposing the static encrypted nonce variant"
//-----------------------------------------------------------------------------
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply) {
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply, uint8_t first_block_no, uint8_t first_key_type) {
struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs;
pcs = &mpcs;
@ -1055,6 +1055,10 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool
uint8_t buf[MIFARE_BLOCK_SIZE] = {0x00};
uint64_t ui64Key = bytes_to_num(key, 6);
bool with_data = flags & 1;
bool without_backdoor = (flags >> 1) & 1;
if (with_data && without_backdoor) {
return PM3_EINVARG;
}
uint32_t cuid = 0;
int16_t isOK = PM3_SUCCESS;
uint8_t cascade_levels = 0;
@ -1072,6 +1076,115 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool
LED_C_ON();
if (without_backdoor) {
uint32_t nt1 = 0;
iso14a_card_select_t card_info;
if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (ALL)");
isOK = PM3_ERFTRANS;
goto out;
}
switch (card_info.uidlen) {
case 4 :
cascade_levels = 1;
break;
case 7 :
cascade_levels = 2;
break;
case 10:
cascade_levels = 3;
break;
default:
break;
}
if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
isOK = PM3_ESOFT;
goto out;
};
uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + first_key_type, first_block_no, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL);
if (len != 4) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len);
isOK = PM3_ESOFT;
goto out;
}
uint32_t nt_enc = bytes_to_num(receivedAnswer, 4);
// send some crap to fail auth
CHK_TIMEOUT();
if (iso14443a_fast_select_card(uid, cascade_levels) == 0) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)");
isOK = PM3_ERFTRANS;
goto out;
}
if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
isOK = PM3_ESOFT;
goto out;
};
// Recover clear nt
struct Crypto1State mpcs_tmp = {0, 0};
struct Crypto1State *pcs_tmp = &mpcs_tmp;
crypto1_init(pcs_tmp, ui64Key);
uint32_t nt = crypto1_word(pcs_tmp, nt_enc ^ cuid, 1) ^ nt_enc;
int dist = nonce_distance(nt, nt1);
// ref dist is not always stable. Adjust physical distance to maximise ref dist, and try values around estimated nonces...
Dbprintf("Block %2i key %i nested nT=%08x first nT=%08x dist=%i", first_block_no, first_key_type, nt, nt1, dist);
for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) {
uint16_t sec_gap = sec;
if (sec >= MIFARE_1K_MAXSECTOR) {
// gap between user blocks and advanced verification method blocks
sec_gap += 16;
}
uint16_t blockNo = sec_gap * 4;
for (uint8_t keyType = 0; keyType < 2; keyType++) {
// Test if the action was cancelled
if (BUTTON_PRESS()) {
isOK = PM3_EOPABORTED;
break;
}
len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType, blockNo, receivedAnswer, sizeof(receivedAnswer), par_enc, NULL);
if (len != 4) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth2 error len=%d", len);
isOK = PM3_ESOFT;
goto out;
}
// store nt_enc
memcpy(buf + (keyType * 8) + 4, receivedAnswer, 4);
nt_enc = bytes_to_num(receivedAnswer, 4);
uint8_t nt_par_err = ((((par_enc[0] >> 7) & 1) ^ oddparity8((nt_enc >> 24) & 0xFF)) << 3 |
(((par_enc[0] >> 6) & 1) ^ oddparity8((nt_enc >> 16) & 0xFF)) << 2 |
(((par_enc[0] >> 5) & 1) ^ oddparity8((nt_enc >> 8) & 0xFF)) << 1 |
(((par_enc[0] >> 4) & 1) ^ oddparity8((nt_enc >> 0) & 0xFF)));
// Dbprintf("Sec %2i key %i {nT}=%02x%02x%02x%02x perr=%x", sec, keyType, receivedAnswer[0], receivedAnswer[1], receivedAnswer[2], receivedAnswer[3], nt_par_err);
// store nt_par_err
buf[(keyType * 8) + 2] = nt_par_err;
buf[(keyType * 8) + 3] = 0xAA; // extra check to tell we have nt/nt_enc/par_err
// send some crap to fail auth
CHK_TIMEOUT();
if (iso14443a_fast_select_card(uid, cascade_levels) == 0) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)");
isOK = PM3_ERFTRANS;
goto out;
}
if (mifare_classic_authex_cmd(pcs, cuid, first_block_no, MIFARE_AUTH_KEYA + first_key_type, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
isOK = PM3_ESOFT;
goto out;
};
nt1 = rewind_nonce(nt1, dist);
num_to_bytes(nt1 >> 16, 2, buf + (keyType * 8));
emlSetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + sec, 1, MIFARE_BLOCK_SIZE);
}
}
} else {
for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) {
uint16_t sec_gap = sec;
if (sec >= MIFARE_1K_MAXSECTOR) {
@ -1152,8 +1265,7 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool
// store nt (first half)
num_to_bytes(nt >> 16, 2, buf + (keyType * 8));
// send some crap to fail auth
uint8_t nack[] = {0x04};
ReaderTransmit(nack, sizeof(nack), NULL);
CHK_TIMEOUT();
if (iso14443a_fast_select_card(uid, cascade_levels) == 0) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Can't select card (UID)");
@ -1186,7 +1298,8 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool
buf[(keyType * 8) + 3] = 0xAA; // extra check to tell we have nt/nt_enc/par_err
emlSetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + sec, 1, MIFARE_BLOCK_SIZE);
// send some crap to fail auth
ReaderTransmit(nack, sizeof(nack), NULL);
CHK_TIMEOUT();
}
}
}
out:
@ -1194,7 +1307,7 @@ out:
crypto1_deinit(pcs);
LED_B_ON();
if (reply) {
reply_old(CMD_ACK, isOK, cuid, 0, BigBuf_get_EM_addr() + CARD_MEMORY_RF08S_OFFSET, MIFARE_BLOCK_SIZE * (MIFARE_1K_MAXSECTOR + 1));
reply_mix(CMD_ACK, isOK, cuid, 0, BigBuf_get_EM_addr() + CARD_MEMORY_RF08S_OFFSET, MIFARE_BLOCK_SIZE * (MIFARE_1K_MAXSECTOR + 1));
}
LED_B_OFF();
@ -1787,31 +1900,36 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da
#ifdef WITH_FLASH
if (use_flashmem) {
BigBuf_free();
uint16_t isok = 0;
uint8_t size[2] = {0x00, 0x00};
isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k), size, 2);
if (isok != 2)
uint32_t size = 0;
if (exists_in_spiffs(MF_KEYS_FILE)) {
size = size_in_spiffs(MF_KEYS_FILE);
}
if (size == 0) {
Dbprintf("Spiffs file: %s does not exists or empty.", MF_KEYS_FILE);
goto OUT;
}
keyCount = size[1] << 8 | size[0];
keyCount = size / MF_KEY_LENGTH;
if (keyCount == 0)
goto OUT;
// limit size of available for keys in bigbuff
// a key is 6bytes
uint16_t key_mem_available = MIN(BigBuf_get_size(), keyCount * 6);
uint16_t key_mem_available = MIN(BigBuf_get_size(), keyCount * MF_KEY_LENGTH);
keyCount = key_mem_available / 6;
keyCount = key_mem_available / MF_KEY_LENGTH;
datain = BigBuf_malloc(key_mem_available);
if (datain == NULL)
goto OUT;
isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k) + 2, datain, key_mem_available);
if (isok != key_mem_available)
if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, datain, keyCount * MF_KEY_LENGTH, RDV40_SPIFFS_SAFETY_SAFE)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u keys from spiffs file: %s", keyCount, MF_KEYS_FILE);
} else {
Dbprintf("Spiffs file: %s cannot be read.", MF_KEYS_FILE);
goto OUT;
}
}
#endif
@ -2809,6 +2927,7 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) {
uint8_t rdbl00[4] = {ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8};
uint8_t gen4gdmAuth[4] = {MIFARE_MAGIC_GDM_AUTH_KEY, 0x00, 0x6C, 0x92};
uint8_t gen4gdmGetConf[4] = {MIFARE_MAGIC_GDM_READ_CFG, 0x00, 0x39, 0xF7};
uint8_t gen4gdmGetMagicBlock[4] = {MIFARE_MAGIC_GDM_READBLOCK, 0x00, 0xC2, 0x66};
uint8_t gen4GetConf[8] = {GEN_4GTU_CMD, 0x00, 0x00, 0x00, 0x00, GEN_4GTU_GETCNF, 0, 0};
uint8_t superGen1[9] = {0x0A, 0x00, 0x00, 0xA6, 0xB0, 0x00, 0x10, 0x14, 0x1D};
bool isGen2 = false;
@ -2836,8 +2955,17 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) {
// check for GDM config
ReaderTransmit(gen4gdmGetConf, sizeof(gen4gdmGetConf), NULL);
res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par);
if (res > 1) {
// could be ZUID or full USCUID, the magic blocks don't exist on ZUID so
// a failure here indicates a feature limited chip like ZUID
// check for GDM hidden block read
ReaderTransmit(gen4gdmGetMagicBlock, sizeof(gen4gdmGetMagicBlock), NULL);
res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par);
if (res > 1) {
flag |= MAGIC_FLAG_GDM_WUP_40;
} else {
flag |= MAGIC_FLAG_GDM_WUP_40_ZUID;
}
}
}
@ -3127,7 +3255,8 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t *
goto OUT;
};
first_nt_counter++;
} else for (uint8_t i = 0; i < nr_nested; i++) {
} else {
for (uint8_t i = 0; i < nr_nested; i++) {
if (need_first_auth) {
cuid = 0;
@ -3204,6 +3333,7 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t *
}
oldntenc = ntenc;
}
}
data[1] = (cuid >> 24) & 0xFF;
data[2] = (cuid >> 16) & 0xFF;
@ -3367,7 +3497,8 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) {
retval = PM3_ESOFT;
goto OUT;
}
cmd[ofs++] = card_info->sak;
cmd[ofs] = block_len <= card_info->uidlen ? card_info->sak : cmd[ofs];
ofs++;
cmd[ofs++] = card_info->atqa[0];
cmd[ofs++] = card_info->atqa[1];
AddCrc14A(cmd, sizeof(block_cmd) + MIFARE_BLOCK_SIZE);

View file

@ -37,7 +37,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8
void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key);
void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain);
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply);
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply, uint8_t first_block_no, uint8_t first_key_type);
void MifareAcquireNonces(uint32_t arg0, uint32_t flags);
void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem);
void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);

View file

@ -983,3 +983,12 @@ int nonce_distance(uint32_t from, uint32_t to) {
int nonce16_index(uint16_t nt) {
return nonce16_distance(0x0100, nt) + 1;
}
uint32_t rewind_nonce(uint32_t from, uint16_t dist) {
uint16_t x = from >> 16;
for (uint16_t i = 0; i < dist; i++) {
x = ((x << 1 | x >> 15) & 0xffff) ^ ((x >> 1 ^ x >> 2 ^ x >> 4) & 0x100);
}
uint32_t nt = x;
return nt << 16 | prng_successor(nt, 16);
}

View file

@ -128,4 +128,5 @@ bool validate_parity_nonce(uint32_t ntenc, uint8_t ntparenc, uint32_t nt);
int nonce_distance(uint32_t from, uint32_t to);
int nonce16_distance(uint16_t x, uint16_t y);
int nonce16_index(uint16_t nt);
uint32_t rewind_nonce(uint32_t from, uint16_t dist);
#endif

373
armsrc/sam_common.c Normal file
View file

@ -0,0 +1,373 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
// Routines to support MFC <-> SAM communication
//-----------------------------------------------------------------------------
#include <string.h>
#include "sam_common.h"
#include "iclass.h"
#include "proxmark3_arm.h"
#include "BigBuf.h"
#include "commonutil.h"
#include "ticks.h"
#include "dbprint.h"
#include "i2c.h"
#include "iso15693.h"
#include "protocols.h"
/**
* @brief Transmits data to and receives data from a HID®'s iCLASS® SE Processor.
*
* This function sends a specified number of bytes to the SAM and receives a response.
*
* @param data Pointer to the data to be transmitted.
* @param n Number of bytes to be transmitted.
* @param resp Pointer to the buffer where the response will be stored.
* @param resplen Pointer to the variable where the length of the response will be stored.
* @return Status code indicating success or failure of the operation.
*/
int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) {
bool res = I2C_BufferWrite(data, n, I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN);
if (res == false) {
DbpString("failed to send to SIM CARD");
goto out;
}
*resplen = ISO7816_MAX_FRAME;
res = sc_rx_bytes(resp, resplen, SIM_WAIT_DELAY);
if (res == false) {
DbpString("failed to receive from SIM CARD");
goto out;
}
if (*resplen < 2) {
DbpString("received too few bytes from SIM CARD");
res = false;
goto out;
}
uint16_t more_len = 0;
if (resp[*resplen - 2] == 0x61 || resp[*resplen - 2] == 0x9F) {
more_len = resp[*resplen - 1];
} else {
// we done, return
goto out;
}
// Don't discard data we already received except the SW code.
// If we only received 1 byte, this is the echo of INS, we discard it.
*resplen -= 2;
if (*resplen == 1) {
*resplen = 0;
}
uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len};
res = I2C_BufferWrite(cmd_getresp, sizeof(cmd_getresp), I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN);
if (res == false) {
DbpString("failed to send to SIM CARD 2");
goto out;
}
more_len = 255 - *resplen;
res = sc_rx_bytes(resp + *resplen, &more_len, SIM_WAIT_DELAY);
if (res == false) {
DbpString("failed to receive from SIM CARD 2");
goto out;
}
*resplen += more_len;
out:
return res;
}
static inline void swap_clock_counters(volatile unsigned int *a, unsigned int *b) {
unsigned int c = *a;
*a = *b;
*b = c;
}
/**
* @brief Swaps the timer counter values.
*
* AT91SAM7S512 has a single Timer-Counter, that is reused in clocks Ticks
* and CountSspClk. This function stops the current clock and restores previous
* values. It is used to switch between different clock sources.
* It probably makes communication timing off, but at least makes it work.
*/
static void swap_clocks(void) {
static unsigned int tc0, tc1, tc2 = 0;
StopTicks();
swap_clock_counters(&(AT91C_BASE_TC0->TC_CV), &tc0);
swap_clock_counters(&(AT91C_BASE_TC1->TC_CV), &tc1);
swap_clock_counters(&(AT91C_BASE_TC2->TC_CV), &tc2);
}
void switch_clock_to_ticks(void) {
swap_clocks();
StartTicks();
}
void switch_clock_to_countsspclk(void) {
swap_clocks();
StartCountSspClk();
}
/**
* @brief Sends a payload to the SAM
*
* This function prepends the payload with the necessary APDU and application
* headers and sends it to the SAM.
*
* @param addr_src 0x14 for command from NFC, 0x44 for command from application
* @param addr_dest 0x0A for command to SAM
* @param addr_reply same as add_src or 0x00 if no reply is expected
* @param payload Pointer to the data to be sent.
* @param payload_len Length of the data to be sent.
* @param response Pointer to the buffer where the response will be stored.
* @param response_len Pointer to the variable where the length of the response will be stored.
* @param length Length of the data to be sent.
* @return Status code indicating success or failure of the operation.
*/
int sam_send_payload(
const uint8_t addr_src,
const uint8_t addr_dest,
const uint8_t addr_reply,
const uint8_t *const payload,
const uint16_t *payload_len,
uint8_t *response,
uint16_t *response_len
) {
int res = PM3_SUCCESS;
uint8_t *buf = response;
buf[0] = 0xA0; // CLA
buf[1] = 0xDA; // INS (PUT DATA)
buf[2] = 0x02; // P1 (TLV format?)
buf[3] = 0x63; // P2
buf[4] = SAM_TX_ASN1_PREFIX_LENGTH + (uint8_t) * payload_len; // LEN
buf[5] = addr_src;
buf[6] = addr_dest;
buf[7] = addr_reply;
buf[8] = 0x00;
buf[9] = 0x00;
buf[10] = 0x00;
memcpy(
&buf[11],
payload,
*payload_len
);
uint16_t length = SAM_TX_ASN1_PREFIX_LENGTH + SAM_TX_APDU_PREFIX_LENGTH + (uint8_t) * payload_len;
LogTrace(buf, length, 0, 0, NULL, true);
if (g_dbglevel >= DBG_INFO) {
DbpString("SAM REQUEST APDU: ");
Dbhexdump(length, buf, false);
}
if (sam_rxtx(buf, length, response, response_len) == false) {
if (g_dbglevel >= DBG_ERROR)
DbpString("SAM ERROR");
res = PM3_ECARDEXCHANGE;
goto out;
}
LogTrace(response, *response_len, 0, 0, NULL, false);
if (g_dbglevel >= DBG_INFO) {
DbpString("SAM RESPONSE APDU: ");
Dbhexdump(*response_len, response, false);
}
out:
return res;
}
/**
* @brief Retreives SAM firmware version.
*
* Used just as ping or sanity check here.
*
* @return Status code indicating success or failure of the operation.
*/
int sam_get_version(void) {
int res = PM3_SUCCESS;
if (g_dbglevel >= DBG_DEBUG)
DbpString("start sam_get_version");
uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t response_len = ISO7816_MAX_FRAME;
uint8_t payload[] = {
0xa0, 0x02, // <- SAM command
0x82, 0x00 // <- get version
};
uint16_t payload_len = sizeof(payload);
sam_send_payload(
0x44, 0x0a, 0x44,
payload,
&payload_len,
response,
&response_len
);
// resp:
// c1 64 00 00 00
// bd 11 <- SAM response
// 8a 0f <- get version response
// 80 02
// 01 29 <- version
// 81 06
// 68 3d 05 20 26 b6 <- build ID
// 82 01
// 01
// 90 00
if (g_dbglevel >= DBG_DEBUG)
DbpString("end sam_get_version");
if (response[5] != 0xbd) {
Dbprintf("Invalid SAM response");
goto error;
} else {
uint8_t *sam_response_an = sam_find_asn1_node(response + 5, 0x8a);
if (sam_response_an == NULL) {
if (g_dbglevel >= DBG_ERROR)
DbpString("SAM get response failed");
goto error;
}
uint8_t *sam_version_an = sam_find_asn1_node(sam_response_an, 0x80);
if (sam_version_an == NULL) {
if (g_dbglevel >= DBG_ERROR)
DbpString("SAM get version failed");
goto error;
}
uint8_t *sam_build_an = sam_find_asn1_node(sam_response_an, 0x81);
if (sam_build_an == NULL) {
if (g_dbglevel >= DBG_ERROR)
DbpString("SAM get firmware ID failed");
goto error;
}
if (g_dbglevel >= DBG_INFO) {
DbpString("SAM get version successful");
Dbprintf("Firmware version: %X.%X", sam_version_an[2], sam_version_an[3]);
Dbprintf("Firmware ID: ");
Dbhexdump(sam_build_an[1], sam_build_an + 2, false);
}
goto out;
}
error:
res = PM3_ESOFT;
out:
BigBuf_free();
if (g_dbglevel >= DBG_DEBUG)
DbpString("end sam_get_version");
return res;
}
/**
* @brief Finds an ASN.1 node of a specified type within a given root node.
*
* This function searches through a single level of the ASN.1 structure starting
* from the root node to find a node of the specified type.
*
* @param root Pointer to the root node of the ASN.1 structure.
* @param type The type of the ASN.1 node to find.
* @return Pointer to the ASN.1 node of the specified type if found, otherwise NULL.
*/
uint8_t *sam_find_asn1_node(const uint8_t *root, const uint8_t type) {
const uint8_t *end = (uint8_t *) root + *(root + 1);
uint8_t *current = (uint8_t *) root + 2;
while (current < end) {
if (*current == type) {
return current;
} else {
current += 2 + *(current + 1);
}
}
return NULL;
}
/**
* @brief Appends an ASN.1 node to the end of a given node.
*
* This function appends an ASN.1 node of a specified type and length to the end of
* the ASN.1 structure at specified node level.
*
* It is the most naive solution that does not handle the case where the node to append is
* not the last node at the same level. It also does not also care about proper
* order of the nodes.
*
* @param root Pointer to the root node of the ASN.1 structure.
* @param root Pointer to the node to be appended of the ASN.1 structure.
* @param type The type of the ASN.1 node to append.
* @param data Pointer to the data to be appended.
* @param len The length of the data to be appended.
*/
void sam_append_asn1_node(const uint8_t *root, const uint8_t *node, uint8_t type, const uint8_t *const data, uint8_t len) {
uint8_t *end = (uint8_t *) root + *(root + 1) + 2;
*(end) = type;
*(end + 1) = len;
memcpy(end + 2, data, len);
for (uint8_t *current = (uint8_t *) root; current <= node; current += 2) {
*(current + 1) += 2 + len;
};
return;
}
void sam_send_ack(void) {
uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t response_len = ISO7816_MAX_FRAME;
uint8_t payload[] = {
0xa0, 0
};
uint16_t payload_len = sizeof(payload);
sam_send_payload(
0x44, 0x0a, 0x00,
payload,
&payload_len,
response,
&response_len
);
BigBuf_free();
}

49
armsrc/sam_common.h Normal file
View file

@ -0,0 +1,49 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef __SAM_COMMON_H
#define __SAM_COMMON_H
#include "common.h"
static const uint8_t SAM_TX_APDU_PREFIX_LENGTH = 5;
static const uint8_t SAM_TX_ASN1_PREFIX_LENGTH = 6;
static const uint8_t SAM_RX_ASN1_PREFIX_LENGTH = 5;
int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen);
void switch_clock_to_ticks(void);
void switch_clock_to_countsspclk(void);
int sam_send_payload(
const uint8_t addr_src,
const uint8_t addr_dest,
const uint8_t addr_reply,
const uint8_t *const payload,
const uint16_t *payload_len,
uint8_t *response,
uint16_t *response_len
);
int sam_get_version(void);
uint8_t *sam_find_asn1_node(const uint8_t *root, const uint8_t type);
void sam_append_asn1_node(const uint8_t *root, const uint8_t *node, uint8_t type, const uint8_t *const data, uint8_t len);
void sam_send_ack(void);
#endif

View file

@ -16,7 +16,7 @@
// Routines to support MFC <-> SAM communication
//-----------------------------------------------------------------------------
#include "sam_mfc.h"
#include "sam_seos.h"
#include "sam_common.h"
#include "iclass.h"
#include "proxmark3_arm.h"

View file

@ -17,5 +17,6 @@
#define __SAM_MFC_H
#include "common.h"
#include "sam_common.h"
#endif

View file

@ -16,6 +16,7 @@
// Routines to support Picopass <-> SAM communication
//-----------------------------------------------------------------------------
#include "sam_picopass.h"
#include "sam_common.h"
#include "iclass.h"
#include "crc16.h"
#include "proxmark3_arm.h"
@ -30,66 +31,79 @@
#include "optimized_cipher.h"
#include "fpgaloader.h"
static int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) {
StartTicks();
/**
* @brief Sets the card detected status for the SAM (Secure Access Module).
*
* This function informs that a card has been detected by the reader and
* initializes SAM communication with the card.
*
* @param card_select Pointer to the descriptor of the detected card.
* @return Status code indicating success or failure of the operation.
*/
static int sam_set_card_detected(picopass_hdr_t *card_select) {
int res = PM3_SUCCESS;
if (g_dbglevel >= DBG_DEBUG)
DbpString("start sam_set_card_detected");
bool res = I2C_BufferWrite(data, n, I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN);
if (res == false) {
DbpString("failed to send to SIM CARD");
goto out;
}
uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t response_len = ISO7816_MAX_FRAME;
*resplen = ISO7816_MAX_FRAME;
// a0 12
// ad 10
// a0 0e
// 80 02
// 00 04 <- Picopass
// 81 08
// 9b fc a4 00 fb ff 12 e0 <- CSN
res = sc_rx_bytes(resp, resplen, SIM_WAIT_DELAY);
if (res == false) {
DbpString("failed to receive from SIM CARD");
goto out;
}
uint8_t payload[] = {
0xa0, 18, // <- SAM command
0xad, 16, // <- set detected card
0xa0, 4 + 10,
0x80, 2, // <- protocol
0x00, 0x04, // <- Picopass
0x81, 8, // <- CSN
card_select->csn[0], card_select->csn[1], card_select->csn[2], card_select->csn[3],
card_select->csn[4], card_select->csn[5], card_select->csn[6], card_select->csn[7]
};
uint16_t payload_len = sizeof(payload);
if (*resplen < 2) {
DbpString("received too few bytes from SIM CARD");
res = false;
goto out;
}
sam_send_payload(
0x44, 0x0a, 0x44,
payload,
&payload_len,
response,
&response_len
);
uint16_t more_len = 0;
// resp:
// c1 64 00 00 00
// bd 02 <- response
// 8a 00 <- empty response (accepted)
// 90 00
if (resp[*resplen - 2] == 0x61 || resp[*resplen - 2] == 0x9F) {
more_len = resp[*resplen - 1];
if (response[5] != 0xbd) {
if (g_dbglevel >= DBG_ERROR)
Dbprintf("Invalid SAM response");
goto error;
} else {
// we done, return
// uint8_t * sam_response_an = sam_find_asn1_node(response + 5, 0x8a);
// if(sam_response_an == NULL){
// if (g_dbglevel >= DBG_ERROR)
// Dbprintf("Invalid SAM response");
// goto error;
// }
goto out;
}
// Don't discard data we already received except the SW code.
// If we only received 1 byte, this is the echo of INS, we discard it.
*resplen -= 2;
if (*resplen == 1) {
*resplen = 0;
}
uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len};
res = I2C_BufferWrite(cmd_getresp, sizeof(cmd_getresp), I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN);
if (res == false) {
DbpString("failed to send to SIM CARD 2");
goto out;
}
more_len = 255 - *resplen;
res = sc_rx_bytes(resp + *resplen, &more_len, SIM_WAIT_DELAY);
if (res == false) {
DbpString("failed to receive from SIM CARD 2");
goto out;
}
*resplen += more_len;
error:
res = PM3_ESOFT;
out:
StopTicks();
BigBuf_free();
if (g_dbglevel >= DBG_DEBUG)
DbpString("end sam_set_card_detected");
return res;
}
@ -218,23 +232,19 @@ int sam_picopass_get_pacs(void) {
uint8_t *sam_apdu = BigBuf_calloc(ISO7816_MAX_FRAME);
// -----------------------------------------------------------------------------
// first
// a0 da 02 63 1a 44 0a 44 00 00 00 a0 12 ad 10 a0 0e 80 02 00 04 81 08 9b fc a4 00 fb ff 12 e0
hexstr_to_byte_array("a0da02631a440a44000000a012ad10a00e800200048108", sam_apdu, &sam_len);
memcpy(sam_apdu + sam_len, hdr.csn, sizeof(hdr.csn));
sam_len += sizeof(hdr.csn);
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
res = PM3_ECARDEXCHANGE;
goto out;
}
print_dbg("-- 1", resp, resp_len);
// first - set detected card (0xAD)
switch_clock_to_ticks();
sam_set_card_detected(&hdr);
// -----------------------------------------------------------------------------
// second
// a0 da 02 63 0d 44 0a 44 00 00 00 a0 05 a1 03 80 01 04
hexstr_to_byte_array("a0da02630d440a44000000a005a103800104", sam_apdu, &sam_len);
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
// second - get PACS (0xA1)
// a0 05
// a1 03
// 80 01
// 04
hexstr_to_byte_array("a005a103800104", sam_apdu, &sam_len);
if (sam_send_payload(0x44, 0x0a, 0x44, sam_apdu, (uint16_t *) &sam_len, resp, &resp_len) != PM3_SUCCESS) {
res = PM3_ECARDEXCHANGE;
goto out;
}
@ -245,7 +255,7 @@ int sam_picopass_get_pacs(void) {
// Tag|c00a140a000000a110a10e8004 0c05de64 8102 0004 820201f4
// -----------------------------------------------------------------------------
// third AIA block 5
// third AIA block 5 (emulated tag <-> SAM exchange starts here)
// a0da02631c140a00000000bd14a012a010800a ffffff0006fffffff88e 81020000
// picopass legacy is fixed. wants AIA and crc. ff ff ff ff ff ff ff ff ea f5
// picpoasss SE ff ff ff 00 06 ff ff ff f8 8e
@ -300,7 +310,7 @@ int sam_picopass_get_pacs(void) {
}
// start ssp clock again...
StartCountSspClk();
switch_clock_to_countsspclk();
// NOW we auth against tag
uint8_t cmd_check[9] = { ICLASS_CMD_CHECK };
@ -325,6 +335,7 @@ int sam_picopass_get_pacs(void) {
hexstr_to_byte_array("A0DA026316140A00000000BD0EA00CA00A8004311E32E981020000", sam_apdu, &sam_len);
memcpy(sam_apdu + 19, mac, sizeof(mac));
switch_clock_to_ticks();
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
res = PM3_ECARDEXCHANGE;
goto out;
@ -355,7 +366,7 @@ int sam_picopass_get_pacs(void) {
// c1 61 c1 00 00 a1 10 a1 0e 80 04 0c 06 45 56 81 02 00 04 82 02 01 f4 90 00
// read block 6
StartCountSspClk();
switch_clock_to_countsspclk();
start_time = GetCountSspClk();
iclass_send_as_reader(resp + 11, 4, &start_time, &eof_time, shallow_mod);
@ -373,6 +384,7 @@ int sam_picopass_get_pacs(void) {
hexstr_to_byte_array("A0DA02631C140A00000000BD14A012A010800A030303030003E017432381020000", sam_apdu, &sam_len);
memcpy(sam_apdu + 19, resp, resp_len);
switch_clock_to_ticks();
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
res = PM3_ECARDEXCHANGE;
goto out;
@ -382,7 +394,7 @@ int sam_picopass_get_pacs(void) {
// c161c10000a110a10e8004 0606455681020004820201f49000
// read the credential blocks
StartCountSspClk();
switch_clock_to_countsspclk();
start_time = GetCountSspClk();
iclass_send_as_reader(resp + 11, 4, &start_time, &eof_time, shallow_mod);
@ -400,6 +412,7 @@ int sam_picopass_get_pacs(void) {
hexstr_to_byte_array("A0DA026334140A00000000BD2CA02AA0288022030303030003E017769CB4A198E0DEC82AD4C8211F9968712BE7393CF8E71D7E804C81020000", sam_apdu, &sam_len);
memcpy(sam_apdu + 19, resp, resp_len);
switch_clock_to_ticks();
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
res = PM3_ECARDEXCHANGE;
goto out;
@ -409,7 +422,13 @@ int sam_picopass_get_pacs(void) {
// -----------------------------------------------------------------------------
// TEN ask for PACS data
// A0DA02630C440A00000000BD04A0028200
// A0 DA 02 63 0C
// 44 0A 00 00 00 00
// BD 04
// A0 02
// 82 00
// (emulated tag <-> SAM exchange ends here)
hexstr_to_byte_array("A0DA02630C440A00000000BD04A0028200", sam_apdu, &sam_len);
memcpy(sam_apdu + 19, resp, resp_len);
@ -424,7 +443,12 @@ int sam_picopass_get_pacs(void) {
goto out;
}
// c164000000bd098a07 030506951f9a00 9000
// resp:
// c1 64 00 00 00
// bd 09
// 8a 07
// 03 05 06 95 1f 9a 00 <- decoded PACS data
// 90 00
uint8_t *pacs = BigBuf_calloc(resp[8]);
memcpy(pacs, resp + 9, resp[8]);
@ -439,6 +463,7 @@ out:
off:
switch_off();
StopTicks();
BigBuf_free();
return res;
}

View file

@ -17,6 +17,7 @@
#define __SAM_PICOPASS_H
#include "common.h"
#include "sam_common.h"
int sam_picopass_get_pacs(void);

View file

@ -14,9 +14,392 @@
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// Routines to support SEOS <-> SAM communication
// communication and ASN.1 messages based on https://github.com/bettse/seader/blob/main/seader.asn1
//-----------------------------------------------------------------------------
#include "sam_seos.h"
#include "sam_common.h"
#include "iclass.h"
#include "proxmark3_arm.h"
#include "iso14443a.h"
#include "iclass.h"
#include "crc16.h"
#include "proxmark3_arm.h"
#include "BigBuf.h"
#include "cmd.h"
#include "commonutil.h"
#include "ticks.h"
#include "dbprint.h"
#include "i2c.h"
#include "protocols.h"
#include "optimized_cipher.h"
#include "fpgaloader.h"
#include "pm3_cmd.h"
#include "cmd.h"
/**
* @brief Sets the card detected status for the SAM (Secure Access Module).
*
* This function informs that a card has been detected by the reader and
* initializes SAM communication with the card.
*
* @param card_select Pointer to the descriptor of the detected card.
* @return Status code indicating success or failure of the operation.
*/
static int sam_set_card_detected(iso14a_card_select_t *card_select) {
int res = PM3_SUCCESS;
if (g_dbglevel >= DBG_DEBUG)
DbpString("start sam_set_card_detected");
uint8_t *request = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t request_len = ISO7816_MAX_FRAME;
uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t response_len = ISO7816_MAX_FRAME;
const uint8_t payload[] = {
0xa0, 8, // <- SAM command
0xad, 6, // <- set detected card
0xa0, 4, // <- detected card details
0x80, 2, // <- protocol
0x00, 0x02 // <- ISO14443A
};
memcpy(request, payload, sizeof(payload));
sam_append_asn1_node(request, request + 4, 0x81, card_select->uid, card_select->uidlen);
sam_append_asn1_node(request, request + 4, 0x82, card_select->atqa, 2);
sam_append_asn1_node(request, request + 4, 0x83, &card_select->sak, 1);
request_len = request[1] + 2;
sam_send_payload(
0x44, 0x0a, 0x44,
request,
&request_len,
response,
&response_len
);
// resp:
// c1 64 00 00 00
// bd 02 <- response
// 8a 00 <- empty response (accepted)
// 90 00
if (response[5] != 0xbd) {
if (g_dbglevel >= DBG_ERROR)
Dbprintf("Invalid SAM response");
goto error;
} else {
// uint8_t * sam_response_an = sam_find_asn1_node(response + 5, 0x8a);
// if(sam_response_an == NULL){
// if (g_dbglevel >= DBG_ERROR)
// Dbprintf("Invalid SAM response");
// goto error;
// }
goto out;
}
error:
res = PM3_ESOFT;
out:
BigBuf_free();
if (g_dbglevel >= DBG_DEBUG)
DbpString("end sam_set_card_detected");
return res;
}
/**
* @brief Copies the payload from an NFC buffer to a SAM buffer.
*
* Wraps received data from NFC into an ASN1 tree, so it can be transmitted to the SAM .
*
* @param sam_tx Pointer to the SAM transmit buffer.
* @param nfc_rx Pointer to the NFC receive buffer.
* @param nfc_len Length of the data to be copied from the NFC buffer.
*
* @return Length of SAM APDU to be sent.
*/
inline static uint16_t sam_seos_copy_payload_nfc2sam(uint8_t *sam_tx, uint8_t *nfc_rx, uint8_t nfc_len) {
// NFC resp:
// 6f 0c 84 0a a0 00 00 04 40 00 01 01 00 01 90 00 fb e3
// SAM req:
// bd 1c
// a0 1a
// a0 18
// 80 12
// 6f 0c 84 0a a0 00 00 04 40 00 01 01 00 01 90 00 fb e3
// 81 02
// 00 00
const uint8_t payload[] = {
0xbd, 4,
0xa0, 2,
0xa0, 0
};
const uint8_t tag81[] = {
0x00, 0x00
};
memcpy(sam_tx, payload, sizeof(payload));
sam_append_asn1_node(sam_tx, sam_tx + 4, 0x80, nfc_rx, nfc_len);
sam_append_asn1_node(sam_tx, sam_tx + 4, 0x81, tag81, sizeof(tag81));
return sam_tx[1] + 2; // length of the ASN1 tree
}
/**
* @brief Copies the payload from the SAM receive buffer to the NFC transmit buffer.
*
* Unpacks data to be transmitted from ASN1 tree in APDU received from SAM.
*
* @param nfc_tx_buf Pointer to the buffer where the NFC transmit data will be stored.
* @param sam_rx_buf Pointer to the buffer containing the data received from the SAM.
* @return Length of NFC APDU to be sent.
*/
inline static uint16_t sam_seos_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf) {
// SAM resp:
// c1 61 c1 00 00
// a1 21 <- nfc command
// a1 1f <- nfc send
// 80 10 <- data
// 00 a4 04 00 0a a0 00 00 04 40 00 01 01 00 01 00
// 81 02 <- protocol
// 02 02
// 82 02 <- timeout
// 01 2e
// 85 03 <- format
// 06 c0 00
// 90 00
// NFC req:
// 00 a4 04 00 0a a0 00 00 04 40 00 01 01 00 01 00
// copy data out of c1->a1>->a1->80 node
uint16_t nfc_tx_len = (uint8_t) * (sam_rx_buf + 10);
memcpy(nfc_tx_buf, sam_rx_buf + 11, nfc_tx_len);
return nfc_tx_len;
}
/**
* @brief Sends a request to the SAM and retrieves the response.
*
* Unpacks request to the SAM and relays ISO14A traffic to the card.
* If no request data provided, sends a request to get PACS data.
*
* @param request Pointer to the buffer containing the request to be sent to the SAM.
* @param request_len Length of the request to be sent to the SAM.
* @param response Pointer to the buffer where the retreived data will be stored.
* @param response_len Pointer to the variable where the length of the retreived data will be stored.
* @return Status code indicating success or failure of the operation.
*/
static int sam_send_request_iso14a(const uint8_t *const request, const uint8_t request_len, uint8_t *response, uint8_t *response_len) {
int res = PM3_SUCCESS;
if (g_dbglevel >= DBG_DEBUG)
DbpString("start sam_send_request_iso14a");
uint8_t buf1[ISO7816_MAX_FRAME] = {0};
uint8_t buf2[ISO7816_MAX_FRAME] = {0};
uint8_t *sam_tx_buf = buf1;
uint16_t sam_tx_len;
uint8_t *sam_rx_buf = buf2;
uint16_t sam_rx_len;
uint8_t *nfc_tx_buf = buf1;
uint16_t nfc_tx_len;
uint8_t *nfc_rx_buf = buf2;
uint16_t nfc_rx_len;
if (request_len > 0) {
sam_tx_len = request_len;
memcpy(sam_tx_buf, request, sam_tx_len);
} else {
// send get pacs
static const uint8_t payload[] = {
0xa0, 19, // <- SAM command
0xBE, 17, // <- samCommandGetContentElement2
0x80, 1,
0x04, // <- implicitFormatPhysicalAccessBits
0x84, 12,
0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x04 // <- SoRootOID
};
sam_tx_len = sizeof(payload);
memcpy(sam_tx_buf, payload, sam_tx_len);
}
sam_send_payload(
0x44, 0x0a, 0x44,
sam_tx_buf, &sam_tx_len,
sam_rx_buf, &sam_rx_len
);
if (sam_rx_buf[1] == 0x61) { // commands to be relayed to card starts with 0x61
// tag <-> SAM exchange starts here
while (sam_rx_buf[1] == 0x61) {
switch_clock_to_countsspclk();
nfc_tx_len = sam_seos_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf);
nfc_rx_len = iso14_apdu(
nfc_tx_buf,
nfc_tx_len,
false,
nfc_rx_buf,
ISO7816_MAX_FRAME,
NULL
);
switch_clock_to_ticks();
sam_tx_len = sam_seos_copy_payload_nfc2sam(sam_tx_buf, nfc_rx_buf, nfc_rx_len - 2);
sam_send_payload(
0x14, 0x0a, 0x14,
sam_tx_buf, &sam_tx_len,
sam_rx_buf, &sam_rx_len
);
// last SAM->TAG
// c1 61 c1 00 00 a1 02 >>82<< 00 90 00
if (sam_rx_buf[7] == 0x82) {
// tag <-> SAM exchange ends here
break;
}
}
static const uint8_t hfack[] = {
0xbd, 0x04, 0xa0, 0x02, 0x82, 0x00
};
sam_tx_len = sizeof(hfack);
memcpy(sam_tx_buf, hfack, sam_tx_len);
sam_send_payload(
0x14, 0x0a, 0x00,
sam_tx_buf, &sam_tx_len,
sam_rx_buf, &sam_rx_len
);
}
// resp for SamCommandGetContentElement:
// c1 64 00 00 00
// bd 09
// 8a 07
// 03 05 <- include tag for pm3 client
// 06 85 80 6d c0 <- decoded PACS data
// 90 00
// resp for samCommandGetContentElement2:
// c1 64 00 00 00
// bd 1e
// b3 1c
// a0 1a
// 80 05
// 06 85 80 6d c0
// 81 0e
// 2b 06 01 04 01 81 e4 38 01 01 02 04 3c ff
// 82 01
// 07
// 90 00
if (request_len == 0) {
if (
!(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0x8a && sam_rx_buf[5 + 4] == 0x03)
&&
!(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0xb3 && sam_rx_buf[5 + 4] == 0xa0)
) {
if (g_dbglevel >= DBG_ERROR)
Dbprintf("No PACS data in SAM response");
res = PM3_ESOFT;
}
}
*response_len = sam_rx_buf[5 + 1] + 2;
memcpy(response, sam_rx_buf + 5, *response_len);
goto out;
out:
return res;
}
/**
* @brief Retrieves PACS data from SEOS card using SAM.
*
* This function is called by appmain.c
* It sends a request to the SAM to get the PACS data from the SEOS card.
* The PACS data is then returned to the PM3 client.
*
* @return Status code indicating success or failure of the operation.
*/
int sam_seos_get_pacs(PacketCommandNG *c) {
bool disconnectAfter = c->oldarg[0] & 0x01;
bool skipDetect = c->oldarg[1] & 0x01;
uint8_t *cmd = c->data.asBytes;
uint16_t cmd_len = (uint16_t) c->oldarg[2];
int res = PM3_EFAILED;
clear_trace();
I2C_Reset_EnterMainProgram();
set_tracing(true);
StartTicks();
// step 1: ping SAM
sam_get_version();
if (!skipDetect) {
// step 2: get card information
iso14a_card_select_t card_a_info;
// implicit StartSspClk() happens here
iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD);
if (!iso14443a_select_card(NULL, &card_a_info, NULL, true, 0, false)) {
goto err;
}
switch_clock_to_ticks();
// step 3: SamCommand CardDetected
sam_set_card_detected(&card_a_info);
}
// step 3: SamCommand RequestPACS, relay NFC communication
uint8_t sam_response[ISO7816_MAX_FRAME] = { 0x00 };
uint8_t sam_response_len = 0;
res = sam_send_request_iso14a(cmd, cmd_len, sam_response, &sam_response_len);
if (res != PM3_SUCCESS) {
goto err;
}
if (g_dbglevel >= DBG_INFO)
print_result("Response data", sam_response, sam_response_len);
goto out;
goto off;
err:
res = PM3_ENOPACS;
reply_ng(CMD_HF_SAM_SEOS, res, NULL, 0);
goto off;
out:
reply_ng(CMD_HF_SAM_SEOS, PM3_SUCCESS, sam_response, sam_response_len);
goto off;
off:
if (disconnectAfter) {
switch_off();
}
set_tracing(false);
StopTicks();
BigBuf_free();
return res;
}

View file

@ -17,5 +17,8 @@
#define __SAM_SEOS_H
#include "common.h"
#include "pm3_cmd.h"
int sam_seos_get_pacs(PacketCommandNG *c);
#endif

View file

@ -28,6 +28,7 @@
#include "printf.h"
#include "string.h"
#include "flashmem.h"
#include "pmflash.h"
//#include <stddef.h>
//#include <unistd.h>
@ -236,7 +237,7 @@ typedef uint8_t u8_t;
// Instead of giving parameters in config struct, singleton build must
// give parameters in defines below.
#ifndef SPIFFS_CFG_PHYS_SZ
#define SPIFFS_CFG_PHYS_SZ(ignore) (1024 * 64 * (spi_flash_pages64k - 1))
#define SPIFFS_CFG_PHYS_SZ(ignore) ((1024 * 64 * spi_flash_pages64k) - (1024 * 4 * (FLASH_RESERVED_TRAILING_4K_SECTORS + 1)))
#endif
#ifndef SPIFFS_CFG_PHYS_ERASE_SZ
#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (4*1024)

View file

@ -270,6 +270,7 @@ set (TARGET_SOURCES
${PM3_ROOT}/client/src/crypto/asn1dump.c
${PM3_ROOT}/client/src/crypto/asn1utils.c
${PM3_ROOT}/client/src/crypto/libpcrypto.c
${PM3_ROOT}/client/src/crypto/originality.c
${PM3_ROOT}/client/src/emv/test/cda_test.c
${PM3_ROOT}/client/src/emv/test/crypto_test.c
${PM3_ROOT}/client/src/emv/test/cryptotest.c

View file

@ -693,6 +693,7 @@ SRCS = mifare/aiddesfire.c \
crypto/asn1dump.c \
crypto/asn1utils.c\
crypto/libpcrypto.c\
crypto/originality.c\
emv/cmdemv.c \
emv/crypto.c\
emv/crypto_polarssl.c\

View file

@ -442,9 +442,6 @@ uint64_t CRACK_STATES_BITSLICED(uint32_t cuid, uint8_t *best_first_bytes, statel
#if MAX_BITSLICES > 64
&& results.bytes64[1] == 0
#endif
#if MAX_BITSLICES > 64
&& results.bytes64[1] == 0
#endif
#if MAX_BITSLICES > 128
&& results.bytes64[2] == 0
&& results.bytes64[3] == 0

View file

@ -47,6 +47,10 @@ AABBCCDDEEFF
4D3A99C351DD
1A982C7E459A
#
# Gym
FAFAFAFAFAFA
FBFBFBFBFBFB
#
# key A Wien
D3F7D3F7D3F7
#
@ -2506,8 +2510,6 @@ AA034F342A55
456776908C48
# BusFacil - Brazilian public transport card for some cities
7b296f353c6b
3fa7217ec575
fae9b14365a9
c567dd4a6004
c567dd4a6005
@ -2598,10 +2600,6 @@ b1ea40b2caa6
3abf8431003b
# Sector 15 - see above
# SKGT personalised subscription card
# Sector 0, 2, 16, key A
a0a1a2a3a4a5
# Sector 8-14, 17-39, key A
ffffffffffff
# Sector 1, key A
# blue
f1df0ca8948b
@ -2755,3 +2753,300 @@ D37C8F1793F7
543071543071
5F01015F0101
200510241234
#
# Momentum-Firmware 20241201
AC935925A876
ADC169F922CB
AD00EFD353E4
AEF617B3D004
AEF617B3D040
AE381EA0811B
AE683AC2A232
AF2BFB44A4A5
A2CA48CA4C05
A2D5D7469472
A23412F92811
A64536FAC799
A7127F539A16
A8700E07A58F
A9B9C1D0E3F1
BC305FE2DA65
BE3C1BF60B37
B0A3212D47A5
B0D58BF147B7
B02094F92A71
B1EEAA640EF6
B19A0664ECA6
B4FAE0FAD22E
B456E1951216
B48D7E4E508F
B54D99618ADC
B6728D9B95BA
CA22AF33A19B
CBC83E1548B4
CB5ED0E57B08
CC5F59A0CE0A
CF0EC6ACF2F9
C1F6C7B55F5E
C290A397F84A
C34FAA1931CA
C49C9BF59547
C497C3BE8273
C7D1CB6774B0
C789E4568B99
C992F85B2DDD
DCD003CF0EA3
DD30A13519C3
DD6E74174648
DE5865F29C44
DF7C4EC20B50
D017A84BB582
D156F66D38EC
D3AEB15D410B
D324152F5BB0
D3849E31EE4F
D410EFF9113E
D51BCA1DFFFF
D6818C29ED9B
D7142E0F6D0D
D881B675D881
D9762D114AE5
EA19E58DD046
EA4987F8D096
EB9D9C1B03F6
EDA4BF3E7B04
EDC9CC9109A2
EEF144866688
E10F0E7A8DD5
E108EA397A9A
E24359F37FE4
E3007FA4F781
E5051FAB4371
E861FDE1B59F
E87E3554727E
E9B376925A00
E954024EE754
FA4D2B3BAFEA
FA8CA10C7D59
FE98F38F3EE2
F18D91EE3033
F3E3F9F977B9
F4756F7EEAE1
F654D6C7004F
F697E87E759D
F7A545095C49
F833E24C3F1C
0CD464CDC100
0D61BA88789C
0E726E11CFCC
0FC4B1D2EBBA
01E2C14F1B18
02DB253DC0C7
03A7AAAA28AD
0380293A9E6D
0402B44FB679
050BF33DC217
08A55BC96DC1
08D6A7765640
08ED3F92AA53
09F4EC8D7A66
1A8CFF2F1BC7
1C000EB0752F
1D14130D1A0B
1E13EFF32CE2
101209170A13
11DDA4862A1C
113355779933
1153AABAFF6C
12BA20088ED3
120A7837BB5D
1202165D4EAB
122F595302AA
124F004321D3
132F641C948B
14CD299DC0C7
141DF3B1C017
1415FFFED68D
15B35D0BF715
156EED7C5F9D
1581C317B073
164EE10EFFFF
16785FD65BA7
17C06D19E92F
17E0FA2308FD
1719EB5DAC66
19BA6776233F
190E6242CE7B
193DFE0FA18E
2B29232D3624
2CAD8A83DF28
2D2F182C4024
2EAD4DD0F7B0
2FA9B556A4F6
201106141030
202011F918A2
204C0D3DCD9A
20525276F443
215E9DED9DDF
25FE2B4E4FA9
251BDBF1C71D
251780F9FBE6
25467EB0212F
280FD37AD407
280713CBA260
286A8893AC6F
3D6F823FFFFF
3E0557273982
3F41891454EE
30C1DC9DD040
310308EC52EF
321803E38664
32774E46C64F
330075000850
3333F411AAAA
334E91BE3377
381F84DB8134
38540EEE8B1C
4A1094F378D8
4A6E1CAD6D3D
4C44200BC9C5
4D4946415245
40454EE64229
41CD3CD99DD5
4149206E9BAE
42F82DB5C4AF
4204784B0DC9
43204334546F
439FB891279F
44ACB624CA14
44B61F116125
4427385D72AB
444E4650475A
45450AC8DCA8
45524DACC5E9
45574D373B9D
475A444E4650
4752533E1965
48B390984150
48C8852D15F9
4844426F6E69
485242F22BE0
49EE8D52AAB6
49FEE42DDC18
49414556EF4D
5AD3FC074A4C
5A15888F3419
5A2050DA7E3F
5A4920FD6F87
5CD02DAD8ADE
5C475D2C70C6
5D819B4BFAF3
5E696FA0EAD1
5FA28B8E8BA4
5F8892561BED
505209266A1F
507A6181E4BF
538BF58687EB
544954CBB2C4
54546255CDE9
549BB4FD70C4
552049EFF3F4
55213B4F7328
555D8BBC2D3E
56A4B81B3FC3
5669C363A4A5
57E39104CC87
570FB865D650
5703815494EF
57059FFD3EE6
58DBC850A4D5
585462E190F2
5990EC1571D7
6AC79644E0CD
6A530C91F85B
6A86C1895A21
6C4953590463
6C79548B3FC3
6D2BF79566A8
60D53F070572
6036F9D72D68
61152534ACEF
62616E616E61
66A4932816D3
6611DFFAAE32
68C867397AD5
6862FD600F78
7ADD3D735725
7AEB989A5525
7BF0BE85080F
7B6C00CBAC92
7CB033257498
7C20975C6EC9
7C3AF198425F
7EDAE7923287
7F796F60FFFF
701AA491A4A5
72A0C485D3F7
7213B13D02E0
722538817225
7246FCE86427
735DD20237A9
746A70C4EF6F
78DF1176C8FD
79E8B59A51E0
8AC04C1A4E15
8A0DFD9B7AEA
8A35039F6CD6
8C524B535E1D
8DF64AB19A16
8D2B780A148D
8D96A0BA7234
8EF0AA6432FA
80003D23C6F5
8000806B5072
81C0BBCE32E9
81D6CC146E50
8380ACDC017E
84A3FD4BA0C6
840C16869171
8430A669558C
9AE05868233F
9B2C3E00B561
9EE3896C4530
9F14D35BAC08
9001D0E23F8C
907E5C641D94
9089B668FFFF
91B1B62402D5
93FB38FE585A
96AECCC0F7EB
96227EDADBCF
#
# BW Kantine
56cf3acd90ca
542089792be2
5420aeada758
#
# CSC Laundry
212223242555
717273747555
#
# Hotel cards, BETECH brand, Vietnam
AAC34D9A4E65
#
# Dutch Statistics Agency (CBS)
DC7B15AA0938
#
# keys from https://pastebin.com/tpKwph0h
AAAAAABBBBCC
74ABCB1405DE
A25CDE2F781A
6054AC9541C8
828DDEEE4D98
ED2B22929167
C552C1B92395
F4A4AA2F63A4
25ECB7B2BAB1
8B02EF84CDF1
23EAFB5DA46D
AB921CF0752C

View file

@ -22,6 +22,8 @@ F9DCEBA0
89A69E60
# ref lock
314159E0
#Zonsin ZX-COPY10
7B3D5C48
# ref. http://www.proxmark.org/forum/viewtopic.php?pid=28115#p28115
AA55BBBB
# ref. http://www.proxmark.org/forum/viewtopic.php?pid=33376#p33376

View file

@ -271,6 +271,7 @@ set (TARGET_SOURCES
${PM3_ROOT}/client/src/crypto/asn1dump.c
${PM3_ROOT}/client/src/crypto/asn1utils.c
${PM3_ROOT}/client/src/crypto/libpcrypto.c
${PM3_ROOT}/client/src/crypto/originality.c
${PM3_ROOT}/client/src/emv/test/cda_test.c
${PM3_ROOT}/client/src/emv/test/crypto_test.c
${PM3_ROOT}/client/src/emv/test/cryptotest.c

View file

@ -50,20 +50,20 @@ arguments = [[
-c read magic configuration
-u UID (8-20 hexsymbols), set UID on tag
-t tag type to impersonate
1 = Mifare Mini S20 4-byte
2 = Mifare Mini S20 7-byte 15 = NTAG 210
3 = Mifare Mini S20 10-byte 16 = NTAG 212
4 = Mifare 1k S50 4-byte 17 = NTAG 213
5 = Mifare 1k S50 7-byte 18 = NTAG 215
6 = Mifare 1k S50 10-byte 19 = NTAG 216
7 = Mifare 4k S70 4-byte 20 = NTAG I2C 1K
8 = Mifare 4k S70 7-byte 21 = NTAG I2C 2K
9 = Mifare 4k S70 10-byte 22 = NTAG I2C 1K PLUS
*** 10 = UL - NOT WORKING FULLY 23 = NTAG I2C 2K PLUS
*** 11 = UL-C - NOT WORKING FULLY 24 = NTAG 213F
12 = UL EV1 48b 25 = NTAG 216F
13 = UL EV1 128b
*** 14 = UL Plus - NOT WORKING YET
1 = Mifare Mini S20 4-byte | 15 = NTAG 210
2 = Mifare Mini S20 7-byte | 16 = NTAG 212
3 = Mifare Mini S20 10-byte | 17 = NTAG 213
4 = Mifare 1k S50 4-byte | 18 = NTAG 215
5 = Mifare 1k S50 7-byte | 19 = NTAG 216
6 = Mifare 1k S50 10-byte | 20 = NTAG I2C 1K
7 = Mifare 4k S70 4-byte | 21 = NTAG I2C 2K
8 = Mifare 4k S70 7-byte | 22 = NTAG I2C 1K PLUS
9 = Mifare 4k S70 10-byte | 23 = NTAG I2C 2K PLUS
*** 10 = UL - NOT WORKING FULLY | 24 = NTAG 213F
*** 11 = UL-C - NOT WORKING FULLY | 25 = NTAG 216F
12 = UL EV1 48b |
13 = UL EV1 128b |
*** 14 = UL Plus - NOT WORKING YET |
-p NTAG password (8 hexsymbols), set NTAG password on tag.
-a NTAG pack ( 4 hexsymbols), set NTAG pack on tag.
@ -75,7 +75,11 @@ arguments = [[
-z ATS (<1b length><0-16 ATS> hexsymbols), Configure ATS. Length set to 00 will disable ATS.
-w Wipe tag. 0 for Mifare or 1 for UL. Fills tag with zeros and put default values for type selected.
-m Ultralight mode (00 UL EV1, 01 NTAG, 02 UL-C, 03 UL) Set type of UL.
-n Ultralight protocol (00 MFC, 01 UL), switches between UL and MFC mode
-n Ultralight protocol (00 MFC, 01 UL), switches between UL and MFC mode]]
-- Need to split because reached maximum string length processed by lua
arguments2 = [[
-b Set maximum read/write blocks (2 hexsymbols)
NOTE: Ultralight EV1 and NTAG Version info and Signature are stored respectively in blocks 250-251 and 242-249
-k Ultimate Magic Card Key (IF DIFFERENT THAN DEFAULT 00000000)
]]
---
@ -110,6 +114,7 @@ local function help()
print(usage)
print(ansicolors.cyan..'Arguments'..ansicolors.reset)
print(arguments)
print(arguments2)
print(ansicolors.cyan..'Example usage'..ansicolors.reset)
print(example)
end
@ -186,6 +191,7 @@ local function read_config()
end
-- extract data from CONFIG - based on CONFIG in https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/magic_cards_notes.md#gen-4-gtu
ulprotocol, uidlength, readpass, gtumode, ats, atqa1, atqa2, sak, ulmode = magicconfig:sub(1,2), magicconfig:sub(3,4), magicconfig:sub(5,12), magicconfig:sub(13,14), magicconfig:sub(15,48), magicconfig:sub(51,52), magicconfig:sub(49,50), magicconfig:sub(53,54), magicconfig:sub(55,56)
maxRWblk = magicconfig:sub(57, 58)
atqaf = atqa1..' '..atqa2
cardtype, cardprotocol, gtustr, atsstr = 'unknown', 'unknown', 'unknown', 'unknown'
if magicconfig == nil then lib14a.disconnect(); return nil, "can't read configuration, "..err_lock end
@ -291,6 +297,7 @@ local function read_config()
print(' - Version ', cversion)
print(' - Signature ', signature1..signature2)
end
print(' - Max R/W Block ', maxRWblk)
end
lib14a.disconnect()
return true, 'Ok'
@ -637,6 +644,26 @@ local function write_ulm(ulm)
return true, 'Ok'
end
---
-- Write maximum read/write block number,
local function write_maxRWblk(data)
-- input number check
if data == nil then return nil, 'empty block number' end
if #data == 0 then return nil, 'empty block number' end
if #data ~= 2 then return nil, 'block number wrong length. Should be 1 hex byte' end
print('Set max R/W block', data)
local info = connect()
if not info then return false, "Can't select card" end
local resp
-- set maximum read/write block
resp = send("CF".._key.."6B"..data)
lib14a.disconnect()
if resp ~= '9000FD07' then return nil, 'Failed to write maximum read/write block'
else
return true, 'Ok'
end
end
---
-- Set type for magic card presets.
local function set_type(tagtype)
-- tagtype checks
@ -649,6 +676,7 @@ local function set_type(tagtype)
send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000900")
lib14a.disconnect()
write_uid('04112233')
write_maxRWblk('13')
-- Setting Mifare mini S20 7-byte
elseif tagtype == 2 then
print('Setting: Ultimate Magic card to Mifare mini S20 7-byte')
@ -656,6 +684,7 @@ local function set_type(tagtype)
send("CF".._key.."F000010000000002000978009102DABC19101011121314151644000900")
lib14a.disconnect()
write_uid('04112233445566')
write_maxRWblk('13')
-- Setting Mifare mini S20 10-byte
elseif tagtype == 3 then
print('Setting: Ultimate Magic card to Mifare mini S20 10-byte')
@ -663,6 +692,7 @@ local function set_type(tagtype)
send("CF".._key.."F000020000000002000978009102DABC19101011121314151684000900")
lib14a.disconnect()
write_uid('04112233445566778899')
write_maxRWblk('13')
-- Setting Mifare 1k S50 4--byte
elseif tagtype == 4 then
print('Setting: Ultimate Magic card to Mifare 1k S50 4-byte')
@ -670,6 +700,7 @@ local function set_type(tagtype)
send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000800")
lib14a.disconnect()
write_uid('04112233')
write_maxRWblk('3F')
-- Setting Mifare 1k S50 7-byte
elseif tagtype == 5 then
print('Setting: Ultimate Magic card to Mifare 1k S50 7-byte')
@ -677,6 +708,7 @@ local function set_type(tagtype)
send("CF".._key.."F000010000000002000978009102DABC19101011121314151644000800")
lib14a.disconnect()
write_uid('04112233445566')
write_maxRWblk('3F')
-- Setting Mifare 1k S50 10-byte
elseif tagtype == 6 then
print('Setting: Ultimate Magic card to Mifare 1k S50 10-byte')
@ -684,6 +716,7 @@ local function set_type(tagtype)
send("CF".._key.."F000020000000002000978009102DABC19101011121314151684000800")
lib14a.disconnect()
write_uid('04112233445566778899')
write_maxRWblk('3F')
-- Setting Mifare 4k S70 4-byte
elseif tagtype == 7 then
print('Setting: Ultimate Magic card to Mifare 4k S70 4-byte')
@ -691,6 +724,7 @@ local function set_type(tagtype)
send("CF".._key.."F000000000000002000978009102DABC19101011121314151602001800")
lib14a.disconnect()
write_uid('04112233')
write_maxRWblk('FF')
-- Setting Mifare 4k S70 7-byte
elseif tagtype == 8 then
print('Setting: Ultimate Magic card to Mifare 4k S70 7-byte')
@ -698,6 +732,7 @@ local function set_type(tagtype)
send("CF".._key.."F000010000000002000978009102DABC19101011121314151642001800")
lib14a.disconnect()
write_uid('04112233445566')
write_maxRWblk('FF')
-- Setting Mifare 4k S70 10-byte
elseif tagtype == 9 then
print('Setting: Ultimate Magic card to Mifare 4k S70 10-byte')
@ -705,6 +740,7 @@ local function set_type(tagtype)
send("CF".._key.."F000020000000002000978009102DABC19101011121314151682001800")
lib14a.disconnect()
write_uid('04112233445566778899')
write_maxRWblk('FF')
-- Setting UL
elseif tagtype == 10 then
print('Setting: Ultimate Magic card to UL')
@ -1016,7 +1052,7 @@ function main(args)
local err, msg
if #args == 0 then return help() end
-- Read the parameters
for o, a in getopt.getopt(args, 'hck:u:t:p:a:s:o:v:q:g:z:n:m:w:') do
for o, a in getopt.getopt(args, 'hck:u:t:p:a:s:o:v:q:g:z:n:m:w:b:') do
-- help
if o == "h" then return help() end
-- set Ultimate Magic Card Key for read write
@ -1049,6 +1085,8 @@ function main(args)
if o == "m" then err, msg = write_ulm(a) end
-- write UL protocol
if o == "n" then err, msg = write_ulp(a) end
-- write max r/w block
if o == "b" then err, msg = write_maxRWblk(a) end
if err == nil then return oops(msg) end
end
end

View file

@ -14,7 +14,7 @@ import json
from fm11rf08s_recovery import recovery
author = "@csBlueChip"
script_ver = "1.2.0"
script_ver = "1.4.0"
# Copyright @csBlueChip
@ -33,7 +33,7 @@ script_ver = "1.2.0"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# The original version of this script can be found at:
# https://github.com/csBlueChip/Proxmark_Stuff/tree/main/MiFare_Docs/Fudan_RF08(S)/PM3_Script
# https://github.com/csBlueChip/Proxmark_Stuff/tree/main/MiFare_Docs/Fudan_RF08S/PM3_Script
# The original version is released with an MIT Licence.
# Or please reach out to me [BlueChip] personally for alternative licenses.
@ -117,7 +117,7 @@ globals:
args = parseCli()
# No logfile name yet
lprint("Fudan FM11RF08[S] full card recovery")
lprint("Fudan FM11RF08S full card recovery")
lprint("\nDump folder... " + color(f"{dpath}", fg="yellow"))
# FIXME: script is announced as for RF08 and for RF08S but it comprises RF32N key
@ -142,7 +142,10 @@ globals:
lprint(f" Keys not loaded, use {s} to run recovery script [slow]", prompt="[" + color("!", fg="red") + "]")
else:
# FIXME: recovery() is only for RF08S. TODO for the other ones with a "darknested" attack
keyfile = recoverKeys()
keyfile = recoverKeys(uid=uid, kdf=[["Bambu v1", kdfBambu1]])
if keyfile == False:
lprint("Script failed - aborting")
return
key = loadKeys(keyfile)
if key is not None:
@ -201,7 +204,7 @@ def checkVer():
def parseCli():
"""Parse the CLi arguments"""
parser = argparse.ArgumentParser(description='Full recovery of Fudan FM11RF08* cards.')
parser = argparse.ArgumentParser(description='Full recovery of Fudan FM11RF08S cards.')
parser.add_argument('-n', '--nokeys', action='store_true', help='extract data even if keys are missing')
parser.add_argument('-r', '--recover', action='store_true', help='run key recovery script if required')
@ -265,7 +268,7 @@ def getUIDfromBlock0(blk0):
def decodeBlock0(blk0):
"""Extract data from block 0"""
lprint()
lprint(" UID BCC ++----- RF08 ID -----++")
lprint(" UID BCC ++---- RF08* ID -----++")
lprint(" ! ! SAK !! !!")
lprint(" ! ! ! ATQA !! Fudan Sig !!")
lprint(" !---------. !. !. !---. VV .---------------. VV")
@ -291,10 +294,13 @@ def decodeBlock0(blk0):
hash = blk0[27:44] # Fudan hash "99 AA BB CC DD EE"
is08S = False
type = f"[{fida:02X}:{fidb:02X}]" # type/name
if fidb == 0x90:
if fida == 0x01 or fida == 0x03 or fida == 0x04:
type += " - Fudan FM11RF08S"
is08S = True
elif fidb == 0x1D:
if fida == 0x01 or fida == 0x02 or fida == 0x03:
@ -333,6 +339,11 @@ def decodeBlock0(blk0):
lprint(f" Fudan ID : {type}") # show type
lprint(f" Fudan Sig: {hash}") # show ?Partial HMAC?
if not is08S:
lprint("\n This script is only for the RF08S cards")
lprint(" Other cards can be cracked with `hf mf autopwn`")
sys.exit(13)
def fudanValidate(blk0, live=False):
"""Fudan validation"""
@ -396,22 +407,34 @@ If keys cannot be loaded AND --recover is specified, then run key recovery
return key
def recoverKeys():
def recoverKeys(uid, kdf=[[]]):
"""Run key recovery script"""
badrk = 0 # 'bad recovered key' count (ie. not recovered)
keys = False
lprint(f"\nTrying KDFs:");
for fn in kdf:
lprint(f" {fn[0]:s}", end='')
keys = fn[1](uid)
if keys != False:
lprint(" .. Success", prompt='')
break
lprint(" .. Fail", prompt='')
lprint("\nRunning recovery script, ETA: Less than 30 minutes")
lprint('\n`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,')
r = recovery(quiet=False, keyset=keys)
lprint('`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,')
if r == False:
return False
r = recovery(quiet=False)
keyfile = r['keyfile']
rkey = r['found_keys']
# fdump = r['dumpfile']
# rdata = r['data']
lprint('`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,')
for k in range(0, 16+1):
for ab in [0, 1]:
if rkey[k][ab] == "":
@ -427,9 +450,57 @@ def recoverKeys():
lprint(f"[{kn}/", end='', prompt='')
lprint("A]" if ab == 0 else "B]", end='', prompt='')
if badrk > 0:
lprint()
lprint("", prompt='')
return keyfile
def kdfBambu1(uid):
from Cryptodome.Protocol.KDF import HKDF
from Cryptodome.Hash import SHA256
# Generate all keys
try:
# extracted from Bambu firmware
salt = bytes([0x9a,0x75,0x9c,0xf2,0xc4,0xf7,0xca,0xff,0x22,0x2c,0xb9,0x76,0x9b,0x41,0xbc,0x96])
keyA = HKDF(uid, 6, salt, SHA256, 16, context=b"RFID-A\0")
keyB = HKDF(uid, 6, salt, SHA256, 16, context=b"RFID-B\0")
except Exception as e:
print(f"{e}")
return False
# --- Grab block 13 (in sector 3) ---
cmd = f"hf mf rdbl -c 0 --key {keyA[3].hex()} --blk 12"
#lprint(f" `{cmd}`", flush=True, log=False, end='')
for retry in range(5):
p.console(cmd)
found = False
for line in p.grabbed_output.split('\n'):
if " | " in line and "# | s" not in line:
lsub = line[4:76]
found = True
if found:
break
if not found:
return False
# --- Try to decode it as a bambu date string ---
try:
dl = bytes.fromhex(lsub[6:53]).decode('ascii').rstrip('\x00')
except Exception:
return False
# dl 2024_03_22_16_29
# yy y y m m d d h h m m
exp = r"20[2-3][0-9]_[0-1][0-9]_[0-3][0-9]_[0-2][0-9]_[0-5][0-9]"
if not re.search(exp, dl):
return False
# --- valid date string, we are confident this is a bambu card ---
keys = []
for i in range(0, 15+1):
keys.append([keyA[i].hex(), keyB[i].hex()])
return keys
def verifyKeys(key):
"""Verify keys

View file

@ -76,7 +76,7 @@ for tool, bin in tools.items():
exit()
def recovery(init_check=False, final_check=False, keep=False, debug=False, supply_chain=False, quiet=True):
def recovery(init_check=False, final_check=False, keep=False, debug=False, supply_chain=False, quiet=True, keyset=False):
def show(s='', prompt="[" + color("=", fg="yellow") + "] ", **kwargs):
if not quiet:
s = f"{prompt}" + f"\n{prompt}".join(s.split('\n'))
@ -94,7 +94,7 @@ def recovery(init_check=False, final_check=False, keep=False, debug=False, suppl
if uid is None:
show("Card not found")
return
return False
show("UID: " + color(f"{uid:08X}", fg="green"))
def show_key(sec, key_type, key):
@ -106,6 +106,14 @@ def recovery(init_check=False, final_check=False, keep=False, debug=False, suppl
save_path = prefs['file.default.dumppath'] + os.path.sep
found_keys = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)]
if keyset != False:
n = min(len(found_keys),len(keyset))
show(f"{n} Key pairs supplied: ")
for i in range(0, n):
found_keys[i] = keyset[i]
show(f" Sector {i:2d} : A = {found_keys[i][0]:12s} B = {found_keys[i][1]:12s}")
if init_check:
show("Checking default keys...")
p.console("hf mf fchk")
@ -135,7 +143,7 @@ def recovery(init_check=False, final_check=False, keep=False, debug=False, suppl
if (nonces_with_data == ""):
show("Error getting nonces, abort.")
return
return False
try:
with open(nonces_with_data, 'r') as file:
@ -143,7 +151,7 @@ def recovery(init_check=False, final_check=False, keep=False, debug=False, suppl
dict_nwd = json.load(file)
except json.decoder.JSONDecodeError:
show(f"Error parsing {nonces_with_data}, abort.")
return
return False
nt = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)]
nt_enc = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)]

View file

@ -12,18 +12,43 @@ except ModuleNotFoundError:
return str(s)
spi = {
0x68:{
"manufacturer": "Boya",
"jedec" : {
0x40: {
0x15: {
"part": "BY25Q16BS",
"size": "16mbits",
"sizeB": "2MB",
},
},
},
},
0x85:{
"manufacturer": "Puya",
"jedec" : {
0x60: {
0x15: {
"part": "P25Q16H",
"size": "16mbits",
"sizeB": "2MB",
},
0x16: {
"part": "P25Q32H",
"size": "32mbits",
"sizeB": "4MB",
},
0x17: {
"part": "P25Q64H",
"size": "64mbits",
"sizeB": "8MB",
},
},
},
},
0xEF:{
"manufacturer": "Winbond",
"jedec" : {
0x30: {
0x11: {
"part": "W25X10BV",
@ -42,6 +67,11 @@ spi = {
},
},
0x40: {
0x12: {
"part": "W25Q20BV",
"size": "2mbits",
"sizeB": "256KB",
},
0x13: {
"part": "W25Q40BV",
"size": "4mbits",
@ -62,8 +92,33 @@ spi = {
"size": "32mbits",
"sizeB": "4MB",
},
0x17: {
"part": "W25Q64BV",
"size": "64mbits",
"sizeB": "8MB",
},
},
0x70: {
0x14: {
"part": "W25Q80JV",
"size": "8mbits",
"sizeB": "1MB",
},
0x15: {
"part": "W25Q16JV",
"size": "16mbits",
"sizeB": "2MB",
},
0x16: {
"part": "W25Q32JV",
"size": "32mbits",
"sizeB": "4MB",
},
0x17: {
"part": "W25Q64JV",
"size": "64mbits",
"sizeB": "8MB",
},
0x22: {
"part": "W25Q02JV-IM",
"size": "2mbits",
@ -71,6 +126,7 @@ spi = {
},
},
},
},
}
p = pm3.pm3()
@ -90,16 +146,16 @@ for line in p.grabbed_output.split('\n'):
did_h = did >> 8
did_l = did & 0xff
t = None
print(f"\n JEDEC ID....... 0x{mid:X} / 0x{did:X}")
if mid in spi:
mfr = spi[mid]['manufacturer']
if did_h in spi[mid]:
if did_h in spi[mid]['jedec']:
if did_l in spi[mid][did_h]:
if did_l in spi[mid]['jedec'][did_h]:
t = spi[mid][did_h][did_l]
t = spi[mid]['jedec'][did_h][did_l]
print("\n Manufacturer... " + color(f"{mfr}", fg="green") +
"\n Device......... " + color(f"{t['part']}", fg="green") +
"\n Size........... " + color(f"{t['size']} ({t['sizeB']})", fg="yellow")

View file

@ -1,81 +1,177 @@
#!/usr/bin/python3
### Parameters
import os
import subprocess
import signal
import numpy as np
from pyaudio import PyAudio, paFloat32, paContinue
# Sound output parameters
volume = 1.0
sample_buf_size = 44
sampling_freq = 44100 # Hz
# Frequency generator parameters
min_freq = 200 #Hz
max_freq = 2000 #Hz
min_freq = 100 # Hz
max_freq = 6000 # Hz
# Proxmark3 parameters
pm3_client="/usr/local/bin/proxmark3"
pm3_reader_dev_file="/dev/ttyACM0"
pm3_tune_cmd="hf tune"
pm3_client = "pm3"
pm3_tune_cmd = "hf tune --value"
frequency = 440
buffer = []
### Modules
import numpy
import pyaudio
from select import select
from subprocess import Popen, DEVNULL, PIPE
def find_zero_crossing_index(array):
for i in range(1, len(array)):
if array[i-1] < 0 and array[i] >= 0:
return i
return None # Return None if no zero-crossing is found
### Main program
p = pyaudio.PyAudio()
def generate_sine_wave(frequency, sample_rate, duration, frame_count):
"""Generate a sine wave at a given frequency."""
t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
wave = np.sin(2 * np.pi * frequency * t)
return wave[:frame_count]
# PyAudio Callback function
def pyaudio_callback(in_data, frame_count, time_info, status):
# if in_data is None:
# return (in_data, pyaudio.paContinue)
global frequency
global buffer
wave = generate_sine_wave(frequency, sampling_freq, 0.01, frame_count*2)
i = find_zero_crossing_index(buffer)
if i is None:
buffer = wave
else:
buffer = np.concatenate((buffer[:i], wave))
data = (buffer[:frame_count] * volume).astype(np.float32).tobytes()
buffer = buffer[frame_count:]
return (data, paContinue)
# pyaudio.paComplete
def silent_pyaudio():
"""
Lifted and adapted from https://stackoverflow.com/questions/67765911/
PyAudio is noisy af every time you initialise it, which makes reading the
log output rather difficult. The output appears to be being made by the
C internals, so we can't even redirect the logs with Python's logging
facility. Therefore the nuclear option was selected: swallow all stderr
and stdout for the duration of PyAudio's use.
"""
# Open a pair of null files
null_fds = [os.open(os.devnull, os.O_RDWR) for x in range(2)]
# Save the actual stdout (1) and stderr (2) file descriptors.
save_fds = [os.dup(1), os.dup(2)]
# Assign the null pointers to stdout and stderr.
os.dup2(null_fds[0], 1)
os.dup2(null_fds[1], 2)
pyaudio = PyAudio()
os.dup2(save_fds[0], 1)
os.dup2(save_fds[1], 2)
# Close all file descriptors
for fd in null_fds + save_fds:
os.close(fd)
return pyaudio
def run_pm3_cmd(callback):
# Start the process
process = subprocess.Popen(
[pm3_client, '-c', pm3_tune_cmd],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
bufsize=1, # Line buffered
shell=False
)
# Read the output line by line as it comes
try:
with process.stdout as pipe:
for line in pipe:
# Process each line
l = line.strip() # Strip to remove any extraneous newline characters
callback(l)
except Exception as e:
print(f"An error occurred: {e}")
finally:
# Ensure the subprocess is properly terminated
process.terminate()
process.wait()
def linear_to_exponential_freq(v, min_v, max_v, min_freq, max_freq):
# First, map v to a range between 0 and 1
if max_v != min_v:
normalized_v = (v - min_v) / (max_v - min_v)
else:
normalized_v = 0.5
normalized_v = 1 - normalized_v
# Calculate the ratio of the max frequency to the min frequency
freq_ratio = max_freq / min_freq
# Calculate the exponential frequency using the mapped v
freq = min_freq * (freq_ratio ** normalized_v)
return freq
class foo():
def __init__(self):
self.p = silent_pyaudio()
# For paFloat32 sample values must be in range [-1.0, 1.0]
stream = p.open(format=pyaudio.paFloat32,
self.stream = self.p.open(format=paFloat32,
channels=1,
rate=sampling_freq,
output=True)
output=True,
stream_callback=pyaudio_callback)
# Initial voltage to frequency values
min_v = 100.0
max_v = 0.0
v = 0
out_freq = min_freq
self.min_v = 50000.0
self.max_v = 0.0
# Spawn the Proxmark3 client
pm3_proc = Popen([pm3_client, pm3_reader_dev_file, "-c", pm3_tune_cmd], bufsize=0, env={}, stdin=DEVNULL, stdout=PIPE, stderr=DEVNULL)
mv_recbuf = ""
# Setting the signal handler for SIGINT (Ctrl+C)
signal.signal(signal.SIGINT, self.signal_handler)
# Read voltages from the Proxmark3, generate the sine wave, output to soundcard
sample_buf = [0.0 for x in range(0, sample_buf_size)]
i = 0
sinev = 0
while True:
# Start the stream
self.stream.start_stream()
# Read Proxmark3 client's stdout and extract voltage values
if(select([pm3_proc.stdout], [], [], 0)[0]):
def __exit__(self):
self.stream.stop_stream()
self.stream.close()
self.p.terminate()
b = pm3_proc.stdout.read(256).decode("ascii")
if "Done" in b:
break;
for c in b:
if c in "0123456789 mV":
mv_recbuf += c
else:
mv_recbuf = ""
if mv_recbuf[-3:] == " mV":
v = int(mv_recbuf[:-3]) / 1000
if v < min_v:
min_v = v - 0.001
if v > max_v:
max_v = v
def signal_handler(self, sig, frame):
print("\nYou pressed Ctrl+C! Press Enter")
self.__exit__()
def callback(self, line):
if 'mV' not in line:
return
v = int(line.split(' ')[1])
if v == 0:
return
self.min_v = min(self.min_v, v)
self.max_v = max(self.max_v, v)
# Recalculate the audio frequency to generate
out_freq = (max_freq - min_freq) * (max_v - v) / (max_v - min_v) \
+ min_freq
global frequency
frequency = linear_to_exponential_freq(v, self.min_v, self.max_v, min_freq, max_freq)
# Generate the samples and write them to the soundcard
sinevs = out_freq / sampling_freq * numpy.pi * 2
sample_buf[i] = sinev
sinev += sinevs
sinev = sinev if sinev < numpy.pi * 2 else sinev - numpy.pi * 2
i = (i + 1) % sample_buf_size
if not i:
stream.write((numpy.sin(sample_buf) * volume).
astype(numpy.float32).tobytes())
# frequency = max_freq - ((max_freq - min_freq) * (v - self.min_v) / (self.max_v - self.min_v) + min_freq)
#frequency = (frequency + new_frequency)/2
def main():
f = foo()
run_pm3_cmd(f.callback)
if __name__ == "__main__":
main()

View file

@ -859,8 +859,8 @@
"AID": "014D44",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro App 1",
"Name": "Delhi Metro Travel Card (DEL) (Alternative Endian)",
"Description": "DEL Delhi Metro (App 1)",
"Type": "transport"
},
{
@ -875,24 +875,48 @@
"AID": "024D44",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro App 2",
"Name": "Delhi Metro Travel Card (DEL) (Alternative Endian)",
"Description": "DEL Delhi Metro (App 2)",
"Type": "transport"
},
{
"AID": "025342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
"AID": "034D44",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro App 3",
"Name": "Delhi Metro Travel Card (DEL) (Alternative Endian)",
"Description": "DEL Delhi Metro (App 3)",
"Type": "transport"
},
{
"AID": "035342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
"AID": "044D44",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro App 4",
"Name": "Delhi Metro Travel Card (DEL) (Alternative Endian)",
"Description": "DEL Delhi Metro (App 4)",
"Type": "transport"
},
{
"AID": "045342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
@ -907,8 +931,16 @@
"AID": "054D44",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro App 5",
"Name": "Delhi Metro Travel Card (DEL) (Alternative Endian)",
"Description": "DEL Delhi Metro (App 5)",
"Type": "transport"
},
{
"AID": "055342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
@ -923,16 +955,40 @@
"AID": "064D44",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro App 6",
"Name": "Delhi Metro Travel Card (DEL) (Alternative Endian)",
"Description": "DEL Delhi Metro (App 6)",
"Type": "transport"
},
{
"AID": "065342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
"AID": "074D44",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro App 7",
"Name": "Delhi Metro Travel Card (DEL) (Alternative Endian)",
"Description": "DEL Delhi Metro (App 7)",
"Type": "transport"
},
{
"AID": "075342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
"AID": "085342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
@ -943,6 +999,78 @@
"Description": "Umo Mobility Card",
"Type": "transport"
},
{
"AID": "095342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
"AID": "0A5342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
"AID": "0B5342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
"AID": "0C5342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
"AID": "0D5342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
"AID": "0E5342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
"AID": "0F5342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
"AID": "105342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
"AID": "115342",
"Vendor": "Bangkok Expressway and Metro Public Limited Company (BEM)",
"Country": "TH",
"Name": "MRT Stored Value Card (BKK)",
"Description": "Might also be used by BKK MRT Plus and/or BKK Park & Ride Plus Cards",
"Type": "transport"
},
{
"AID": "2211AF",
"Vendor": "National Transport Authority",
@ -951,6 +1079,70 @@
"Description": "DUB Leap Card // Transport for Ireland // FIDs: 01,1F: Backup Data; 02-0A: Standard Data",
"Type": "transport"
},
{
"AID": "444D01",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro (App 1)",
"Type": "transport"
},
{
"AID": "444D02",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro (App 2)",
"Type": "transport"
},
{
"AID": "444D03",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro (App 3)",
"Type": "transport"
},
{
"AID": "444D04",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro (App 4)",
"Type": "transport"
},
{
"AID": "444D05",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro (App 5)",
"Type": "transport"
},
{
"AID": "444D05",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro (App 5)",
"Type": "transport"
},
{
"AID": "444D06",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro (App 6)",
"Type": "transport"
},
{
"AID": "444D07",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro (App 7)",
"Type": "transport"
},
{
"AID": "484000",
"Vendor": "Sistema de Tren Elétrico Urbano (SITEUR)",
@ -1063,12 +1255,28 @@
"Description": "SOF Sofia City Card",
"Type": "transport"
},
{
"AID": "CC00CC",
"Vendor": "Central Ohio Transit Authority (COTA) via Masabi Ltd",
"Country": "US",
"Name": "COTA Smartcard (CMH)",
"Description": "CMH COTA Smartcard; Masabi Justride Tap and Ride DESFire Smartcard",
"Type": "transport"
},
{
"AID": "D000D0",
"Vendor": "Greater Dayton Regional Transit Authority (RTA) via Masabi Ltd",
"Country": "US",
"Name": "RTA Tapp Pay Card (DAY)",
"Description": "DAY RTA Tapp Pay Card; Masabi Justride Tap and Ride DESFire Smartcard",
"Type": "transport"
},
{
"AID": "DD00DD",
"Vendor": "Regional Transporation District (RTD) via masabi justride",
"Vendor": "Regional Transporation District (RTD) via Masabi Ltd",
"Country": "US",
"Name": "MyRide Card (DEN)",
"Description": "DEN MyRide Card",
"Description": "DEN MyRide Card; Masabi Justride Tap and Ride DESFire Smartcard",
"Type": "transport"
},
{
@ -1151,6 +1359,30 @@
"Description": "FIDs 02: Card Balance; 04: Refill History; 08: Card Information; 0E: Trip History",
"Type": "transport"
},
{
"AID": "F21201",
"Vendor": "Green Bay Metro Transit via Genfare",
"Country": "US",
"Name": "Tap-N-Go Card (GRB)",
"Description": "GRB Tap-N-Go Card",
"Type": "transport"
},
{
"AID": "F21202",
"Vendor": "Green Bay Metro Transit via Genfare",
"Country": "US",
"Name": "Tap-N-Go Card (GRB)",
"Description": "GRB Tap-N-Go Card",
"Type": "transport"
},
{
"AID": "F212A0",
"Vendor": "CTtransit",
"Country": "US",
"Name": "Go CT Card (BDL)",
"Description": "BDL Go CT Card",
"Type": "transport"
},
{
"AID": "F21360",
"Vendor": "INIT",

View file

@ -17,6 +17,7 @@
//-----------------------------------------------------------------------------
#include "cmdflashmem.h"
#include <ctype.h>
#include <string.h>
#include "cmdparser.h" // command_t
#include "cliparser.h"
#include "pmflash.h" // rdv40validation_t
@ -192,7 +193,7 @@ static int CmdFlashMemLoad(const char *Cmd) {
CLIParserInit(&ctx, "mem load",
"Loads binary file into flash memory on device\n"
"Warning: mem area to be written must have been wiped first\n"
"( this is already taken care when loading dictionaries )",
"( dictionaries are serviced as files in spiffs so no wipe is needed )",
"mem load -f myfile -> upload file myfile values at default offset 0\n"
"mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n"
"mem load -f mfc_default_keys -m -> upload MFC keys\n"
@ -217,6 +218,7 @@ static int CmdFlashMemLoad(const char *Cmd) {
bool is_t55xx = arg_get_lit(ctx, 4);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
char spiffsDest[32] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParserFree(ctx);
@ -246,57 +248,46 @@ static int CmdFlashMemLoad(const char *Cmd) {
switch (d) {
case DICTIONARY_MIFARE:
offset = DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages);
keylen = 6;
res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount);
keylen = MF_KEY_LENGTH;
res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount);
if (res || !keycount) {
free(data);
return PM3_EFILE;
}
// limited space on flash mem
if (keycount > DEFAULT_MF_KEYS_MAX) {
keycount = DEFAULT_MF_KEYS_MAX;
datalen = keycount * keylen;
if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) {
PrintAndLogEx(ERR, "error, filesize is larger than available memory");
free(data);
return PM3_EOVFLOW;
}
data[0] = (keycount >> 0) & 0xFF;
data[1] = (keycount >> 8) & 0xFF;
datalen += 2;
strcpy(spiffsDest, MF_KEYS_FILE);
break;
case DICTIONARY_T55XX:
offset = DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages);
keylen = 4;
res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount);
keylen = T55XX_KEY_LENGTH;
res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount);
if (res || !keycount) {
free(data);
return PM3_EFILE;
}
// limited space on flash mem
if (keycount > DEFAULT_T55XX_KEYS_MAX) {
keycount = DEFAULT_T55XX_KEYS_MAX;
datalen = keycount * keylen;
if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) {
PrintAndLogEx(ERR, "error, filesize is larger than available memory");
free(data);
return PM3_EOVFLOW;
}
data[0] = (keycount >> 0) & 0xFF;
data[1] = (keycount >> 8) & 0xFF;
datalen += 2;
strcpy(spiffsDest, T55XX_KEYS_FILE);
break;
case DICTIONARY_ICLASS:
offset = DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages);
res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount);
keylen = ICLASS_KEY_LENGTH;
res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount);
if (res || !keycount) {
free(data);
return PM3_EFILE;
}
// limited space on flash mem
if (keycount > DEFAULT_ICLASS_KEYS_MAX) {
keycount = DEFAULT_ICLASS_KEYS_MAX;
datalen = keycount * keylen;
if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) {
PrintAndLogEx(ERR, "error, filesize is larger than available memory");
free(data);
return PM3_EOVFLOW;
}
data[0] = (keycount >> 0) & 0xFF;
data[1] = (keycount >> 8) & 0xFF;
datalen += 2;
strcpy(spiffsDest, ICLASS_KEYS_FILE);
break;
case DICTIONARY_NONE:
res = loadFile_safe(filename, ".bin", (void **)&data, &datalen);
@ -326,7 +317,18 @@ static int CmdFlashMemLoad(const char *Cmd) {
uint32_t bytes_sent = 0;
uint32_t bytes_remaining = datalen;
// we will treat dictionary files as spiffs files, so we need to handle this here
if (d != DICTIONARY_NONE) {
res = flashmem_spiffs_load(spiffsDest, data, datalen);
if (res != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "Failed writing passwrods to file %s", spiffsDest);
free(data);
return res;
}
PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" passwords to file "_GREEN_("%s"), keycount, spiffsDest);
SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0);
SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0);
} else {
// fast push mode
g_conn.block_after_ACK = true;
@ -362,8 +364,10 @@ static int CmdFlashMemLoad(const char *Cmd) {
}
g_conn.block_after_ACK = false;
free(data);
PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu")" bytes to offset "_GREEN_("%u"), datalen, offset);
}
free(data);
return PM3_SUCCESS;
}

View file

@ -84,6 +84,7 @@ static const iso14a_polling_frame_t ECP_FRAME = {
};
// based on ISO/IEC JTC1/SC17 STANDING DOCUMENT 5 (Updated 20 September 2024) Register of IC manufacturers
static const manufactureName_t manufactureMapping[] = {
// ID, "Vendor Country"
{ 0x01, "Motorola UK" },
@ -103,12 +104,12 @@ static const manufactureName_t manufactureMapping[] = {
{ 0x0F, "Hynix / Hyundai, Korea" },
{ 0x10, "LG-Semiconductors Co. Ltd Korea" },
{ 0x11, "Emosyn-EM Microelectronics USA" },
{ 0x12, "INSIDE Technology France" },
{ 0x12, "Wisekey Semiconductors (previously INSIDE Technology) France" },
{ 0x13, "ORGA Kartensysteme GmbH Germany" },
{ 0x14, "SHARP Corporation Japan" },
{ 0x15, "ATMEL France" },
{ 0x16, "EM Microelectronic-Marin SA Switzerland" },
{ 0x17, "KSW Microtec GmbH Germany" },
{ 0x17, "SMARTRAC TECHNOLOGY GmbH Germany" },
{ 0x18, "ZMD AG Germany" },
{ 0x19, "XICOR, Inc. USA" },
{ 0x1A, "Sony Corporation Japan" },
@ -124,7 +125,7 @@ static const manufactureName_t manufactureMapping[] = {
{ 0x24, "Masktech Germany Gmbh Germany" },
{ 0x25, "Innovision Research and Technology Plc UK" },
{ 0x26, "Hitachi ULSI Systems Co., Ltd. Japan" },
{ 0x27, "Cypak AB Sweden" },
{ 0x27, "Yubico AB Sweden" },
{ 0x28, "Ricoh Japan" },
{ 0x29, "ASK France" },
{ 0x2A, "Unicore Microsystems, LLC Russian Federation" },
@ -140,7 +141,7 @@ static const manufactureName_t manufactureMapping[] = {
{ 0x34, "Mikron JSC Russia" },
{ 0x35, "Fraunhofer Institute for Photonic Microsystems Germany" },
{ 0x36, "IDS Microchip AG Switzerland" },
{ 0x37, "Thinfilm - Kovio USA" },
{ 0x37, "Kovio USA" },
{ 0x38, "HMT Microelectronic Ltd Switzerland" },
{ 0x39, "Silicon Craft Technology Thailand" },
{ 0x3A, "Advanced Film Device Inc. Japan" },
@ -149,7 +150,7 @@ static const manufactureName_t manufactureMapping[] = {
{ 0x3D, "HID Global USA" },
{ 0x3E, "Productivity Engineering Gmbh Germany" },
{ 0x3F, "Austriamicrosystems AG (reserved) Austria" },
{ 0x40, "Gemalto SA France" },
{ 0x40, "Thales DIS (previously Gemalto SA) France" },
{ 0x41, "Renesas Electronics Corporation Japan" },
{ 0x42, "3Alogics Inc Korea" },
{ 0x43, "Top TroniQ Asia Limited Hong Kong" },
@ -165,7 +166,7 @@ static const manufactureName_t manufactureMapping[] = {
{ 0x4D, "Balluff GmbH Germany" },
{ 0x4E, "Oberthur Technologies France" },
{ 0x4F, "Silterra Malaysia Sdn. Bhd. Malaysia" },
{ 0x50, "DELTA Danish Electronics, Light & Acoustics Denmark" },
{ 0x50, "Presto Engineering Denmark" },
{ 0x51, "Giesecke & Devrient GmbH Germany" },
{ 0x52, "Shenzhen China Vision Microelectronics Co., Ltd. China" },
{ 0x53, "Shanghai Feiju Microelectronics Co. Ltd. China" },
@ -185,18 +186,57 @@ static const manufactureName_t manufactureMapping[] = {
{ 0x61, "Wearlinks Technology Inc. China" },
{ 0x62, "Userstar Information Systems Co., Ltd Taiwan" },
{ 0x63, "Pragmatic Printing Ltd. UK" },
{ 0x64, "Associacao do Laboratorio de Sistemas Integraveis Tecnologico - LSI-TEC Brazil" },
{ 0x64, "Associação do Laboratório de Sistemas Integráveis Tecnológico - LSI-TEC Brazil" },
{ 0x65, "Tendyron Corporation China" },
{ 0x66, "MUTO Smart Co., Ltd. Korea" },
{ 0x67, "ON Semiconductor USA" },
{ 0x68, "TUBITAK BILGEM Turkey" },
{ 0x68, "TÜBITAK BILGEM Turkey" }, // Don't use "İ", Proxspace doesn't like it
{ 0x69, "Huada Semiconductor Co., Ltd China" },
{ 0x6A, "SEVENEY France" },
{ 0x6B, "ISSM France" },
{ 0x6B, "THALES DIS Design Services SAS (previously ISSM) France" },
{ 0x6C, "Wisesec Ltd Israel" },
{ 0x6D, "LTD \"NM-Teh\" Russia" },
{ 0x70, "ifm electronic gmbh Germany" },
{ 0x71, "Sichuan Kiloway Technologies Co., Ltd. China" },
{ 0x72, "Ford Motor Company US" },
{ 0x73, "Beijing Tsingteng MicroSystem Co.,Ltd China" },
{ 0x74, "Huada EverCore Co., Ltd China" },
{ 0x75, "Smartchip Microelectronics Corporation Taiwan" },
{ 0x76, "Tongxin Microelectronics Co., Ltd. China" },
{ 0x77, "Ningbo IOT Microelectronics Co Ltd China" },
{ 0x78, "AU Optronics Taiwan" },
{ 0x79, "CUBIC USA" },
{ 0x7A, "Abbott Diabetes Care USA" },
{ 0x7B, "Shenzen Nation RFID Technology Co Ltd China" },
{ 0x7C, "DB HiTek Co Ltd Korea" },
{ 0x7D, "SATO Vicinity Australia" },
{ 0x7E, "Holtek Taiwan" },
// Previously, following entries were listed in the doc as 0x7f, 0x80 etc.
// Now, they are listed as 'FF 00', 'FF 01',...
{ 0x7F, "Shenzhen Goodix Technology Co., Ltd. China" },
{ 0x80, "Panthronics AG Austria" },
{ 0x81, "Beijing Huada Infosec Technology Co., Ltd China" },
{ 0x82, "Shanghai Oriental Magnetic Card Engineering Co Ltd. China" },
{ 0x83, "8ApeX Inc USA" },
{ 0x84, "Abbott Ireland" },
{ 0x85, "Proqure Inc USA" },
{ 0x86, "Schreiner Group GmbH & Co. KG Germany" },
{ 0x87, "Beijing SmartChip Microelectronics Technology Company Limited China" },
{ 0x88, "Datang Microelectronics Technology Co., Ltd. China" },
{ 0x89, "Wise Security Technology (Guangzhou) Co., Ltd. China" },
{ 0x8A, "CEC Huada Electronic Design Co., Ltd. China" },
{ 0x8B, "Shanghai Techsun RFID Technology Co., Ltd. China" },
{ 0x8C, "North China Institute of Computing Technology China" },
{ 0x8D, "Shanghai Huahong Integrated Circuit Co., Ltd. China" },
{ 0x8E, "Shanghai MintSilicon Microelectronics Inc., Ltd. China" },
{ 0x8F, "Xinsheng Technology Co., Ltd. China" },
{ 0x90, "IDEX Biometrics ASA Norway" },
{ 0x91, "Novo Nordisk A/S Denmark" },
{ 0x92, "Shandong Huayi Micro-Electronics Technology Co., Ltd. China" },
{ 0x93, "Abbott Heart Failure USA" },
{ 0x94, "P&M Information Technology (Shenzhen) Co., Ltd. China" },
{ 0x95, "MARS TECHNOLOGY PTE. LTD. Singapore" },
{ 0x96, "Trovan Limited Isle of Man" },
{ 0x00, "no tag-info available" } // must be the last entry
};
@ -1884,6 +1924,7 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
printTag("MIFARE DESFire EV2 2K/4K/8K/16K/32K");
printTag("MIFARE DESFire EV3 2K/4K/8K");
printTag("MIFARE DESFire Light 640B");
printTag("MIFARE Duox");
type |= MTDESFIRE;
} else {
printTag("MIFARE Plus EV1 2K/4K CL2 in SL3");
@ -2681,7 +2722,11 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
}
if (isST) {
PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf st info") "`");
if (card.ats_len > 0) {
PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf st25ta info") "`");
} else {
PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfu info") "`");
}
}
if (isEMV) {
@ -2716,7 +2761,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
PrintAndLogEx(HINT, "Hint: use `" _YELLOW_("hf mf c*") "` magic commands");
// if GEN4 GDM in Gen1a more, hint about it
if ((isMagic & MAGIC_FLAG_GDM_WUP_40) == MAGIC_FLAG_GDM_WUP_40) {
if (((isMagic & MAGIC_FLAG_GDM_WUP_40) == MAGIC_FLAG_GDM_WUP_40) || ((isMagic & MAGIC_FLAG_GDM_WUP_40_ZUID) == MAGIC_FLAG_GDM_WUP_40_ZUID)) {
PrintAndLogEx(HINT, "Hint: use `" _YELLOW_("hf mf gdm* --gen1a") "` magic commands");
}
}

View file

@ -43,6 +43,7 @@
#include "cliparser.h"
#include "util_posix.h" // msleep
#include "iso15.h" // typedef structs / enum
#include "crypto/originality.h"
#define FrameSOF Iso15693FrameSOF
#define Logic0 Iso15693Logic0
@ -281,121 +282,35 @@ static const productName_t uidmapping[] = {
static int CmdHF15Help(const char *Cmd);
static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) {
#define PUBLIC_ECDA_KEYLEN 33
const ecdsa_publickey_t nxp_15693_public_keys[] = {
{"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"},
{"MIFARE Classic / QL88", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"},
{"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"},
{"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"},
{"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"},
{"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"},
{"MIKRON Public key", "04F971EDA742A4A80D32DCF6A814A707CC3DC396D35902F72929FDCD698B3468F2"},
{"VivoKey Spark1 Public key", "04D64BB732C0D214E7EC580736ACF847284B502C25C0F7F2FA86AACE1DADA4387A"},
{"TruST25 (ST) key 01?", "041D92163650161A2548D33881C235D0FB2315C2C31A442F23C87ACF14497C0CBA"},
{"TruST25 (ST) key 04?", "04101E188A8B4CDDBC62D5BC3E0E6850F0C2730E744B79765A0E079907FBDB01BC"},
};
/*
uint8_t nxp_15693_public_keys[][PUBLIC_ECDA_KEYLEN] = {
// ICODE SLIX2 / DNA
{
0x04, 0x88, 0x78, 0xA2, 0xA2, 0xD3, 0xEE, 0xC3,
0x36, 0xB4, 0xF2, 0x61, 0xA0, 0x82, 0xBD, 0x71,
0xF9, 0xBE, 0x11, 0xC4, 0xE2, 0xE8, 0x96, 0x64,
0x8B, 0x32, 0xEF, 0xA5, 0x9C, 0xEA, 0x6E, 0x59, 0xF0
},
// unknown. Needs identification
{
0x04, 0x4F, 0x6D, 0x3F, 0x29, 0x4D, 0xEA, 0x57,
0x37, 0xF0, 0xF4, 0x6F, 0xFE, 0xE8, 0x8A, 0x35,
0x6E, 0xED, 0x95, 0x69, 0x5D, 0xD7, 0xE0, 0xC2,
0x7A, 0x59, 0x1E, 0x6F, 0x6F, 0x65, 0x96, 0x2B, 0xAF
},
// unknown. Needs identification
{
0x04, 0xA7, 0x48, 0xB6, 0xA6, 0x32, 0xFB, 0xEE,
0x2C, 0x08, 0x97, 0x70, 0x2B, 0x33, 0xBE, 0xA1,
0xC0, 0x74, 0x99, 0x8E, 0x17, 0xB8, 0x4A, 0xCA,
0x04, 0xFF, 0x26, 0x7E, 0x5D, 0x2C, 0x91, 0xF6, 0xDC
},
// manufacturer public key
{
0x04, 0x6F, 0x70, 0xAC, 0x55, 0x7F, 0x54, 0x61,
0xCE, 0x50, 0x52, 0xC8, 0xE4, 0xA7, 0x83, 0x8C,
0x11, 0xC7, 0xA2, 0x36, 0x79, 0x7E, 0x8A, 0x07,
0x30, 0xA1, 0x01, 0x83, 0x7C, 0x00, 0x40, 0x39, 0xC2
},
// MIKRON public key.
{
0x04, 0xf9, 0x71, 0xed, 0xa7, 0x42, 0xa4, 0xa8,
0x0d, 0x32, 0xdc, 0xf6, 0xa8, 0x14, 0xa7, 0x07,
0xcc, 0x3d, 0xc3, 0x96, 0xd3, 0x59, 0x02, 0xf7,
0x29, 0x29, 0xfd, 0xcd, 0x69, 0x8b, 0x34, 0x68, 0xf2
}
};
*/
uint8_t revuid[HF15_UID_LENGTH] = {0};
reverse_array_copy(uid, sizeof(revuid), revuid);
uint8_t revsign[32] = {0};
reverse_array_copy(signature, sizeof(revsign), revsign);
uint8_t i;
int reason = 0;
bool is_valid = false;
for (i = 0; i < ARRAYLEN(nxp_15693_public_keys); i++) {
int dl = 0;
uint8_t key[PUBLIC_ECDA_KEYLEN];
param_gethex_to_eol(nxp_15693_public_keys[i].value, 0, key, PUBLIC_ECDA_KEYLEN, &dl);
int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 8, signature, 32, false);
is_valid = (res == 0);
if (is_valid) {
int index = -1;
index = originality_check_verify(uid, 8, signature, 32, PK_MFC);
if (index >= 0) {
reason = 1;
break;
}
} else {
// try with sha256
res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 8, signature, 32, true);
is_valid = (res == 0);
if (is_valid) {
index = originality_check_verify_ex(uid, 8, signature, 32, PK_MFC, false, true);
if (index >= 0) {
reason = 2;
break;
}
} else {
// try with reversed uid / signature
res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, revuid, sizeof(revuid), revsign, sizeof(revsign), false);
is_valid = (res == 0);
if (is_valid) {
index = originality_check_verify_ex(uid, 8, signature, 32, PK_MFC, true, false);
if (index >= 0) {
reason = 3;
} else {
// try with sha256 and reversed uid / signature
index = originality_check_verify_ex(uid, 8, signature, 32, PK_MFC, true, true);
if (index >= 0) {
reason = 3;
break;
}
// try with sha256
res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, revuid, sizeof(revuid), revsign, sizeof(revsign), true);
is_valid = (res == 0);
if (is_valid) {
reason = 4;
break;
}
}
}
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature"));
if (is_valid == false || i == ARRAYLEN(nxp_15693_public_keys)) {
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32));
PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed"));
return PM3_ESOFT;
int ret = originality_check_print(signature, 32, index);
if (ret != PM3_SUCCESS) {
return ret;
}
PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_15693_public_keys[i].desc);
PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_15693_public_keys[i].value);
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32));
PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful"));
switch (reason) {
case 1:
PrintAndLogEx(INFO, " Params used: UID and signature, plain");
@ -416,14 +331,15 @@ static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) {
// get a product description based on the UID
// uid[8] tag uid
// returns description of the best match
static const char *getTagInfo_15(const uint8_t *uid) {
static void printTagInfo_15(const uint8_t *uid) {
if (uid == NULL) {
return "";
return;
}
uint64_t myuid, mask;
int i = 0, best = -1;
memcpy(&myuid, uid, sizeof(uint64_t));
// find first best match
while (uidmapping[i].mask > 0) {
if (uidmapping[i].mask > 64) {
mask = uidmapping[i].mask;
@ -441,10 +357,23 @@ static const char *getTagInfo_15(const uint8_t *uid) {
}
i++;
}
if (best >= 0) {
i = 0;
while (uidmapping[i].mask > 0) {
if (uidmapping[i].mask > 64) {
mask = uidmapping[i].mask;
} else {
mask = (~0ULL) << (64 - uidmapping[i].mask);
}
if (((myuid & mask) == uidmapping[i].uid) && (uidmapping[i].mask == uidmapping[best].mask)) {
PrintAndLogEx(SUCCESS, "TYPE MATCH " _YELLOW_("%s"), uidmapping[i].desc);
}
i++;
}
} else {
PrintAndLogEx(SUCCESS, "TYPE...... " _YELLOW_("%s"), uidmapping[i].desc);
}
if (best >= 0)
return uidmapping[best].desc;
return uidmapping[i].desc;
}
// return a clear-text message to an errorcode
@ -531,7 +460,7 @@ static int getUID(bool verbose, bool loop, uint8_t *buf) {
if (verbose) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%s"), iso15693_sprintUID(NULL, buf));
PrintAndLogEx(SUCCESS, "TYPE... " _YELLOW_("%s"), getTagInfo_15(buf));
printTagInfo_15(buf);
PrintAndLogEx(NORMAL, "");
}
res = PM3_SUCCESS;
@ -963,6 +892,66 @@ static int NxpSysInfo(uint8_t *uid) {
return PM3_SUCCESS;
}
static int StCheckSig(uint8_t *uid) {
// request to be sent to device/card
uint8_t approxlen = 2 + 8 + 1 + 2;
iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen);
if (packet == NULL) {
PrintAndLogEx(FAILED, "failed to allocate memory");
return PM3_EMALLOC;
}
// ISO15693 Protocol params
packet->raw[packet->rawlen++] = arg_get_raw_flag(HF15_UID_LENGTH, false, false, false);
packet->raw[packet->rawlen++] = ISO15693_READBLOCK;
// add UID (scan, uid)
memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH);
packet->rawlen += HF15_UID_LENGTH;
packet->flags = (ISO15_CONNECT | ISO15_READ_RESPONSE | ISO15_NO_DISCONNECT);
uint16_t blkoff = packet->rawlen;
char signature_hex[65] = {0};
for (int j = 0; j < 17; j++) {
packet->rawlen = blkoff;
// block no
packet->raw[packet->rawlen++] = 0x3F + j;
// crc
AddCrc15(packet->raw, packet->rawlen);
packet->rawlen += 2;
clearCommandBuffer();
SendCommandNG(CMD_HF_ISO15693_COMMAND, (uint8_t *)packet, ISO15_RAW_LEN(packet->rawlen));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_ISO15693_COMMAND, &resp, 2000) == false) {
PrintAndLogEx(DEBUG, "iso15693 timeout");
free(packet);
DropField();
return PM3_ETIMEOUT;
}
ISO15_ERROR_HANDLING_RESPONSE
uint8_t *d = resp.data.asBytes;
ISO15_ERROR_HANDLING_CARD_RESPONSE(d, resp.length)
if (j == 0) {
if (memcmp(d + 1, "K04S", 4) != 0) {
// No signature
free(packet);
return PM3_ESOFT;
}
} else {
memcpy(signature_hex + ((j - 1) * 4), d + 1, 4);
}
packet->flags = (ISO15_READ_RESPONSE | ISO15_NO_DISCONNECT);
}
free(packet);
DropField();
uint8_t signature[16];
size_t signature_len;
hexstr_to_byte_array(signature_hex, signature, &signature_len);
uint8_t uid_swap[HF15_UID_LENGTH];
reverse_array_copy(uid, HF15_UID_LENGTH, uid_swap);
int index = originality_check_verify_ex(uid_swap, HF15_UID_LENGTH, signature, signature_len, PK_ST25TV, false, true);
PrintAndLogEx(NORMAL, "");
return originality_check_print(signature, signature_len, index);
}
/**
* Commandline handling: HF15 CMD SYSINFO
* get system information from tag/VICC
@ -1071,7 +1060,7 @@ static int CmdHF15Info(const char *Cmd) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
PrintAndLogEx(SUCCESS, "UID....... " _GREEN_("%s"), iso15693_sprintUID(NULL, uid));
PrintAndLogEx(SUCCESS, "TYPE...... " _YELLOW_("%s"), getTagInfo_15(d + 2));
printTagInfo_15(d + 2);
PrintAndLogEx(SUCCESS, "SYSINFO... %s", sprint_hex(d, resp.length - 2));
// DSFID
@ -1109,19 +1098,29 @@ static int CmdHF15Info(const char *Cmd) {
uint8_t nxp_version = d[6] & 0x18;
PrintAndLogEx(DEBUG, "NXP Version: %02x", nxp_version);
if (d[8] == 0x04 && d[7] == 0x01 && nxp_version == 0x08) {
if (d[8] == 0x04) {
// NXP
if (d[7] == 0x01 && nxp_version == 0x08) {
PrintAndLogEx(DEBUG, "SLIX2 Detected, getting NXP System Info");
return NxpSysInfo(uid);
} else if (d[8] == 0x04 && d[7] == 0x01 && nxp_version == 0x18) { // If it is an NTAG 5
} else if (d[7] == 0x01 && nxp_version == 0x18) { // If it is an NTAG 5
PrintAndLogEx(DEBUG, "NTAG 5 Detected, getting NXP System Info");
return NxpSysInfo(uid);
} else if (d[8] == 0x04 && (d[7] == 0x01 || d[7] == 0x02 || d[7] == 0x03)) { // If SLI, SLIX, SLIX-l, or SLIX-S check EAS status
} else if ((d[7] == 0x01 || d[7] == 0x02 || d[7] == 0x03)) { // If SLI, SLIX, SLIX-l, or SLIX-S check EAS status
PrintAndLogEx(DEBUG, "SLI, SLIX, SLIX-L, or SLIX-S Detected checking EAS status");
return NxpTestEAS(uid);
}
} else if (d[8] == 0x02) {
// ST, check d[7]:
// ST25TV512C/ST25TV02KC 0x08
// ST25TV512/ST25TV02K 0x23
// ST25TV04K-P 0x35
// ST25TV16K/ST25TV64K 0x48
if (d[7] == 0x08) {
PrintAndLogEx(DEBUG, "ST25TVxC Detected, getting ST Signature");
return StCheckSig(uid);
}
}
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
@ -1405,7 +1404,7 @@ static void print_tag_15693(iso15_tag_t *tag, bool dense_output, bool verbose) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " --%.*s", (tag->bytesPerPage * 3), dashes);
PrintAndLogEx(SUCCESS, "UID....... " _GREEN_("%s"), iso15693_sprintUID(NULL, tag->uid));
PrintAndLogEx(SUCCESS, "TYPE...... " _YELLOW_("%s"), getTagInfo_15(tag->uid));
printTagInfo_15(tag->uid);
PrintAndLogEx(SUCCESS, "DSFID..... 0x%02X", tag->dsfid);
PrintAndLogEx(SUCCESS, "AFI....... 0x%02X", tag->afi);
PrintAndLogEx(SUCCESS, "IC ref.... 0x%02X", tag->ic);
@ -1934,7 +1933,7 @@ static int CmdHF15Dump(const char *Cmd) {
tag->pagesCount = d[dCpt++] + 1;
tag->bytesPerPage = d[dCpt++] + 1;
} else {
// Set tag memory layout values (if can't be readed in SYSINFO)
// Set tag memory layout values (if can't be read in SYSINFO)
tag->bytesPerPage = blocksize;
tag->pagesCount = 128;
}
@ -1943,7 +1942,7 @@ static int CmdHF15Dump(const char *Cmd) {
tag->ic = d[dCpt++];
}
// add lenght for blockno (1)
// add length for blockno (1)
packet->rawlen++;
packet->raw[0] |= ISO15_REQ_OPTION; // Add option to dump lock status
packet->raw[1] = ISO15693_READBLOCK;
@ -3338,7 +3337,7 @@ static int CmdHF15View(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf 15 view",
"Print a ISO-15693 tag dump file (bin/eml/json)",
"hf 15 view -f hf-iclass-AA162D30F8FF12F1-dump.bin\n"
"hf 15 view -f hf-15-1122334455667788-dump.bin\n"
);
void *argtable[] = {
arg_param_begin,

View file

@ -39,7 +39,7 @@
// Average EF_DG2 seems to be around 20-25kB or so, but ICAO doesn't set an upper limit
// Iris data seems to be suggested to be around 35kB per eye (Presumably bumping up the file size to around 70kB)
// but as we cannot read that until we implement PACE, 35k seems to be a safe point.
#define EMRTD_MAX_FILE_SIZE 35000
#define EMRTD_MAX_FILE_SIZE 70000
// ISO7816 commands
#define EMRTD_P1_SELECT_BY_EF 0x02
@ -411,13 +411,13 @@ static void emrtd_deskey(uint8_t *seed, const uint8_t *type, int length, uint8_t
PrintAndLogEx(DEBUG, "seed.............. %s", sprint_hex_inrow(seed, 16));
// combine seed and type
uint8_t data[50];
uint8_t data[50] = { 0x00 };
memcpy(data, seed, length);
memcpy(data + length, type, 4);
PrintAndLogEx(DEBUG, "data.............. %s", sprint_hex_inrow(data, length + 4));
// SHA1 the key
unsigned char key[64];
unsigned char key[64] = { 0x00 };
sha1hash(data, length + 4, key);
PrintAndLogEx(DEBUG, "key............... %s", sprint_hex_inrow(key, length + 4));
@ -945,7 +945,7 @@ static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t
char dobcd = emrtd_calculate_check_digit(dob);
char expirycd = emrtd_calculate_check_digit(expiry);
char kmrz[25];
char kmrz[25] = { 0x00 };
snprintf(kmrz, sizeof(kmrz), "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd);
PrintAndLogEx(DEBUG, "kmrz.............. " _GREEN_("%s"), kmrz);

View file

@ -41,6 +41,7 @@
#include "preferences.h"
#include "generator.h"
#include "cmdhf14b.h"
#include "cmdhw.h"
#define NUM_CSNS 9
@ -264,7 +265,7 @@ static uint8_t card_app2_limit[] = {
0xff,
};
static iclass_config_card_item_t iclass_config_options[30] = {
static iclass_config_card_item_t iclass_config_options[33] = {
//Byte A8 - LED Operations
{"(LED) - Led idle (Off) / Led read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{"(LED) - Led idle (Red) / Led read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
@ -300,11 +301,18 @@ static iclass_config_card_item_t iclass_config_options[30] = {
{"(ELITE Key) - Set ELITE Key and Enable Dual key (Elite + Standard)", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}},
{"(ELITE Key) - Set ELITE Key and ENABLE Keyrolling", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}},
{"(ELITE Key) - Set ELITE Key and DISABLE Standard Key", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}},
//Erroneous / incorrect reader behaviors
//Erroneous / incorrect reader behaviors (read below)
//Elite Bugger:
//Sets block 3 of card 0 presented to the reader to 0, sets block 3 of card 1 presented to the reader to the original value of card 0's block 3
//Continues setting block 3 of presented cards to block 3 of the previous card the reader scanned
//This renders cards unreadable and hardly recoverable unless the order of the scanned cards is known.
{"(ELITE Bugger) - Renders cards unusable.", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}},
//Reset Operations
{"(RESET) - Reset READER to defaults", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{"(RESET) - Reset ENROLLER to defaults", {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF}}
{"(RESET) - Reset ENROLLER to defaults", {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF}},
//Reader Master Key Operations
{"(MASTER Key) - Change Reader Master Key to Custom Key", {0x28, 0xCB, 0x91, 0x9D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{"(MASTER Key) - Restore Reader Master Key to Factory Defaults", {0x28, 0xCB, 0x91, 0x9D, 0x00, 0x00, 0x00, 0x1C, 0xE0, 0x5C, 0x91, 0xCF, 0x63, 0x34, 0x23, 0xB9}}
};
static const iclass_config_card_item_t *get_config_card_item(int idx) {
@ -2920,6 +2928,199 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) {
return PM3_SUCCESS;
}
static int CmdHFiClass_TearBlock(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf iclass trbl",
"Tear off an iCLASS tag block",
"hf iclass trbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --tdb 100 --tde 150\n"
"hf iclass trbl --blk 10 -d AAAAAAAAAAAAAAAA --ki 0 --tdb 100 --tde 150");
void *argtable[] = {
arg_param_begin,
arg_str0("k", "key", "<hex>", "Access key as 8 hex bytes"),
arg_int0(NULL, "ki", "<dec>", "Key index to select key from memory 'hf iclass managekeys'"),
arg_int1(NULL, "blk", "<dec>", "block number"),
arg_str1("d", "data", "<hex>", "data to write as 8 hex bytes"),
arg_str0("m", "mac", "<hex>", "replay mac data (4 hex bytes)"),
arg_lit0(NULL, "credit", "key is assumed to be the credit key"),
arg_lit0(NULL, "elite", "elite computations applied to key"),
arg_lit0(NULL, "raw", "no computations applied to key"),
arg_lit0(NULL, "nr", "replay of NR/MAC"),
arg_lit0("v", "verbose", "verbose output"),
arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"),
arg_int1(NULL, "tdb", "<dec>", "tearoff delay start in ms"),
arg_int1(NULL, "tde", "<dec>", "tearoff delay end in ms"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int key_len = 0;
uint8_t key[8] = {0};
CLIGetHexWithReturn(ctx, 1, key, &key_len);
int key_nr = arg_get_int_def(ctx, 2, -1);
if (key_len > 0 && key_nr >= 0) {
PrintAndLogEx(ERR, "Please specify key or index, not both");
CLIParserFree(ctx);
return PM3_EINVARG;
}
bool auth = false;
if (key_len > 0) {
auth = true;
if (key_len != 8) {
PrintAndLogEx(ERR, "Key is incorrect length");
CLIParserFree(ctx);
return PM3_EINVARG;
}
} else if (key_nr >= 0) {
if (key_nr < ICLASS_KEYS_MAX) {
auth = true;
memcpy(key, iClass_Key_Table[key_nr], 8);
PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8));
} else {
PrintAndLogEx(ERR, "Key number is invalid");
CLIParserFree(ctx);
return PM3_EINVARG;
}
}
int blockno = arg_get_int_def(ctx, 3, 0);
int data_len = 0;
uint8_t data[8] = {0};
CLIGetHexWithReturn(ctx, 4, data, &data_len);
if (data_len != 8) {
PrintAndLogEx(ERR, "Data must be 8 hex bytes (16 hex symbols)");
CLIParserFree(ctx);
return PM3_EINVARG;
}
int mac_len = 0;
uint8_t mac[4] = {0};
CLIGetHexWithReturn(ctx, 5, mac, &mac_len);
if (mac_len) {
if (mac_len != 4) {
PrintAndLogEx(ERR, "MAC must be 4 hex bytes (8 hex symbols)");
CLIParserFree(ctx);
return PM3_EINVARG;
}
}
int tearoff_start = arg_get_int_def(ctx, 12, 100);
int tearoff_end = arg_get_int_def(ctx, 13, 200);
if (tearoff_end <= tearoff_start) {
PrintAndLogEx(ERR, "Tearoff end delay must be bigger than the start delay.");
return PM3_EINVARG;
}
if (tearoff_start < 0 || tearoff_end <= 0) {
PrintAndLogEx(ERR, "Tearoff start/end delays should be bigger than 0.");
return PM3_EINVARG;
}
bool use_credit_key = arg_get_lit(ctx, 6);
bool elite = arg_get_lit(ctx, 7);
bool rawkey = arg_get_lit(ctx, 8);
bool use_replay = arg_get_lit(ctx, 9);
bool verbose = arg_get_lit(ctx, 10);
bool shallow_mod = arg_get_lit(ctx, 11);
CLIParserFree(ctx);
if ((use_replay + rawkey + elite) > 1) {
PrintAndLogEx(ERR, "Can not use a combo of 'elite', 'raw', 'nr'");
return PM3_EINVARG;
}
int isok = 0;
tearoff_params_t params;
bool read_ok = false;
while (tearoff_start < tearoff_end && !read_ok) {
//perform read here, repeat if failed or 00s
uint8_t data_read_orig[8] = {0};
bool first_read = false;
bool reread = false;
while (!first_read) {
int res_orig = iclass_read_block_ex(key, blockno, 0x88, elite, rawkey, use_replay, verbose, auth, shallow_mod, data_read_orig, false);
if (res_orig == PM3_SUCCESS && !reread) {
if (memcmp(data_read_orig, zeros, 8) == 0) {
reread = true;
} else {
first_read = true;
reread = false;
}
} else if (res_orig == PM3_SUCCESS && reread) {
first_read = true;
reread = false;
}
}
params.on = true;
params.delay_us = tearoff_start;
handle_tearoff(&params, false);
PrintAndLogEx(INFO, "Tear off delay: "_YELLOW_("%d")" ms", tearoff_start);
isok = iclass_write_block(blockno, data, mac, key, use_credit_key, elite, rawkey, use_replay, verbose, auth, shallow_mod);
switch (isok) {
case PM3_SUCCESS:
PrintAndLogEx(SUCCESS, "Wrote block " _YELLOW_("%d") " / " _YELLOW_("0x%02X") " ( " _GREEN_("ok") " )", blockno, blockno);
break;
case PM3_ETEAROFF:
break;
default:
PrintAndLogEx(FAILED, "Writing failed");
break;
}
//read the data back
uint8_t data_read[8] = {0};
first_read = false;
reread = false;
bool decrease = false;
while (!first_read) {
int res = iclass_read_block_ex(key, blockno, 0x88, elite, rawkey, use_replay, verbose, auth, shallow_mod, data_read, false);
if (res == PM3_SUCCESS && !reread) {
if (memcmp(data_read, zeros, 8) == 0) {
reread = true;
} else {
first_read = true;
reread = false;
}
} else if (res == PM3_SUCCESS && reread) {
first_read = true;
reread = false;
} else if (res != PM3_SUCCESS) {
decrease = true;
}
}
if (decrease && tearoff_start > 0) { //if there was an error reading repeat the tearoff with the same delay
tearoff_start--;
}
bool tear_success = true;
for (int i = 0; i < PICOPASS_BLOCK_SIZE; i++) {
if (data[i] != data_read[i]) {
tear_success = false;
}
}
if (tear_success) { //tearoff succeeded
read_ok = true;
PrintAndLogEx(SUCCESS, _GREEN_("Tear-off Success!"));
PrintAndLogEx(INFO, "Read: %s", sprint_hex(data_read, sizeof(data_read)));
} else { //tearoff did not succeed
PrintAndLogEx(FAILED, _RED_("Tear-off Failed!"));
tearoff_start++;
}
PrintAndLogEx(INFO, "---------------");
}
PrintAndLogEx(NORMAL, "");
return isok;
}
static int CmdHFiClass_loclass(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf iclass loclass",
@ -4145,6 +4346,7 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) {
}
if (check_values) {
PrintAndLogEx(SUCCESS, _GREEN_("CONFIRMED VALID RAW key ") _RED_("%s"), sprint_hex(div_key, 8));
PrintAndLogEx(INFO, "You can now run -> "_YELLOW_("hf iclass unhash -k %s")" <-to find the pre-images.", sprint_hex(div_key, 8));
verified = true;
} else {
PrintAndLogEx(INFO, _YELLOW_("Raw Key Invalid"));
@ -4162,6 +4364,107 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) {
return PM3_SUCCESS;
}
static void generate_single_key_block_inverted_opt(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) {
uint8_t bits_index = index / 16383;
uint8_t ending_bits[] = { //all possible 70 combinations of 4x0 and 4x1 as key ending bits
0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D, 0x2E, 0x33,
0x35, 0x36, 0x39, 0x3A, 0x3C, 0x47, 0x4B, 0x4D, 0x4E, 0x53,
0x55, 0x56, 0x59, 0x5A, 0x5C, 0x63, 0x65, 0x66, 0x69, 0x6A,
0x6C, 0x71, 0x72, 0x74, 0x78, 0x87, 0x8B, 0x8D, 0x8E, 0x93,
0x95, 0x96, 0x99, 0x9A, 0x9C, 0xA3, 0xA5, 0xA6, 0xA9, 0xAA,
0xAC, 0xB1, 0xB2, 0xB4, 0xB8, 0xC3, 0xC5, 0xC6, 0xC9, 0xCA,
0xCC, 0xD1, 0xD2, 0xD4, 0xD8, 0xE1, 0xE2, 0xE4, 0xE8, 0xF0
};
uint8_t binary_endings[8]; // Array to store binary values for each ending bit
// Extract each bit from the ending_bits[k] and store it in binary_endings
uint8_t ending = ending_bits[bits_index];
for (int i = 7; i >= 0; i--) {
binary_endings[i] = ending & 1;
ending >>= 1;
}
uint8_t binary_mids[8]; // Array to store the 2-bit chunks of index
// Iterate over the 16-bit integer and store 2 bits at a time in the result array
for (int i = 0; i < 8; i++) {
// Shift and mask to get 2 bits and store them as an 8-bit value
binary_mids[7 - i] = (index >> (i * 2)) & 0x03; // 0x03 is a mask for 2 bits (binary 11)
}
memcpy(keyBlock, startingKey, PICOPASS_BLOCK_SIZE);
// Start from the second byte, index 1 as we're never gonna touch the first byte
for (int i = 1; i < PICOPASS_BLOCK_SIZE; i++) {
// Clear the last bit of the current byte (AND with 0xFE)
keyBlock[i] &= 0xF8;
// Set the last bit to the corresponding value from binary_endings (OR with binary_endings[i])
keyBlock[i] |= ((binary_mids[i] & 0x03) << 1) | (binary_endings[i] & 0x01);
}
}
static int CmdHFiClassLegacyRecSim(void) {
PrintAndLogEx(INFO, _YELLOW_("This simulation assumes the card is standard keyed."));
uint8_t key[PICOPASS_BLOCK_SIZE] = {0};
uint8_t original_key[PICOPASS_BLOCK_SIZE];
uint8_t csn[8] = {0};
uint8_t new_div_key[8] = {0};
uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
if (select_only(csn, CCNR, true, false) == false) {
DropField();
return PM3_ESOFT;
}
HFiClassCalcDivKey(csn, iClass_Key_Table[0], new_div_key, false);
memcpy(key, new_div_key, PICOPASS_BLOCK_SIZE);
memcpy(original_key, key, PICOPASS_BLOCK_SIZE);
uint8_t zero_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t zero_key_two[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int bits_found = -1;
uint32_t index = 0;
#define MAX_UPDATES 16777216
while (bits_found == -1 && index < MAX_UPDATES) {
uint8_t genkeyblock[PICOPASS_BLOCK_SIZE];
uint8_t xorkeyblock[PICOPASS_BLOCK_SIZE] = {0};
generate_single_key_block_inverted_opt(zero_key, index, genkeyblock);
memcpy(xorkeyblock, genkeyblock, PICOPASS_BLOCK_SIZE);
for (int i = 0; i < 8 ; i++) {
key[i] = xorkeyblock[i] ^ original_key[i];
memcpy(zero_key_two, xorkeyblock, PICOPASS_BLOCK_SIZE);
}
// Extract the last 3 bits of the first byte
uint8_t last_three_bits = key[0] & 0x07; // 0x07 is 00000111 in binary - bitmask
bool same_bits = true;
// Check if the last 3 bits of all bytes are the same
for (int i = 1; i < PICOPASS_BLOCK_SIZE; i++) {
if ((key[i] & 0x07) != last_three_bits) {
same_bits = false;
}
}
if (same_bits) {
bits_found = index;
PrintAndLogEx(SUCCESS, "Original Key: " _GREEN_("%s"), sprint_hex(original_key, sizeof(original_key)));
PrintAndLogEx(SUCCESS, "Weak Key: " _GREEN_("%s"), sprint_hex(key, sizeof(key)));
PrintAndLogEx(SUCCESS, "Key Updates Required to Weak Key: " _GREEN_("%d"), index);
PrintAndLogEx(SUCCESS, "Estimated Time: ~" _GREEN_("%d")" hours", index / 6545);
}
index++;
}//end while
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
static int CmdHFiClassLegacyRecover(const char *Cmd) {
CLIParserContext *ctx;
@ -4179,6 +4482,7 @@ static int CmdHFiClassLegacyRecover(const char *Cmd) {
arg_lit0(NULL, "debug", "Re-enables tracing for debugging. Limits cycles to 1."),
arg_lit0(NULL, "notest", "Perform real writes on the card!"),
arg_lit0(NULL, "allnight", "Loops the loop for 10 times, recommended loop value of 5000."),
arg_lit0(NULL, "est", "Estimates the key updates based on the card's CSN assuming standard key."),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -4193,6 +4497,12 @@ static int CmdHFiClassLegacyRecover(const char *Cmd) {
bool test = true;
bool no_test = arg_get_lit(ctx, 5);
bool allnight = arg_get_lit(ctx, 6);
bool sim = arg_get_lit(ctx, 7);
if (sim) {
CmdHFiClassLegacyRecSim();
return PM3_SUCCESS;
}
if (no_test) {
test = false;
@ -5124,79 +5434,14 @@ static int CmdHFiClassSAM(const char *Cmd) {
// CSN, config, epurse, NR/MAC, AIA
// PACS
// 03 05
// 06 85 80 6d c0
// first byte skip
// second byte length
// third padded
// fourth ..
uint8_t *d = resp.data.asBytes;
uint8_t n = d[1] - 1; // skip length byte
uint8_t pad = d[2];
char *binstr = (char *)calloc((n * 8) + 1, sizeof(uint8_t));
if (binstr == NULL) {
return PM3_EMALLOC;
}
bytes_2_binstr(binstr, d + 3, n);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + 2, resp.length - 2));
PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr));
binstr[strlen(binstr) - pad] = '\0';
PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr));
size_t hexlen = 0;
uint8_t hex[16] = {0};
binstr_2_bytes(hex, &hexlen, binstr);
PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen));
uint32_t top = 0, mid = 0, bot = 0;
if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) {
PrintAndLogEx(ERR, "Binary string contains none <0|1> chars");
free(binstr);
return PM3_EINVARG;
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Wiegand decode");
wiegand_message_t packed = initialize_message_object(top, mid, bot, strlen(binstr));
HIDTryUnpack(&packed);
PrintAndLogEx(NORMAL, "");
if (strlen(binstr) >= 26 && verbose) {
// iCLASS Legacy
PrintAndLogEx(INFO, "Clone to " _YELLOW_("iCLASS Legacy"));
PrintAndLogEx(SUCCESS, " hf iclass encode --ki 0 --bin %s", binstr);
PrintAndLogEx(NORMAL, "");
// HID Prox II
PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("HID Prox II"));
PrintAndLogEx(SUCCESS, " lf hid clone -w H10301 --bin %s", binstr);
PrintAndLogEx(NORMAL, "");
// MIFARE Classic
char mfcbin[28] = {0};
mfcbin[0] = '1';
memcpy(mfcbin + 1, binstr, strlen(binstr));
binstr_2_bytes(hex, &hexlen, mfcbin);
PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic") " (Pm3 simulation)");
PrintAndLogEx(SUCCESS, " hf mf eclr;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 0 -d 049DBA42A23E80884400C82000000000;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 5 -d 020000000000000000000000%s;", sprint_hex_inrow(hex, hexlen));
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 7 -d 484944204953787788AA204752454154;");
PrintAndLogEx(SUCCESS, " hf mf sim --1k -i;");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic 1K"));
PrintAndLogEx(SUCCESS, " hf mf encodehid --bin %s", binstr);
PrintAndLogEx(NORMAL, "");
}
free(binstr);
HIDDumpPACSBits(d + 2, d[1], verbose);
return PM3_SUCCESS;
}
@ -5216,6 +5461,7 @@ static command_t CommandTable[] = {
{"view", CmdHFiClassView, AlwaysAvailable, "Display content from tag dump file"},
{"wrbl", CmdHFiClass_WriteBlock, IfPm3Iclass, "Write Picopass / iCLASS block"},
{"creditepurse", CmdHFiClassCreditEpurse, IfPm3Iclass, "Credit epurse value"},
{"trbl", CmdHFiClass_TearBlock, IfPm3Iclass, "Performs tearoff attack on iClass block"},
{"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("Recovery") " --------------------"},
// {"autopwn", CmdHFiClassAutopwn, IfPm3Iclass, "Automatic key recovery tool for iCLASS"},
{"chk", CmdHFiClassCheckKeys, IfPm3Iclass, "Check keys"},

View file

@ -2373,3 +2373,167 @@ uint64_t GetCrypto1ProbableKey(AuthData_t *ad) {
crypto1_destroy(revstate);
return key;
}
// FMCOS 2.0
void annotateFMCOS20(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) {
if (cmdsize < 2)
return;
int pos = 0;
switch (cmd[0]) {
case 2:
case 3:
pos = 2;
break;
case 0:
pos = 1;
break;
default:
pos = 3;
break;
}
switch (cmd[pos]) {
case FMCOS20_CMD_EXTERNAL_AUTHENTICATION:
snprintf(exp, size, "EXT. AUTH");
break;
case FMCOS20_CMD_GET_CHALLENGE:
snprintf(exp, size, "GET CHALLENGE");
break;
case FMCOS20_CMD_INTERNAL_AUTHENTICATION:
snprintf(exp, size, "INT. AUTH");
break;
case FMCOS20_CMD_SELECT:
snprintf(exp, size, "SELECT");
break;
case FMCOS20_CMD_VERIFY_PIN:
snprintf(exp, size, "VERIFY PIN");
break;
case FMCOS20_CMD_READ_BINARY:
snprintf(exp, size, "READ BINARY");
break;
case FMCOS20_CMD_READ_RECORD:
snprintf(exp, size, "READ RECORD");
break;
case FMCOS20_CMD_UPDATE_BINARY:
snprintf(exp, size, "UPDATE BINARY");
break;
case FMCOS20_CMD_UPDATE_RECORD:
snprintf(exp, size, "UPDATE RECORD");
break;
case FMCOS20_CMD_APPEND_RECORD:
snprintf(exp, size, "APPEND RECORD");
break;
case FMCOS20_CMD_ERASE_DF:
snprintf(exp, size, "ERASE DF");
break;
case FMCOS20_CMD_WRITE_KEY:
snprintf(exp, size, "WRITE KEY");
break;
case FMCOS20_CMD_CREATE_FILE:
snprintf(exp, size, "CREATE FILE");
break;
case FMCOS20_CMD_CARD_BLOCK:
snprintf(exp, size, "CARD BLOCK");
break;
case FMCOS20_CMD_APP_UNBLOCK:
snprintf(exp, size, "APP UNBLOCK");
break;
case FMCOS20_CMD_APP_BLOCK:
if (cmd[pos + 1] == 0)
snprintf(exp, size, "APP BLOCK (TEMP)");
else if (cmd[pos + 1] == 1)
snprintf(exp, size, "APP BLOCK (PERM)");
else
snprintf(exp, size, "APP BLOCK");
break;
case FMCOS20_CMD_PIN_UNBLOCK:
snprintf(exp, size, "PIN UNBLOCK");
break;
case FMCOS20_CMD_CHANGE_PIN:
if (cmd[pos + 1] == 0)
snprintf(exp, size, "RESET PIN");
else if (cmd[pos + 1] == 1)
snprintf(exp, size, "CHANGE PIN");
break;
case FMCOS20_CMD_INITIALIZE_TRANSACTION:
if (cmd[pos + 1] == 0)
snprintf(exp, size, "INIT. TRANSACTION (CREDIT)");
else if (cmd[pos + 1] == 1)
snprintf(exp, size, "INIT. TRANSACTION (PURCHASE)");
else if (cmd[pos + 1] == 2)
snprintf(exp, size, "INIT. TRANSACTION (CASH WITHDRAW)");
else if (cmd[pos + 1] == 3)
snprintf(exp, size, "INIT. TRANSACTION (CAPP PURCHASE)");
else if (cmd[pos + 1] == 4)
snprintf(exp, size, "INIT. TRANSACTION (OVERDRAFT)");
else if (cmd[pos + 1] == 5)
snprintf(exp, size, "INIT. TRANSACTION (WITHDRAW)");
break;
case FMCOS20_CMD_CREDIT_LOAD:
snprintf(exp, size, "CREDIT LOAD");
break;
case FMCOS20_CMD_PURCHASE:
if (cmd[pos + 1] == 0)
snprintf(exp, size, "PURCHASE");
else if (cmd[pos + 1] == 1)
snprintf(exp, size, "CAPP PURCHASE / CASH WITHDRAW");
else if (cmd[pos + 1] == 3)
snprintf(exp, size, "WITHDRAW");
break;
case FMCOS20_CMD_UPDATE_OVERDRAW_LIMIT:
snprintf(exp, size, "UPDATE OVERDRAFT");
break;
case FMCOS20_CMD_GET_TRANSACTION_PROOF:
snprintf(exp, size, "TRANSACTION RECORD");
break;
case FMCOS20_CMD_GET_BALANCE:
snprintf(exp, size, "GET BALANCE");
break;
case FMCOS20_CMD_INITIALIZE_GREY_LOCK_UNLOCK:
if (cmd[pos + 1] == 8)
snprintf(exp, size, "INIT. GRAY LOCK");
else if (cmd[pos + 1] == 9)
snprintf(exp, size, "INIT. GRAY UNLOCK");
break;
case FMCOS20_CMD_GREY_LOCK_UNLOCK:
if (cmd[pos + 1] == 8)
snprintf(exp, size, "GRAY LOCK");
else if (cmd[pos + 1] == 9)
snprintf(exp, size, "GRAY UNLOCK");
break;
case FMCOS20_CMD_DEBIT_UNLOCK:
snprintf(exp, size, "DEBIT UNLOCK");
break;
case FMCOS20_CMD_CALCULATE_ROM_CRC:
snprintf(exp, size, "CALC. ROM CRC");
break;
case FMCOS20_CMD_GET_RESPONSE:
snprintf(exp, size, "GET RESPONSE");
break;
case FMCOS20_CMD_UNBLOCK:
snprintf(exp, size, "UNBLOCK");
break;
case FMCOS20_CMD_PULL:
snprintf(exp, size, "PULL");
break;
case FMCOS20_CMD_CHARGE:
snprintf(exp, size, "CHARGE");
break;
case FMCOS20_CMD_WRITE_EEPROM:
snprintf(exp, size, "WRITE EEPROM");
break;
case FMCOS20_CMD_READ_EEPROM:
snprintf(exp, size, "READ EEPROM");
break;
case FMCOS20_CMD_INITIALIZE_EEPROM:
snprintf(exp, size, "INIT. EEPROM");
break;
case FMCOS20_CMD_READ_ROM:
snprintf(exp, size, "READ ROM");
break;
default:
//snprintf(exp, size, "?");
break;
}
}

View file

@ -76,4 +76,6 @@ bool NestedCheckKey(uint64_t key, AuthData_t *ad, uint8_t *cmd, uint8_t cmdsize,
bool CheckCrypto1Parity(const uint8_t *cmd_enc, uint8_t cmdsize, uint8_t *cmd, const uint8_t *parity_enc);
uint64_t GetCrypto1ProbableKey(AuthData_t *ad);
void annotateFMCOS20(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize);
#endif // CMDHFLIST

View file

@ -46,6 +46,7 @@
#include "generator.h" // keygens.
#include "fpga.h"
#include "mifare/mifarehost.h"
#include "crypto/originality.h"
static int CmdHelp(const char *Cmd);
@ -68,52 +69,9 @@ static int usage_hf14_keybrute(void) {
*/
int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) {
// ref: MIFARE Classic EV1 Originality Signature Validation
#define PUBLIC_MFCEV1_ECDA_KEYLEN 33
const ecdsa_publickey_t nxp_mfc_public_keys[] = {
{"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"},
{"MIFARE Classic / QL88", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"},
{"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"},
{"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"},
{"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"},
{"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"},
{"MIKRON Public key", "04F971EDA742A4A80D32DCF6A814A707CC3DC396D35902F72929FDCD698B3468F2"},
{"VivoKey Spark1 Public key", "04D64BB732C0D214E7EC580736ACF847284B502C25C0F7F2FA86AACE1DADA4387A"},
{"TruST25 (ST) key 01?", "041D92163650161A2548D33881C235D0FB2315C2C31A442F23C87ACF14497C0CBA"},
{"TruST25 (ST) key 04?", "04101E188A8B4CDDBC62D5BC3E0E6850F0C2730E744B79765A0E079907FBDB01BC"},
};
uint8_t i;
bool is_valid = false;
for (i = 0; i < ARRAYLEN(nxp_mfc_public_keys); i++) {
int dl = 0;
uint8_t key[PUBLIC_MFCEV1_ECDA_KEYLEN];
param_gethex_to_eol(nxp_mfc_public_keys[i].value, 0, key, PUBLIC_MFCEV1_ECDA_KEYLEN, &dl);
int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, uidlen, signature, signature_len, false);
is_valid = (res == 0);
if (is_valid)
break;
}
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature"));
if (is_valid == false || i == ARRAYLEN(nxp_mfc_public_keys)) {
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32));
PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed"));
return PM3_ESOFT;
}
PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_mfc_public_keys[i].desc);
PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfc_public_keys[i].value);
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32));
PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful"));
return PM3_SUCCESS;
int index = originality_check_verify(uid, uidlen, signature, signature_len, PK_MFC);
PrintAndLogEx(NORMAL, "");
return originality_check_print(signature, signature_len, index);
}
static int mf_read_uid(uint8_t *uid, int *uidlen, int *nxptype) {
@ -1159,7 +1117,7 @@ static int CmdHF14AMfRdSc(const char *Cmd) {
}
if (s >= MIFARE_4K_MAXSECTOR) {
PrintAndLogEx(WARNING, "Sector number must be less then 40");
PrintAndLogEx(WARNING, "Sector number must be less than 40");
return PM3_EINVARG;
}
@ -2481,7 +2439,10 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) {
CLIParserInit(&ctx, "hf mf autopwn",
"This command automates the key recovery process on MIFARE Classic cards.\n"
"It uses the fchk, chk, darkside, nested, hardnested and staticnested to recover keys.\n"
"If all keys are found, it try dumping card content both to file and emulator memory.",
"If all keys are found, it try dumping card content both to file and emulator memory.\n"
"\n"
"default file name template is `hf-mf-<uid>-<dump|key>.`\n"
"using suffix the template becomes `hf-mf-<uid>-<dump|key>-<suffix>.` \n",
"hf mf autopwn\n"
"hf mf autopwn -s 0 -a -k FFFFFFFFFFFF --> target MFC 1K card, Sector 0 with known key A 'FFFFFFFFFFFF'\n"
"hf mf autopwn --1k -f mfc_default_keys --> target MFC 1K card, default dictionary\n"
@ -2496,6 +2457,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) {
arg_lit0("a", NULL, "Input key A (def)"),
arg_lit0("b", NULL, "Input key B"),
arg_str0("f", "file", "<fn>", "filename of dictionary"),
arg_str0(NULL, "suffix", "<txt>", "Add this suffix to generated files"),
arg_lit0(NULL, "slow", "Slower acquisition (required by some non standard cards)"),
arg_lit0("l", "legacy", "legacy mode (use the slow `hf mf chk`)"),
arg_lit0("v", "verbose", "verbose output"),
@ -2543,29 +2505,34 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) {
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
bool slow = arg_get_lit(ctx, 6);
bool legacy_mfchk = arg_get_lit(ctx, 7);
bool verbose = arg_get_lit(ctx, 8);
int outfnlen = 0;
char outfilename[127] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)outfilename, 127, &outfnlen);
bool no_save = arg_get_lit(ctx, 9);
bool m0 = arg_get_lit(ctx, 10);
bool m1 = arg_get_lit(ctx, 11);
bool m2 = arg_get_lit(ctx, 12);
bool m4 = arg_get_lit(ctx, 13);
bool slow = arg_get_lit(ctx, 7);
bool legacy_mfchk = arg_get_lit(ctx, 8);
bool verbose = arg_get_lit(ctx, 9);
bool in = arg_get_lit(ctx, 14);
bool no_save = arg_get_lit(ctx, 10);
bool m0 = arg_get_lit(ctx, 11);
bool m1 = arg_get_lit(ctx, 12);
bool m2 = arg_get_lit(ctx, 13);
bool m4 = arg_get_lit(ctx, 14);
bool in = arg_get_lit(ctx, 15);
#if defined(COMPILER_HAS_SIMD_X86)
bool im = arg_get_lit(ctx, 15);
bool is = arg_get_lit(ctx, 16);
bool ia = arg_get_lit(ctx, 17);
bool i2 = arg_get_lit(ctx, 18);
bool im = arg_get_lit(ctx, 16);
bool is = arg_get_lit(ctx, 17);
bool ia = arg_get_lit(ctx, 18);
bool i2 = arg_get_lit(ctx, 19);
#endif
#if defined(COMPILER_HAS_SIMD_AVX512)
bool i5 = arg_get_lit(ctx, 19);
bool i5 = arg_get_lit(ctx, 20);
#endif
#if defined(COMPILER_HAS_SIMD_NEON)
bool ie = arg_get_lit(ctx, 15);
bool ie = arg_get_lit(ctx, 16);
#endif
CLIParserFree(ctx);
@ -2733,7 +2700,13 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) {
}
// read uid to generate a filename for the key file
char *fptr = GenerateFilename("hf-mf-", "-key.bin");
char suffix[FILE_PATH_SIZE];
if (outfnlen) {
snprintf(suffix, sizeof(suffix) - strlen(outfilename), "-key-%s.bin", outfilename);
} else {
snprintf(suffix, sizeof(suffix), "-key.bin");
}
char *fptr = GenerateFilename("hf-mf-", suffix);
// check if tag doesn't have static nonce
int has_staticnonce = detect_classic_static_nonce();
@ -3261,7 +3234,13 @@ all_found:
}
free(fptr);
fptr = GenerateFilename("hf-mf-", "-dump");
if (outfnlen) {
snprintf(suffix, sizeof(suffix), "-dump-%s", outfilename);
} else {
snprintf(suffix, sizeof(suffix), "-dump");
}
fptr = GenerateFilename("hf-mf-", suffix);
if (fptr == NULL) {
free(dump);
free(e_sector);
@ -4513,7 +4492,7 @@ static int CmdHF14AMfEGetSc(const char *Cmd) {
CLIParserFree(ctx);
if (s >= MIFARE_4K_MAXSECTOR) {
PrintAndLogEx(WARNING, "Sector number must be less then 40");
PrintAndLogEx(WARNING, "Sector number must be less than 40");
return PM3_EINVARG;
}
@ -5577,7 +5556,7 @@ static int CmdHF14AMfCGetSc(const char *Cmd) {
CLIParserFree(ctx);
if (s >= MIFARE_4K_MAXSECTOR) {
PrintAndLogEx(WARNING, "Sector number must be less then 40");
PrintAndLogEx(WARNING, "Sector number must be less than 40");
return PM3_EINVARG;
}
@ -7202,7 +7181,8 @@ static int CmdHf14AGen3Block(const char *Cmd) {
" - You can specify part of manufacturer block as\n"
" 4/7-bytes for UID change only\n"
"\n"
"NOTE: BCC, SAK, ATQA will be calculated automatically"
"NOTE: BCC and ATQA will be calculated automatically\n"
"SAK will be automatically set to default values if not specified"
,
"hf mf gen3blk --> print current data\n"
"hf mf gen3blk -d 01020304 --> set 4 byte uid\n"
@ -7962,11 +7942,11 @@ static int parse_gtu_cfg(uint8_t *d, size_t n) {
uint8_t atslen = d[7];
if (atslen == 0) {
PrintAndLogEx(INFO, ".............. ATS length %u bytes ( %s )", atslen, _YELLOW_("zero"));
PrintAndLogEx(INFO, "..............%02X ATS length %u bytes ( %s )", d[7], atslen, _YELLOW_("zero"));
} else if (atslen <= 16) {
PrintAndLogEx(INFO, ".............. ATS length %u bytes ( %s )", atslen, _GREEN_("ok"));
PrintAndLogEx(INFO, "..............%02X ATS length %u bytes ( %s )", d[7], atslen, _GREEN_("ok"));
} else {
PrintAndLogEx(INFO, ".............. ATS length %u bytes ( %s )", atslen, _RED_("fail"));
PrintAndLogEx(INFO, "..............%02X ATS length %u bytes ( %s )", d[7], atslen, _RED_("fail"));
atslen = 0;
}
@ -7975,7 +7955,7 @@ static int parse_gtu_cfg(uint8_t *d, size_t n) {
// ATS seems to have 16 bytes reserved
PrintAndLogEx(INFO, _CYAN_("Config 2 - ATS"));
PrintAndLogEx(INFO, "%s", sprint_hex_inrow(d + 8, 16));
if (atslen <= 16) {
if ((atslen > 0) && (atslen <= 16)) {
PrintAndLogEx(INFO, "%s.............. ATS ( %d bytes )", sprint_hex_inrow(&d[8], d[7]), d[7]);
PrintAndLogEx(INFO, "..................%s Reserved for ATS", sprint_hex_inrow(d + 8 + d[7], 16 - d[7]));
} else {
@ -8064,7 +8044,8 @@ static int CmdHF14AGen4Info(const char *cmd) {
size_t resplen = 0;
int res = 0;
if (dlen != 32) {
if (dlen == 0) {
if (IfPm3Iso14443a()) {
res = mfG4GetConfig(pwd, resp, &resplen, verbose);
if (res != PM3_SUCCESS || resplen == 0) {
if (res == PM3_ETIMEOUT)
@ -8073,12 +8054,22 @@ static int CmdHF14AGen4Info(const char *cmd) {
PrintAndLogEx(ERR, "Error get config. Maybe not a Gen4 card?. error=%d rlen=%zu", res, resplen);
return PM3_ESOFT;
}
} else {
PrintAndLogEx(ERR, "Offline mode, please provide data");
return PM3_ESOFT;
}
} else if (dlen != 32) {
PrintAndLogEx(FAILED, "Data must be 32 bytes length, got " _YELLOW_("%u"), dlen);
return PM3_EINVARG;
} else {
memcpy(resp, data, dlen);
resplen = 32;
}
parse_gtu_cfg(resp, resplen);
if (! IfPm3Iso14443a()) {
return PM3_SUCCESS;
}
uint8_t uid_len = resp[1];
@ -9883,6 +9874,7 @@ static int CmdHF14AMfISEN(const char *Cmd) {
arg_rem("FM11RF08S specific options:", "Incompatible with above options, except -k; output in JSON"),
arg_lit0(NULL, "collect_fm11rf08s", "collect all nT/{nT}/par_err."),
arg_lit0(NULL, "collect_fm11rf08s_with_data", "collect all nT/{nT}/par_err and data blocks."),
arg_lit0(NULL, "collect_fm11rf08s_without_backdoor", "collect all nT/{nT}/par_err without backdoor. Requires first auth keytype and block"),
arg_str0("f", "file", "<fn>", "Specify a filename for collected data"),
arg_param_end
};
@ -9954,9 +9946,18 @@ static int CmdHF14AMfISEN(const char *Cmd) {
if (collect_fm11rf08s_with_data) {
collect_fm11rf08s = 1;
}
bool collect_fm11rf08s_without_backdoor = arg_get_lit(ctx, 23);
if (collect_fm11rf08s_without_backdoor) {
collect_fm11rf08s = 1;
}
if (collect_fm11rf08s_with_data && collect_fm11rf08s_without_backdoor) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "Don't mix with_data and without_backdoor options");
return PM3_EINVARG;
}
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 23), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParamStrToBuf(arg_get_str(ctx, 24), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParserFree(ctx);
@ -10005,12 +10006,15 @@ static int CmdHF14AMfISEN(const char *Cmd) {
if (collect_fm11rf08s) {
uint64_t t1 = msclock();
uint32_t flags = collect_fm11rf08s_with_data;
SendCommandMIX(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, flags, 0, 0, key, sizeof(key));
if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1000)) {
if (resp.status == PM3_ESOFT) {
uint32_t flags = collect_fm11rf08s_with_data | (collect_fm11rf08s_without_backdoor << 1);
SendCommandMIX(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, flags, blockn, keytype, key, sizeof(key));
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500)) {
if (resp.oldarg[0] != PM3_SUCCESS) {
return NONCE_FAIL;
}
} else {
PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
return PM3_ETIMEOUT;
}
uint8_t num_sectors = MIFARE_1K_MAXSECTOR + 1;
iso14a_fm11rf08s_nonces_with_data_t nonces_dump = {0};
@ -10146,7 +10150,7 @@ static command_t CommandTable[] = {
{"gen3blk", CmdHf14AGen3Block, IfPm3Iso14443a, "Overwrite manufacturer block"},
{"gen3freeze", CmdHf14AGen3Freeze, IfPm3Iso14443a, "Perma lock UID changes. irreversible"},
{"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GTU") " --------------------------"},
{"ginfo", CmdHF14AGen4Info, IfPm3Iso14443a, "Info about configuration of the card"},
{"ginfo", CmdHF14AGen4Info, AlwaysAvailable, "Info about configuration of the card"},
{"ggetblk", CmdHF14AGen4GetBlk, IfPm3Iso14443a, "Read block from card"},
{"gload", CmdHF14AGen4Load, IfPm3Iso14443a, "Load dump to card"},
{"gsave", CmdHF14AGen4Save, IfPm3Iso14443a, "Save dump from card into file or emulator"},

View file

@ -45,6 +45,7 @@
#include "generator.h"
#include "mifare/aiddesfire.h"
#include "util.h"
#include "crypto/originality.h"
#define MAX_KEY_LEN 24
#define MAX_KEYS_LIST_LEN 1024
@ -147,6 +148,7 @@ typedef enum {
PLUS_EV2,
NTAG413DNA,
NTAG424,
DUOX,
} nxp_cardtype_t;
typedef enum {
@ -271,6 +273,10 @@ static char *getVersionStr(uint8_t type, uint8_t major, uint8_t minor) {
snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("Plus EV1") " )", major, minor);
else if (type == 0x02 && major == 0x22 && minor == 0x00)
snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("Plus EV2") " )", major, minor);
else if (type == 0x01 && major == 0xA0 && minor == 0x00)
snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DUOX") " )", major, minor);
else if ((type & 0x08) == 0x08)
snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire Light") " )", major, minor);
else
snprintf(retStr, sizeof(buf), "%x.%x ( " _YELLOW_("Unknown") " )", major, minor);
return buf;
@ -337,6 +343,10 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) {
if (type == 0x01 && major == 0x33 && minor == 0x00)
return DESFIRE_EV3;
// Duox
if (type == 0x01 && major == 0xA0 && minor == 0x00)
return DUOX;
// DESFire Light
if (type == 0x08 && major == 0x30 && minor == 0x00)
return DESFIRE_LIGHT;
@ -442,61 +452,10 @@ int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, si
PrintAndLogEx(DEBUG, "SIGNATURE=NULL");
return PM3_EINVARG;
}
// ref: MIFARE Desfire Originality Signature Validation
// See tools/recover_pk.py to recover Pk from UIDs and signatures
#define PUBLIC_DESFIRE_ECDA_KEYLEN 57
const ecdsa_publickey_t nxp_desfire_public_keys[] = {
{"NTAG424DNA, DESFire Ev2", "048A9B380AF2EE1B98DC417FECC263F8449C7625CECE82D9B916C992DA209D68422B81EC20B65A66B5102A61596AF3379200599316A00A1410"},
{"NTAG413DNA, DESFire Ev1", "04BB5D514F7050025C7D0F397310360EEC91EAF792E96FC7E0F496CB4E669D414F877B7B27901FE67C2E3B33CD39D1C797715189AC951C2ADD"},
{"DESFire Ev2", "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A"},
{"DESFire Ev3", "041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743"},
{"NTAG424DNA, NTAG424DNATT, DESFire Light Ev2", "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3B"},
{"DESFire Light", "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"},
{"MIFARE Plus Ev1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"},
{"MIFARE Plus Ev2", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"},
{"DESFire Ev2 XL", "04CD5D45E50B1502F0BA4656FF37669597E7E183251150F9574CC8DA56BF01C7ABE019E29FEA48F9CE22C3EA4029A765E1BC95A89543BAD1BC"},
{"MIFARE Plus Troika", "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"},
};
uint32_t i;
bool is_valid = false;
for (i = 0; i < ARRAYLEN(nxp_desfire_public_keys); i++) {
int dl = 0;
uint8_t key[PUBLIC_DESFIRE_ECDA_KEYLEN];
param_gethex_to_eol(nxp_desfire_public_keys[i].value, 0, key, PUBLIC_DESFIRE_ECDA_KEYLEN, &dl);
int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP224R1, key, uid, uidlen, signature, signature_len, false);
is_valid = (res == 0);
if (is_valid)
break;
}
// PrintAndLogEx(NORMAL, "");
// PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature"));
if (is_valid == false || i == ARRAYLEN(nxp_desfire_public_keys)) {
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp224r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16));
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16));
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16));
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48));
PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed"));
return PM3_ESOFT;
}
PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_desfire_public_keys[i].desc);
PrintAndLogEx(INFO, "IC signature public key value: %.32s", nxp_desfire_public_keys[i].value);
PrintAndLogEx(INFO, " : %.32s", nxp_desfire_public_keys[i].value + 32);
PrintAndLogEx(INFO, " : %.32s", nxp_desfire_public_keys[i].value + 64);
PrintAndLogEx(INFO, " : %.32s", nxp_desfire_public_keys[i].value + 96);
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp224r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16));
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16));
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16));
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48));
PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful"));
return PM3_SUCCESS;
int index = originality_check_verify(uid, uidlen, signature, signature_len, PK_MFDES);
PrintAndLogEx(NORMAL, "");
return originality_check_print(signature, signature_len, index);
}
static void swap24(uint8_t *data) {
@ -794,6 +753,8 @@ static int CmdHF14ADesInfo(const char *Cmd) {
PrintAndLogEx(INFO, "\t2.2 - DESFire Ev2 XL, Originality check, proximity check, EAL5");
if (major == 3 && minor == 0)
PrintAndLogEx(INFO, "\t3.0 - DESFire Ev3, Originality check, proximity check, badass EAL6 ?");
if (major == 0xA0 && minor == 0)
PrintAndLogEx(INFO, "\tx.x - DUOX, Originality check, proximity check, EAL6++");
if (major == 0 && minor == 2)
PrintAndLogEx(INFO, "\t0.2 - DESFire Light, Originality check, ");
@ -811,7 +772,8 @@ static int CmdHF14ADesInfo(const char *Cmd) {
if (cardtype == DESFIRE_EV2 || cardtype == DESFIRE_EV2_XL ||
cardtype == DESFIRE_LIGHT ||
cardtype == DESFIRE_EV3 ||
cardtype == NTAG413DNA) {
cardtype == NTAG413DNA ||
cardtype == DUOX) {
// Signature originality check
uint8_t signature[250] = {0}; // must be 56
size_t signature_len = 0;

View file

@ -35,6 +35,7 @@
#include "crypto/libpcrypto.h"
#include "cmdhfmf.h" // printblock, header
#include "cmdtrace.h"
#include "crypto/originality.h"
static const uint8_t mfp_default_key[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
static uint16_t mfp_card_adresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0x9006, 0x9007, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001};
@ -190,55 +191,9 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) {
// --- GET SIGNATURE
static int plus_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) {
// ref: MIFARE Plus EV1 Originality Signature Validation
#define PUBLIC_PLUS_ECDA_KEYLEN 57
const ecdsa_publickey_t nxp_plus_public_keys[] = {
{"MIFARE Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"},
{"MIFARE Plus Ev2", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"},
{"MIFARE Plus Troika", "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"}
};
uint8_t i;
bool is_valid = false;
for (i = 0; i < ARRAYLEN(nxp_plus_public_keys); i++) {
int dl = 0;
uint8_t key[PUBLIC_PLUS_ECDA_KEYLEN];
param_gethex_to_eol(nxp_plus_public_keys[i].value, 0, key, PUBLIC_PLUS_ECDA_KEYLEN, &dl);
int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP224R1, key, uid, uidlen, signature, signature_len, false);
is_valid = (res == 0);
if (is_valid)
break;
}
int index = originality_check_verify(uid, uidlen, signature, signature_len, PK_MFP);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature"));
if (is_valid == false || i == ARRAYLEN(nxp_plus_public_keys)) {
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp224r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16));
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16));
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16));
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48));
PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed"));
return PM3_ESOFT;
}
PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_plus_public_keys[i].desc);
PrintAndLogEx(INFO, "IC signature public key value: %.32s", nxp_plus_public_keys[i].value);
PrintAndLogEx(INFO, " : %.32s", nxp_plus_public_keys[i].value + 32);
PrintAndLogEx(INFO, " : %.32s", nxp_plus_public_keys[i].value + 64);
PrintAndLogEx(INFO, " : %.32s", nxp_plus_public_keys[i].value + 96);
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp224r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16));
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16));
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16));
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48));
PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful"));
return PM3_SUCCESS;
return originality_check_print(signature, signature_len, index);
}
static int get_plus_signature(uint8_t *signature, int *signature_len) {
@ -262,9 +217,15 @@ static int get_plus_signature(uint8_t *signature, int *signature_len) {
// GET VERSION
static int plus_print_version(uint8_t *version) {
if ((version[14] == 0x00) && (version[15] == 0x04)) {
PrintAndLogEx(SUCCESS, "UID: " _GREEN_("%s"), sprint_hex(version + 16, 4));
PrintAndLogEx(SUCCESS, "Batch number: " _GREEN_("%s"), sprint_hex(version + 20, 5));
PrintAndLogEx(SUCCESS, "Production date: week " _GREEN_("%02x") " / " _GREEN_("20%02x"), version[7 + 7 + 6 + 5], version[7 + 7 + 7 + 4 + 1]);
} else {
PrintAndLogEx(SUCCESS, "UID: " _GREEN_("%s"), sprint_hex(version + 14, 7));
PrintAndLogEx(SUCCESS, "Batch number: " _GREEN_("%s"), sprint_hex(version + 21, 5));
PrintAndLogEx(SUCCESS, "Production date: week " _GREEN_("%02x") " / " _GREEN_("20%02x"), version[7 + 7 + 7 + 5], version[7 + 7 + 7 + 5 + 1]);
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Hardware Information"));
PrintAndLogEx(INFO, " Raw : %s", sprint_hex(version, 7));
@ -328,15 +289,24 @@ static int CmdHFMFPInfo(const char *Cmd) {
uint64_t select_status = resp.oldarg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision
bool Version4BUID = false;
bool supportVersion = false;
bool supportSignature = false;
// version check
uint8_t version[30] = {0};
uint8_t uid4b[4] = {0};
uint8_t uid7b[7] = {0};
int version_len = sizeof(version);
if (get_plus_version(version, &version_len) == PM3_SUCCESS) {
plus_print_version(version);
supportVersion = true;
if ((version[14] == 0x00) && (version[15] == 0x04)) {
Version4BUID = true;
memcpy(uid4b, version + 16, 4);
} else {
memcpy(uid7b, version + 14, 7);
}
} else {
// info about 14a part, historical bytes.
infoHF14A(false, false, false);
@ -346,7 +316,15 @@ static int CmdHFMFPInfo(const char *Cmd) {
uint8_t signature[56] = {0};
int signature_len = sizeof(signature);
if (get_plus_signature(signature, &signature_len) == PM3_SUCCESS) {
if (supportVersion) {
if (Version4BUID) {
plus_print_signature(uid4b, 4, signature, signature_len);
} else {
plus_print_signature(uid7b, 7, signature, signature_len);
}
} else {
plus_print_signature(card.uid, card.uidlen, signature, signature_len);
}
supportSignature = true;
}

View file

@ -35,6 +35,7 @@
#include "fileutils.h" // saveFile
#include "cmdtrace.h" // trace list
#include "preferences.h" // setDeviceDebugLevel
#include "crypto/originality.h"
#define MAX_UL_BLOCKS 0x0F
#define MAX_ULC_BLOCKS 0x2F
@ -53,6 +54,8 @@
#define MAX_MY_D_MOVE_LEAN 0x0F
#define MAX_UL_NANO_40 0x0A
#define MAX_UL_AES 0x37
#define MAX_ST25TN512 0x3F
#define MAX_ST25TN01K 0x3F
static int CmdHelp(const char *Cmd);
@ -103,7 +106,9 @@ static uint64_t UL_TYPES_ARRAY[] = {
MFU_TT_MAGIC_1A, MFU_TT_MAGIC_1B,
MFU_TT_MAGIC_NTAG, MFU_TT_NTAG_210u,
MFU_TT_UL_MAGIC, MFU_TT_UL_C_MAGIC,
MFU_TT_UL_AES
MFU_TT_UL_AES,
MFU_TT_ST25TN512, MFU_TT_ST25TN01K,
};
static uint8_t UL_MEMORY_ARRAY[ARRAYLEN(UL_TYPES_ARRAY)] = {
@ -124,7 +129,9 @@ static uint8_t UL_MEMORY_ARRAY[ARRAYLEN(UL_TYPES_ARRAY)] = {
// MAGIC_1A, MAGIC_1B, MAGIC_NTAG,
MAX_UL_BLOCKS, MAX_UL_BLOCKS, MAX_NTAG_216,
// NTAG_210u, UL_MAGIC, UL_C_MAGIC
MAX_NTAG_210, MAX_UL_BLOCKS, MAX_ULC_BLOCKS, MAX_UL_AES
MAX_NTAG_210, MAX_UL_BLOCKS, MAX_ULC_BLOCKS, MAX_UL_AES,
// ST25TN512, ST25TN01K,
MAX_ST25TN512, MAX_ST25TN01K,
};
static const ul_family_t ul_family[] = {
@ -789,7 +796,13 @@ static int ul_print_default(uint8_t *data, uint8_t *real_uid) {
PrintAndLogEx(SUCCESS, " BCC1: %02X ( " _GREEN_("ok") " )", data[8]);
else
PrintAndLogEx(NORMAL, " BCC1: %02X, crc should be %02X", data[8], crc1);
if (uid[0] == 0x04) {
PrintAndLogEx(SUCCESS, " Internal: %02X ( %s )", data[9], (data[9] == 0x48) ? _GREEN_("default") : _RED_("not default"));
} else if (uid[0] == 0x02) {
PrintAndLogEx(SUCCESS, " Sysblock: %02X ( %s )", data[9], (data[9] == 0x2C) ? _GREEN_("default") : _RED_("not default"));
} else {
PrintAndLogEx(SUCCESS, " Internal: %02X", data[9]);
}
} else {
PrintAndLogEx(SUCCESS, "Blocks 0-2: %s", sprint_hex(data + 0, 12));
}
@ -1011,6 +1024,10 @@ int ul_print_type(uint64_t tagtype, uint8_t spaces) {
snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 move lean (SLE 66R01L)"), spaces, "");
else if (tagtype & MFU_TT_FUDAN_UL)
snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("FUDAN Ultralight Compatible (or other compatible)"), spaces, "");
else if (tagtype & MFU_TT_ST25TN512)
snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("ST ST25TN512 64bytes"), spaces, "");
else if (tagtype & MFU_TT_ST25TN01K)
snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("ST ST25TN01K 160bytes"), spaces, "");
else
snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("Unknown %06" PRIx64), spaces, "", tagtype);
@ -1397,132 +1414,14 @@ static int ulev1_print_counters(void) {
}
static int ulev1_print_signature(uint64_t tagtype, uint8_t *uid, uint8_t *signature, size_t signature_len) {
#define PUBLIC_ECDA_KEYLEN 33
#define PUBLIC_ECDA_192_KEYLEN 49
// known public keys for the originality check (source: https://github.com/alexbatalov/node-nxp-originality-verifier)
// ref: AN11350 NTAG 21x Originality Signature Validation
// ref: AN11341 MIFARE Ultralight EV1 Originality Signature Validation
const ecdsa_publickey_t nxp_mfu_public_keys[] = {
{"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"},
{"MIFARE Classic / QL88", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"},
{"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"},
{"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"},
{"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"},
{"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"},
{"MIKRON Public key", "04F971EDA742A4A80D32DCF6A814A707CC3DC396D35902F72929FDCD698B3468F2"},
{"VivoKey Spark1 Public key", "04D64BB732C0D214E7EC580736ACF847284B502C25C0F7F2FA86AACE1DADA4387A"},
{"TruST25 (ST) key 01?", "041D92163650161A2548D33881C235D0FB2315C2C31A442F23C87ACF14497C0CBA"},
{"TruST25 (ST) key 04?", "04101E188A8B4CDDBC62D5BC3E0E6850F0C2730E744B79765A0E079907FBDB01BC"},
};
// https://www.nxp.com/docs/en/application-note/AN13452.pdf
const ecdsa_publickey_t nxp_mfu_192_public_keys[] = {
{"NXP Ultralight AES", "0453BF8C49B7BD9FE3207A91513B9C1D238ECAB07186B772104AB535F7D3AE63CF7C7F3DD0D169DA3E99E43C6399621A86"},
};
/*
uint8_t nxp_mfu_public_keys[6][PUBLIC_ECDA_KEYLEN] = {
// UL, NTAG21x and NDEF
{
0x04, 0x49, 0x4e, 0x1a, 0x38, 0x6d, 0x3d, 0x3c,
0xfe, 0x3d, 0xc1, 0x0e, 0x5d, 0xe6, 0x8a, 0x49,
0x9b, 0x1c, 0x20, 0x2d, 0xb5, 0xb1, 0x32, 0x39,
0x3e, 0x89, 0xed, 0x19, 0xfe, 0x5b, 0xe8, 0xbc, 0x61
},
// UL EV1
{
0x04, 0x90, 0x93, 0x3b, 0xdc, 0xd6, 0xe9, 0x9b,
0x4e, 0x25, 0x5e, 0x3d, 0xa5, 0x53, 0x89, 0xa8,
0x27, 0x56, 0x4e, 0x11, 0x71, 0x8e, 0x01, 0x72,
0x92, 0xfa, 0xf2, 0x32, 0x26, 0xa9, 0x66, 0x14, 0xb8
},
// unknown. Needs identification
{
0x04, 0x4F, 0x6D, 0x3F, 0x29, 0x4D, 0xEA, 0x57,
0x37, 0xF0, 0xF4, 0x6F, 0xFE, 0xE8, 0x8A, 0x35,
0x6E, 0xED, 0x95, 0x69, 0x5D, 0xD7, 0xE0, 0xC2,
0x7A, 0x59, 0x1E, 0x6F, 0x6F, 0x65, 0x96, 0x2B, 0xAF
},
// unknown. Needs identification
{
0x04, 0xA7, 0x48, 0xB6, 0xA6, 0x32, 0xFB, 0xEE,
0x2C, 0x08, 0x97, 0x70, 0x2B, 0x33, 0xBE, 0xA1,
0xC0, 0x74, 0x99, 0x8E, 0x17, 0xB8, 0x4A, 0xCA,
0x04, 0xFF, 0x26, 0x7E, 0x5D, 0x2C, 0x91, 0xF6, 0xDC
},
// manufacturer public key
{
0x04, 0x6F, 0x70, 0xAC, 0x55, 0x7F, 0x54, 0x61,
0xCE, 0x50, 0x52, 0xC8, 0xE4, 0xA7, 0x83, 0x8C,
0x11, 0xC7, 0xA2, 0x36, 0x79, 0x7E, 0x8A, 0x07,
0x30, 0xA1, 0x01, 0x83, 0x7C, 0x00, 0x40, 0x39, 0xC2
},
// MIKRON public key.
{
0x04, 0xf9, 0x71, 0xed, 0xa7, 0x42, 0xa4, 0xa8,
0x0d, 0x32, 0xdc, 0xf6, 0xa8, 0x14, 0xa7, 0x07,
0xcc, 0x3d, 0xc3, 0x96, 0xd3, 0x59, 0x02, 0xf7,
0x29, 0x29, 0xfd, 0xcd, 0x69, 0x8b, 0x34, 0x68, 0xf2
}
};
*/
uint8_t i;
bool is_valid = false;
int index = -1;
if (signature_len == 32) {
for (i = 0; i < ARRAYLEN(nxp_mfu_public_keys); i++) {
int dl = 0;
uint8_t key[PUBLIC_ECDA_KEYLEN] = {0};
param_gethex_to_eol(nxp_mfu_public_keys[i].value, 0, key, PUBLIC_ECDA_KEYLEN, &dl);
int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 7, signature, signature_len, false);
is_valid = (res == 0);
if (is_valid)
break;
index = originality_check_verify(uid, 7, signature, signature_len, PK_MFUL);
} else if (signature_len == 48) {
index = originality_check_verify(uid, 7, signature, signature_len, PK_MFULAES);
}
}
bool is_192_valid = false;
if (signature_len == 48) {
for (i = 0; i < ARRAYLEN(nxp_mfu_192_public_keys); i++) {
int dl = 0;
uint8_t key[PUBLIC_ECDA_192_KEYLEN] = {0};
param_gethex_to_eol(nxp_mfu_192_public_keys[i].value, 0, key, PUBLIC_ECDA_192_KEYLEN, &dl);
int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP192R1, key, uid, 7, signature, signature_len, false);
is_192_valid = (res == 0);
if (is_192_valid)
break;
}
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature"));
if (is_192_valid) {
PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_mfu_192_public_keys[i].desc);
PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfu_192_public_keys[i].value);
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp192r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, signature_len));
PrintAndLogEx(SUCCESS, " Signature verification ( " _GREEN_("successful") " )");
return PM3_SUCCESS;
}
if (is_valid) {
PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_mfu_public_keys[i].desc);
PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfu_public_keys[i].value);
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, signature_len));
PrintAndLogEx(SUCCESS, " Signature verification ( " _GREEN_("successful") " )");
return PM3_SUCCESS;
}
PrintAndLogEx(INFO, " Elliptic curve parameters: %s", (signature_len == 48) ? "NID_secp192r1" : "NID_secp128r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, signature_len));
PrintAndLogEx(SUCCESS, " Signature verification ( " _RED_("fail") " )");
return PM3_ESOFT;
return originality_check_print(signature, signature_len, index);
}
static int ulev1_print_version(uint8_t *data) {
@ -1540,21 +1439,13 @@ static int ulev1_print_version(uint8_t *data) {
}
static int ntag_print_counter(void) {
// NTAG has one counter/tearing. At address 0x02.
// NTAG has one counter. At address 0x02. With no tearing.
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Counter"));
uint8_t tear[1] = {0};
uint8_t counter[3] = {0, 0, 0};
uint16_t len;
len = ulev1_readTearing(0x02, tear, sizeof(tear));
(void)len;
len = ulev1_readCounter(0x02, counter, sizeof(counter));
(void)len;
PrintAndLogEx(INFO, " [02]: %s", sprint_hex(counter, 3));
PrintAndLogEx(SUCCESS, " - %02X tearing ( %s )"
, tear[0]
, (tear[0] == 0xBD) ? _GREEN_("ok") : _RED_("fail")
);
return len;
}
@ -2123,17 +2014,55 @@ uint64_t GetHF14AMfU_Type(void) {
// Ultralight - ATQA / SAK
if (card.atqa[1] != 0x00 || card.atqa[0] != 0x44 || card.sak != 0x00) {
//PrintAndLogEx(NORMAL, "Tag is not Ultralight | NTAG | MY-D [ATQA: %02X %02X SAK: %02X]\n", card.atqa[1], card.atqa[0], card.sak);
//PrintAndLogEx(NORMAL, "Tag is not Ultralight | NTAG | MY-D |ST25TN [ATQA: %02X %02X SAK: %02X]\n", card.atqa[1], card.atqa[0], card.sak);
DropField();
return MFU_TT_UL_ERROR;
}
if (card.uid[0] != 0x05) {
if (card.uid[0] == 0x02) {
// ST25TN
// read SYSBLOCK
uint8_t data[4] = {0x00};
int status = ul_read(0x02, data, sizeof(data));
if (status <= 1) {
tagtype = MFU_TT_UL;
} else {
status = ul_read(data[1] + 1, data, sizeof(data));
if (status <= 1) {
tagtype = MFU_TT_UL;
} else {
// data[3] == KID == 0x05 Key ID
// data[2] == REV == 0x13 Product version
if ((data[1] == 0x90) && (data[0] == 0x90)) {
tagtype = MFU_TT_ST25TN01K;
} else if ((data[1] == 0x90) && (data[0] == 0x91)) {
tagtype = MFU_TT_ST25TN512;
}
}
}
} else if (card.uid[0] == 0x05) {
// Infineon MY-D tests Exam high nibble
DropField();
uint8_t nib = (card.uid[1] & 0xf0) >> 4;
switch (nib) {
// case 0: tagtype = SLE66R35E7; break; //or SLE 66R35E7 - mifare compat... should have different sak/atqa for mf 1k
case 1:
tagtype = MFU_TT_MY_D;
break; // or SLE 66RxxS ... up to 512 pages of 8 user bytes...
case 2:
tagtype = MFU_TT_MY_D_NFC;
break; // or SLE 66RxxP ... up to 512 pages of 8 user bytes... (or in nfc mode FF pages of 4 bytes)
case 3:
tagtype = (MFU_TT_MY_D_MOVE | MFU_TT_MY_D_MOVE_NFC);
break; // or SLE 66R01P // 38 pages of 4 bytes //notice: we can not currently distinguish between these two
case 7:
tagtype = MFU_TT_MY_D_MOVE_LEAN;
break; // or SLE 66R01L // 16 pages of 4 bytes
}
} else {
uint8_t version[10] = {0x00};
int len = ulev1_getVersion(version, sizeof(version));
DropField();
switch (len) {
case 0x0A: {
/*
@ -2221,7 +2150,6 @@ uint64_t GetHF14AMfU_Type(void) {
tagtype = MFU_TT_UNKNOWN;
break;
}
// This is a test from cards that doesn't answer to GET_VERSION command
// UL vs UL-C vs NTAG203 vs FUDAN FM11NT021 (which is NTAG213 compatiable)
if (tagtype & (MFU_TT_UL | MFU_TT_UL_C | MFU_TT_NTAG_203)) {
@ -2275,25 +2203,6 @@ uint64_t GetHF14AMfU_Type(void) {
tagtype = ul_fudan_check();
DropField();
}
} else {
DropField();
// Infinition MY-D tests Exam high nibble
uint8_t nib = (card.uid[1] & 0xf0) >> 4;
switch (nib) {
// case 0: tagtype = SLE66R35E7; break; //or SLE 66R35E7 - mifare compat... should have different sak/atqa for mf 1k
case 1:
tagtype = MFU_TT_MY_D;
break; // or SLE 66RxxS ... up to 512 pages of 8 user bytes...
case 2:
tagtype = MFU_TT_MY_D_NFC;
break; // or SLE 66RxxP ... up to 512 pages of 8 user bytes... (or in nfc mode FF pages of 4 bytes)
case 3:
tagtype = (MFU_TT_MY_D_MOVE | MFU_TT_MY_D_MOVE_NFC);
break; // or SLE 66R01P // 38 pages of 4 bytes //notice: we can not currently distinguish between these two
case 7:
tagtype = MFU_TT_MY_D_MOVE_LEAN;
break; // or SLE 66R01L // 16 pages of 4 bytes
}
}
tagtype |= ul_magic_test();
@ -2503,6 +2412,39 @@ static int CmdHF14AMfUInfo(const char *Cmd) {
}
}
// ST25TN info & signature
if (tagtype & (MFU_TT_ST25TN512 | MFU_TT_ST25TN01K)) {
status = ul_read(0x02, data, sizeof(data));
if (status <= 1) {
PrintAndLogEx(ERR, "Error: tag didn't answer to READ SYSBLOCK");
DropField();
return PM3_ESOFT;
}
status = ul_read(data[1] + 1, data, sizeof(data));
if (status <= 1) {
PrintAndLogEx(ERR, "Error: tag didn't answer to READ SYSBLOCK");
DropField();
return PM3_ESOFT;
}
PrintAndLogEx(INFO, "--- " _CYAN_("Tag System Information"));
PrintAndLogEx(INFO, " Key ID: %02x", data[3]);
PrintAndLogEx(INFO, " Product Version: %02x", data[2]);
PrintAndLogEx(INFO, " Product Code: %02x%02x", data[1], data[0]);
uint8_t signature[32] = {0};
for (int blkoff = 0; blkoff < 8; blkoff++) {
status = ul_read(0x34 + blkoff, signature + (blkoff * 4), 4);
if (status <= 1) {
PrintAndLogEx(ERR, "Error: tag didn't answer to READ SYSBLOCK");
DropField();
return PM3_ESOFT;
}
}
// check signature
int index = originality_check_verify_ex(card.uid, 7, signature, sizeof(signature), PK_ST25TN, false, true);
PrintAndLogEx(NORMAL, "");
originality_check_print(signature, sizeof(signature), index);
}
// Read signature
if ((tagtype & (MFU_TT_UL_EV1_48 | MFU_TT_UL_EV1_128 | MFU_TT_UL_EV1 | MFU_TT_UL_NANO_40 |
MFU_TT_NTAG_210u | MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C |
@ -5833,6 +5775,127 @@ out:
return PM3_SUCCESS;
}
static int CmdHF14AMfUIncr(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mfu incr",
"Increment a MIFARE Ultralight Ev1 counter\n"
"Will read but not increment counter if NTAG is detected",
"hf mfu incr -c 0 -v 1337\n"
"hf mfu incr -c 2 -v 0 -p FFFFFFFF");
void *argtable[] = {
arg_param_begin,
arg_int1("c", "cnt", "<dec>", "Counter index from 0"),
arg_int1("v", "val", "<dec>", "Value to increment by (0-16777215)"),
arg_str0("p", "pwd", "<hex>", "PWD to authenticate with"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
uint8_t counter = arg_get_int_def(ctx, 1, 3);
uint32_t value = arg_get_u32_def(ctx, 2, 16777216);
int pwd_len;
uint8_t pwd[4] = { 0x00 };
CLIGetHexWithReturn(ctx, 3, pwd, &pwd_len);
bool has_key = false;
if (pwd_len) {
has_key = true;
if (pwd_len != 4) {
PrintAndLogEx(WARNING, "incorrect PWD length");
return PM3_EINVARG;
}
}
CLIParserFree(ctx);
if (counter > 2) {
PrintAndLogEx(WARNING, "Counter index must be in range 0-2");
return PM3_EINVARG;
}
if (value > 16777215) {
PrintAndLogEx(WARNING, "Value to increment must be in range 0-16777215");
return PM3_EINVARG;
}
uint8_t increment_cmd[6] = { MIFARE_ULEV1_INCR_CNT, counter, 0x00, 0x00, 0x00, 0x00 };
for (uint8_t i = 0; i < 3; i++) {
increment_cmd[i + 2] = (value >> (8 * i)) & 0xff;
}
iso14a_card_select_t card;
if (ul_select(&card) == false) {
PrintAndLogEx(FAILED, "failed to select card, exiting...");
return PM3_ESOFT;
}
uint64_t tagtype = GetHF14AMfU_Type();
uint64_t tags_with_counter_ul = MFU_TT_UL_EV1_48 | MFU_TT_UL_EV1_128 | MFU_TT_UL_EV1;
uint64_t tags_with_counter_ntag = MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C | MFU_TT_NTAG_213_TT | MFU_TT_NTAG_215 | MFU_TT_NTAG_216;
if ((tagtype & (tags_with_counter_ul | tags_with_counter_ntag)) == 0) {
PrintAndLogEx(WARNING, "tag type does not have counters");
DropField();
return PM3_ESOFT;
}
bool is_ntag = (tagtype & tags_with_counter_ntag) != 0;
if (is_ntag && (counter != 2)) {
PrintAndLogEx(WARNING, "NTAG only has one counter at index 2");
DropField();
return PM3_EINVARG;
}
uint8_t pack[4] = { 0, 0, 0, 0 };
if (has_key) {
if (ulev1_requestAuthentication(pwd, pack, sizeof(pack)) == PM3_EWRONGANSWER) {
PrintAndLogEx(FAILED, "authentication failed UL-EV1/NTAG");
DropField();
return PM3_ESOFT;
}
}
uint8_t current_counter[3] = { 0, 0, 0 };
int len = ulev1_readCounter(counter, current_counter, sizeof(current_counter));
if (len != sizeof(current_counter)) {
PrintAndLogEx(FAILED, "failed to read old counter");
if (is_ntag) {
PrintAndLogEx(HINT, "NTAG detected, try reading with PWD");
}
DropField();
return PM3_ESOFT;
}
uint32_t current_counter_num = current_counter[0] | (current_counter[1] << 8) | (current_counter[2] << 16);
PrintAndLogEx(INFO, "Current counter... " _GREEN_("%8d") " - " _GREEN_("%s"), current_counter_num, sprint_hex(current_counter, 3));
if ((tagtype & tags_with_counter_ntag) != 0) {
PrintAndLogEx(WARNING, "NTAG detected, unable to manually increment counter");
DropField();
return PM3_ESOFT;
}
uint8_t resp[1] = { 0x00 };
if (ul_send_cmd_raw(increment_cmd, sizeof(increment_cmd), resp, sizeof(resp)) < 0) {
PrintAndLogEx(FAILED, "failed to increment counter");
DropField();
return PM3_ESOFT;
}
uint8_t new_counter[3] = { 0, 0, 0 };
int new_len = ulev1_readCounter(counter, new_counter, sizeof(new_counter));
if (new_len != sizeof(current_counter)) {
PrintAndLogEx(FAILED, "failed to read new counter");
DropField();
return PM3_ESOFT;
}
uint32_t new_counter_num = new_counter[0] | (new_counter[1] << 8) | (new_counter[2] << 16);
PrintAndLogEx(INFO, "New counter....... " _GREEN_("%8d") " - " _GREEN_("%s"), new_counter_num, sprint_hex(new_counter, 3));
DropField();
return PM3_SUCCESS;
}
static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"list", CmdHF14AMfuList, AlwaysAvailable, "List MIFARE Ultralight / NTAG history"},
@ -5845,6 +5908,7 @@ static command_t CommandTable[] = {
{"cauth", CmdHF14AMfUCAuth, IfPm3Iso14443a, "Ultralight-C - Authentication"},
{"setpwd", CmdHF14AMfUCSetPwd, IfPm3Iso14443a, "Ultralight-C - Set 3DES key"},
{"dump", CmdHF14AMfUDump, IfPm3Iso14443a, "Dump MIFARE Ultralight family tag to binary file"},
{"incr", CmdHF14AMfUIncr, IfPm3Iso14443a, "Increments Ev1/NTAG counter"},
{"info", CmdHF14AMfUInfo, IfPm3Iso14443a, "Tag information"},
{"ndefread", CmdHF14MfuNDEFRead, IfPm3Iso14443a, "Prints NDEF records from card"},
{"rdbl", CmdHF14AMfURdBl, IfPm3Iso14443a, "Read block"},

View file

@ -96,6 +96,8 @@ int CmdHF14MfUTamper(const char *Cmd);
#define MFU_TT_MAGIC_4 0x400000000ULL
#define MFU_TT_MAGIC_4_GDM 0x800000000ULL
#define MFU_TT_MAGIC_NTAG21X 0x1000000000ULL
#define MFU_TT_ST25TN512 0x2000000000ULL
#define MFU_TT_ST25TN01K 0x4000000000ULL
#define MFU_TT_UL_MAGIC (MFU_TT_UL | MFU_TT_MAGIC)
#define MFU_TT_UL_C_MAGIC (MFU_TT_UL_C | MFU_TT_MAGIC)
// Don't forget to fill UL_TYPES_ARRAY and UL_MEMORY_ARRAY if new types are added

File diff suppressed because it is too large Load diff

View file

@ -21,7 +21,14 @@
#include "common.h"
// structure and database for uid -> tagtype lookups
typedef struct {
uint8_t uid;
const char *desc;
} sioMediaTypeName_t;
int infoSeos(bool verbose);
int CmdHFSeos(const char *Cmd);
int seos_kdf(bool encryption, uint8_t *masterKey, uint8_t keyslot,
uint8_t *adfOid, size_t adfoid_len, uint8_t *diversifier, uint8_t diversifier_len, uint8_t *out, int encryption_algorithm, int hash_algorithm);
#endif

View file

@ -33,6 +33,7 @@
#include "commonutil.h" // get_sw
#include "protocols.h" // ISO7816 APDU return codes
#include "crypto/libpcrypto.h" // ecdsa
#include "crypto/originality.h"
#define TIMEOUT 2000
@ -148,48 +149,9 @@ static void print_st25ta_system_info(uint8_t *d, uint8_t n) {
}
static int print_st25ta_signature(uint8_t *uid, uint8_t *signature) {
#define PUBLIC_ECDA_KEYLEN 33
// known public keys for the originality check (source: https://github.com/alexbatalov/node-nxp-originality-verifier)
// ref: AN11350 NTAG 21x Originality Signature Validation
// ref: AN11341 MIFARE Ultralight EV1 Originality Signature Validation
const ecdsa_publickey_t nxp_mfu_public_keys[] = {
{"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"},
{"MIFARE Classic / QL88", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"},
{"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"},
{"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"},
{"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"},
{"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"},
{"MIKRON Public key", "04F971EDA742A4A80D32DCF6A814A707CC3DC396D35902F72929FDCD698B3468F2"},
{"VivoKey Spark1 Public key", "04D64BB732C0D214E7EC580736ACF847284B502C25C0F7F2FA86AACE1DADA4387A"},
{"TruST25 (ST) key 01?", "041D92163650161A2548D33881C235D0FB2315C2C31A442F23C87ACF14497C0CBA"},
{"TruST25 (ST) key 04?", "04101E188A8B4CDDBC62D5BC3E0E6850F0C2730E744B79765A0E079907FBDB01BC"},
};
for (uint8_t i = 0; i < ARRAYLEN(nxp_mfu_public_keys); i++) {
int dl = 0;
uint8_t key[PUBLIC_ECDA_KEYLEN] = {0};
param_gethex_to_eol(nxp_mfu_public_keys[i].value, 0, key, PUBLIC_ECDA_KEYLEN, &dl);
int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 7, signature, 32, true);
if (res == 0) {
int index = originality_check_verify_ex(uid, 7, signature, 32, PK_ST25TA, false, true);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature"));
PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_mfu_public_keys[i].desc);
PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfu_public_keys[i].value);
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32));
PrintAndLogEx(SUCCESS, " Signature verification ( " _GREEN_("successful") " )");
return PM3_SUCCESS;
}
}
return PM3_ESOFT;
return originality_check_print(signature, 32, index);
}
static int st25ta_get_signature(uint8_t *signature) {
@ -221,7 +183,13 @@ static int st25ta_get_signature(uint8_t *signature) {
}
activate_field = false;
}
if (resplen != 32) {
if ((resplen == 2) && (resp[0] == 0x69) && (resp[1] == 0x82)) {
PrintAndLogEx(WARNING, "GetSignature: Security status not satisfied");
}
DropField();
return PM3_ESOFT;
}
if (signature) {
memcpy(signature, resp, 32);
}

View file

@ -449,17 +449,19 @@ static int CmdEM410xSim(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 410x sim",
"Enables simulation of EM 410x card.\n"
"Simulation runs until the button is pressed or another USB command is issued.",
"Simulation runs until the button is pressed or another USB command is issued.\n"
"Most common readers expects the code to be sent in loop without a break (i.e. --gap 0).\n"
"For other, more advanced readers there might be a need to set a non-zero gap value.",
"lf em 410x sim --id 0F0368568B\n"
"lf em 410x sim --id 0F0368568B --clk 32\n"
"lf em 410x sim --id 0F0368568B --gap 0"
"lf em 410x sim --id 0F0368568B --gap 20"
);
void *argtable[] = {
arg_param_begin,
arg_u64_0(NULL, "clk", "<dec>", "<32|64> clock (default 64)"),
arg_str1(NULL, "id", "<hex>", "EM Tag ID number (5 hex bytes)"),
arg_u64_0(NULL, "gap", "<dec>", "gap (0's) between ID repeats (default 20)"),
arg_u64_0(NULL, "gap", "<dec>", "gap (0's) between ID repeats (default 0)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -467,7 +469,7 @@ static int CmdEM410xSim(const char *Cmd) {
// clock is 64 in EM410x tags
int clk = arg_get_u32_def(ctx, 1, 64);
int uid_len = 0;
int gap = arg_get_u32_def(ctx, 3, 20);
int gap = arg_get_u32_def(ctx, 3, 0);
uint8_t uid[5] = {0};
CLIGetHexWithReturn(ctx, 2, uid, &uid_len);
CLIParserFree(ctx);
@ -732,6 +734,7 @@ static int CmdEM410xClone(const char *Cmd) {
packet.cmd = HTSF_82xx;
memcpy(packet.pwd, "\xBB\xDD\x33\x99", HITAGS_PAGE_SIZE);
packet.mode = HITAGS_UID_REQ_FADV;
SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet));
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 4000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");

View file

@ -488,8 +488,23 @@ static int ht2_check_dictionary(uint32_t key_count, uint8_t *keys, uint8_t keyl
uint8_t *pkeys = keys;
uint32_t toti = key_count;
uint32_t cnt = 0;
while (key_count--) {
cnt++;
if (kbd_enter_pressed()) {
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
PrintAndLogEx(INFO, "User aborted");
break;
}
PrintAndLogEx(INPLACE, "Checking Keys %u / %u", cnt, toti);
msleep(30);
if (keylen == 4) {
packet.cmd = HT2F_PASSWORD;
memcpy(packet.pwd, pkeys, keylen);
@ -503,7 +518,7 @@ static int ht2_check_dictionary(uint32_t key_count, uint8_t *keys, uint8_t keyl
clearCommandBuffer();
SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *)&packet, sizeof(packet));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 2000) == false) {
if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 4000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
return PM3_ETIMEOUT;

View file

@ -358,6 +358,9 @@ static int CmdLFHitagSRead(const char *Cmd) {
// access right
if (page_addr == HITAGS_UID_PADR) {
PrintAndLogEx(NORMAL, _RED_("RO ")NOLF);
\
} else if (packet.cmd == HTSF_82xx && page_addr > 40) { // using an 82xx (pages>40 are RO)
PrintAndLogEx(NORMAL, _RED_("RO ")NOLF);
} else if (page_addr == HITAGS_CONFIG_PADR) {
if (card->config_page.s.LCON)
PrintAndLogEx(NORMAL, _YELLOW_("OTP ")NOLF);
@ -444,6 +447,316 @@ static int CmdLFHitagSRead(const char *Cmd) {
return PM3_SUCCESS;
}
static int CmdLFHitagSDump(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag hts dump",
"Read all Hitag S memory and save to file\n"
" Crypto mode: \n"
" - key format ISK high + ISK low\n"
" - default key 4F4E4D494B52 (ONMIKR)\n\n"
" 8268/8310 password mode: \n"
" - default password BBDD3399\n",
"lf hitag hts dump --82xx -> use def pwd\n"
"lf hitag hts dump --82xx -k BBDD3399 -> pwd mode\n"
"lf hitag hts dump --crypto -> use def crypto\n"
"lf hitag hts dump -k 4F4E4D494B52 -> crypto mode\n"
"lf hitag hts dump --nrar 0102030411223344\n"
);
void *argtable[] = {
arg_param_begin,
arg_lit0("8", "82xx", "8268/8310 mode"),
arg_str0(NULL, "nrar", "<hex>", "nonce / answer writer, 8 hex bytes"),
arg_lit0(NULL, "crypto", "crypto mode"),
arg_str0("k", "key", "<hex>", "pwd or key, 4 or 6 hex bytes"),
arg_int0("m", "mode", "<dec>", "response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)"),
arg_str0("f", "file", "<fn>", "specify file name"),
arg_lit0(NULL, "ns", "no save to file"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
lf_hitag_data_t packet;
memset(&packet, 0, sizeof(packet));
if (process_hitags_common_args(ctx, &packet) < 0) {
CLIParserFree(ctx);
return PM3_EINVARG;
}
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
bool nosave = arg_get_lit(ctx, 7);
CLIParserFree(ctx);
// read all pages
packet.page = 0;
packet.page_count = 0;
clearCommandBuffer();
SendCommandNG(CMD_LF_HITAGS_READ, (uint8_t *) &packet, sizeof(packet));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 5000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
print_error(resp.reason);
return PM3_ESOFT;
}
lf_hts_read_response_t *card = (lf_hts_read_response_t *)resp.data.asBytes;
const int hts_mem_sizes[] = {1, 8, 64, 64};
int mem_size = hts_mem_sizes[card->config_page.s.MEMT] * HITAGS_PAGE_SIZE;
hitags_config_t config = card->config_page.s;
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
hitags_config_print(config);
if (nosave) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Called with no save option");
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
if (fnlen < 1) {
char *fptr = filename;
fptr += snprintf(filename, sizeof(filename), "lf-hitags-");
FillFileNameByUID(fptr, card->pages[HITAGS_UID_PADR], "-dump", HITAGS_PAGE_SIZE);
}
pm3_save_dump(filename, (uint8_t *)card->pages, mem_size, jsfHitag);
return PM3_SUCCESS;
}
static int CmdLFHitagSRestore(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag hts restore",
"Restore a dump file onto Hitag S tag\n"
" Crypto mode: \n"
" - key format ISK high + ISK low\n"
" - default key 4F4E4D494B52 (ONMIKR)\n\n"
" 8268/8310 password mode: \n"
" - default password BBDD3399\n",
"lf hitag hts restore -f myfile --82xx -> use def pwd\n"
"lf hitag hts restore -f myfile --82xx -k BBDD3399 -> pwd mode\n"
"lf hitag hts restore -f myfile --crypto -> use def crypto\n"
"lf hitag hts restore -f myfile -k 4F4E4D494B52 -> crypto mode\n"
"lf hitag hts restore -f myfile --nrar 0102030411223344\n"
);
void *argtable[] = {
arg_param_begin,
arg_lit0("8", "82xx", "8268/8310 mode"),
arg_str0(NULL, "nrar", "<hex>", "nonce / answer writer, 8 hex bytes"),
arg_lit0(NULL, "crypto", "crypto mode"),
arg_str0("k", "key", "<hex>", "pwd or key, 4 or 6 hex bytes"),
arg_int0("m", "mode", "<dec>", "response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)"),
arg_str0("f", "file", "<fn>", "specify file name"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
lf_hitag_data_t packet;
memset(&packet, 0, sizeof(packet));
if (process_hitags_common_args(ctx, &packet) < 0) {
CLIParserFree(ctx);
return PM3_EINVARG;
}
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
if (fnlen == 0) {
PrintAndLogEx(ERR, "Must specify a file");
return PM3_EINVARG;
}
// read dump file
uint32_t *dump = NULL;
size_t bytes_read = 0;
if (pm3_load_dump(filename, (void **)&dump, &bytes_read, jsfHitag) != PM3_SUCCESS) {
return PM3_EFILE;
}
// read config to determine memory size and other stuff
packet.page = HITAGS_CONFIG_PADR;
packet.page_count = 1;
clearCommandBuffer();
SendCommandNG(CMD_LF_HITAGS_READ, (uint8_t *)&packet, sizeof(packet));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
free(dump);
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
print_error(resp.reason);
free(dump);
return PM3_ESOFT;
}
lf_hts_read_response_t *config = (lf_hts_read_response_t *)resp.data.asBytes;
hitags_config_t tag_config = config->config_page.s;
const int hts_mem_sizes[] = {1, 8, 64, 64};
int mem_size = hts_mem_sizes[tag_config.MEMT] * HITAGS_PAGE_SIZE;
if (bytes_read != mem_size) {
free(dump);
PrintAndLogEx(FAILED, "Wrong length of dump file. Expected %d bytes, got %zu", mem_size, bytes_read);
return PM3_EFILE;
}
uint8_t *dump_bytes = (uint8_t *)dump;
bool auth_changed = false;
for (int page = packet.page_count + 1; page < hts_mem_sizes[tag_config.MEMT]; page++) { // skip config page
if (packet.cmd == HTSF_82xx && page > 40) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(WARNING, "Using " _YELLOW_("82xx") ", Pages " _YELLOW_("41-63") " will be skipped");
PrintAndLogEx(NORMAL, "");
break;
}
size_t offset = page * HITAGS_PAGE_SIZE;
packet.page = page;
memcpy(packet.data, &dump_bytes[offset], HITAGS_PAGE_SIZE);
PrintAndLogEx(INPLACE, " Writing page "_YELLOW_("%d")", data: " _GREEN_("%02X %02X %02X %02X"), page,
dump_bytes[offset],
dump_bytes[offset + 1],
dump_bytes[offset + 2],
dump_bytes[offset + 3]);
clearCommandBuffer();
SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet));
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
free(dump);
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(FAILED, "Write failed for page %d", page);
print_error(resp.reason);
free(dump);
return PM3_ESOFT;
}
switch (page) {
case 2: // auth first page
if (packet.cmd == HTSF_82xx) {
if (memcmp(packet.pwd, &dump_bytes[offset], HITAGS_PAGE_SIZE) == 0) {
break;
}
auth_changed = true;
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(WARNING, "Password Changed! Old: " _BACK_BLUE_("%02X %02X %02X %02X") ", New: "_BACK_BLUE_("%02X %02X %02X %02X"),
packet.pwd[0], packet.pwd[1], packet.pwd[2], packet.pwd[3],
dump_bytes[offset], dump_bytes[offset + 1],
dump_bytes[offset + 2], dump_bytes[offset + 3]);
memcpy(packet.pwd, &dump_bytes[offset], HITAG_PASSWORD_SIZE);
PrintAndLogEx(SUCCESS, "Using new password for subsequent writes");
}
break;
case 3: // crypto mode
if (packet.cmd == HTSF_KEY) {
if (memcmp(packet.key, &dump_bytes[offset - HITAGS_PAGE_SIZE], HITAG_CRYPTOKEY_SIZE) == 0) {
break;
}
auth_changed = true;
memcpy(packet.key, &dump_bytes[offset - HITAGS_PAGE_SIZE], HITAG_CRYPTOKEY_SIZE);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(WARNING, "New key detected: " _BACK_BLUE_("%02X %02X %02X %02X %02X %02X"),
packet.key[0], packet.key[1], packet.key[2],
packet.key[3], packet.key[4], packet.key[5]);
PrintAndLogEx(SUCCESS, "Using new key for subsequent writes");
}
break;
}
}
// restore config page at end
size_t config_offset = HITAGS_PAGE_SIZE * 1; // page 1
packet.page = HITAGS_CONFIG_PADR;
memcpy(packet.data, &dump_bytes[HITAGS_PAGE_SIZE], HITAGS_PAGE_SIZE);
PrintAndLogEx(SUCCESS, "Applying "_YELLOW_("restored config: ") _GREEN_("%02X %02X %02X %02X"),
dump_bytes[config_offset],
dump_bytes[config_offset + 1],
dump_bytes[config_offset + 2],
dump_bytes[config_offset + 3]);
clearCommandBuffer();
SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet));
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
free(dump);
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(FAILED, "Failed to apply config");
print_error(resp.reason);
free(dump);
return PM3_ESOFT;
}
PrintAndLogEx(INFO, "Write process completed");
if (auth_changed) {
if (packet.cmd == HTSF_82xx) {
PrintAndLogEx(SUCCESS, "New Password: " _BACK_BLUE_("%02X %02X %02X %02X"),
packet.pwd[0], packet.pwd[1], packet.pwd[2], packet.pwd[3]);
} else if (packet.cmd == HTSF_KEY) {
PrintAndLogEx(SUCCESS, "New Key: " _BACK_BLUE_("%02X %02X %02X %02X %02X %02X"),
packet.key[0], packet.key[1], packet.key[2],
packet.key[3], packet.key[4], packet.key[5]);
}
}
return PM3_SUCCESS;
}
static int CmdLFHitagSWrite(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag hts wrbl",
@ -454,6 +767,7 @@ static int CmdLFHitagSWrite(const char *Cmd) {
" 8268/8310 password mode: \n"
" - default password BBDD3399\n",
" lf hitag hts wrbl -p 6 -d 01020304 -> Hitag S/8211, plain mode\n"
" lf hitag hts wrbl -p 6 -d 01020304 --82xx -> use def pwd\n"
" lf hitag hts wrbl -p 6 -d 01020304 --82xx -k BBDD3399 -> 8268/8310, password mode\n"
" lf hitag hts wrbl -p 6 -d 01020304 --nrar 0102030411223344 -> Hitag S, challenge mode\n"
" lf hitag hts wrbl -p 6 -d 01020304 --crypto -> Hitag S, crypto mode, default key\n"
@ -615,6 +929,8 @@ static command_t CommandTable[] = {
{"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("General") " ------------------------"},
{"reader", CmdLFHitagSReader, IfPm3Hitag, "Act like a Hitag S reader"},
{"rdbl", CmdLFHitagSRead, IfPm3Hitag, "Read Hitag S page"},
{"dump", CmdLFHitagSDump, IfPm3Hitag, "Dump Hitag S pages to a file"},
{"restore", CmdLFHitagSRestore, IfPm3Hitag, "Restore Hitag S memory from dump file"},
{"wrbl", CmdLFHitagSWrite, IfPm3Hitag, "Write Hitag S page"},
{"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Simulation") " -----------------------"},
{"sim", CmdLFHitagSSim, IfPm3Hitag, "Simulate Hitag S transponder"},

View file

@ -4266,7 +4266,7 @@ static int CmdT55xxProtect(const char *Cmd) {
return PM3_SUCCESS;
}
// if the difference between a and b is less then or eq to d i.e. does a = b +/- d
// if the difference between a and b is less than or eq to d i.e. does a = b +/- d
#define APPROX_EQ(a, b, d) ((abs(a - b) <= d) ? true : false)
static uint8_t t55sniff_get_packet(const int *pulseBuffer, char *data, uint8_t width0, uint8_t width1, uint8_t tolerance) {

View file

@ -146,7 +146,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t
uint16_t data_len = hdr->data_len;
uint8_t *frame = hdr->frame;
// sanity check tracking position is less then available trace size
// sanity check tracking position is less than available trace size
if (tracepos + TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr) > traceLen) {
PrintAndLogEx(DEBUG, "trace pos offset %"PRIu64 " larger than reported tracelen %u",
tracepos + TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr),
@ -779,6 +779,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
switch (protocol) {
case ISO_14443A:
case ISO_7816_4:
case PROTO_FMCOS20:
annotateIso14443a(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
break;
case PROTO_MIFARE:
@ -836,6 +837,9 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
case SEOS:
annotateSeos(explanation, sizeof(explanation), frame, data_len);
break;
case PROTO_FMCOS20:
annotateFMCOS20(explanation, sizeof(explanation), frame, data_len);
break;
default:
break;
}
@ -1310,6 +1314,7 @@ int CmdTraceList(const char *Cmd) {
"trace list -t thinfilm -> interpret as " _YELLOW_("Thinfilm") "\n"
"trace list -t topaz -> interpret as " _YELLOW_("Topaz") "\n"
"trace list -t mfp -> interpret as " _YELLOW_("MIFARE Plus") "\n"
"trace list -t fmcos20 -> interpret as " _YELLOW_("FMCOS 2.0") "\n"
"\n"
"trace list -t mf -f mfc_default_keys.dic -> use default dictionary file\n"
"trace list -t 14a --frame -> show frame delay times\n"
@ -1377,6 +1382,7 @@ int CmdTraceList(const char *Cmd) {
else if (strcmp(type, "thinfilm") == 0) protocol = THINFILM;
else if (strcmp(type, "topaz") == 0) protocol = TOPAZ;
else if (strcmp(type, "mfp") == 0) protocol = PROTO_MFPLUS;
else if (strcmp(type, "fmcos20") == 0) protocol = PROTO_FMCOS20;
else if (strcmp(type, "") == 0) protocol = -1;
else {
PrintAndLogEx(FAILED, "Unknown protocol \"%s\"", type);
@ -1460,6 +1466,10 @@ int CmdTraceList(const char *Cmd) {
PrintAndLogEx(INFO, _YELLOW_("Hitag 1 / Hitag 2 / Hitag S") " - Timings in ETU (8us)");
}
if (protocol == PROTO_FMCOS20) {
PrintAndLogEx(INFO, _YELLOW_("FMCOS 2.0 / CPU Card") " - Timings n/a");
}
if (protocol == FELICA) {
if (use_us)
PrintAndLogEx(INFO, _YELLOW_("ISO18092 / FeliCa") " - all times are in microseconds");

View file

@ -0,0 +1,218 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
// originality checks with known pk
//-----------------------------------------------------------------------------
#include "originality.h"
#include <string.h> // memcpy
#include "ui.h"
// See tools/recover_pk.py to recover Pk from UIDs and signatures
const ecdsa_publickey_ng_t manufacturer_public_keys[] = {
{
PK_MFC, MBEDTLS_ECP_DP_SECP128R1, 33, "NXP MIFARE Classic MFC1C14_x",
"044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"
},
{
PK_MFC, MBEDTLS_ECP_DP_SECP128R1, 33, "MIFARE Classic / QL88",
"046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"
},
// ref: TagInfo
// NTAG 210/212 ? not present in recover_pk
{
PK_MFUL, MBEDTLS_ECP_DP_SECP128R1, 33, "NXP Public key",
"04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"
},
// ref: AN11341 MIFARE Ultralight EV1 Originality Signature Validation
{
PK_MFUL, MBEDTLS_ECP_DP_SECP128R1, 33, "NXP Ultralight EV1",
"0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"
},
// ref: AN11350 NTAG 21x Originality Signature Validation
{
PK_MFUL, MBEDTLS_ECP_DP_SECP128R1, 33, "NXP NTAG21x (2013)",
"04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"
},
// ref: AN13452 MIFARE Ultralight AES features and hints
{
PK_MFULAES, MBEDTLS_ECP_DP_SECP192R1, 49, "NXP Ultralight AES",
"0453BF8C49B7BD9FE3207A91513B9C1D238ECAB07186B772104AB535F7D3AE63CF7C7F3DD0D169DA3E99E43C6399621A86"
},
// ref: TagInfo
{
PK_MFULAES, MBEDTLS_ECP_DP_SECP192R1, 49, "NXP Ultralight AES (alt key)",
"04DC34DAA903F2726A6225B11C692AF6AB4396575CA12810CBBCE3F781A097B3833B50AB364A70D9C2B641A728A599AE74"
},
{
PK_MFP, MBEDTLS_ECP_DP_SECP224R1, 57, "MIFARE Plus EV1",
"044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"
},
// not present in recover_pk
{
PK_MFP, MBEDTLS_ECP_DP_SECP224R1, 57, "MIFARE Plus EV2",
"04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"
},
{
PK_MFP, MBEDTLS_ECP_DP_SECP224R1, 57, "MIFARE Plus Troika",
"040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"
},
// ref: AN12343 MIFARE DESFire Light Features and Hints
// not present in recover_pk
{
PK_MFDES, MBEDTLS_ECP_DP_SECP224R1, 57, "DESFire Light",
"040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"
},
{
PK_MFDES, MBEDTLS_ECP_DP_SECP224R1, 57, "NTAG413DNA, DESFire EV1",
"04BB5D514F7050025C7D0F397310360EEC91EAF792E96FC7E0F496CB4E669D414F877B7B27901FE67C2E3B33CD39D1C797715189AC951C2ADD"
},
{
PK_MFDES, MBEDTLS_ECP_DP_SECP224R1, 57, "NTAG424DNA, NTAG424DNATT, DESFire EV2, DESFire Light EV2",
"04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A"
},
// ref: AN12196 NTAG 424 DNA and NTAG 424 DNA TagTamper features and hints
{
PK_MFDES, MBEDTLS_ECP_DP_SECP224R1, 57, "NTAG424DNA, DESFire EV2, DESFire Light",
"048A9B380AF2EE1B98DC417FECC263F8449C7625CECE82D9B916C992DA209D68422B81EC20B65A66B5102A61596AF3379200599316A00A1410"
},
{
PK_MFDES, MBEDTLS_ECP_DP_SECP224R1, 57, "DESFire EV2 XL",
"04CD5D45E50B1502F0BA4656FF37669597E7E183251150F9574CC8DA56BF01C7ABE019E29FEA48F9CE22C3EA4029A765E1BC95A89543BAD1BC"
},
{
PK_MFDES, MBEDTLS_ECP_DP_SECP224R1, 57, "DESFire EV3",
"041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743"
},
// ref: AN5101 TruST25 digital signature for ST25TA512B, ST25TA02KB, ST25TA02KB-D and ST25TA02KB-P devices
{
PK_ST25TA, MBEDTLS_ECP_DP_SECP128R1, 33, "ST25TA TruST25 (ST) key 01?",
"041D92163650161A2548D33881C235D0FB2315C2C31A442F23C87ACF14497C0CBA"
},
// ref: AN5660 TruST25 digital signature for ST25TN512 and ST25TN01K devices
{
PK_ST25TN, MBEDTLS_ECP_DP_SECP128R1, 33, "ST25TN TruST25 (ST) KeyID 05",
"0440004F974F7C76BC8718E523D85FA7B354A9A992BFA966CB8219242F9D274FD6"
},
// ref: AN5104 TruST25 digital signature for ST25TV512 and ST25TV02K devices ?
// ref: AN5149 TruST25 digital signature for ST25DV02K-W1, ST25DV02K-W2 devices ?
// ref: AN5580 TruST25 digital signature for ST25TV512C and ST25TV02KC devices
{
PK_ST25TV, MBEDTLS_ECP_DP_SECP128R1, 33, "ST25TV TruST25 (ST) KeyID 04",
"04101E188A8B4CDDBC62D5BC3E0E6850F0C2730E744B79765A0E079907FBDB01BC"
},
{
PK_15, MBEDTLS_ECP_DP_SECP128R1, 33, "NXP ICODE DNA, ICODE SLIX2",
"048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"
},
{
PK_15, MBEDTLS_ECP_DP_SECP128R1, 33, "VivoKey Spark1 Public key",
"04D64BB732C0D214E7EC580736ACF847284B502C25C0F7F2FA86AACE1DADA4387A"
},
// FIXME: what type(s) of card exactly? MFC? MFUL? not present in recover_pk
{
PK_MIK, MBEDTLS_ECP_DP_SECP128R1, 33, "MIKRON Public key",
"04F971EDA742A4A80D32DCF6A814A707CC3DC396D35902F72929FDCD698B3468F2"
},
};
// return pk if match index else -1
int originality_check_verify(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t signature_len, pk_type_t type) {
return originality_check_verify_ex(data, data_len, signature, signature_len, type, false, false);
}
int originality_check_verify_ex(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t signature_len, pk_type_t type, bool reverse, bool hash) {
// test if signature is null
bool is_zero = true;
for (uint8_t i = 0; i < signature_len; i++) {
if (signature[i] != 0) {
is_zero = false;
}
}
if (is_zero) {
return -1;
}
uint8_t tmp_data[data_len];
uint8_t tmp_signature[signature_len];
if (reverse) {
reverse_array_copy(data, data_len, tmp_data);
reverse_array_copy(signature, signature_len, tmp_signature);
} else {
memcpy(tmp_data, data, data_len);
memcpy(tmp_signature, signature, signature_len);
}
for (uint8_t i = 0; i < ARRAYLEN(manufacturer_public_keys); i++) {
if ((type != PK_ALL) && (type != manufacturer_public_keys[i].type))
continue;
int dl = 0;
uint8_t key[manufacturer_public_keys[i].keylen];
param_gethex_to_eol(manufacturer_public_keys[i].value, 0, key, manufacturer_public_keys[i].keylen, &dl);
if (ecdsa_signature_r_s_verify(manufacturer_public_keys[i].grp_id, key, tmp_data, data_len, tmp_signature, signature_len, hash) == 0)
return i;
}
return -1;
}
int originality_check_print(uint8_t *signature, int signature_len, int index) {
if ((index < 0) || (index >= ARRAYLEN(manufacturer_public_keys))) {
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16));
if (signature_len > 16) {
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16));
}
if (signature_len > 32) {
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16));
}
if (signature_len > 48) {
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48));
}
PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed"));
return PM3_ESOFT;
}
PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), manufacturer_public_keys[index].desc);
PrintAndLogEx(INFO, "IC signature public key value: %.32s", manufacturer_public_keys[index].value);
if (manufacturer_public_keys[index].keylen > 16) {
PrintAndLogEx(INFO, " : %.32s", manufacturer_public_keys[index].value + 32);
}
if (manufacturer_public_keys[index].keylen > 32) {
PrintAndLogEx(INFO, " : %.32s", manufacturer_public_keys[index].value + 64);
}
if (manufacturer_public_keys[index].keylen > 48) {
PrintAndLogEx(INFO, " : %.32s", manufacturer_public_keys[index].value + 96);
}
PrintAndLogEx(INFO, " Elliptic curve parameters: %s", mbedtls_ecp_curve_info_from_grp_id(manufacturer_public_keys[index].grp_id)->name);
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16));
if (signature_len > 16) {
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16));
}
if (signature_len > 32) {
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16));
}
if (signature_len > 48) {
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48));
}
PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful"));
return PM3_SUCCESS;
}

View file

@ -0,0 +1,42 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
// originality checks with known pk
//-----------------------------------------------------------------------------
#ifndef ORIGINALITY_H
#define ORIGINALITY_H
#include "common.h"
#include "commonutil.h"
#include "libpcrypto.h"
#include <mbedtls/pk.h>
#include <mbedtls/ecp.h>
typedef enum {PK_MFC, PK_MFUL, PK_MFULAES, PK_MFP, PK_MFDES, PK_ST25TA, PK_ST25TN, PK_ST25TV, PK_15, PK_MIK, PK_ALL} pk_type_t;
typedef struct {
const pk_type_t type;
const mbedtls_ecp_group_id grp_id;
const uint8_t keylen;
const char *desc;
const char *value;
} ecdsa_publickey_ng_t;
int originality_check_verify(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t signature_len, pk_type_t type);
int originality_check_verify_ex(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t signature_len, pk_type_t type, bool reverse, bool hash);
int originality_check_print(uint8_t *signature, int signature_len, int index);
#endif /* originality.h */

View file

@ -1222,14 +1222,17 @@ static int DesfireAuthenticateEV1(DesfireContext_t *dctx, DesfireSecureChannel s
// - Encrypt our response
if (secureChannel == DACd40) {
// Original DESFire (MF3ICD40) silicon can only do encryption operations, so all PCD
// side operations must be decrypt, even when encrypting when doing D40 compatible
// secure channel operations
memset(IV, 0, DESFIRE_MAX_CRYPTO_BLOCK_SIZE);
DesfireCryptoEncDecEx(dctx, DCOMainKey, RndA, rndlen, encRndA, true, true, IV);
DesfireCryptoEncDecEx(dctx, DCOMainKey, RndA, rndlen, encRndA, true, false, IV);
memcpy(both, encRndA, rndlen);
bin_xor(rotRndB, encRndA, rndlen);
memset(IV, 0, DESFIRE_MAX_CRYPTO_BLOCK_SIZE);
DesfireCryptoEncDecEx(dctx, DCOMainKey, rotRndB, rndlen, encRndB, true, true, IV);
DesfireCryptoEncDecEx(dctx, DCOMainKey, rotRndB, rndlen, encRndB, true, false, IV);
memcpy(both + rndlen, encRndB, rndlen);
} else if (secureChannel == DACEV1) {

View file

@ -313,6 +313,10 @@ static void DesfireSecureChannelEncodeD40(DesfireContext_t *ctx, uint8_t cmd, ui
size_t srcmaclen = padded_data_length(srcdatalen - hdrlen, desfire_get_key_block_length(ctx->keyType));
uint8_t mac[32] = {0};
PrintAndLogEx(DEBUG, "MACing");
// Even though original DESFire (MF3ICD40) silicon can only encrypt which means normally
// every PCD operation must be decrypt, verifying a MAC involves the same operation on both
// sides so this is still encrypt here
DesfireCryptoEncDecEx(ctx, DCOSessionKeyMac, data, srcmaclen, NULL, true, true, mac);
if (DesfireEV1D40TransmitMAC(ctx, cmd)) {
@ -889,4 +893,3 @@ bool PrintChannelModeWarning(uint8_t cmd, DesfireSecureChannel secureChannel, De
return found;
}

View file

@ -273,7 +273,9 @@ int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastCh
// max timeout for one chunk of 85keys, 60*3sec = 180seconds
// s70 with 40*2 keys to check, 80*85 = 6800 auth.
// takes about 97s, still some margin before abort
if (timeout > 180) {
// timeout = 180 => ~360s @ Mifare Classic 1k @ ~2300 keys in dict
// ~2300 keys @ Mifare Classic 1k => ~620s
if (timeout > 60 * 12) {
PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting...");
return PM3_ETIMEOUT;
}
@ -1142,7 +1144,7 @@ int mf_chinese_set_uid(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const
res = mf_chinese_set_block(0, block0, NULL, params);
if (res == PM3_SUCCESS) {
params = MAGIC_SINGLE | MAGIC_WUPC;
params = MAGIC_SINGLE | (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC);
memset(block0, 0, sizeof(block0));
res = mf_chinese_get_block(0, block0, params);
if (res == 0) {
@ -1612,6 +1614,10 @@ uint16_t detect_mf_magic(bool is_mfc, uint8_t key_type, uint64_t key) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 4 GDM / USCUID") " ( Gen1 Magic Wakeup )");
}
if ((isMagic & MAGIC_FLAG_GDM_WUP_40_ZUID) == MAGIC_FLAG_GDM_WUP_40_ZUID) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 4 GDM / USCUID") " ( ZUID Gen1 Magic Wakeup )");
}
if ((isMagic & MAGIC_FLAG_GEN_UNFUSED) == MAGIC_FLAG_GEN_UNFUSED) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Write Once / FUID"));
}

View file

@ -279,6 +279,7 @@ const static vocabulary_t vocabulary[] = {
{ 1, "hf iclass view" },
{ 0, "hf iclass wrbl" },
{ 0, "hf iclass creditepurse" },
{ 0, "hf iclass trbl" },
{ 0, "hf iclass chk" },
{ 1, "hf iclass loclass" },
{ 1, "hf iclass lookup" },
@ -428,6 +429,7 @@ const static vocabulary_t vocabulary[] = {
{ 0, "hf mfu cauth" },
{ 0, "hf mfu setpwd" },
{ 0, "hf mfu dump" },
{ 0, "hf mfu incr" },
{ 0, "hf mfu info" },
{ 0, "hf mfu ndefread" },
{ 0, "hf mfu rdbl" },
@ -677,6 +679,8 @@ const static vocabulary_t vocabulary[] = {
{ 1, "lf hitag hts list" },
{ 0, "lf hitag hts reader" },
{ 0, "lf hitag hts rdbl" },
{ 0, "lf hitag hts dump" },
{ 0, "lf hitag hts restore" },
{ 0, "lf hitag hts wrbl" },
{ 0, "lf hitag hts sim" },
{ 1, "lf idteck help" },

View file

@ -283,7 +283,7 @@ static int l_GetFromFlashMemSpiffs(lua_State *L) {
return returnToLuaWithError(L, "No FLASH MEM support");
}
uint32_t start_index = 0, len = 0x40000; //FLASH_MEM_MAX_SIZE
uint32_t start_index = 0, len = 0x40000; // 256KB FLASH_MEM_MAX_SIZE as default value
char destfilename[32] = {0};
size_t size;

View file

@ -696,14 +696,18 @@ void print_progress(uint64_t count, uint64_t max, barMode_t style) {
max = (count > max) ? count : max;
#if defined(HAVE_READLINE)
static int prev_cols = 0;
int rows;
int tmp_cols;
rl_get_screen_size(NULL, &tmp_cols);
// if cols==0: impossible to get screen size, e.g. when scripted
if (tmp_cols != 0) {
// don't call it if cols==0, it would segfault
rl_reset_screen_size(); // refresh Readline idea of the actual screen width
rl_get_screen_size(&rows, &cols);
rl_get_screen_size(NULL, &cols);
if (cols < 36)
return;
}
(void) rows;
if (prev_cols > cols) {
PrintAndLogEx(NORMAL, _CLEAR_ _TOP_ "");
}

View file

@ -1663,3 +1663,75 @@ void HIDUnpack(int idx, wiegand_message_t *packed) {
hid_print_card(&card, FormatTable[idx]);
}
}
int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbose) {
uint8_t n = length - 1;
uint8_t pad = data[0];
char *binstr = (char *)calloc((length * 8) + 1, sizeof(uint8_t));
if (binstr == NULL) {
return PM3_EMALLOC;
}
bytes_2_binstr(binstr, data + 1, n);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(data, length));
PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr));
binstr[strlen(binstr) - pad] = '\0';
PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr));
size_t hexlen = 0;
uint8_t hex[16] = {0};
binstr_2_bytes(hex, &hexlen, binstr);
PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen));
uint32_t top = 0, mid = 0, bot = 0;
if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) {
PrintAndLogEx(ERR, "Binary string contains none <0|1> chars");
free(binstr);
return PM3_EINVARG;
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Wiegand decode");
wiegand_message_t packed = initialize_message_object(top, mid, bot, strlen(binstr));
HIDTryUnpack(&packed);
PrintAndLogEx(NORMAL, "");
if (strlen(binstr) >= 26 && verbose) {
// iCLASS Legacy
PrintAndLogEx(INFO, "Clone to " _YELLOW_("iCLASS Legacy"));
PrintAndLogEx(SUCCESS, " hf iclass encode --ki 0 --bin %s", binstr);
PrintAndLogEx(NORMAL, "");
// HID Prox II
PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("HID Prox II"));
PrintAndLogEx(SUCCESS, " lf hid clone -w H10301 --bin %s", binstr);
PrintAndLogEx(NORMAL, "");
// MIFARE Classic
char mfcbin[28] = {0};
mfcbin[0] = '1';
memcpy(mfcbin + 1, binstr, strlen(binstr));
binstr_2_bytes(hex, &hexlen, mfcbin);
PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic") " (Pm3 simulation)");
PrintAndLogEx(SUCCESS, " hf mf eclr;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 0 -d 049DBA42A23E80884400C82000000000;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 5 -d 020000000000000000000000%s;", sprint_hex_inrow(hex, hexlen));
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 7 -d 484944204953787788AA204752454154;");
PrintAndLogEx(SUCCESS, " hf mf sim --1k -i;");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic 1K"));
PrintAndLogEx(SUCCESS, " hf mf encodehid --bin %s", binstr);
PrintAndLogEx(NORMAL, "");
}
free(binstr);
return PM3_SUCCESS;
}

View file

@ -54,6 +54,7 @@ bool HIDPack(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bo
bool HIDTryUnpack(wiegand_message_t *packed);
void HIDPackTryAll(wiegand_card_t *card, bool preamble);
void HIDUnpack(int idx, wiegand_message_t *packed);
int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbose);
void print_wiegand_code(wiegand_message_t *packed);
void print_desc_wiegand(cardformat_t *fmt, wiegand_message_t *packed);
#endif

View file

@ -165,7 +165,7 @@ static uint8_t get_length_from_header(wiegand_message_t *data) {
len = 0;
}
while (hfmt > 1) {
while (hfmt > 0) {
hfmt >>= 1;
len++;
}

View file

@ -554,8 +554,8 @@ size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_of
end = nbits;
step = 1;
} else {
i = nbits;
end = 0;
i = nbits - 1;
end = -1;
step = -1;
}

View file

@ -366,7 +366,6 @@ void Flashmem_print_status(void) {
);
}
Dbprintf(" Device.................. " _YELLOW_("%s"), spi_flash_data.device);
Dbprintf(" Memory size............. " _YELLOW_("%d kB (%d pages * 64k)"), spi_flash_pages64k * 64, spi_flash_pages64k);
uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0};
@ -384,43 +383,6 @@ void Flashmem_print_status(void) {
FlashStop();
}
void Flashmem_print_info(void) {
if (!FlashInit()) return;
DbpString(_CYAN_("Flash memory dictionary loaded"));
// load dictionary offsets.
uint8_t keysum[2];
uint16_t num;
Flash_CheckBusy(BUSY_TIMEOUT);
uint16_t isok = Flash_ReadDataCont(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k), keysum, 2);
if (isok == 2) {
num = ((keysum[1] << 8) | keysum[0]);
if (num != 0xFFFF && num != 0x0)
Dbprintf(" Mifare.................. "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_MF_KEYS_MAX);
}
Flash_CheckBusy(BUSY_TIMEOUT);
isok = Flash_ReadDataCont(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k), keysum, 2);
if (isok == 2) {
num = ((keysum[1] << 8) | keysum[0]);
if (num != 0xFFFF && num != 0x0)
Dbprintf(" T55x7................... "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_T55XX_KEYS_MAX);
}
Flash_CheckBusy(BUSY_TIMEOUT);
isok = Flash_ReadDataCont(DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages64k), keysum, 2);
if (isok == 2) {
num = ((keysum[1] << 8) | keysum[0]);
if (num != 0xFFFF && num != 0x0)
Dbprintf(" iClass.................. "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_ICLASS_KEYS_MAX);
}
FlashStop();
}
bool FlashDetect(void) {
flash_device_type_t flash_data = {0};
bool ret = false;
@ -442,21 +404,13 @@ bool FlashDetect(void) {
} else {
if (g_dbglevel > 3) Dbprintf("Flash_ReadID failed reading Mfr/Dev (0x90)");
}
// default device is 'unknown'
spi_flash_data.device = SpiFlashTable[0].device;
// Check JEDEC data is valid, compare the reported device types and then calculate the number of pages
// It is covering the most (known) cases of devices but probably there are vendors with different data
// They will be handled when there is such cases
if (ret) {
for (int i = 0; i < ARRAYLEN(SpiFlashTable); i++) {
if (SpiFlashTable[i].manufacturer_id == spi_flash_data.manufacturer_id) {
if (SpiFlashTable[i].jedec_id == spi_flash_data.jedec_id) {
spi_flash_pages64k = SpiFlashTable[i].pages64k;
spi_flash_data.device = SpiFlashTable[i].device;
break;
}
if (SpiFlashTable[i].device_id == spi_flash_data.device_id) {
spi_flash_data.device = SpiFlashTable[i].device;
break;
}
if (spi_flash_data.jedec_id > 0 && spi_flash_data.jedec_id < 0xFFFF) {
if (((spi_flash_data.device_id + 1) & 0x0F) == (spi_flash_data.jedec_id & 0x000F)) {
spi_flash_pages64k = 1 << (spi_flash_data.jedec_id & 0x000F);
}
}
}

View file

@ -134,42 +134,13 @@ uint16_t Flash_Write(uint32_t address, uint8_t *in, uint16_t len);
uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len);
uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len);
void Flashmem_print_status(void);
void Flashmem_print_info(void);
typedef struct {
uint8_t manufacturer_id;
uint8_t device_id;
uint16_t jedec_id;
uint8_t pages64k;
char *device;
} spi_flash_t;
static const spi_flash_t SpiFlashTable[] = {
// first element is the default of 4 * 64kB pages (256kB)
{ 0x00, 0x00, 0x0000, 4, "unknown" }, // 256k
// Manufacturer: Puya
{ 0x85, 0x00, 0x6015, 32, "P25Q16H" }, // 2048k
/// Manufacturer: Renesas
{ 0x1F, 0x46, 0x0000, 32, "AT25XE161D" }, // 2048k
{ 0x1F, 0x47, 0x0000, 64, "AT25XE321D" }, // 4096k
// Manufacturer: Winbond
{ 0xEF, 0x00, 0x3012, 4, "W25X20BV" }, // 256k
{ 0xEF, 0x00, 0x3013, 8, "W25X40BV" }, // 512k
{ 0xEF, 0x00, 0x4013, 8, "W25Q40BV" }, // 512k
{ 0xEF, 0x00, 0x4014, 16, "W25Q80BV" }, // 1024k
{ 0xEF, 0x14, 0x4015, 32, "W25Q16BV" }, // 2048k
{ 0xEF, 0x15, 0x4016, 64, "W25Q32BV" }, // 4096k
{ 0xEF, 0x21, 0x7022, 4, "W25Q02JV" },
// identified by Manufacturer /Device ID
// { 0xEF, 0x05, 0x0000, 1, "Winbond!!!" },
{ 0xEF, 0x10, 0x0000, 2, "W25*10BV!!!" }, // 128k
{ 0xEF, 0x11, 0x0000, 4, "W25*20BV" }, // 256k
{ 0xEF, 0x12, 0x0000, 8, "W25*40BV" }, // 512k
{ 0xEF, 0x13, 0x0000, 16, "W25*80BV" } // 1024k
};
extern uint8_t spi_flash_pages64k;
bool FlashDetect(void);

View file

@ -336,4 +336,5 @@ void WaitUS(uint32_t us) {
void StopTicks(void) {
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS;
}

View file

@ -294,6 +294,7 @@ Options:
-a Input key A (def)
-b Input key B
-f, --file <fn> filename of dictionary
-o <fn> filename suffix for dump and key files
-s, --slow Slower acquisition (required by some non standard cards)
-l, --legacy legacy mode (use the slow `hf mf chk`)
-v, --verbose verbose output (statistics)

View file

@ -2081,7 +2081,7 @@
"command": "hf 15 view",
"description": "Print a ISO-15693 tag dump file (bin/eml/json)",
"notes": [
"hf 15 view -f hf-iclass-AA162D30F8FF12F1-dump.bin"
"hf 15 view -f hf-15-1122334455667788-dump.bin"
],
"offline": true,
"options": [
@ -3500,9 +3500,10 @@
"--loop <dec> The number of key retrieval cycles to perform, max 10000, default 100",
"--debug Re-enables tracing for debugging. Limits cycles to 1.",
"--notest Perform real writes on the card!",
"--allnight Loops the loop for 10 times, recommended loop value of 5000."
"--allnight Loops the loop for 10 times, recommended loop value of 5000.",
"--est Estimates the key updates based on the card's CSN assuming standard key."
],
"usage": "hf iclass legrec [-h] --macs <hex> [--index <dec>] [--loop <dec>] [--debug] [--notest] [--allnight]"
"usage": "hf iclass legrec [-h] --macs <hex> [--index <dec>] [--loop <dec>] [--debug] [--notest] [--allnight] [--est]"
},
"hf iclass loclass": {
"command": "hf iclass loclass",
@ -3683,6 +3684,32 @@
],
"usage": "hf iclass sniff [-hj]"
},
"hf iclass trbl": {
"command": "hf iclass trbl",
"description": "Tear off an iCLASS tag block",
"notes": [
"hf iclass trbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --tdb 100 --tde 150",
"hf iclass trbl --blk 10 -d AAAAAAAAAAAAAAAA --ki 0 --tdb 100 --tde 150"
],
"offline": false,
"options": [
"-h, --help This help",
"-k, --key <hex> Access key as 8 hex bytes",
"--ki <dec> Key index to select key from memory 'hf iclass managekeys'",
"--blk <dec> block number",
"-d, --data <hex> data to write as 8 hex bytes",
"-m, --mac <hex> replay mac data (4 hex bytes)",
"--credit key is assumed to be the credit key",
"--elite elite computations applied to key",
"--raw no computations applied to key",
"--nr replay of NR/MAC",
"-v, --verbose verbose output",
"--shallow use shallow (ASK) reader modulation instead of OOK",
"--tdb <dec> tearoff delay start in ms",
"--tde <dec> tearoff delay end in ms"
],
"usage": "hf iclass trbl [-hv] [-k <hex>] [--ki <dec>] --blk <dec> -d <hex> [-m <hex>] [--credit] [--elite] [--raw] [--nr] [--shallow] --tdb <dec> --tde <dec>"
},
"hf iclass unhash": {
"command": "hf iclass unhash",
"description": "Reverses the hash0 function used generate iclass diversified keys after DES encryption, Function returns the DES crypted CSN. Next step bruteforcing.",
@ -4313,7 +4340,7 @@
},
"hf mf autopwn": {
"command": "hf mf autopwn",
"description": "This command automates the key recovery process on MIFARE Classic cards. It uses the fchk, chk, darkside, nested, hardnested and staticnested to recover keys. If all keys are found, it try dumping card content both to file and emulator memory.",
"description": "This command automates the key recovery process on MIFARE Classic cards. It uses the fchk, chk, darkside, nested, hardnested and staticnested to recover keys. If all keys are found, it try dumping card content both to file and emulator memory. default file name template is `hf-mf-<uid>-<dump|key>.` using suffix the template becomes `hf-mf-<uid>-<dump|key>-<suffix>.`",
"notes": [
"hf mf autopwn",
"hf mf autopwn -s 0 -a -k FFFFFFFFFFFF -> target MFC 1K card, Sector 0 with known key A 'FFFFFFFFFFFF'",
@ -4329,6 +4356,7 @@
"-a Input key A (def)",
"-b Input key B",
"-f, --file <fn> filename of dictionary",
"--suffix <txt> Add this suffix to generated files",
"--slow Slower acquisition (required by some non standard cards)",
"-l, --legacy legacy mode (use the slow `hf mf chk`)",
"-v, --verbose verbose output",
@ -4344,7 +4372,7 @@
"--i2 AVX2",
"--i5 AVX512"
],
"usage": "hf mf autopwn [-hablv] [-k <hex>]... [-s <dec>] [-f <fn>] [--slow] [--ns] [--mini] [--1k] [--2k] [--4k] [--in] [--im] [--is] [--ia] [--i2] [--i5]"
"usage": "hf mf autopwn [-hablv] [-k <hex>]... [-s <dec>] [-f <fn>] [--suffix <txt>] [--slow] [--ns] [--mini] [--1k] [--2k] [--4k] [--in] [--im] [--is] [--ia] [--i2] [--i5]"
},
"hf mf brute": {
"command": "hf mf brute",
@ -4881,7 +4909,7 @@
},
"hf mf gen3blk": {
"command": "hf mf gen3blk",
"description": "Overwrite full manufacturer block for magic Gen3 card - You can specify part of manufacturer block as 4/7-bytes for UID change only NOTE: BCC, SAK, ATQA will be calculated automatically",
"description": "Overwrite full manufacturer block for magic Gen3 card - You can specify part of manufacturer block as 4/7-bytes for UID change only NOTE: BCC and ATQA will be calculated automatically SAK will be automatically set to default values if not specified",
"notes": [
"hf mf gen3blk -> print current data",
"hf mf gen3blk -d 01020304 -> set 4 byte uid",
@ -5151,9 +5179,10 @@
"FM11RF08S specific options: Incompatible with above options, except -k; output in JSON",
"--collect_fm11rf08s collect all nT/{nT}/par_err.",
"--collect_fm11rf08s_with_data collect all nT/{nT}/par_err and data blocks.",
"--collect_fm11rf08s_without_backdoor collect all nT/{nT}/par_err without backdoor. Requires first auth keytype and block",
"-f, --file <fn> Specify a filename for collected data"
],
"usage": "hf mf isen [-hab] [--blk <dec>] [-c <dec>] [-k <hex>] [--blk2 <dec>] [--a2] [--b2] [--c2 <dec>] [--key2 <hex>] [-n <dec>] [--reset] [--hardreset] [--addread] [--addauth] [--incblk2] [--corruptnrar] [--corruptnrarparity] FM11RF08S specific options: [--collect_fm11rf08s] [--collect_fm11rf08s_with_data] [-f <fn>]"
"usage": "hf mf isen [-hab] [--blk <dec>] [-c <dec>] [-k <hex>] [--blk2 <dec>] [--a2] [--b2] [--c2 <dec>] [--key2 <hex>] [-n <dec>] [--reset] [--hardreset] [--addread] [--addauth] [--incblk2] [--corruptnrar] [--corruptnrarparity] FM11RF08S specific options: [--collect_fm11rf08s] [--collect_fm11rf08s_with_data] [--collect_fm11rf08s_without_backdoor] [-f <fn>]"
},
"hf mf mad": {
"command": "hf mf mad",
@ -7116,6 +7145,22 @@
],
"usage": "hf 14a list [-h1crux] [--frame] [-f <fn>]"
},
"hf mfu incr": {
"command": "hf mfu incr",
"description": "Increment a MIFARE Ultralight Ev1 counter Will read but not increment counter if NTAG is detected",
"notes": [
"hf mfu incr -c 0 -v 1337",
"hf mfu incr -c 2 -v 0 -p FFFFFFFF"
],
"offline": false,
"options": [
"-h, --help This help",
"-c, --cnt <dec> Counter index from 0",
"-v, --val <dec> Value to increment by (0-16777215)",
"-p, --pwd <hex> PWD to authenticate with"
],
"usage": "hf mfu incr [-h] -c <dec> -v <dec> [-p <hex>]"
},
"hf mfu info": {
"command": "hf mfu info",
"description": "Get info about MIFARE Ultralight Family styled tag. Sometimes the tags are locked down, and you may need a key to be able to read the information",
@ -8691,18 +8736,18 @@
},
"lf em 410x sim": {
"command": "lf em 410x sim",
"description": "Enables simulation of EM 410x card. Simulation runs until the button is pressed or another USB command is issued.",
"description": "Enables simulation of EM 410x card. Simulation runs until the button is pressed or another USB command is issued. Most common readers expects the code to be sent in loop without a break (i.e. --gap 0). For other, more advanced readers there might be a need to set a non-zero gap value.",
"notes": [
"lf em 410x sim --id 0F0368568B",
"lf em 410x sim --id 0F0368568B --clk 32",
"lf em 410x sim --id 0F0368568B --gap 0"
"lf em 410x sim --id 0F0368568B --gap 20"
],
"offline": false,
"options": [
"-h, --help This help",
"--clk <dec> <32|64> clock (default 64)",
"--id <hex> EM Tag ID number (5 hex bytes)",
"--gap <dec> gap (0's) between ID repeats (default 20)"
"--gap <dec> gap (0's) between ID repeats (default 0)"
],
"usage": "lf em 410x sim [-h] [--clk <dec>] --id <hex> [--gap <dec>]"
},
@ -9798,6 +9843,29 @@
],
"usage": "lf hitag list [-h1crux] [--frame] [-f <fn>]"
},
"lf hitag hts dump": {
"command": "lf hitag hts dump",
"description": "Read all Hitag S memory and save to file Crypto mode: - key format ISK high + ISK low - default key 4F4E4D494B52 (ONMIKR) 8268/8310 password mode: - default password BBDD3399",
"notes": [
"lf hitag hts dump --82xx -> use def pwd",
"lf hitag hts dump --82xx -k BBDD3399 -> pwd mode",
"lf hitag hts dump --crypto -> use def crypto",
"lf hitag hts dump -k 4F4E4D494B52 -> crypto mode",
"lf hitag hts dump --nrar 0102030411223344"
],
"offline": false,
"options": [
"-h, --help This help",
"-8, --82xx 8268/8310 mode",
"--nrar <hex> nonce / answer writer, 8 hex bytes",
"--crypto crypto mode",
"-k, --key <hex> pwd or key, 4 or 6 hex bytes",
"-m, --mode <dec> response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)",
"-f, --file <fn> specify file name",
"--ns no save to file"
],
"usage": "lf hitag hts dump [-h8] [--nrar <hex>] [--crypto] [-k <hex>] [-m <dec>] [-f <fn>] [--ns]"
},
"lf hitag hts help": {
"command": "lf hitag hts help",
"description": "help This help list List Hitag S trace history --------------------------------------------------------------------------------------- lf hitag hts list available offline: yes Alias of `trace list -t hitags` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
@ -9856,6 +9924,28 @@
],
"usage": "lf hitag hts reader [-h@]"
},
"lf hitag hts restore": {
"command": "lf hitag hts restore",
"description": "Restore a dump file onto Hitag S tag Crypto mode: - key format ISK high + ISK low - default key 4F4E4D494B52 (ONMIKR) 8268/8310 password mode: - default password BBDD3399",
"notes": [
"lf hitag hts restore -f myfile --82xx -> use def pwd",
"lf hitag hts restore -f myfile --82xx -k BBDD3399 -> pwd mode",
"lf hitag hts restore -f myfile --crypto -> use def crypto",
"lf hitag hts restore -f myfile -k 4F4E4D494B52 -> crypto mode",
"lf hitag hts restore -f myfile --nrar 0102030411223344"
],
"offline": false,
"options": [
"-h, --help This help",
"-8, --82xx 8268/8310 mode",
"--nrar <hex> nonce / answer writer, 8 hex bytes",
"--crypto crypto mode",
"-k, --key <hex> pwd or key, 4 or 6 hex bytes",
"-m, --mode <dec> response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)",
"-f, --file <fn> specify file name"
],
"usage": "lf hitag hts restore [-h8] [--nrar <hex>] [--crypto] [-k <hex>] [-m <dec>] [-f <fn>]"
},
"lf hitag hts sim": {
"command": "lf hitag hts sim",
"description": "Simulate Hitag S transponder You need to `lf hitag hts eload` first",
@ -9875,6 +9965,7 @@
"description": "Write a page in Hitag S memory. Crypto mode: - key format ISK high + ISK low - default key 4F4E4D494B52 (ONMIKR) 8268/8310 password mode: - default password BBDD3399",
"notes": [
"lf hitag hts wrbl -p 6 -d 01020304 -> Hitag S/8211, plain mode",
"lf hitag hts wrbl -p 6 -d 01020304 --82xx -> use def pwd",
"lf hitag hts wrbl -p 6 -d 01020304 --82xx -k BBDD3399 -> 8268/8310, password mode",
"lf hitag hts wrbl -p 6 -d 01020304 --nrar 0102030411223344 -> Hitag S, challenge mode",
"lf hitag hts wrbl -p 6 -d 01020304 --crypto -> Hitag S, crypto mode, default key",
@ -11758,7 +11849,7 @@
},
"mem load": {
"command": "mem load",
"description": "Loads binary file into flash memory on device Warning: mem area to be written must have been wiped first ( this is already taken care when loading dictionaries )",
"description": "Loads binary file into flash memory on device Warning: mem area to be written must have been wiped first ( dictionaries are serviced as files in spiffs so no wipe is needed )",
"notes": [
"mem load -f myfile -> upload file myfile values at default offset 0",
"mem load -f myfile -o 1024 -> upload file myfile values at offset 1024",
@ -12797,6 +12888,7 @@
"trace list -t thinfilm -> interpret as Thinfilm",
"trace list -t topaz -> interpret as Topaz",
"trace list -t mfp -> interpret as MIFARE Plus",
"trace list -t fmcos20 -> interpret as FMCOS 2.0",
"",
"trace list -t mf -f mfc_default_keys.dic -> use default dictionary file",
"trace list -t 14a --frame -> show frame delay times",
@ -13001,8 +13093,8 @@
}
},
"metadata": {
"commands_extracted": 749,
"commands_extracted": 753,
"extracted_by": "PM3Help2JSON v1.00",
"extracted_on": "2024-11-20T23:37:32"
"extracted_on": "2025-01-05T12:10:45"
}
}

View file

@ -402,6 +402,7 @@ Check column "offline" for their availability.
|`hf iclass view `|Y |`Display content from tag dump file`
|`hf iclass wrbl `|N |`Write Picopass / iCLASS block`
|`hf iclass creditepurse `|N |`Credit epurse value`
|`hf iclass trbl `|N |`Performs tearoff attack on iClass block`
|`hf iclass chk `|N |`Check keys`
|`hf iclass loclass `|Y |`Use loclass to perform bruteforce reader attack`
|`hf iclass lookup `|Y |`Uses authentication trace to check for key in dictionary file`
@ -615,6 +616,7 @@ Check column "offline" for their availability.
|`hf mfu cauth `|N |`Ultralight-C - Authentication`
|`hf mfu setpwd `|N |`Ultralight-C - Set 3DES key`
|`hf mfu dump `|N |`Dump MIFARE Ultralight family tag to binary file`
|`hf mfu incr `|N |`Increments Ev1/NTAG counter`
|`hf mfu info `|N |`Tag information`
|`hf mfu ndefread `|N |`Prints NDEF records from card`
|`hf mfu rdbl `|N |`Read block`
@ -1081,6 +1083,8 @@ Check column "offline" for their availability.
|`lf hitag hts list `|Y |`List Hitag S trace history`
|`lf hitag hts reader `|N |`Act like a Hitag S reader`
|`lf hitag hts rdbl `|N |`Read Hitag S page`
|`lf hitag hts dump `|N |`Dump Hitag S pages to a file`
|`lf hitag hts restore `|N |`Restore Hitag S memory from dump file`
|`lf hitag hts wrbl `|N |`Write Hitag S page`
|`lf hitag hts sim `|N |`Simulate Hitag S transponder`

View file

@ -31,27 +31,29 @@ Therefore a flash address can be interpreted as such:
^^^ offset ^^^ offset 0xF7F
```
Please note that for other flash memory sizes than 256KB a "Page 3" will be the last page of the memory, and address offsets would be dependant on the memory size.
## Layout
^[Top](#top)
Page 0:
* available for user data
* to dump it: `mem dump f page0_dump o 0 l 65536`
* to erase it: `mem wipe p 0`
* to dump it: `mem dump -f page0_dump -o 0 -l 65536`
* to erase it: `mem wipe -p 0`
Page 1:
* available for user data
* to dump it: `mem dump f page1_dump o 65536 l 65536`
* to erase it: `mem wipe p 1`
* to dump it: `mem dump -f page1_dump -o 65536 -l 65536`
* to erase it: `mem wipe -p 1`
Page 2:
* available for user data
* to dump it: `mem dump f page2_dump o 131072 l 65536`
* to erase it: `mem wipe p 2`
* to dump it: `mem dump -f page2_dump -o 131072 -l 65536`
* to erase it: `mem wipe -p 2`
Page 3:
* used by Proxmark3 RDV4 specific functions: flash signature and keys dictionaries, see below for details
* to dump it: `mem dump f page3_dump o 196608 l 65536`
Page 3 (or the last page for memories other than 256KB):
* used by Proxmark3 RDV4 specific functions: flash signature, see below for details
* to dump it: `mem dump -f page3_dump -o 196608 -l 65536`
* to erase it:
* **Beware** it will erase your flash signature so better to back it up first as you won't be able to regenerate it by yourself!
* edit the source code to enable Page 3 as a valid input in the `mem wipe` command.
@ -60,29 +62,19 @@ Page 3:
## Page3 Layout
^[Top](#top)
Page3 is used as follows by the Proxmark3 RDV4 firmware:
* **MF_KEYS**
* offset: page 3 sector 9 (0x9) @ 3*0x10000+9*0x1000=0x39000
* length: 2 sectors
* **ICLASS_KEYS**
* offset: page 3 sector 11 (0xB) @ 3*0x10000+11*0x1000=0x3B000
* length: 1 sector
* **T55XX_KEYS**
* offset: page 3 sector 12 (0xC) @ 3*0x10000+12*0x1000=0x3C000
* length: 1 sector
* **T55XX_CONFIG**
* offset: page 3 sector 13 (0xD) @ 3*0x10000+13*0x1000=0x3D000
* length: 1 sector (actually only a few bytes are used to store `t55xx_config` structure)
Page3 (or the last page for memories other than 256KB) is used as follows by the Proxmark3 RDV4 firmware:
* **RSA SIGNATURE**, see below for details
* offset: page 3 sector 15 (0xF) offset 0xF7F @ 3*0x10000+15*0x1000+0xF7F=0x3FF7F (decimal 262015)
* length: 128 bytes
* offset should have been 0x3FF80 but historically it's one byte off and therefore the last byte of the flash is unused
* **Reserved for future use**
* offset: page 3 sector 14 (0xE)
* **SPIFFS sectors**
* offset: page 3 sectors 13..0 (0xD..0x0)
## RSA signature
^[Top](#top)

View file

@ -41,7 +41,7 @@ Alternatively, and only if the issue still persists after following the steps ab
## Apple Silicon (M1) Notes
^[Top](#top)
Ensure Rosetta 2 is installed as it's currently needed to run `arm-none-eabi-gcc` as it's delivered as a precombiled x86_64 binary.
Ensure Rosetta 2 is installed as it's currently needed to run `arm-none-eabi-gcc` as it's delivered as a precombiled x86_64 binary. **Note: Starting from v4.19552, it is no longer necessary to install Rosetta 2, Apple Silicons are already supported**
If you see an error like:

View file

@ -117,6 +117,17 @@ or
proxmark3 /dev/ttyACM0 --flash --unlock-bootloader --image /tmp/my-bootrom.elf --image /tmp/my-fullimage.elf
```
## Updating SPI flash structure and contents (RDV4.x, some PM3 Easy variants)
^[Top](#top)
For the devices equipped with external SPI flash memory chip in some cases it might be essential to update the memory structure as well as to upload new keys from the dictionaries. To do so execute following command inside the client:
```
[usb] pm3 --> script run init_rdv4
```
For more details prease refer to [this doc](./2_Configuration-and-Verification.md).
### The button trick
^[Top](#top)

View file

@ -50,7 +50,7 @@ Result
### Step 2 Create the Compatibility Container file (CC File)
The CC File is a standard file to store the needed NDEF information to find your NDEF records. This example will contrain the setup for a single NDEF record.
Note: You can define more then one NDEF data file if needed (not covered in this example)
Note: You can define more than one NDEF data file if needed (not covered in this example)
Type : Standard data file
FID : 01 <- File ID can be any uniqure File ID for this AID

View file

@ -15,6 +15,12 @@ RUN pacman -S --noconfirm ocl-icd
# Create rrg user
RUN useradd -ms /bin/bash rrg
RUN passwd -d rrg
ARG UART_GID
# dialout group may already exist on another numeric ID than on host
RUN if [ -n "${UART_GID}" ]; then \
groupadd -g ${UART_GID} mydialout || true; \
usermod -aG ${UART_GID} rrg; \
fi
RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers
USER rrg

View file

@ -1,3 +0,0 @@
#!/bin/bash
docker build -t "pm3-arch:1.0" .

View file

@ -1,4 +0,0 @@
#!/bin/bash
docker rm $(docker ps -aq --filter ancestor=pm3-arch:1.0)
docker image rm pm3-arch:1.0

View file

@ -1,3 +0,0 @@
#!/bin/bash
docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-arch:1.0

View file

@ -1,5 +1,5 @@
#!/bin/bash
for os in archlinux debian-12-bookworm fedora-36 fedora-37 homebrew kali opensuse-leap opensuse-tumbleweed parrot-core-latest ubuntu-20.04 ubuntu-22.04; do
( cd $os && ./docker_build.sh )
for os in archlinux debian-12-bookworm debian-12-bookworm-arm64 debian-12-bookworm-armhf debian-13-trixie fedora-36 fedora-37 homebrew kali opensuse-leap opensuse-tumbleweed parrot-core-latest ubuntu-20.04 ubuntu-22.04; do
( cd $os && ../build.sh )
done

26
docker/build.sh Executable file
View file

@ -0,0 +1,26 @@
#!/bin/bash
if [ ! -e docker_conf.inc ]; then
echo "This script must be run from within one of the subfolders"
exit 1
fi
. docker_conf.inc
# Make sure to connect a Proxmark3 when building if you want to be able to access it from within the Docker instance
UART_PORT="$(../../pm3 --list|grep /dev|head -n1|cut -d' ' -f2)"
if [ -n "$UART_PORT" ]; then
UART_GID="$(stat -c '%g' $UART_PORT)"
BUILDARG="--build-arg UART_GID=$UART_GID"
else
BUILDARG=""
fi
# For cross-platform support:
# cf https://github.com/multiarch/qemu-user-static
#sudo apt install qemu-user-static
# credential=yes needed to get proper sudo support in cross-platform Docker instances
#docker run --rm --privileged multiarch/qemu-user-static --reset -p yes --credential yes
#docker buildx create --use
#docker buildx inspect --bootstrap
#docker buildx build $DOCKER_PLATFORM $BUILDARG -t "$DOCKER_IMAGE" --load .
# Seems to work without buildx:
docker build $DOCKER_PLATFORM $BUILDARG -t "$DOCKER_IMAGE" .

View file

@ -20,6 +20,12 @@ RUN apt-get install -y opencl-dev && \
# Create rrg user
RUN useradd -ms /bin/bash rrg
RUN passwd -d rrg
ARG UART_GID
# dialout group may already exist on another numeric ID than on host
RUN if [ -n "${UART_GID}" ]; then \
groupadd -g ${UART_GID} mydialout || true; \
usermod -aG ${UART_GID} rrg; \
fi
RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers
USER rrg

View file

@ -1,3 +0,0 @@
#!/bin/bash
docker build -t "pm3-debian-bullseye:1.0" .

View file

@ -1,4 +0,0 @@
#!/bin/bash
docker rm $(docker ps -aq --filter ancestor=pm3-debian-bullseye:1.0)
docker image rm pm3-debian-bullseye:1.0

View file

@ -1,3 +0,0 @@
#!/bin/bash
docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-debian-bullseye:1.0

View file

@ -0,0 +1,25 @@
FROM arm64v8/debian:bookworm-slim
ENV LANG=C
ENV DEBIAN_FRONTEND=noninteractive
# qtbase5-dev skipped
RUN apt-get update && \
apt-get dist-upgrade -y && \
apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev libssl-dev libgd-dev sudo && \
apt-get clean
# Create rrg user
RUN useradd -ms /bin/bash rrg
RUN passwd -d rrg
ARG UART_GID
# dialout group may already exist on another numeric ID than on host
RUN if [ -n "${UART_GID}" ]; then \
groupadd -g ${UART_GID} mydialout || true; \
usermod -aG ${UART_GID} rrg; \
fi
RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers
USER rrg
WORKDIR "/home/rrg"
CMD ["/bin/bash"]

View file

@ -0,0 +1,17 @@
# Notes to run tests
```
sudo apt update
sudo apt install -y python3-minimal
sudo apt install -y python3-pip
sudo apt install python3.11-venv
python3 -m venv /tmp/venv
source /tmp/venv/bin/activate
python3 -m pip install --use-pep517 pyaes
python3 -m pip install ansicolors sslcrypto
git config --global --add safe.directory /home/rrg/proxmark3
cd proxmark3
make clean
make -j
tools/pm3_tests.sh --long
```

View file

@ -0,0 +1,25 @@
FROM arm32v7/debian:bookworm-slim
ENV LANG=C
ENV DEBIAN_FRONTEND=noninteractive
# qtbase5-dev skipped
RUN apt-get update && \
apt-get dist-upgrade -y && \
apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev libssl-dev libgd-dev sudo && \
apt-get clean
# Create rrg user
RUN useradd -ms /bin/bash rrg
RUN passwd -d rrg
ARG UART_GID
# dialout group may already exist on another numeric ID than on host
RUN if [ -n "${UART_GID}" ]; then \
groupadd -g ${UART_GID} mydialout || true; \
usermod -aG ${UART_GID} rrg; \
fi
RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers
USER rrg
WORKDIR "/home/rrg"
CMD ["/bin/bash"]

View file

@ -0,0 +1,17 @@
# Notes to run tests
```
sudo apt update
sudo apt install -y python3-minimal
sudo apt install -y python3-pip
sudo apt install python3.11-venv
python3 -m venv /tmp/venv
source /tmp/venv/bin/activate
python3 -m pip install --use-pep517 pyaes
python3 -m pip install ansicolors sslcrypto
git config --global --add safe.directory /home/rrg/proxmark3
cd proxmark3
make clean
make -j
tools/pm3_tests.sh --long
```

View file

@ -18,6 +18,12 @@ RUN apt-get install -y opencl-dev && \
# Create rrg user
RUN useradd -ms /bin/bash rrg
RUN passwd -d rrg
ARG UART_GID
# dialout group may already exist on another numeric ID than on host
RUN if [ -n "${UART_GID}" ]; then \
groupadd -g ${UART_GID} mydialout || true; \
usermod -aG ${UART_GID} rrg; \
fi
RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers
USER rrg

View file

@ -1,3 +0,0 @@
#!/bin/bash
docker build -t "pm3-debian-bookworm:1.0" .

View file

@ -1,4 +0,0 @@
#!/bin/bash
docker rm $(docker ps -aq --filter ancestor=pm3-debian-bookworm:1.0)
docker image rm pm3-debian-bookworm:1.0

View file

@ -1,3 +0,0 @@
#!/bin/bash
docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-debian-bookworm:1.0

View file

@ -18,6 +18,12 @@ RUN apt-get install -y opencl-dev && \
# Create rrg user
RUN useradd -ms /bin/bash rrg
RUN passwd -d rrg
ARG UART_GID
# dialout group may already exist on another numeric ID than on host
RUN if [ -n "${UART_GID}" ]; then \
groupadd -g ${UART_GID} mydialout || true; \
usermod -aG ${UART_GID} rrg; \
fi
RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers
USER rrg

View file

@ -1,3 +0,0 @@
#!/bin/bash
docker build -t "pm3-debian-trixie:1.0" .

View file

@ -1,4 +0,0 @@
#!/bin/bash
docker rm $(docker ps -aq --filter ancestor=pm3-debian-trixie:1.0)
docker image rm pm3-debian-trixie:1.0

View file

@ -1,3 +0,0 @@
#!/bin/bash
docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-debian-trixie:1.0

View file

@ -13,6 +13,12 @@ RUN yum -y install mesa-libOpenCL ocl-icd-devel
# Create rrg user
RUN useradd -ms /bin/bash rrg
RUN passwd -d rrg
ARG UART_GID
# dialout group may already exist on another numeric ID than on host
RUN if [ -n "${UART_GID}" ]; then \
groupadd -g ${UART_GID} mydialout || true; \
usermod -aG ${UART_GID} rrg; \
fi
RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers
USER rrg

View file

@ -1,3 +0,0 @@
#!/bin/bash
docker build -t "pm3-fedora-36:1.0" .

View file

@ -1,4 +0,0 @@
#!/bin/bash
docker rm $(docker ps -aq --filter ancestor=pm3-fedora-36:1.0)
docker image rm pm3-fedora-36:1.0

View file

@ -1,3 +0,0 @@
#!/bin/bash
docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-fedora-36:1.0

Some files were not shown because too many files have changed in this diff Show more