Merge branch 'master' into cherry_pick_emv

Signed-off-by: Iceman <iceman@iuse.se>
This commit is contained in:
Iceman
2025-01-14 16:26:35 +01:00
committed by GitHub
149 changed files with 7795 additions and 2976 deletions
+1
View File
@@ -12,6 +12,7 @@
name: "CodeQL"
on:
workflow_dispatch:
push:
branches: [ master ]
pull_request:
+1
View File
@@ -2,6 +2,7 @@ on: pull_request_target
name: Changelog Reminder
jobs:
remind:
if: github.repository_owner = 'RfidResearchGroup'
name: Changelog Reminder
runs-on: ubuntu-latest
steps:
+2 -2
View File
@@ -18,5 +18,5 @@ jobs:
- name: check unique keys in dic files
shell: bash
run: |
find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | sed 's/ *#.*//;/^$/d' | sort | uniq -i -d -c | sort -n -r "
if [[ $(find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | sed 's/ *#.*//;/^$/d' | sort | uniq -i -d -c | sort -n -r " | grep -v '^\./' | wc -l) -gt 0 ]]; then exit 1; fi
find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | sed 's/ *#.*//;/^$/d' | sed 's/\(.*\)/\U\1/' | sort | uniq -i -d -c | sort -n -r "
if [[ $(find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | sed 's/ *#.*//;/^$/d' | sed 's/\(.*\)/\U\1/' | sort | uniq -i -d -c | sort -n -r " | grep -v '^\./' | wc -l) -gt 0 ]]; then exit 1; fi
+45 -14
View File
@@ -3,20 +3,51 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Fixed symlink name in `mem spiffs tree` (@ANTodorov)
- Fixed reported file/link names when `mem spiffs wipe` (ANTodorov)
- Changed `hf mf info` - now differentiates between full USCUID and cut down ZUID chips (@nvx)
- Changed `lf hitag chk` - added key counter, client side abort and minor delay (@iceman1001)
- Added `hf seos sam` - Added support for HID SAM SEOS communications (@jkramarz)
- Changed (extended) area accessible by spiffs into last page of FLASH (@piotrva)
- Changed flash-stored key dictionaries (Mifare, iClass, T55XX) and T55XX configurations to SPIFFS files (@piotrva)
- Changed `lf em 410x sim` to use default gap value of 0 and extended help (@piotrva)
- Changed `hf 14a info` - now identifies MIAFRE Duox (@iceman1001)
- Added `hf iclass trbl` to perform tear-off attacks on iClass (@antiklesys)
- Added support for connection to host device in all Docker envs (@doegox)
- Changed `hf 15 info` to show all type matches and check ST25TVxC signature (@doegox)
- Added initial support for ST25TN and its signature verification (@doegox)
- Changed originality checks handling to refactor code and pk data (@doegox)
- Changed `uniq.yaml` workflow to be case-insensitive (@iceman1001)
- Fixed `mem load --mfc` not erasing all SPI flash blocks after extending to 4095 keys (@piotrva)
- Changed extended area for Mifare keys in SPI flash to hold 4095 keys (@piotrva)
- Fixed DESFire D40 secure channel crypto (@nvx)
- Fixed `hf mfp info` fix signature check on 4b UID cards (@doegox)
- Automatically set maximum read/write block when using predefined types in `hf_mf_ultimatecard` script (@piotrva)
- Changed SPI flash detection to calculate the size instead of table lookup, updated spi_flash_decode.py script with more ICs (@ANTodorov)
- Fixed `hf/lf tune` segfault when called from script (@doegox)
- Added option to set and get maximum read/write block number using `hf_mf_ultimatecard` script (@piotrva)
- Added JEDEC information for SPI flash W25Q64JV (@ANTodorov)
- Added special iclass legacy config cards in `hf iclass configcard` (@antiklesys)
- Added simulation function to `hf iclass legrec` (@antiklesys)
- Added keys from Momentum firmware projects. (@onovy)
- Added Dutch Statistics Agency default key (@eagle00789)
- Fixed Wiegand decode with hex input dropping the first bit (@emilyastranova)
- Changed `hf mf autopwn` - now allows for custom suffix (@zxkmm)
## [Orca.4.19552][2024-11-22]
- Fixed `hf_legic.lua` - removed bit32 commands from the script (@diorch1968)
- Fixed `mem spiffs tree` - now show correct symlink name (@ANTodorov)
- Fixed `mem spiffs wipe` - reported file/link names is now correct (@ANTodorov)
- Updated atrs list (@iceman1001)
- Added support for a new KDF (@iceman1001)
- Added Inner range aid and mad entries (@iceman1001)
- Changed `mem spiffs` - Use all available space in SPI flash (@ANTodorov)
- Fixed wrong size check in MifareSim (@iceman1001)
- Fixed `hf mf sim` - wrong size check in MifareSim (@iceman1001)
- Fixed `hf mf sim` not to respond to authentication attempts for sectors out of bound for selected Mifare type (@piotrva)
- Added option to build against non-default python3 with CMake as well (@doegox)
- Added option to build against non-default python3 with Makefile (@ANTodorov)
- Changed `hf 14a info` `hf mf info` - now detects FM1216-137 CPU cards (@iceman1001)
- Changed `hf iclass configcard` expanding the list of available options and functionalities (@antiklesys)
- Changed `hf iclass configcard` - expanding the list of available options and functionalities (@antiklesys)
- Fixed `intertic.py` - missing comma in array (@iceman1001)
- Added improved algorithm for `hf iclass legrec` leveraging reduced entropy from hash0 constraints (@antiklesys)
- Changed `hf iclass legrec` - improved algorithm leveraging reduced entropy from hash0 constraints (@antiklesys)
- Fixed `hf iclass configcard` when generating elite or keyroll elite configcards for Rev.C legacy readers (@antiklesys)
- Changed `hf mf c*` - now accepts a --gdm flag to write using uscuid/gdm 20/23 alt magic wakeup (@nvx)
- Changed `pm3_console()` - Python/Lua/C: replace `passthru` by `capture` and `quiet` (@doegox)
@@ -26,12 +57,12 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Changed `hf iclass legrec` - updated script implementation to ensure functionality (@antiklesys)
- Added recovered iclass custom key to dictionary (@antiklesys)
- Added support for all Hitag S response protocol mode (@douniwan5788)
- Fixed 'hf_young.c' - flags declaration was missing a semicolon (@jakkpotts)
- Fixed `hf_young` - flags declaration was missing a semicolon (@jakkpotts)
- Changed `hf mf sim` - add option to allow key b to be used even if readable (@doegox)
- Changed `data num` - outputed binary strings are now properly zero padded (@iceman1001)
- Changed `hf iclass info` - now tries default keys and decode if legacy (@iceman1001)
- Changed `hf iclass chk` - now loads dictionary file by default (@iceman1001)
- Added an Makefile variable `DONT_BUILD_NATIVE` in mfd_aes_brute Makefile to easify downstream package
- Added Makefile variable `DONT_BUILD_NATIVE` in mfd_aes_brute Makefile to easify downstream package (@Cryolitia)
- Auto detect whether compile option `march=native` is supported for mfd_aes_brute Makefile
- Changed `hf mf sim` - support data-first and nested reader attacks (@doegox)
- Fixed `lf search` and `lf em 4x50 rdbl -b <blk>` does not coredump reading EM4450 tag (@ANTodorov)
@@ -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
View File
@@ -37,7 +37,8 @@ APP_CFLAGS = $(PLATFORM_DEFS) \
SRC_LF = lfops.c lfsampling.c pcf7931.c lfdemod.c lfadc.c
SRC_HF = hfops.c
SRC_ISO15693 = iso15693.c iso15693tools.c
SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_mfc.c sam_seos.c emvsim.c
SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_common.c sam_mfc.c sam_seos.c emvsim.c
#UNUSED: mifaresniff.c
SRC_ISO14443b = iso14443b.c
SRC_FELICA = felica.c
+39 -23
View File
@@ -441,7 +441,41 @@ static void SendStatus(uint32_t wait) {
ModInfo();
#ifdef WITH_FLASH
Flashmem_print_info();
DbpString(_CYAN_("Flash memory dictionary loaded"));
uint32_t num = 0;
if (exists_in_spiffs(MF_KEYS_FILE)) {
num = size_in_spiffs(MF_KEYS_FILE) / MF_KEY_LENGTH;
} else {
num = 0;
}
if (num > 0) {
Dbprintf(" Mifare.................. "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, MF_KEYS_FILE);
} else {
Dbprintf(" Mifare.................. "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, MF_KEYS_FILE);
}
if (exists_in_spiffs(T55XX_KEYS_FILE)) {
num = size_in_spiffs(T55XX_KEYS_FILE) / T55XX_KEY_LENGTH;
} else {
num = 0;
}
if (num > 0) {
Dbprintf(" T55xx................... "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, T55XX_KEYS_FILE);
} else {
Dbprintf(" T55xx................... "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, T55XX_KEYS_FILE);
}
if (exists_in_spiffs(ICLASS_KEYS_FILE)) {
num = size_in_spiffs(ICLASS_KEYS_FILE) / ICLASS_KEY_LENGTH;
} else {
num = 0;
}
if (num > 0) {
Dbprintf(" iClass.................. "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, ICLASS_KEYS_FILE);
} else {
Dbprintf(" iClass.................. "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, ICLASS_KEYS_FILE);
}
#endif
DbpString("");
reply_ng(CMD_STATUS, PM3_SUCCESS, NULL, 0);
@@ -1792,7 +1826,7 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES: {
MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes, true);
MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes, true, packet->oldarg[1], packet->oldarg[2]);
break;
}
case CMD_HF_MIFARE_ACQ_NONCES: {
@@ -2226,7 +2260,7 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_HF_SAM_SEOS: {
// sam_seos_get_pacs();
sam_seos_get_pacs(packet);
break;
}
@@ -2762,28 +2796,10 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
if (payload->startidx == DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k)) {
if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k)) {
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(3, 0xC);
} else if (payload->startidx == DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k)) {
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(3, 0x8);
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(3, 0x9);
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(3, 0xA);
} else if (payload->startidx == DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages64k)) {
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(3, 0xB);
} else if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k)) {
Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable();
Flash_Erase4k(3, 0xF);
Flash_Erase4k(spi_flash_pages64k - 1, 0xF);
}
uint16_t res = Flash_Write(payload->startidx, payload->data, payload->len);
+36 -45
View File
@@ -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
View File
@@ -1036,7 +1036,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
// acquire static encrypted nonces in order to perform the attack described in
// Philippe Teuwen, "MIFARE Classic: exposing the static encrypted nonce variant"
//-----------------------------------------------------------------------------
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply) {
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply, uint8_t first_block_no, uint8_t first_key_type) {
struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs;
pcs = &mpcs;
@@ -1055,6 +1055,10 @@ int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool
uint8_t buf[MIFARE_BLOCK_SIZE] = {0x00};
uint64_t ui64Key = bytes_to_num(key, 6);
bool with_data = flags & 1;
bool without_backdoor = (flags >> 1) & 1;
if (with_data && without_backdoor) {
return PM3_EINVARG;
}
uint32_t cuid = 0;
int16_t isOK = PM3_SUCCESS;
uint8_t cascade_levels = 0;
@@ -1072,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
View File
@@ -37,7 +37,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8
void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key);
void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain);
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply);
int MifareAcquireStaticEncryptedNonces(uint32_t flags, const uint8_t *key, bool reply, uint8_t first_block_no, uint8_t first_key_type);
void MifareAcquireNonces(uint32_t arg0, uint32_t flags);
void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem);
void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);
+9
View File
@@ -983,3 +983,12 @@ int nonce_distance(uint32_t from, uint32_t to) {
int nonce16_index(uint16_t nt) {
return nonce16_distance(0x0100, nt) + 1;
}
uint32_t rewind_nonce(uint32_t from, uint16_t dist) {
uint16_t x = from >> 16;
for (uint16_t i = 0; i < dist; i++) {
x = ((x << 1 | x >> 15) & 0xffff) ^ ((x >> 1 ^ x >> 2 ^ x >> 4) & 0x100);
}
uint32_t nt = x;
return nt << 16 | prng_successor(nt, 16);
}
+1
View File
@@ -128,4 +128,5 @@ bool validate_parity_nonce(uint32_t ntenc, uint8_t ntparenc, uint32_t nt);
int nonce_distance(uint32_t from, uint32_t to);
int nonce16_distance(uint16_t x, uint16_t y);
int nonce16_index(uint16_t nt);
uint32_t rewind_nonce(uint32_t from, uint16_t dist);
#endif
+373
View File
@@ -0,0 +1,373 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// Routines to support MFC <-> SAM communication
//-----------------------------------------------------------------------------
#include <string.h>
#include "sam_common.h"
#include "iclass.h"
#include "proxmark3_arm.h"
#include "BigBuf.h"
#include "commonutil.h"
#include "ticks.h"
#include "dbprint.h"
#include "i2c.h"
#include "iso15693.h"
#include "protocols.h"
/**
* @brief Transmits data to and receives data from a HID®'s iCLASS® SE Processor.
*
* This function sends a specified number of bytes to the SAM and receives a response.
*
* @param data Pointer to the data to be transmitted.
* @param n Number of bytes to be transmitted.
* @param resp Pointer to the buffer where the response will be stored.
* @param resplen Pointer to the variable where the length of the response will be stored.
* @return Status code indicating success or failure of the operation.
*/
int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) {
bool res = I2C_BufferWrite(data, n, I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN);
if (res == false) {
DbpString("failed to send to SIM CARD");
goto out;
}
*resplen = ISO7816_MAX_FRAME;
res = sc_rx_bytes(resp, resplen, SIM_WAIT_DELAY);
if (res == false) {
DbpString("failed to receive from SIM CARD");
goto out;
}
if (*resplen < 2) {
DbpString("received too few bytes from SIM CARD");
res = false;
goto out;
}
uint16_t more_len = 0;
if (resp[*resplen - 2] == 0x61 || resp[*resplen - 2] == 0x9F) {
more_len = resp[*resplen - 1];
} else {
// we done, return
goto out;
}
// Don't discard data we already received except the SW code.
// If we only received 1 byte, this is the echo of INS, we discard it.
*resplen -= 2;
if (*resplen == 1) {
*resplen = 0;
}
uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len};
res = I2C_BufferWrite(cmd_getresp, sizeof(cmd_getresp), I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN);
if (res == false) {
DbpString("failed to send to SIM CARD 2");
goto out;
}
more_len = 255 - *resplen;
res = sc_rx_bytes(resp + *resplen, &more_len, SIM_WAIT_DELAY);
if (res == false) {
DbpString("failed to receive from SIM CARD 2");
goto out;
}
*resplen += more_len;
out:
return res;
}
static inline void swap_clock_counters(volatile unsigned int *a, unsigned int *b) {
unsigned int c = *a;
*a = *b;
*b = c;
}
/**
* @brief Swaps the timer counter values.
*
* AT91SAM7S512 has a single Timer-Counter, that is reused in clocks Ticks
* and CountSspClk. This function stops the current clock and restores previous
* values. It is used to switch between different clock sources.
* It probably makes communication timing off, but at least makes it work.
*/
static void swap_clocks(void) {
static unsigned int tc0, tc1, tc2 = 0;
StopTicks();
swap_clock_counters(&(AT91C_BASE_TC0->TC_CV), &tc0);
swap_clock_counters(&(AT91C_BASE_TC1->TC_CV), &tc1);
swap_clock_counters(&(AT91C_BASE_TC2->TC_CV), &tc2);
}
void switch_clock_to_ticks(void) {
swap_clocks();
StartTicks();
}
void switch_clock_to_countsspclk(void) {
swap_clocks();
StartCountSspClk();
}
/**
* @brief Sends a payload to the SAM
*
* This function prepends the payload with the necessary APDU and application
* headers and sends it to the SAM.
*
* @param addr_src 0x14 for command from NFC, 0x44 for command from application
* @param addr_dest 0x0A for command to SAM
* @param addr_reply same as add_src or 0x00 if no reply is expected
* @param payload Pointer to the data to be sent.
* @param payload_len Length of the data to be sent.
* @param response Pointer to the buffer where the response will be stored.
* @param response_len Pointer to the variable where the length of the response will be stored.
* @param length Length of the data to be sent.
* @return Status code indicating success or failure of the operation.
*/
int sam_send_payload(
const uint8_t addr_src,
const uint8_t addr_dest,
const uint8_t addr_reply,
const uint8_t *const payload,
const uint16_t *payload_len,
uint8_t *response,
uint16_t *response_len
) {
int res = PM3_SUCCESS;
uint8_t *buf = response;
buf[0] = 0xA0; // CLA
buf[1] = 0xDA; // INS (PUT DATA)
buf[2] = 0x02; // P1 (TLV format?)
buf[3] = 0x63; // P2
buf[4] = SAM_TX_ASN1_PREFIX_LENGTH + (uint8_t) * payload_len; // LEN
buf[5] = addr_src;
buf[6] = addr_dest;
buf[7] = addr_reply;
buf[8] = 0x00;
buf[9] = 0x00;
buf[10] = 0x00;
memcpy(
&buf[11],
payload,
*payload_len
);
uint16_t length = SAM_TX_ASN1_PREFIX_LENGTH + SAM_TX_APDU_PREFIX_LENGTH + (uint8_t) * payload_len;
LogTrace(buf, length, 0, 0, NULL, true);
if (g_dbglevel >= DBG_INFO) {
DbpString("SAM REQUEST APDU: ");
Dbhexdump(length, buf, false);
}
if (sam_rxtx(buf, length, response, response_len) == false) {
if (g_dbglevel >= DBG_ERROR)
DbpString("SAM ERROR");
res = PM3_ECARDEXCHANGE;
goto out;
}
LogTrace(response, *response_len, 0, 0, NULL, false);
if (g_dbglevel >= DBG_INFO) {
DbpString("SAM RESPONSE APDU: ");
Dbhexdump(*response_len, response, false);
}
out:
return res;
}
/**
* @brief Retreives SAM firmware version.
*
* Used just as ping or sanity check here.
*
* @return Status code indicating success or failure of the operation.
*/
int sam_get_version(void) {
int res = PM3_SUCCESS;
if (g_dbglevel >= DBG_DEBUG)
DbpString("start sam_get_version");
uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t response_len = ISO7816_MAX_FRAME;
uint8_t payload[] = {
0xa0, 0x02, // <- SAM command
0x82, 0x00 // <- get version
};
uint16_t payload_len = sizeof(payload);
sam_send_payload(
0x44, 0x0a, 0x44,
payload,
&payload_len,
response,
&response_len
);
// resp:
// c1 64 00 00 00
// bd 11 <- SAM response
// 8a 0f <- get version response
// 80 02
// 01 29 <- version
// 81 06
// 68 3d 05 20 26 b6 <- build ID
// 82 01
// 01
// 90 00
if (g_dbglevel >= DBG_DEBUG)
DbpString("end sam_get_version");
if (response[5] != 0xbd) {
Dbprintf("Invalid SAM response");
goto error;
} else {
uint8_t *sam_response_an = sam_find_asn1_node(response + 5, 0x8a);
if (sam_response_an == NULL) {
if (g_dbglevel >= DBG_ERROR)
DbpString("SAM get response failed");
goto error;
}
uint8_t *sam_version_an = sam_find_asn1_node(sam_response_an, 0x80);
if (sam_version_an == NULL) {
if (g_dbglevel >= DBG_ERROR)
DbpString("SAM get version failed");
goto error;
}
uint8_t *sam_build_an = sam_find_asn1_node(sam_response_an, 0x81);
if (sam_build_an == NULL) {
if (g_dbglevel >= DBG_ERROR)
DbpString("SAM get firmware ID failed");
goto error;
}
if (g_dbglevel >= DBG_INFO) {
DbpString("SAM get version successful");
Dbprintf("Firmware version: %X.%X", sam_version_an[2], sam_version_an[3]);
Dbprintf("Firmware ID: ");
Dbhexdump(sam_build_an[1], sam_build_an + 2, false);
}
goto out;
}
error:
res = PM3_ESOFT;
out:
BigBuf_free();
if (g_dbglevel >= DBG_DEBUG)
DbpString("end sam_get_version");
return res;
}
/**
* @brief Finds an ASN.1 node of a specified type within a given root node.
*
* This function searches through a single level of the ASN.1 structure starting
* from the root node to find a node of the specified type.
*
* @param root Pointer to the root node of the ASN.1 structure.
* @param type The type of the ASN.1 node to find.
* @return Pointer to the ASN.1 node of the specified type if found, otherwise NULL.
*/
uint8_t *sam_find_asn1_node(const uint8_t *root, const uint8_t type) {
const uint8_t *end = (uint8_t *) root + *(root + 1);
uint8_t *current = (uint8_t *) root + 2;
while (current < end) {
if (*current == type) {
return current;
} else {
current += 2 + *(current + 1);
}
}
return NULL;
}
/**
* @brief Appends an ASN.1 node to the end of a given node.
*
* This function appends an ASN.1 node of a specified type and length to the end of
* the ASN.1 structure at specified node level.
*
* It is the most naive solution that does not handle the case where the node to append is
* not the last node at the same level. It also does not also care about proper
* order of the nodes.
*
* @param root Pointer to the root node of the ASN.1 structure.
* @param root Pointer to the node to be appended of the ASN.1 structure.
* @param type The type of the ASN.1 node to append.
* @param data Pointer to the data to be appended.
* @param len The length of the data to be appended.
*/
void sam_append_asn1_node(const uint8_t *root, const uint8_t *node, uint8_t type, const uint8_t *const data, uint8_t len) {
uint8_t *end = (uint8_t *) root + *(root + 1) + 2;
*(end) = type;
*(end + 1) = len;
memcpy(end + 2, data, len);
for (uint8_t *current = (uint8_t *) root; current <= node; current += 2) {
*(current + 1) += 2 + len;
};
return;
}
void sam_send_ack(void) {
uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t response_len = ISO7816_MAX_FRAME;
uint8_t payload[] = {
0xa0, 0
};
uint16_t payload_len = sizeof(payload);
sam_send_payload(
0x44, 0x0a, 0x00,
payload,
&payload_len,
response,
&response_len
);
BigBuf_free();
}
+49
View File
@@ -0,0 +1,49 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
#ifndef __SAM_COMMON_H
#define __SAM_COMMON_H
#include "common.h"
static const uint8_t SAM_TX_APDU_PREFIX_LENGTH = 5;
static const uint8_t SAM_TX_ASN1_PREFIX_LENGTH = 6;
static const uint8_t SAM_RX_ASN1_PREFIX_LENGTH = 5;
int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen);
void switch_clock_to_ticks(void);
void switch_clock_to_countsspclk(void);
int sam_send_payload(
const uint8_t addr_src,
const uint8_t addr_dest,
const uint8_t addr_reply,
const uint8_t *const payload,
const uint16_t *payload_len,
uint8_t *response,
uint16_t *response_len
);
int sam_get_version(void);
uint8_t *sam_find_asn1_node(const uint8_t *root, const uint8_t type);
void sam_append_asn1_node(const uint8_t *root, const uint8_t *node, uint8_t type, const uint8_t *const data, uint8_t len);
void sam_send_ack(void);
#endif
+1 -1
View File
@@ -16,7 +16,7 @@
// Routines to support MFC <-> SAM communication
//-----------------------------------------------------------------------------
#include "sam_mfc.h"
#include "sam_seos.h"
#include "sam_common.h"
#include "iclass.h"
#include "proxmark3_arm.h"
+1
View File
@@ -17,5 +17,6 @@
#define __SAM_MFC_H
#include "common.h"
#include "sam_common.h"
#endif
+94 -69
View File
@@ -16,6 +16,7 @@
// Routines to support Picopass <-> SAM communication
//-----------------------------------------------------------------------------
#include "sam_picopass.h"
#include "sam_common.h"
#include "iclass.h"
#include "crc16.h"
#include "proxmark3_arm.h"
@@ -30,66 +31,79 @@
#include "optimized_cipher.h"
#include "fpgaloader.h"
static int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) {
StartTicks();
/**
* @brief Sets the card detected status for the SAM (Secure Access Module).
*
* This function informs that a card has been detected by the reader and
* initializes SAM communication with the card.
*
* @param card_select Pointer to the descriptor of the detected card.
* @return Status code indicating success or failure of the operation.
*/
static int sam_set_card_detected(picopass_hdr_t *card_select) {
int res = PM3_SUCCESS;
if (g_dbglevel >= DBG_DEBUG)
DbpString("start sam_set_card_detected");
bool res = I2C_BufferWrite(data, n, I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN);
if (res == false) {
DbpString("failed to send to SIM CARD");
goto out;
}
uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t response_len = ISO7816_MAX_FRAME;
*resplen = ISO7816_MAX_FRAME;
// a0 12
// ad 10
// a0 0e
// 80 02
// 00 04 <- Picopass
// 81 08
// 9b fc a4 00 fb ff 12 e0 <- CSN
res = sc_rx_bytes(resp, resplen, SIM_WAIT_DELAY);
if (res == false) {
DbpString("failed to receive from SIM CARD");
goto out;
}
uint8_t payload[] = {
0xa0, 18, // <- SAM command
0xad, 16, // <- set detected card
0xa0, 4 + 10,
0x80, 2, // <- protocol
0x00, 0x04, // <- Picopass
0x81, 8, // <- CSN
card_select->csn[0], card_select->csn[1], card_select->csn[2], card_select->csn[3],
card_select->csn[4], card_select->csn[5], card_select->csn[6], card_select->csn[7]
};
uint16_t payload_len = sizeof(payload);
if (*resplen < 2) {
DbpString("received too few bytes from SIM CARD");
res = false;
goto out;
}
sam_send_payload(
0x44, 0x0a, 0x44,
payload,
&payload_len,
response,
&response_len
);
uint16_t more_len = 0;
// resp:
// c1 64 00 00 00
// bd 02 <- response
// 8a 00 <- empty response (accepted)
// 90 00
if (resp[*resplen - 2] == 0x61 || resp[*resplen - 2] == 0x9F) {
more_len = resp[*resplen - 1];
if (response[5] != 0xbd) {
if (g_dbglevel >= DBG_ERROR)
Dbprintf("Invalid SAM response");
goto error;
} else {
// we done, return
// uint8_t * sam_response_an = sam_find_asn1_node(response + 5, 0x8a);
// if(sam_response_an == NULL){
// if (g_dbglevel >= DBG_ERROR)
// Dbprintf("Invalid SAM response");
// goto error;
// }
goto out;
}
// Don't discard data we already received except the SW code.
// If we only received 1 byte, this is the echo of INS, we discard it.
*resplen -= 2;
if (*resplen == 1) {
*resplen = 0;
}
uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len};
res = I2C_BufferWrite(cmd_getresp, sizeof(cmd_getresp), I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN);
if (res == false) {
DbpString("failed to send to SIM CARD 2");
goto out;
}
more_len = 255 - *resplen;
res = sc_rx_bytes(resp + *resplen, &more_len, SIM_WAIT_DELAY);
if (res == false) {
DbpString("failed to receive from SIM CARD 2");
goto out;
}
*resplen += more_len;
error:
res = PM3_ESOFT;
out:
StopTicks();
BigBuf_free();
if (g_dbglevel >= DBG_DEBUG)
DbpString("end sam_set_card_detected");
return res;
}
@@ -218,23 +232,19 @@ int sam_picopass_get_pacs(void) {
uint8_t *sam_apdu = BigBuf_calloc(ISO7816_MAX_FRAME);
// -----------------------------------------------------------------------------
// first
// a0 da 02 63 1a 44 0a 44 00 00 00 a0 12 ad 10 a0 0e 80 02 00 04 81 08 9b fc a4 00 fb ff 12 e0
hexstr_to_byte_array("a0da02631a440a44000000a012ad10a00e800200048108", sam_apdu, &sam_len);
memcpy(sam_apdu + sam_len, hdr.csn, sizeof(hdr.csn));
sam_len += sizeof(hdr.csn);
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
res = PM3_ECARDEXCHANGE;
goto out;
}
print_dbg("-- 1", resp, resp_len);
// first - set detected card (0xAD)
switch_clock_to_ticks();
sam_set_card_detected(&hdr);
// -----------------------------------------------------------------------------
// second
// a0 da 02 63 0d 44 0a 44 00 00 00 a0 05 a1 03 80 01 04
hexstr_to_byte_array("a0da02630d440a44000000a005a103800104", sam_apdu, &sam_len);
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
// second - get PACS (0xA1)
// a0 05
// a1 03
// 80 01
// 04
hexstr_to_byte_array("a005a103800104", sam_apdu, &sam_len);
if (sam_send_payload(0x44, 0x0a, 0x44, sam_apdu, (uint16_t *) &sam_len, resp, &resp_len) != PM3_SUCCESS) {
res = PM3_ECARDEXCHANGE;
goto out;
}
@@ -245,7 +255,7 @@ int sam_picopass_get_pacs(void) {
// Tag|c00a140a000000a110a10e8004 0c05de64 8102 0004 820201f4
// -----------------------------------------------------------------------------
// third AIA block 5
// third AIA block 5 (emulated tag <-> SAM exchange starts here)
// a0da02631c140a00000000bd14a012a010800a ffffff0006fffffff88e 81020000
// picopass legacy is fixed. wants AIA and crc. ff ff ff ff ff ff ff ff ea f5
// picpoasss SE ff ff ff 00 06 ff ff ff f8 8e
@@ -300,7 +310,7 @@ int sam_picopass_get_pacs(void) {
}
// start ssp clock again...
StartCountSspClk();
switch_clock_to_countsspclk();
// NOW we auth against tag
uint8_t cmd_check[9] = { ICLASS_CMD_CHECK };
@@ -325,6 +335,7 @@ int sam_picopass_get_pacs(void) {
hexstr_to_byte_array("A0DA026316140A00000000BD0EA00CA00A8004311E32E981020000", sam_apdu, &sam_len);
memcpy(sam_apdu + 19, mac, sizeof(mac));
switch_clock_to_ticks();
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
res = PM3_ECARDEXCHANGE;
goto out;
@@ -355,7 +366,7 @@ int sam_picopass_get_pacs(void) {
// c1 61 c1 00 00 a1 10 a1 0e 80 04 0c 06 45 56 81 02 00 04 82 02 01 f4 90 00
// read block 6
StartCountSspClk();
switch_clock_to_countsspclk();
start_time = GetCountSspClk();
iclass_send_as_reader(resp + 11, 4, &start_time, &eof_time, shallow_mod);
@@ -373,6 +384,7 @@ int sam_picopass_get_pacs(void) {
hexstr_to_byte_array("A0DA02631C140A00000000BD14A012A010800A030303030003E017432381020000", sam_apdu, &sam_len);
memcpy(sam_apdu + 19, resp, resp_len);
switch_clock_to_ticks();
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
res = PM3_ECARDEXCHANGE;
goto out;
@@ -382,7 +394,7 @@ int sam_picopass_get_pacs(void) {
// c161c10000a110a10e8004 0606455681020004820201f49000
// read the credential blocks
StartCountSspClk();
switch_clock_to_countsspclk();
start_time = GetCountSspClk();
iclass_send_as_reader(resp + 11, 4, &start_time, &eof_time, shallow_mod);
@@ -400,6 +412,7 @@ int sam_picopass_get_pacs(void) {
hexstr_to_byte_array("A0DA026334140A00000000BD2CA02AA0288022030303030003E017769CB4A198E0DEC82AD4C8211F9968712BE7393CF8E71D7E804C81020000", sam_apdu, &sam_len);
memcpy(sam_apdu + 19, resp, resp_len);
switch_clock_to_ticks();
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
res = PM3_ECARDEXCHANGE;
goto out;
@@ -409,7 +422,13 @@ int sam_picopass_get_pacs(void) {
// -----------------------------------------------------------------------------
// TEN ask for PACS data
// A0DA02630C440A00000000BD04A0028200
// A0 DA 02 63 0C
// 44 0A 00 00 00 00
// BD 04
// A0 02
// 82 00
// (emulated tag <-> SAM exchange ends here)
hexstr_to_byte_array("A0DA02630C440A00000000BD04A0028200", sam_apdu, &sam_len);
memcpy(sam_apdu + 19, resp, resp_len);
@@ -424,7 +443,12 @@ int sam_picopass_get_pacs(void) {
goto out;
}
// c164000000bd098a07 030506951f9a00 9000
// resp:
// c1 64 00 00 00
// bd 09
// 8a 07
// 03 05 06 95 1f 9a 00 <- decoded PACS data
// 90 00
uint8_t *pacs = BigBuf_calloc(resp[8]);
memcpy(pacs, resp + 9, resp[8]);
@@ -439,6 +463,7 @@ out:
off:
switch_off();
StopTicks();
BigBuf_free();
return res;
}
+1
View File
@@ -17,6 +17,7 @@
#define __SAM_PICOPASS_H
#include "common.h"
#include "sam_common.h"
int sam_picopass_get_pacs(void);
+383
View File
@@ -14,9 +14,392 @@
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// Routines to support SEOS <-> SAM communication
// communication and ASN.1 messages based on https://github.com/bettse/seader/blob/main/seader.asn1
//-----------------------------------------------------------------------------
#include "sam_seos.h"
#include "sam_common.h"
#include "iclass.h"
#include "proxmark3_arm.h"
#include "iso14443a.h"
#include "iclass.h"
#include "crc16.h"
#include "proxmark3_arm.h"
#include "BigBuf.h"
#include "cmd.h"
#include "commonutil.h"
#include "ticks.h"
#include "dbprint.h"
#include "i2c.h"
#include "protocols.h"
#include "optimized_cipher.h"
#include "fpgaloader.h"
#include "pm3_cmd.h"
#include "cmd.h"
/**
* @brief Sets the card detected status for the SAM (Secure Access Module).
*
* This function informs that a card has been detected by the reader and
* initializes SAM communication with the card.
*
* @param card_select Pointer to the descriptor of the detected card.
* @return Status code indicating success or failure of the operation.
*/
static int sam_set_card_detected(iso14a_card_select_t *card_select) {
int res = PM3_SUCCESS;
if (g_dbglevel >= DBG_DEBUG)
DbpString("start sam_set_card_detected");
uint8_t *request = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t request_len = ISO7816_MAX_FRAME;
uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t response_len = ISO7816_MAX_FRAME;
const uint8_t payload[] = {
0xa0, 8, // <- SAM command
0xad, 6, // <- set detected card
0xa0, 4, // <- detected card details
0x80, 2, // <- protocol
0x00, 0x02 // <- ISO14443A
};
memcpy(request, payload, sizeof(payload));
sam_append_asn1_node(request, request + 4, 0x81, card_select->uid, card_select->uidlen);
sam_append_asn1_node(request, request + 4, 0x82, card_select->atqa, 2);
sam_append_asn1_node(request, request + 4, 0x83, &card_select->sak, 1);
request_len = request[1] + 2;
sam_send_payload(
0x44, 0x0a, 0x44,
request,
&request_len,
response,
&response_len
);
// resp:
// c1 64 00 00 00
// bd 02 <- response
// 8a 00 <- empty response (accepted)
// 90 00
if (response[5] != 0xbd) {
if (g_dbglevel >= DBG_ERROR)
Dbprintf("Invalid SAM response");
goto error;
} else {
// uint8_t * sam_response_an = sam_find_asn1_node(response + 5, 0x8a);
// if(sam_response_an == NULL){
// if (g_dbglevel >= DBG_ERROR)
// Dbprintf("Invalid SAM response");
// goto error;
// }
goto out;
}
error:
res = PM3_ESOFT;
out:
BigBuf_free();
if (g_dbglevel >= DBG_DEBUG)
DbpString("end sam_set_card_detected");
return res;
}
/**
* @brief Copies the payload from an NFC buffer to a SAM buffer.
*
* Wraps received data from NFC into an ASN1 tree, so it can be transmitted to the SAM .
*
* @param sam_tx Pointer to the SAM transmit buffer.
* @param nfc_rx Pointer to the NFC receive buffer.
* @param nfc_len Length of the data to be copied from the NFC buffer.
*
* @return Length of SAM APDU to be sent.
*/
inline static uint16_t sam_seos_copy_payload_nfc2sam(uint8_t *sam_tx, uint8_t *nfc_rx, uint8_t nfc_len) {
// NFC resp:
// 6f 0c 84 0a a0 00 00 04 40 00 01 01 00 01 90 00 fb e3
// SAM req:
// bd 1c
// a0 1a
// a0 18
// 80 12
// 6f 0c 84 0a a0 00 00 04 40 00 01 01 00 01 90 00 fb e3
// 81 02
// 00 00
const uint8_t payload[] = {
0xbd, 4,
0xa0, 2,
0xa0, 0
};
const uint8_t tag81[] = {
0x00, 0x00
};
memcpy(sam_tx, payload, sizeof(payload));
sam_append_asn1_node(sam_tx, sam_tx + 4, 0x80, nfc_rx, nfc_len);
sam_append_asn1_node(sam_tx, sam_tx + 4, 0x81, tag81, sizeof(tag81));
return sam_tx[1] + 2; // length of the ASN1 tree
}
/**
* @brief Copies the payload from the SAM receive buffer to the NFC transmit buffer.
*
* Unpacks data to be transmitted from ASN1 tree in APDU received from SAM.
*
* @param nfc_tx_buf Pointer to the buffer where the NFC transmit data will be stored.
* @param sam_rx_buf Pointer to the buffer containing the data received from the SAM.
* @return Length of NFC APDU to be sent.
*/
inline static uint16_t sam_seos_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf) {
// SAM resp:
// c1 61 c1 00 00
// a1 21 <- nfc command
// a1 1f <- nfc send
// 80 10 <- data
// 00 a4 04 00 0a a0 00 00 04 40 00 01 01 00 01 00
// 81 02 <- protocol
// 02 02
// 82 02 <- timeout
// 01 2e
// 85 03 <- format
// 06 c0 00
// 90 00
// NFC req:
// 00 a4 04 00 0a a0 00 00 04 40 00 01 01 00 01 00
// copy data out of c1->a1>->a1->80 node
uint16_t nfc_tx_len = (uint8_t) * (sam_rx_buf + 10);
memcpy(nfc_tx_buf, sam_rx_buf + 11, nfc_tx_len);
return nfc_tx_len;
}
/**
* @brief Sends a request to the SAM and retrieves the response.
*
* Unpacks request to the SAM and relays ISO14A traffic to the card.
* If no request data provided, sends a request to get PACS data.
*
* @param request Pointer to the buffer containing the request to be sent to the SAM.
* @param request_len Length of the request to be sent to the SAM.
* @param response Pointer to the buffer where the retreived data will be stored.
* @param response_len Pointer to the variable where the length of the retreived data will be stored.
* @return Status code indicating success or failure of the operation.
*/
static int sam_send_request_iso14a(const uint8_t *const request, const uint8_t request_len, uint8_t *response, uint8_t *response_len) {
int res = PM3_SUCCESS;
if (g_dbglevel >= DBG_DEBUG)
DbpString("start sam_send_request_iso14a");
uint8_t buf1[ISO7816_MAX_FRAME] = {0};
uint8_t buf2[ISO7816_MAX_FRAME] = {0};
uint8_t *sam_tx_buf = buf1;
uint16_t sam_tx_len;
uint8_t *sam_rx_buf = buf2;
uint16_t sam_rx_len;
uint8_t *nfc_tx_buf = buf1;
uint16_t nfc_tx_len;
uint8_t *nfc_rx_buf = buf2;
uint16_t nfc_rx_len;
if (request_len > 0) {
sam_tx_len = request_len;
memcpy(sam_tx_buf, request, sam_tx_len);
} else {
// send get pacs
static const uint8_t payload[] = {
0xa0, 19, // <- SAM command
0xBE, 17, // <- samCommandGetContentElement2
0x80, 1,
0x04, // <- implicitFormatPhysicalAccessBits
0x84, 12,
0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x04 // <- SoRootOID
};
sam_tx_len = sizeof(payload);
memcpy(sam_tx_buf, payload, sam_tx_len);
}
sam_send_payload(
0x44, 0x0a, 0x44,
sam_tx_buf, &sam_tx_len,
sam_rx_buf, &sam_rx_len
);
if (sam_rx_buf[1] == 0x61) { // commands to be relayed to card starts with 0x61
// tag <-> SAM exchange starts here
while (sam_rx_buf[1] == 0x61) {
switch_clock_to_countsspclk();
nfc_tx_len = sam_seos_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf);
nfc_rx_len = iso14_apdu(
nfc_tx_buf,
nfc_tx_len,
false,
nfc_rx_buf,
ISO7816_MAX_FRAME,
NULL
);
switch_clock_to_ticks();
sam_tx_len = sam_seos_copy_payload_nfc2sam(sam_tx_buf, nfc_rx_buf, nfc_rx_len - 2);
sam_send_payload(
0x14, 0x0a, 0x14,
sam_tx_buf, &sam_tx_len,
sam_rx_buf, &sam_rx_len
);
// last SAM->TAG
// c1 61 c1 00 00 a1 02 >>82<< 00 90 00
if (sam_rx_buf[7] == 0x82) {
// tag <-> SAM exchange ends here
break;
}
}
static const uint8_t hfack[] = {
0xbd, 0x04, 0xa0, 0x02, 0x82, 0x00
};
sam_tx_len = sizeof(hfack);
memcpy(sam_tx_buf, hfack, sam_tx_len);
sam_send_payload(
0x14, 0x0a, 0x00,
sam_tx_buf, &sam_tx_len,
sam_rx_buf, &sam_rx_len
);
}
// resp for SamCommandGetContentElement:
// c1 64 00 00 00
// bd 09
// 8a 07
// 03 05 <- include tag for pm3 client
// 06 85 80 6d c0 <- decoded PACS data
// 90 00
// resp for samCommandGetContentElement2:
// c1 64 00 00 00
// bd 1e
// b3 1c
// a0 1a
// 80 05
// 06 85 80 6d c0
// 81 0e
// 2b 06 01 04 01 81 e4 38 01 01 02 04 3c ff
// 82 01
// 07
// 90 00
if (request_len == 0) {
if (
!(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0x8a && sam_rx_buf[5 + 4] == 0x03)
&&
!(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0xb3 && sam_rx_buf[5 + 4] == 0xa0)
) {
if (g_dbglevel >= DBG_ERROR)
Dbprintf("No PACS data in SAM response");
res = PM3_ESOFT;
}
}
*response_len = sam_rx_buf[5 + 1] + 2;
memcpy(response, sam_rx_buf + 5, *response_len);
goto out;
out:
return res;
}
/**
* @brief Retrieves PACS data from SEOS card using SAM.
*
* This function is called by appmain.c
* It sends a request to the SAM to get the PACS data from the SEOS card.
* The PACS data is then returned to the PM3 client.
*
* @return Status code indicating success or failure of the operation.
*/
int sam_seos_get_pacs(PacketCommandNG *c) {
bool disconnectAfter = c->oldarg[0] & 0x01;
bool skipDetect = c->oldarg[1] & 0x01;
uint8_t *cmd = c->data.asBytes;
uint16_t cmd_len = (uint16_t) c->oldarg[2];
int res = PM3_EFAILED;
clear_trace();
I2C_Reset_EnterMainProgram();
set_tracing(true);
StartTicks();
// step 1: ping SAM
sam_get_version();
if (!skipDetect) {
// step 2: get card information
iso14a_card_select_t card_a_info;
// implicit StartSspClk() happens here
iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD);
if (!iso14443a_select_card(NULL, &card_a_info, NULL, true, 0, false)) {
goto err;
}
switch_clock_to_ticks();
// step 3: SamCommand CardDetected
sam_set_card_detected(&card_a_info);
}
// step 3: SamCommand RequestPACS, relay NFC communication
uint8_t sam_response[ISO7816_MAX_FRAME] = { 0x00 };
uint8_t sam_response_len = 0;
res = sam_send_request_iso14a(cmd, cmd_len, sam_response, &sam_response_len);
if (res != PM3_SUCCESS) {
goto err;
}
if (g_dbglevel >= DBG_INFO)
print_result("Response data", sam_response, sam_response_len);
goto out;
goto off;
err:
res = PM3_ENOPACS;
reply_ng(CMD_HF_SAM_SEOS, res, NULL, 0);
goto off;
out:
reply_ng(CMD_HF_SAM_SEOS, PM3_SUCCESS, sam_response, sam_response_len);
goto off;
off:
if (disconnectAfter) {
switch_off();
}
set_tracing(false);
StopTicks();
BigBuf_free();
return res;
}
+3
View File
@@ -17,5 +17,8 @@
#define __SAM_SEOS_H
#include "common.h"
#include "pm3_cmd.h"
int sam_seos_get_pacs(PacketCommandNG *c);
#endif
+2 -1
View File
@@ -28,6 +28,7 @@
#include "printf.h"
#include "string.h"
#include "flashmem.h"
#include "pmflash.h"
//#include <stddef.h>
//#include <unistd.h>
@@ -236,7 +237,7 @@ typedef uint8_t u8_t;
// Instead of giving parameters in config struct, singleton build must
// give parameters in defines below.
#ifndef SPIFFS_CFG_PHYS_SZ
#define SPIFFS_CFG_PHYS_SZ(ignore) (1024 * 64 * (spi_flash_pages64k - 1))
#define SPIFFS_CFG_PHYS_SZ(ignore) ((1024 * 64 * spi_flash_pages64k) - (1024 * 4 * (FLASH_RESERVED_TRAILING_4K_SECTORS + 1)))
#endif
#ifndef SPIFFS_CFG_PHYS_ERASE_SZ
#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (4*1024)

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