mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-07-06 13:01:37 -07:00
Merge branch 'master' into cherry_pick_emv
Signed-off-by: Iceman <iceman@iuse.se>
This commit is contained in:
commit
76ad5a5b51
149 changed files with 7795 additions and 2976 deletions
1
.github/workflows/codeql-analysis.yml
vendored
1
.github/workflows/codeql-analysis.yml
vendored
|
@ -12,6 +12,7 @@
|
|||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
|
|
1
.github/workflows/rebase.yml
vendored
1
.github/workflows/rebase.yml
vendored
|
@ -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:
|
||||
|
|
4
.github/workflows/uniq.yaml
vendored
4
.github/workflows/uniq.yaml
vendored
|
@ -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
|
||||
|
|
59
CHANGELOG.md
59
CHANGELOG.md
|
@ -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)
|
||||
|
@ -45,14 +76,14 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
|
|||
- Added `hf 14b setuid` - set uid on magic 14b tag (@iceman1001)
|
||||
- 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)
|
||||
- Changed split PacketResponseNG status into status and reason (@douniwan5788)
|
||||
- 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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -37,7 +37,8 @@
|
|||
#include "protocols.h"
|
||||
#include "pmflash.h"
|
||||
#include "flashmem.h" // persistence on flash
|
||||
#include "appmain.h" // print stack
|
||||
#include "spiffs.h" // spiffs
|
||||
#include "appmain.h" // print stack
|
||||
|
||||
/*
|
||||
Notes about EM4xxx timings.
|
||||
|
@ -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
|
||||
|
||||
|
|
|
@ -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,121 +1076,230 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool
|
|||
|
||||
LED_C_ON();
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
}
|
||||
if (have_uid == false) { // need a full select cycle to get the uid first
|
||||
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;
|
||||
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;
|
||||
}
|
||||
switch (card_info.uidlen) {
|
||||
case 4 :
|
||||
cascade_levels = 1;
|
||||
break;
|
||||
case 7 :
|
||||
cascade_levels = 2;
|
||||
break;
|
||||
case 10:
|
||||
cascade_levels = 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
have_uid = true;
|
||||
} else { // no need for anticollision. We can directly select the card
|
||||
// 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);
|
||||
}
|
||||
|
||||
uint32_t nt1 = 0;
|
||||
if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
};
|
||||
if ((with_data) && (keyType == 0)) {
|
||||
uint8_t data[16];
|
||||
uint8_t blocks = 4;
|
||||
if (blockNo >= MIFARE_1K_MAXSECTOR * 4) {
|
||||
// special RF08S advanced authentication blocks, let's dump in emulator just in case
|
||||
blocks = 8;
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
for (uint16_t tb = blockNo; tb < blockNo + blocks; tb++) {
|
||||
memset(data, 0x00, sizeof(data));
|
||||
int res = mifare_classic_readblock(pcs, tb, data);
|
||||
if (res == 1) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Read error");
|
||||
isOK = PM3_ESOFT;
|
||||
if (have_uid == false) { // need a full select cycle to get the uid first
|
||||
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;
|
||||
}
|
||||
have_uid = true;
|
||||
} else { // no need for anticollision. We can directly select the card
|
||||
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;
|
||||
}
|
||||
emlSetMem_xt(data, tb, 1, 16);
|
||||
}
|
||||
}
|
||||
// nested authentication
|
||||
uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType + 4, 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;
|
||||
}
|
||||
uint32_t nt_enc = bytes_to_num(receivedAnswer, 4);
|
||||
crypto1_init(pcs, ui64Key);
|
||||
uint32_t nt = crypto1_word(pcs, nt_enc ^ cuid, 1) ^ nt_enc;
|
||||
// Dbprintf("Sec %2i key %i nT=%08x", sec, keyType + 4, nt);
|
||||
// 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);
|
||||
|
||||
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, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
};
|
||||
uint32_t nt1 = 0;
|
||||
if (mifare_classic_authex_cmd(pcs, cuid, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
};
|
||||
if ((with_data) && (keyType == 0)) {
|
||||
uint8_t data[16];
|
||||
uint8_t blocks = 4;
|
||||
if (blockNo >= MIFARE_1K_MAXSECTOR * 4) {
|
||||
// special RF08S advanced authentication blocks, let's dump in emulator just in case
|
||||
blocks = 8;
|
||||
}
|
||||
for (uint16_t tb = blockNo; tb < blockNo + blocks; tb++) {
|
||||
memset(data, 0x00, sizeof(data));
|
||||
int res = mifare_classic_readblock(pcs, tb, data);
|
||||
if (res == 1) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Read error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
}
|
||||
emlSetMem_xt(data, tb, 1, 16);
|
||||
}
|
||||
}
|
||||
// nested authentication
|
||||
uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType + 4, 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;
|
||||
}
|
||||
uint32_t nt_enc = bytes_to_num(receivedAnswer, 4);
|
||||
crypto1_init(pcs, ui64Key);
|
||||
uint32_t nt = crypto1_word(pcs, nt_enc ^ cuid, 1) ^ nt_enc;
|
||||
// Dbprintf("Sec %2i key %i nT=%08x", sec, keyType + 4, nt);
|
||||
// store nt (first half)
|
||||
num_to_bytes(nt >> 16, 2, buf + (keyType * 8));
|
||||
// send some crap to fail auth
|
||||
CHK_TIMEOUT();
|
||||
|
||||
// nested authentication on regular keytype
|
||||
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;
|
||||
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, blockNo, MIFARE_AUTH_KEYA + keyType + 4, ui64Key, AUTH_FIRST, &nt1, NULL, NULL, NULL, false, false)) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Auth1 error");
|
||||
isOK = PM3_ESOFT;
|
||||
goto out;
|
||||
};
|
||||
|
||||
// nested authentication on regular keytype
|
||||
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
|
||||
emlSetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + sec, 1, MIFARE_BLOCK_SIZE);
|
||||
// send some crap to fail auth
|
||||
CHK_TIMEOUT();
|
||||
}
|
||||
// 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
|
||||
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);
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
@ -2837,7 +2956,16 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) {
|
|||
ReaderTransmit(gen4gdmGetConf, sizeof(gen4gdmGetConf), NULL);
|
||||
res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par);
|
||||
if (res > 1) {
|
||||
flag |= MAGIC_FLAG_GDM_WUP_40;
|
||||
// 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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
373
armsrc/sam_common.c
Normal 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
49
armsrc/sam_common.h
Normal 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
|
|
@ -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"
|
||||
|
|
|
@ -17,5 +17,6 @@
|
|||
#define __SAM_MFC_H
|
||||
|
||||
#include "common.h"
|
||||
#include "sam_common.h"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#define __SAM_PICOPASS_H
|
||||
|
||||
#include "common.h"
|
||||
#include "sam_common.h"
|
||||
|
||||
int sam_picopass_get_pacs(void);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -17,5 +17,8 @@
|
|||
#define __SAM_SEOS_H
|
||||
|
||||
#include "common.h"
|
||||
#include "pm3_cmd.h"
|
||||
|
||||
int sam_seos_get_pacs(PacketCommandNG *c);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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\
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -499,7 +499,7 @@ end
|
|||
function segmentsToTag(bytes, tag)
|
||||
if(#bytes>23) then
|
||||
local start=23
|
||||
local i=-1
|
||||
local i=-1
|
||||
if (istable(tag)) then
|
||||
repeat
|
||||
i=i+1
|
||||
|
@ -1768,7 +1768,7 @@ function getSegmentData(bytes, start, index)
|
|||
-- wrp (write proteted) = byte 2
|
||||
segment.WRP = tonumber(bytes[start+2],16)
|
||||
-- wrc (write control) - bit 4-6 of byte 3
|
||||
--segment.WRC = bbit("0x"..bytes[start+3],4,3)
|
||||
--segment.WRC = bbit("0x"..bytes[start+3],4,3)
|
||||
segment.WRC = (tonumber(bytes[start+3], 16) >> 4) & 0x07
|
||||
-- rd (read disabled) - bit 7 of byte 3
|
||||
--segment.RD = bbit("0x"..bytes[start+3],7,1)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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)]
|
||||
|
|
|
@ -12,62 +12,118 @@ except ModuleNotFoundError:
|
|||
return str(s)
|
||||
|
||||
spi = {
|
||||
0x68:{
|
||||
"manufacturer": "Boya",
|
||||
"jedec" : {
|
||||
0x40: {
|
||||
0x15: {
|
||||
"part": "BY25Q16BS",
|
||||
"size": "16mbits",
|
||||
"sizeB": "2MB",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
0x85:{
|
||||
"manufacturer": "Puya",
|
||||
0x60: {
|
||||
0x15: {
|
||||
"part": "P25Q16H",
|
||||
"size": "16mbits",
|
||||
"sizeB": "2MB",
|
||||
"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",
|
||||
0x30: {
|
||||
0x11: {
|
||||
"part": "W25X10BV",
|
||||
"size": "1mbits",
|
||||
"sizeB": "128KB",
|
||||
"jedec" : {
|
||||
0x30: {
|
||||
0x11: {
|
||||
"part": "W25X10BV",
|
||||
"size": "1mbits",
|
||||
"sizeB": "128KB",
|
||||
},
|
||||
0x12: {
|
||||
"part": "W25X20BV",
|
||||
"size": "2mbits",
|
||||
"sizeB": "256KB",
|
||||
},
|
||||
0x13: {
|
||||
"part": "W25X40BV",
|
||||
"size": "4mbits",
|
||||
"sizeB": "512KB",
|
||||
},
|
||||
},
|
||||
0x12: {
|
||||
"part": "W25X20BV",
|
||||
"size": "2mbits",
|
||||
"sizeB": "256KB",
|
||||
0x40: {
|
||||
0x12: {
|
||||
"part": "W25Q20BV",
|
||||
"size": "2mbits",
|
||||
"sizeB": "256KB",
|
||||
},
|
||||
0x13: {
|
||||
"part": "W25Q40BV",
|
||||
"size": "4mbits",
|
||||
"sizeB": "512KB",
|
||||
},
|
||||
0x14: {
|
||||
"part": "W25Q80BV",
|
||||
"size": "8mbits",
|
||||
"sizeB": "1MB",
|
||||
},
|
||||
0x15: {
|
||||
"part": "W25Q16BV",
|
||||
"size": "16mbits",
|
||||
"sizeB": "2MB",
|
||||
},
|
||||
0x16: {
|
||||
"part": "W25Q32BV",
|
||||
"size": "32mbits",
|
||||
"sizeB": "4MB",
|
||||
},
|
||||
0x17: {
|
||||
"part": "W25Q64BV",
|
||||
"size": "64mbits",
|
||||
"sizeB": "8MB",
|
||||
},
|
||||
},
|
||||
0x13: {
|
||||
"part": "W25X40BV",
|
||||
"size": "4mbits",
|
||||
"sizeB": "512KB",
|
||||
},
|
||||
},
|
||||
0x40: {
|
||||
0x13: {
|
||||
"part": "W25Q40BV",
|
||||
"size": "4mbits",
|
||||
"sizeB": "512KB",
|
||||
},
|
||||
0x14: {
|
||||
"part": "W25Q80BV",
|
||||
"size": "8mbits",
|
||||
"sizeB": "1MB",
|
||||
},
|
||||
0x15: {
|
||||
"part": "W25Q16BV",
|
||||
"size": "16mbits",
|
||||
"sizeB": "2MB",
|
||||
},
|
||||
0x16: {
|
||||
"part": "W25Q32BV",
|
||||
"size": "32mbits",
|
||||
"sizeB": "4MB",
|
||||
},
|
||||
},
|
||||
0x70: {
|
||||
0x22: {
|
||||
"part": "W25Q02JV-IM",
|
||||
"size": "2mbits",
|
||||
"sizeB": "256KB",
|
||||
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",
|
||||
"sizeB": "256KB",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
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]
|
||||
|
||||
# For paFloat32 sample values must be in range [-1.0, 1.0]
|
||||
stream = p.open(format=pyaudio.paFloat32,
|
||||
channels=1,
|
||||
rate=sampling_freq,
|
||||
output=True)
|
||||
|
||||
# Initial voltage to frequency values
|
||||
min_v = 100.0
|
||||
max_v = 0.0
|
||||
v = 0
|
||||
out_freq = min_freq
|
||||
# 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
|
||||
|
||||
# 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 = ""
|
||||
|
||||
# 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:
|
||||
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.
|
||||
"""
|
||||
|
||||
# Read Proxmark3 client's stdout and extract voltage values
|
||||
if(select([pm3_proc.stdout], [], [], 0)[0]):
|
||||
# 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
|
||||
|
||||
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 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]
|
||||
self.stream = self.p.open(format=paFloat32,
|
||||
channels=1,
|
||||
rate=sampling_freq,
|
||||
output=True,
|
||||
stream_callback=pyaudio_callback)
|
||||
|
||||
# Initial voltage to frequency values
|
||||
self.min_v = 50000.0
|
||||
self.max_v = 0.0
|
||||
|
||||
# Setting the signal handler for SIGINT (Ctrl+C)
|
||||
signal.signal(signal.SIGINT, self.signal_handler)
|
||||
|
||||
# Start the stream
|
||||
self.stream.start_stream()
|
||||
|
||||
def __exit__(self):
|
||||
self.stream.stop_stream()
|
||||
self.stream.close()
|
||||
self.p.terminate()
|
||||
|
||||
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()
|
||||
|
|
|
@ -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",
|
||||
|
|
2812
client/src/atrs.h
2812
client/src/atrs.h
File diff suppressed because it is too large
Load diff
|
@ -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,44 +317,57 @@ static int CmdFlashMemLoad(const char *Cmd) {
|
|||
uint32_t bytes_sent = 0;
|
||||
uint32_t bytes_remaining = datalen;
|
||||
|
||||
|
||||
// fast push mode
|
||||
g_conn.block_after_ACK = true;
|
||||
|
||||
while (bytes_remaining > 0) {
|
||||
uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining);
|
||||
|
||||
clearCommandBuffer();
|
||||
|
||||
flashmem_old_write_t payload = {
|
||||
.startidx = offset + bytes_sent,
|
||||
.len = bytes_in_packet,
|
||||
};
|
||||
memcpy(payload.data, data + bytes_sent, bytes_in_packet);
|
||||
SendCommandNG(CMD_FLASHMEM_WRITE, (uint8_t *)&payload, sizeof(payload));
|
||||
|
||||
bytes_remaining -= bytes_in_packet;
|
||||
bytes_sent += bytes_in_packet;
|
||||
|
||||
PacketResponseNG resp;
|
||||
if (WaitForResponseTimeout(CMD_FLASHMEM_WRITE, &resp, 2000) == false) {
|
||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
||||
g_conn.block_after_ACK = false;
|
||||
// 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 PM3_ETIMEOUT;
|
||||
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;
|
||||
|
||||
while (bytes_remaining > 0) {
|
||||
uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining);
|
||||
|
||||
clearCommandBuffer();
|
||||
|
||||
flashmem_old_write_t payload = {
|
||||
.startidx = offset + bytes_sent,
|
||||
.len = bytes_in_packet,
|
||||
};
|
||||
memcpy(payload.data, data + bytes_sent, bytes_in_packet);
|
||||
SendCommandNG(CMD_FLASHMEM_WRITE, (uint8_t *)&payload, sizeof(payload));
|
||||
|
||||
bytes_remaining -= bytes_in_packet;
|
||||
bytes_sent += bytes_in_packet;
|
||||
|
||||
PacketResponseNG resp;
|
||||
if (WaitForResponseTimeout(CMD_FLASHMEM_WRITE, &resp, 2000) == false) {
|
||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
||||
g_conn.block_after_ACK = false;
|
||||
free(data);
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
|
||||
if (resp.status != PM3_SUCCESS) {
|
||||
g_conn.block_after_ACK = false;
|
||||
PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent);
|
||||
free(data);
|
||||
return PM3_EFLASH;
|
||||
}
|
||||
}
|
||||
|
||||
if (resp.status != PM3_SUCCESS) {
|
||||
g_conn.block_after_ACK = false;
|
||||
PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent);
|
||||
free(data);
|
||||
return PM3_EFLASH;
|
||||
}
|
||||
g_conn.block_after_ACK = false;
|
||||
PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu")" bytes to offset "_GREEN_("%u"), datalen, offset);
|
||||
}
|
||||
|
||||
g_conn.block_after_ACK = false;
|
||||
free(data);
|
||||
PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu")" bytes to offset "_GREEN_("%u"), datalen, offset);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,12 +150,12 @@ 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" },
|
||||
{ 0x44, "Gentag Inc. USA" },
|
||||
{ 0x45, "Invengo Information Technology Co.Ltd China" },
|
||||
{ 0x45, "Invengo Information Technology Co. Ltd China" },
|
||||
{ 0x46, "Guangzhou Sysur Microelectronics, Inc China" },
|
||||
{ 0x47, "CEITEC S.A. Brazil" },
|
||||
{ 0x48, "Shanghai Quanray Electronics Co. Ltd. China" },
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
reason = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
int index = -1;
|
||||
index = originality_check_verify(uid, 8, signature, 32, PK_MFC);
|
||||
if (index >= 0) {
|
||||
reason = 1;
|
||||
} 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;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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;
|
||||
} else {
|
||||
// try with reversed uid / signature
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
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
|
||||
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
|
||||
PrintAndLogEx(DEBUG, "SLI, SLIX, SLIX-L, or SLIX-S Detected checking EAS status");
|
||||
return NxpTestEAS(uid);
|
||||
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[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[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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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(¶ms, 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"},
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
@ -2491,14 +2452,15 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) {
|
|||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_strx0("k", "key", "<hex>", "Known key, 12 hex bytes"),
|
||||
arg_int0("s", "sector", "<dec>", "Input sector number"),
|
||||
arg_lit0("a", NULL, "Input key A (def)"),
|
||||
arg_lit0("b", NULL, "Input key B"),
|
||||
arg_str0("f", "file", "<fn>", "filename of dictionary"),
|
||||
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"),
|
||||
arg_strx0("k", "key", "<hex>", "Known key, 12 hex bytes"),
|
||||
arg_int0("s", "sector", "<dec>", "Input sector number"),
|
||||
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"),
|
||||
|
||||
arg_lit0(NULL, "ns", "No save to file"),
|
||||
|
||||
|
@ -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,21 +8044,32 @@ static int CmdHF14AGen4Info(const char *cmd) {
|
|||
size_t resplen = 0;
|
||||
int res = 0;
|
||||
|
||||
if (dlen != 32) {
|
||||
res = mfG4GetConfig(pwd, resp, &resplen, verbose);
|
||||
if (res != PM3_SUCCESS || resplen == 0) {
|
||||
if (res == PM3_ETIMEOUT)
|
||||
PrintAndLogEx(ERR, "No card in the field or card command timeout.");
|
||||
else
|
||||
PrintAndLogEx(ERR, "Error get config. Maybe not a Gen4 card?. error=%d rlen=%zu", res, resplen);
|
||||
if (dlen == 0) {
|
||||
if (IfPm3Iso14443a()) {
|
||||
res = mfG4GetConfig(pwd, resp, &resplen, verbose);
|
||||
if (res != PM3_SUCCESS || resplen == 0) {
|
||||
if (res == PM3_ETIMEOUT)
|
||||
PrintAndLogEx(ERR, "No card in the field or card command timeout.");
|
||||
else
|
||||
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"},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
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]);
|
||||
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) {
|
||||
plus_print_signature(card.uid, card.uidlen, signature, signature_len);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
PrintAndLogEx(SUCCESS, " Internal: %02X ( %s )", data[9], (data[9] == 0x48) ? _GREEN_("default") : _RED_("not default"));
|
||||
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"},
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
||||
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;
|
||||
int index = originality_check_verify_ex(uid, 7, signature, 32, PK_ST25TA, false, true);
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
|
|
218
client/src/crypto/originality.c
Normal file
218
client/src/crypto/originality.c
Normal 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;
|
||||
}
|
42
client/src/crypto/originality.h
Normal file
42
client/src/crypto/originality.h
Normal 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 */
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
rl_reset_screen_size(); // refresh Readline idea of the actual screen width
|
||||
rl_get_screen_size(&rows, &cols);
|
||||
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(NULL, &cols);
|
||||
|
||||
if (cols < 36)
|
||||
return;
|
||||
if (cols < 36)
|
||||
return;
|
||||
}
|
||||
|
||||
(void) rows;
|
||||
if (prev_cols > cols) {
|
||||
PrintAndLogEx(NORMAL, _CLEAR_ _TOP_ "");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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`
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ External 256kbytes flash is a unique feature of the RDV4 edition.
|
|||
|
||||
Flash memory is
|
||||
|
||||
* 256KB (0x40000= 262144)
|
||||
* 256KB (0x40000 = 262144)
|
||||
* divided into 4 pages of 64KB (0x10000 = 65536)
|
||||
* 4 pages divided into 16 sectors of 4KB (0x1000 = 4096), so last sector is at 0x3F000
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker build -t "pm3-arch:1.0" .
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker rm $(docker ps -aq --filter ancestor=pm3-arch:1.0)
|
||||
docker image rm pm3-arch:1.0
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-arch:1.0
|
|
@ -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
26
docker/build.sh
Executable 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" .
|
|
@ -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
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker build -t "pm3-debian-bullseye:1.0" .
|
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-debian-bullseye:1.0
|
25
docker/debian-12-bookworm-arm64/Dockerfile
Normal file
25
docker/debian-12-bookworm-arm64/Dockerfile
Normal 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"]
|
17
docker/debian-12-bookworm-arm64/README.md
Normal file
17
docker/debian-12-bookworm-arm64/README.md
Normal 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
|
||||
```
|
25
docker/debian-12-bookworm-armhf/Dockerfile
Normal file
25
docker/debian-12-bookworm-armhf/Dockerfile
Normal 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"]
|
17
docker/debian-12-bookworm-armhf/README.md
Normal file
17
docker/debian-12-bookworm-armhf/README.md
Normal 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
|
||||
```
|
|
@ -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
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker build -t "pm3-debian-bookworm:1.0" .
|
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-debian-bookworm:1.0
|
|
@ -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
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker build -t "pm3-debian-trixie:1.0" .
|
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-debian-trixie:1.0
|
|
@ -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
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker build -t "pm3-fedora-36:1.0" .
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue