mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-05-12 11:18:11 -07:00
Merge branch 'master' into cherry_pick_emv
Signed-off-by: Iceman <iceman@iuse.se>
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
+45
-14
@@ -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)
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
+39
-23
@@ -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);
|
||||
|
||||
+36
-45
@@ -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
|
||||
|
||||
|
||||
+243
-112
@@ -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);
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
+1
-1
@@ -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
|
||||
|
||||
+94
-69
@@ -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)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user