Merge branch 'master' into patch-1

This commit is contained in:
Iceman 2021-07-14 14:48:45 +02:00 committed by GitHub
commit f027a1fce6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
125 changed files with 8299 additions and 1546 deletions

View file

@ -36,7 +36,8 @@ sudo make install; pushd /tmp; proxmark3 -c 'data load -f lf_EM4x05.pm3;lf searc
- [ ] RPI Zero
- [ ] WSL
- [ ] PSv3.3
- [ ] PSv3.9
- [ ] Archlinux
- [ ] Kali
- [ ] Debian
- [ ] Ubuntu20
@ -46,3 +47,12 @@ sudo make install; pushd /tmp; proxmark3 -c 'data load -f lf_EM4x05.pm3;lf searc
- [ ] OSX
- [ ] Android
- [ ] Termux
# creating release
`make release RELEASE_NAME="ice awesome"`
last line of output, gives you next command to run
Sample: `git push && git push origin v4.15000`
Go to Github releases, create release based on the new created tag and publish
update homebrew repo, file `proxmark3.rb` with a SHA256 sum of the file `v4.15000.tar.gz`

View file

@ -3,11 +3,39 @@ 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]
- Added '-4041x' option to lf indala clone for the Indala 4041X format (@chunkystew)
- Fixed `hf fido` commands now works correctly (@merlokk)
- Added `-4041x` option to lf indala clone for the Indala 4041X format (@chunkystew)
- Added a new client preference, delay of execution, a delay in ms before a cmd is sent. Good for implants (@iceman1001)
- Fix `lf t55xx brute` - now correctly prints last key if it was correct (@scott4290)
- Added support python scripts (@salmg)
- Add new standalone mode `hf_reblay` - relay 14a over bt (@salmg)
- Added one new key from gun cloner found in wild (@scott4290)
- Fix `hf_msdsal` standalone in simulation flow (@salmg)
- Added a picture viewer in QT. To be used with `hf emrtd info` (@iceman1001)
- Fix - move des functions to libcrypto (@merlokk)
- Added `CLIGetOptionList` to cliparser that makes it easier to implement text options in the cli (@merlokk)
- Added experimental support for macOS users utilizing MacPorts instead of Homebrew (@linuxgemini)
- Added `pm3_online_check.py` - a script to verify and initialize a Proxmark3 RDV4 device (@iceman1001)
## [midsummer.4.13441][2021-06-25]
- Fix `hf iclass` - a crash when AA1 limit was larger than AA2 (@pcunning)
- Added bruteforce function for the magic byte in `cmdlfnexwatch.c` and ability to clone with psk2 modulation (@Guilhem7, @MaximeBosca)
- Changed `hw setmux` - improve user feedback for special case (@iceman1001)
- Changed 'filename' - unified file name param across client (@iceman1001)
- Fix `lf em 4x05 brute/chk` - fixed input params crash (@iceman1001)
- Fix `lf hitag reader --23` - now accepts 6bytes key (@iceman1001)
- Fix coverity scans findigs (@merlokk, @iceman1001)
- Fix `hf iclass config` - now fallback to default config card configuration (@iceman1001)
- Changed `nfc parser` - now also identify xvcard types (@iceman1001)
- Added `hf mf gview` - view contents of a magic Gen3 GTU (@iceman1001)
- Added Standalone mode for nexwatch ID credentials (@Guilhem7, @MaximeBosca)
- Fix `lf em 4x50/4x70 *` reverted a missunderstanding in byte order macros (@iceman1001)
- Added more keys (@equipter)
- Changed `hf nfc ndefread` - ndef parser now handles more types (@iceman1001)
- Fix `hf desfire` changekey, GetUID, 3DES sesson key tweak. (@mwalker33)
- Fix `hf fido` commands now works correctly (@merlokk)
- Moved / renamed `client/resource/fido2_defparams.json` -> `client/resource/hf_fido2_defparams.json` (@merlokk)
- Added `hf cipurse` commands to work with cipurse transport cards (@merlokk)
- Added '--gap' option to lf em 410x sim for more control over sim data (@mwalker)
- Added `--gap` option to lf em 410x sim for more control over sim data (@mwalker)
- Changed `hf fido` - refactored load/save json objects (@iceman1001)
- Moved / renamed `fido2.json` -> `client/resource/fido2_defparams.json` (@iceman1001)
- Added openocd shikra support based on @ninjastyle82 patch to deprecated iceman fork (@iceman1001)
@ -17,8 +45,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Added `data asn1` - a command to decode ASN1 byte arrays (@iceman1001)
- Added `hf 15 disableprivacy` - from @g3gg0 fork *WIP* (@iceman1001)
- Added `lf_ident_json.lua` - script to identify t55xx json dump files (@iceman1001)
- Fixed `hf iclass chk` - multithread concurrency issues solved (@iceman1001)
- Fixed hf_iceclass standalone - correct null terminator filename (@metalauricle)
- Fix `hf iclass chk` - multithread concurrency issues solved (@iceman1001)
- Fix hf_iceclass standalone - correct null terminator filename (@metalauricle)
- Changed `trace list -t mfdes - added annotations for EV2, EV3 (@iceman1001)`
- Changed `hf iclass lookup` - fixed swapped args (@iceman1001)
- Changed `hf iclass decrypt` - added the possibility to decode the data as block6 if you have a cardhelper (@iceman1001)

View file

@ -59,7 +59,10 @@ else
endif
ifeq ($(USE_BREW),1)
BREW_PREFIX = $(shell brew --prefix)
BREW_PREFIX = $(shell brew --prefix 2>/dev/null)
ifeq ($(strip $(BREW_PREFIX)),)
MACPORTS_PREFIX ?= /opt/local
endif
endif
ifeq ($(DEBUG),1)

View file

@ -25,6 +25,7 @@
||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)|
||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)|
||**[JTAG](/doc/jtag_notes.md)**|[T5577 Introduction Guide](/doc/T5577_Guide.md)|
||**[MacPorts (Mac OS X, experimental)](/doc/md/Installation_Instructions/Mac-OS-X-MacPorts-Installation-Instructions.md)** |
## Notes / helpful documents
@ -59,12 +60,13 @@ We define generic Proxmark3 platforms as following devices.
- **Note**: unknown pin assignments.
- ⚠ Ryscorp Proxmark3 Pro
- **Note**: device has different fpga and unknown pin assignments.
- ⚠ i-copy
- **Note**: reversed hw, experimental support maintained in [icopyx-community pm3 repo](https://github.com/iCopy-X-Community/icopyx-community-pm3)
**Unknown support status**
- ⚠ VX
- **Note**: unknown device hw
- ⚠ i-copy
- **Note**: unknown device hw
**256kb flash memory size of generic Proxmark3 platforms**
@ -131,11 +133,11 @@ The [public roadmap](https://github.com/RfidResearchGroup/proxmark3/wiki/Public-
## Supported operative systems
This repo compiles nicely on
- WSL1 on Windows 10
- Proxspace v3.9 [release v3.9](https://github.com/Gator96100/ProxSpace/releases)
- Proxspace enviroment [release v3.9](https://github.com/Gator96100/ProxSpace/releases)
- Windows/MinGW environment
- Ubuntu, ParrotOS, Gentoo, Pentoo, Kali, NetHunter, Arch Linux, Fedora, Debian, Raspbian
- Android / Termux
- Mac OS X / Homebrew / Apple Silicon M1
- Mac OS X / Homebrew (or MacPorts, experimental) / Apple Silicon M1
- Docker container
- [ RRG / Iceman repo based ubuntu 18.04 container ](https://hub.docker.com/r/secopsconsult/proxmark3)
- [ Iceman fork based container v1.7 ](https://hub.docker.com/r/iceman1001/proxmark3/)
@ -155,7 +157,7 @@ We don't maintain any precompiled binaries in this repo. There is community effo
## Official channels
Where do you find the community?
- [RFID Hacking community discord server](https://discord.gg/QfPvGFRQxH)
- [Proxmark3 IRC channel](http://webchat.freenode.net/?channels=#proxmark3)
- [Proxmark3 IRC channel](https://web.libera.chat/?channels=#proxmark3)
- [Proxmark3 sub reddit](https://www.reddit.com/r/proxmark3/)
- [Proxmark3 forum](http://www.proxmark.org/forum/index.php)

View file

@ -29,6 +29,9 @@ define KNOWN_STANDALONE_DEFINITIONS
| LF_ICEHID | LF HID collector to flashmem |
| (RDV4 only) | |
+----------------------------------------------------------+
| LF_NEXID | LF Nexwatch collector to flashmem |
| (RDV4 only) | |
+----------------------------------------------------------+
| LF_PROXBRUTE | HID ProxII bruteforce |
| | - Brad Antoniewicz |
+----------------------------------------------------------+
@ -65,6 +68,9 @@ define KNOWN_STANDALONE_DEFINITIONS
| HF_MSDSAL | Read and emulate MSD Visa cards |
| (default) | - Salvador Mendoza |
+----------------------------------------------------------+
| HF_REBLAY | 14A Relay over BT |
| (RDV4 only) | - Salvador Mendoza |
+----------------------------------------------------------+
| HF_TCPRST | IKEA Rothult read/sim/dump/emul |
| | - Nick Draffen |
+----------------------------------------------------------+
@ -76,10 +82,11 @@ define KNOWN_STANDALONE_DEFINITIONS
+----------------------------------------------------------+
endef
STANDALONE_MODES := LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RWC LF_HIDBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN LF_THAREXDE
STANDALONE_MODES += HF_14ASNIFF HF_AVEFUL HF_BOG HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_MATTYRUN HF_MSDSAL HF_TCPRST HF_TMUDFORD HF_YOUNG
STANDALONE_MODES := LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RWC LF_HIDBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN LF_THAREXDE LF_NEXID
STANDALONE_MODES += HF_14ASNIFF HF_AVEFUL HF_BOG HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_MATTYRUN HF_MSDSAL HF_TCPRST HF_TMUDFORD HF_YOUNG HF_REBLAY
STANDALONE_MODES_REQ_BT := HF_REBLAY
STANDALONE_MODES_REQ_SMARTCARD :=
STANDALONE_MODES_REQ_FLASH := LF_ICEHID LF_THAREXDE HF_14ASNIFF HF_BOG HF_COLIN HF_ICECLASS
STANDALONE_MODES_REQ_FLASH := LF_ICEHID LF_NEXID LF_THAREXDE HF_14ASNIFF HF_BOG HF_COLIN HF_ICECLASS
ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),)
STANDALONE_PLATFORM_DEFS += -DWITH_STANDALONE_$(STANDALONE)
ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_SMARTCARD)),)
@ -88,6 +95,9 @@ ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),)
ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_FLASH)),)
STANDALONE_REQ_DEFS += -DWITH_FLASH
endif
ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_BT)),)
STANDALONE_REQ_DEFS += -DWITH_FPC_USART_HOST
endif
else ifneq ($(STANDALONE),)
$(error Invalid STANDALONE: $(STANDALONE). $(KNOWN_DEFINITIONS))
endif

View file

@ -49,6 +49,10 @@ endif
ifneq (,$(findstring WITH_STANDALONE_LF_ICEHID,$(APP_CFLAGS)))
SRC_STANDALONE = lf_icehid.c
endif
# WITH_STANDALONE_LF_NEXID
ifneq (,$(findstring WITH_STANDALONE_LF_NEXID,$(APP_CFLAGS)))
SRC_STANDALONE = lf_nexid.c
endif
# WITH_STANDALONE_LF_EM4100EMUL
ifneq (,$(findstring WITH_STANDALONE_LF_EM4100EMUL,$(APP_CFLAGS)))
SRC_STANDALONE = lf_em4100emul.c
@ -85,3 +89,7 @@ endif
ifneq (,$(findstring WITH_STANDALONE_HF_TMUDFORD,$(APP_CFLAGS)))
SRC_STANDALONE = hf_tmudford.c
endif
# WITH_STANDALONE_HF_REBLAY
ifneq (,$(findstring WITH_STANDALONE_HF_REBLAY,$(APP_CFLAGS)))
SRC_STANDALONE = hf_reblay.c
endif

View file

@ -52,36 +52,40 @@ void ModInfo(void) {
* technologies. Be brave enough to share your knowledge & inspire others. Salvador Mendoza.
*/
static uint8_t ppdol [255] = {0x80, 0xA8, 0x00, 0x00, 0x02, 0x83, 0x00}; // Default GET PROCESSING
// Default GET PROCESSING
static uint8_t ppdol [255] = {0x80, 0xA8, 0x00, 0x00, 0x02, 0x83, 0x00};
// Generate GET PROCESSING
static uint8_t treatPDOL(uint8_t *apdu) {
static uint8_t treatPDOL(uint8_t *apdu) { //Generate GET PROCESSING
uint8_t plen = 7;
//PDOL Format: 80 A8 00 00 + (PDOL Length+2) + 83 + PDOL Length + PDOL + 00
for (uint8_t i = 1; i <= apdu[0]; i++) { //Magic stuff, the generation order is important
if (apdu[i] == 0x9F && apdu[i + 1] == 0x66) { //Terminal Transaction Qualifiers
// PDOL Format: 80 A8 00 00 + (PDOL Length+2) + 83 + PDOL Length + PDOL + 00
for (uint8_t i = 1; i <= apdu[0]; i++) { // Magic stuff, the generation order is important
if (apdu[i] == 0x9F && apdu[i + 1] == 0x66) { // Terminal Transaction Qualifiers
ppdol[plen] = 0xF6;
ppdol[plen + 1] = 0x20;
ppdol[plen + 2] = 0xC0;
ppdol[plen + 3] = 0x00;
plen += 4;
i += 2;
} else if (apdu[i] == 0x9F && apdu[i + 1] == 0x1A) { //Terminal Country Code
} else if (apdu[i] == 0x9F && apdu[i + 1] == 0x1A) { // Terminal Country Code
ppdol[plen] = 0x9F;
ppdol[plen + 1] = 0x1A;
plen += 2;
i += 2;
} else if (apdu[i] == 0x5F && apdu[i + 1] == 0x2A) { //Transaction Currency Code
} else if (apdu[i] == 0x5F && apdu[i + 1] == 0x2A) { // Transaction Currency Code
ppdol[plen] = 0x5F;
ppdol[plen + 1] = 0x2A;
plen += 2;
i += 2;
} else if (apdu[i] == 0x9A) { //Transaction Date
} else if (apdu[i] == 0x9A) { // Transaction Date
ppdol[plen] = 0x9A;
ppdol[plen + 1] = 0x9A;
ppdol[plen + 2] = 0x9A;
plen += 3;
i += 1;
} else if (apdu[i] == 0x95) { //Terminal Verification Results
} else if (apdu[i] == 0x95) { // Terminal Verification Results
ppdol[plen] = 0x95;
ppdol[plen + 1] = 0x95;
ppdol[plen + 2] = 0x95;
@ -89,18 +93,18 @@ static uint8_t treatPDOL(uint8_t *apdu) { //Generate GET PROCES
ppdol[plen + 4] = 0x95;
plen += 5;
i += 1;
} else if (apdu[i] == 0x9C) { //Transaction Type
} else if (apdu[i] == 0x9C) { // Transaction Type
ppdol[plen] = 0x9C;
plen += 1;
i += 1;
} else if (apdu[i] == 0x9F && apdu[i + 1] == 0x37) { //Unpredictable Number
} else if (apdu[i] == 0x9F && apdu[i + 1] == 0x37) { // Unpredictable Number
ppdol[plen] = 0x9F;
ppdol[plen + 1] = 0x37;
ppdol[plen + 2] = 0x9F;
ppdol[plen + 3] = 0x37;
plen += 4;
i += 2;
} else { //To the others, add "0" to complete the format depending on its range
} else { // To the others, add "0" to complete the format depending on its range
uint8_t u = apdu[i + 2];
while (u > 0) {
ppdol[plen] = 0;
@ -124,16 +128,25 @@ void RunMod(void) {
//For reading process
iso14a_card_select_t card_a_info;
uint8_t apdubuffer[MAX_FRAME_SIZE] = { 0x00 };
//Specific for Visa cards: select ppse, select Visa AID, GET PROCESSING, SFI
uint8_t ppse[20] = {0x00, 0xA4, 0x04, 0x00, 0x0e, 0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, 0x00};
uint8_t visa[13] = {0x00, 0xA4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x00};
uint8_t ppse[20] = {
0x00, 0xA4, 0x04, 0x00, 0x0e, 0x32, 0x50, 0x41,
0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44,
0x46, 0x30, 0x31, 0x00
};
uint8_t visa[13] = {
0x00, 0xA4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00,
0x00, 0x03, 0x10, 0x10, 0x00
};
uint8_t processing [8] = {0x80, 0xA8, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00};
uint8_t sfi[5] = {0x00, 0xb2, 0x01, 0x0c, 0x00};
uint8_t *apdus[4] = {ppse, visa, processing, sfi};
uint8_t apdusLen [4] = { sizeof(ppse), sizeof(visa), sizeof(processing), sizeof(sfi)};
uint8_t apduslen[4] = { sizeof(ppse), sizeof(visa), sizeof(processing), sizeof(sfi)};
uint8_t pdol[50], plen = 8;
@ -153,20 +166,15 @@ void RunMod(void) {
char token[19] = {0x00};
bool chktoken = false;
//For emulation steps
#define ATQA 0
#define UIDC1 1
#define SAKC1 3
#define RATS 5
#define SIGNATURE 7
// Allocate 512 bytes for the dynamic modulation, created when the reader queries for it
// Such a response is less time critical, so we can prepare them on the fly
// Allocate 512 bytes for the dynamic modulation, created when the reader queries for it
// Such a response is less time critical, so we can prepare them on the fly
#define DYNAMIC_RESPONSE_BUFFER_SIZE 64
#define DYNAMIC_MODULATION_BUFFER_SIZE 512
uint8_t flags = FLAG_4B_UID_IN_DATA; //UID 4 bytes(could be 7 bytes if needed it)
uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; // in case there is a read command received we shouldn't break
// UID 4 bytes(could be 7 bytes if needed it)
uint8_t flags = FLAG_4B_UID_IN_DATA;
// in case there is a read command received we shouldn't break
uint8_t data[PM3_CMD_DATA_SIZE] = {0x00};
uint8_t visauid[7] = {0x01, 0x02, 0x03, 0x04};
memcpy(data, visauid, 4);
@ -203,7 +211,7 @@ void RunMod(void) {
uint8_t state = STATE_READ;
//Checking if the user wants to go directly to emulation mode using a hardcoded track 2
// Checking if the user wants to go directly to emulation mode using a hardcoded track 2
if (chktoken == true && token[0] != 0x00) {
state = STATE_EMU;
DbpString(_YELLOW_("[ ") "Initialized emulation mode" _YELLOW_(" ]"));
@ -222,11 +230,14 @@ void RunMod(void) {
// Was our button held down or pressed?
int button_pressed = BUTTON_HELD(1000);
if (button_pressed == BUTTON_HOLD) //Holding down the button
if (button_pressed == BUTTON_HOLD)
break;
else if (button_pressed == BUTTON_SINGLE_CLICK) { //Pressing one time change between reading & emulation
else if (button_pressed == BUTTON_SINGLE_CLICK) {
// pressing one time change between reading & emulation
if (state == STATE_READ) {
if (chktoken == true && token[0] != 0x00) { //Only change to emulation if it saved a track 2 in memory
if (chktoken == true && token[0] != 0x00) {
// only change to emulation if it saved a track 2 in memory
state = STATE_EMU;
DbpString(_YELLOW_("[ ") "In emulation mode" _YELLOW_(" ]"));
} else
@ -254,28 +265,33 @@ void RunMod(void) {
chktoken = false;
LED_C_OFF();
LED_B_ON();
uint8_t apdulen = iso14_apdu(apdus[i], (uint16_t) apdusLen[i], false, apdubuffer, NULL);
uint8_t apdulen = iso14_apdu(apdus[i], (uint16_t) apduslen[i], false, apdubuffer, NULL);
if (apdulen > 0) {
DbpString(_YELLOW_("[ ") "Proxmark command" _YELLOW_(" ]"));
Dbhexdump(apdusLen[i], apdus[i], false);
Dbhexdump(apduslen[i], apdus[i], false);
DbpString(_GREEN_("[ ") "Card answer" _GREEN_(" ]"));
Dbhexdump(apdulen - 2, apdubuffer, false);
DbpString("----");
for (uint8_t u = 0; u < apdulen; u++) {
if (i == 1) {
if (apdubuffer[u] == 0x9F && apdubuffer[u + 1] == 0x38) { //Check for PDOL
// check for PDOL
if (apdubuffer[u] == 0x9F && apdubuffer[u + 1] == 0x38) {
for (uint8_t e = 0; e <= apdubuffer[u + 2]; e++)
pdol[e] = apdubuffer[u + e + 2];
plen = treatPDOL(pdol); //Generate a challenge
// generate a challenge
plen = treatPDOL(pdol);
apdus[2] = ppdol;
apdusLen[2] = plen;
apduslen[2] = plen;
existpdol = true;
}
} else if (i == 3) {
if (apdubuffer[u] == 0x57 && apdubuffer[u + 1] == 0x13 && !chktoken) { //Find track 2
// find track 2
if (apdubuffer[u] == 0x57 && apdubuffer[u + 1] == 0x13 && !chktoken) {
chktoken = true;
memcpy(&token, &apdubuffer[u + 2], 19);
break;
@ -307,6 +323,7 @@ void RunMod(void) {
}
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
LED_D_OFF();
} else if (state == STATE_EMU) {
LED_A_OFF();
LED_C_ON();
@ -328,15 +345,18 @@ void RunMod(void) {
// We need to listen to the high-frequency, peak-detected path.
iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN);
int len = 0; // command length
int retval = PM3_SUCCESS; // to check emulation status
// command length
int len = 0;
// to check emulation status
int retval = PM3_SUCCESS;
bool odd_reply = true;
clear_trace();
set_tracing(true);
for (;;) {
LED_B_OFF();
// Clean receive command buffer
// clean receive command buffer
if (!GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len)) {
DbpString(_YELLOW_("!!") "Emulator stopped");
retval = PM3_EOPABORTED;
@ -348,50 +368,84 @@ void RunMod(void) {
// dynamic_response_info will be in charge of responses
dynamic_response_info.response_n = 0;
// Checking the commands order is important and elemental
if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) { // Received a REQUEST
DbpString(_YELLOW_("+") "REQUEST Received");
p_response = &responses[ATQA];
} else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { // Received a HALT
// received a REQUEST
if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) {
odd_reply = !odd_reply;
if (odd_reply) {
p_response = &responses[RESP_INDEX_ATQA];
}
// received a HALT
} else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) {
DbpString(_YELLOW_("+") "Received a HALT");
p_response = NULL;
} else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) { // Received a WAKEUP //Este!!
// received a WAKEUP
} else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) {
DbpString(_YELLOW_("+") "WAKEUP Received");
p_response = &responses[ATQA];
prevCmd = 0;
} else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) { // Received request for UID (cascade 1)
p_response = &responses[RESP_INDEX_ATQA];
// received request for UID (cascade 1)
} else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) {
DbpString(_YELLOW_("+") "Request for UID C1");
p_response = &responses[UIDC1];
} else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { // Received a SELECT (cascade 1)
p_response = &responses[RESP_INDEX_UIDC1];
// received a SELECT (cascade 1)
} else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) {
DbpString(_YELLOW_("+") "Request for SELECT S1");
p_response = &responses[SAKC1];
} else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request
p_response = &responses[RESP_INDEX_SAKC1];
// received a RATS request
} else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) {
DbpString(_YELLOW_("+") "Request for RATS");
p_response = &responses[RATS];
prevCmd = 0;
p_response = &responses[RESP_INDEX_RATS];
} else {
DbpString(_YELLOW_("[ ") "Card reader command" _YELLOW_(" ]"));
Dbhexdump(len, receivedCmd, false);
if (receivedCmd[0] == 0x02 || receivedCmd[0] == 0x03) { //Emulate a Visa MSD(Magnetic stripe data) card
// emulate a Visa MSD(Magnetic stripe data) card
if (receivedCmd[0] == 0x02 || receivedCmd[0] == 0x03) {
dynamic_response_info.response[0] = receivedCmd[0];
//Depending on card reader commands, the Proxmark will answer to fool the reader
if (receivedCmd[2] == 0xA4 && receivedCmd[6] == 0x32 && prevCmd == 0) { //Respond with PPSE
uint8_t ppsea[39] = {0x6F, 0x23, 0x84, 0x0E, 0x32, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31, 0xA5, 0x11, 0xBF, 0x0C, 0x0E, 0x61, 0x0C, 0x4F, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x87, 0x01, 0x01, 0x90, 0x00};
// depending on card reader commands, the Proxmark will answer to fool the reader
// respond with PPSE
if (receivedCmd[2] == 0xA4 && receivedCmd[6] == 0x32 && prevCmd == 0) {
uint8_t ppsea[39] = {
0x6F, 0x23, 0x84, 0x0E, 0x32, 0x50, 0x41, 0x59,
0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46,
0x30, 0x31, 0xA5, 0x11, 0xBF, 0x0C, 0x0E, 0x61,
0x0C, 0x4F, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03,
0x10, 0x10, 0x87, 0x01, 0x01, 0x90, 0x00
};
memcpy(&dynamic_response_info.response[1], ppsea, sizeof(ppsea));
dynamic_response_info.response_n = sizeof(ppsea) + 1;
prevCmd++;
} else if (receivedCmd[2] == 0xA4 && receivedCmd[10] == 0x03 && receivedCmd[11] == 0x10 && prevCmd == 1) { //Respond Visa AID
uint8_t visauid_long[34] = {0x6F, 0x1E, 0x84, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0xA5, 0x13, 0x50, 0x0B, 0x56, 0x49, 0x53, 0x41, 0x20, 0x43, 0x52, 0x45, 0x44, 0x49, 0x54, 0x9F, 0x38, 0x03, 0x9F, 0x66, 0x02, 0x90, 0x00};
// respond Visa AID
} else if (receivedCmd[2] == 0xA4 && receivedCmd[10] == 0x03 && receivedCmd[11] == 0x10 && prevCmd == 1) {
uint8_t visauid_long[34] = {
0x6F, 0x1E, 0x84, 0x07, 0xA0, 0x00, 0x00, 0x00,
0x03, 0x10, 0x10, 0xA5, 0x13, 0x50, 0x0B, 0x56,
0x49, 0x53, 0x41, 0x20, 0x43, 0x52, 0x45, 0x44,
0x49, 0x54, 0x9F, 0x38, 0x03, 0x9F, 0x66, 0x02,
0x90, 0x00
};
memcpy(&dynamic_response_info.response[1], visauid_long, sizeof(visauid_long));
dynamic_response_info.response_n = sizeof(visauid_long) + 1;
prevCmd++;
} else if (receivedCmd[1] == 0x80 && receivedCmd[2] == 0xA8 && receivedCmd[6] == 0x83 && prevCmd == 2) { //GET PROCESSING
// GET PROCESSING
} else if (receivedCmd[1] == 0x80 && receivedCmd[2] == 0xA8 && receivedCmd[6] == 0x83 && prevCmd == 2) {
uint8_t processing_long[10] = {0x80, 0x06, 0x00, 0x80, 0x08, 0x01, 0x01, 0x00, 0x90, 0x00};
memcpy(&dynamic_response_info.response[1], processing_long, sizeof(processing_long));
dynamic_response_info.response_n = sizeof(processing_long) + 1;
prevCmd++;
} else if (receivedCmd[1] == 0x00 && receivedCmd[2] == 0xB2 && prevCmd == 3) { //SFI
// SFI
} else if (receivedCmd[1] == 0x00 && receivedCmd[2] == 0xB2 && prevCmd == 3) {
uint8_t last[4] = {0x70, 0x15, 0x57, 0x13};
uint8_t statusapdu[2] = {0x90, 0x00};
uint8_t card[25];
@ -401,6 +455,7 @@ void RunMod(void) {
memcpy(&dynamic_response_info.response[1], card, sizeof(card));
dynamic_response_info.response_n = sizeof(card) + 1;
prevCmd++;
} else {
uint8_t finished[2] = {0x6f, 0x00};
memcpy(&dynamic_response_info.response[1], finished, sizeof(finished));
@ -424,7 +479,7 @@ void RunMod(void) {
Dbhexdump(dynamic_response_info.response_n, dynamic_response_info.response, false);
DbpString("----");
// Add CRC bytes, always used in ISO 14443A-4 compliant cards
// add CRC bytes, always used in ISO 14443A-4 compliant cards
AddCrc14A(dynamic_response_info.response, dynamic_response_info.response_n);
dynamic_response_info.response_n += 2;

View file

@ -0,0 +1,415 @@
//-----------------------------------------------------------------------------
// Salvador Mendoza (salmg.net) - January 01, 2021
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// Code to relay 14a technology data aka reblay by Salvador Mendoza
//-----------------------------------------------------------------------------
#include "standalone.h"
#include "proxmark3_arm.h"
#include "appmain.h"
#include "fpgaloader.h"
#include "util.h"
#include "dbprint.h"
#include "ticks.h"
#include "string.h"
#include "BigBuf.h"
#include "iso14443a.h"
#include "protocols.h"
#include "cmd.h"
#include "usart.h" // Bluetooth reading & writing
void ModInfo(void) {
DbpString(" HF - Relaying ISO/14443A data over Bluetooth - (Salvador Mendoza)");
}
/* This standalone implements two different modes: reading & emulating, to switch between them
* just press the button.
*
* Reading ISO-14443A technology is not limited to payment cards. This example
* was designed to open new possibilities relaying ISO-14443A data over Bluetooth.
*
* Instructions:
*
* I recommend setting up & run the other end before start sending or receving data in this Proxmark3
* standalone.
*
* For the reading mode:
* - Set up and run the other end first, to where the Proxmark will send the data.
* - After the card is detected, Proxmark3 will send a package. The first byte will be the package
* length, then, the card data. Use the first length byte to read the whole package.
* - Proxmark3 will expect a raw APDU from the other end, then it will be sent to the card.
* - The answer of the card will be sent back to the connection, repeating the cycle.
*
* For the emulation mode:
* - Set up and run the other end first, from where the Proxmark will receive the data.
* - When the Proxmark3 detected the terminal, it will send the command to the connection.
* - The first byte will be the package length, then, the terminal command. Use the first
* length byte to read the whole package.
* - Proxmark3 will expect a raw APDU from the other end, then it will be sent to the terminal.
* - The command of the terminal will be sent back to the connection, repeating the cycle.
*
* Notes:
* - The emulation mode was tested in a real SumUp payment terminal. This does not mean
* that it will work in all the terminals around the world.
* - The emulation mode implements different techniques to try to keep the connection alive:
* WTX or ACK for NACK requests. Some of these requests could be denied depending on
* the reader configuration.
*
*
* Be brave enough to share your knowledge & inspire others.
*/
void RunMod() {
StandAloneMode();
Dbprintf(_YELLOW_(">>") "Relaying ISO/14443A data over Bluetooth a.k.a. reblay Started<<");
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
// Allocate 512 bytes for the dynamic modulation, created when the reader queries for it
// Such a response is less time critical, so we can prepare them on the fly
#define DYNAMIC_RESPONSE_BUFFER_SIZE 512
#define DYNAMIC_MODULATION_BUFFER_SIZE 1024
uint8_t flags = FLAG_4B_UID_IN_DATA; //UID 4 bytes(could be 7 bytes if needed it)
uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; // in case there is a read command received we shouldn't break
uint8_t visauid[7] = {0x01, 0x02, 0x03, 0x04};
memcpy(data, visauid, 4);
// to initialize the emulation
uint8_t tagType = 4; // 4 = ISO/IEC 14443-4 - javacard (JCOP)
tag_response_info_t *responses;
uint32_t cuid = 0;
uint32_t counters[3] = { 0x00, 0x00, 0x00 };
uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd };
uint8_t pages = 0;
// For received Bluetooth package
uint8_t rpacket[MAX_FRAME_SIZE] = { 0x00 };
uint16_t lenpacket = 0;
// For answering the commands
uint8_t apdubuffer[MAX_FRAME_SIZE] = { 0x00 };
uint8_t apdulen = 0;
// Buffer for Bluetooth data
uint8_t buffert[MAX_FRAME_SIZE] = { 0x00 };
uint8_t bufferlen = 0;
// Reading card
iso14a_card_select_t card_a_info;
// For init ping process
uint8_t sak = {0x0};
uint8_t atqa[2] = { 0x00, 0x00 };
uint8_t uidc[10] = { 0x00 };
uint8_t uidlen = 0;
uint8_t ats[MAX_FRAME_SIZE] = { 0x00 };
uint8_t atsl = 0;
uint8_t rdata[14] = { 0x00 };
// Command buffers
uint8_t receivedCmd[MAX_FRAME_SIZE] = { 0x00 };
uint8_t receivedCmdPar[MAX_PARITY_SIZE] = { 0x00 };
uint8_t dynamic_response_buffer[DYNAMIC_RESPONSE_BUFFER_SIZE] = {0};
uint8_t dynamic_modulation_buffer[DYNAMIC_MODULATION_BUFFER_SIZE] = {0};
// Command response - handler
tag_response_info_t dynamic_response_info = {
.response = dynamic_response_buffer,
.response_n = 0,
.modulation = dynamic_modulation_buffer,
.modulation_n = 0
};
#define STATE_READ 0
#define STATE_EMU 1
uint8_t state = STATE_READ;
if (state == STATE_READ) {
DbpString(_YELLOW_("[ ") "In reading mode" _YELLOW_(" ]"));
} else {
DbpString(_YELLOW_("[ ") "In emulation mode" _YELLOW_(" ]"));
}
for (;;) {
WDT_HIT();
// Exit from RunMod, send a usbcommand.
if (data_available()) break;
// Button held down or pressed?
int button_pressed = BUTTON_HELD(1000);
if (button_pressed == BUTTON_HOLD) // Holding down the button
break;
else if (button_pressed == BUTTON_SINGLE_CLICK) { // Pressing one time change between reading & emulation
if (state == STATE_READ) {
state = STATE_EMU;
DbpString(_YELLOW_("[ ") "In emulation mode" _YELLOW_(" ]"));
} else {
state = STATE_READ;
DbpString(_YELLOW_("[ ") "In reading mode" _YELLOW_(" ]"));
}
}
SpinDelay(500);
if (state == STATE_READ) {
LED_A_ON();
clear_trace();
set_tracing(true);
iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD);
if (iso14443a_select_card(NULL, &card_a_info, NULL, true, 0, false)) {
LED_B_ON();
// Get data to send a ping with UID + ATQA + SAK
sak = card_a_info.sak;
uidlen = card_a_info.uidlen;
atsl = card_a_info.ats_len;
memcpy(uidc, card_a_info.uid, uidlen);
memcpy(atqa, card_a_info.atqa, 2);
memcpy(ats, card_a_info.ats, atsl);
DbpString(_YELLOW_("[ ") "UID:" _YELLOW_(" ]"));
Dbhexdump(uidlen, uidc, false);
DbpString(_YELLOW_("[ ") "ATQA:" _YELLOW_(" ]"));
Dbhexdump(2, atqa, false);
Dbprintf(_YELLOW_("[ ") "SAK: %x "_YELLOW_(" ]"), sak);
DbpString(_YELLOW_("[ ") "ATS:" _YELLOW_(" ]"));
Dbhexdump(atsl, ats, false);
memcpy(&rdata[1], uidc, uidlen);
memcpy(&rdata[uidlen + 1], atqa, 2);
memcpy(&rdata[uidlen + 3], &sak, 1);
rdata[0] = uidlen + 3;
// ping = UID + ATQA + SAK
DbpString(_YELLOW_("[ ") "Ping:" _YELLOW_(" ]"));
Dbhexdump(uidlen + 4, rdata, false);
DbpString(_YELLOW_("[ ") "Sending ping" _YELLOW_(" ]"));
if (usart_writebuffer_sync(rdata, uidlen + 4) == PM3_SUCCESS) {
DbpString(_YELLOW_("[ ") "Sent!" _YELLOW_(" ]"));
for (;;) {
if (usart_rxdata_available()) {
lenpacket = usart_read_ng(rpacket, sizeof(rpacket));
if (lenpacket > 1) {
DbpString(_YELLOW_("[ ") "Bluetooth data:" _YELLOW_(" ]"));
Dbhexdump(lenpacket, rpacket, false);
apdulen = iso14_apdu(rpacket, (uint16_t) lenpacket, false, apdubuffer, NULL);
DbpString(_YELLOW_("[ ") "Card response:" _YELLOW_(" ]"));
Dbhexdump(apdulen - 2, apdubuffer, false);
bufferlen = apdulen - 2;
memcpy(&buffert[0], &bufferlen, 1);
memcpy(&buffert[1], apdubuffer, bufferlen);
DbpString(_YELLOW_("[ ") "Buffer:" _YELLOW_(" ]"));
Dbhexdump(bufferlen, buffert, false);
usart_writebuffer_sync(buffert, bufferlen + 1);
} else if (lenpacket == 1) {
DbpString(_YELLOW_("[ ") "Done!" _YELLOW_(" ]"));
LED_C_ON();
for (uint8_t i = 0; i < 3; i++)
SpinDelay(1000);
break;
}
}
LED_B_OFF();
}
} else {
DbpString(_YELLOW_("[ ") "Cannot send it!" _YELLOW_(" ]"));
SpinDelay(1000);
}
}
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
LED_D_OFF();
} else if (state == STATE_EMU) {
LED_A_OFF();
LED_C_ON();
// free eventually allocated BigBuf memory but keep Emulator Memory
BigBuf_free_keep_EM();
if (SimulateIso14443aInit(tagType, flags, data, &responses, &cuid, counters, tearings, &pages) == false) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0);
DbpString(_YELLOW_("!!") "Error initializing the emulation process!");
SpinDelay(500);
state = STATE_READ;
DbpString(_YELLOW_("[ ") "Initialized reading mode" _YELLOW_(" ]"));
break;
}
// We need to listen to the high-frequency, peak-detected path.
iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN);
int len = 0; // Command length
int retval = PM3_SUCCESS; // Check emulation status
uint8_t resp = 0; // Bluetooth response
lenpacket = 0;
uint8_t prevcmd = 0x00; // Keep track of last terminal type command
clear_trace();
set_tracing(true);
for (;;) {
LED_B_OFF();
// Clean receive command buffer
if (!GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len)) {
DbpString(_YELLOW_("!!") "Emulator stopped");
retval = PM3_EOPABORTED;
break;
}
tag_response_info_t *p_response = NULL;
LED_B_ON();
// dynamic_response_info will be in charge of responses
dynamic_response_info.response_n = 0;
if (lenpacket == 0 && resp == 2) { // Check for Bluetooth packages
if (usart_rxdata_available()) {
lenpacket = usart_read_ng(rpacket, sizeof(rpacket));
if (lenpacket > 0) {
DbpString(_YELLOW_("[ ") "Received Bluetooth data" _YELLOW_(" ]"));
Dbhexdump(lenpacket, rpacket, false);
memcpy(&dynamic_response_info.response[1], rpacket, lenpacket);
dynamic_response_info.response[0] = prevcmd;
dynamic_response_info.response_n = lenpacket + 1;
resp = 1;
}
}
}
if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) { // Received a REQUEST
DbpString(_YELLOW_("+") "REQUEST Received");
p_response = &responses[RESP_INDEX_ATQA];
} else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { // Received a HALT
DbpString(_YELLOW_("+") "Received a HALT");
p_response = NULL;
resp = 0;
} else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) { // Received a WAKEUP
DbpString(_YELLOW_("+") "WAKEUP Received");
p_response = &responses[RESP_INDEX_ATQA];
resp = 0;
} else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) { // Received request for UID (cascade 1)
DbpString(_YELLOW_("+") "Request for UID C1");
p_response = &responses[RESP_INDEX_UIDC1];
} else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { // Received a SELECT (cascade 1)
DbpString(_YELLOW_("+") "Request for SELECT S1");
p_response = &responses[RESP_INDEX_SAKC1];
} else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request
DbpString(_YELLOW_("+") "Request for RATS");
p_response = &responses[RESP_INDEX_RATS];
resp = 1;
} else if (receivedCmd[0] == 0xf2 && len == 4) { // ACKed - Time extension
DbpString(_YELLOW_("!!") "Reader accepted time extension!");
p_response = NULL;
} else if ((receivedCmd[0] == 0xb2 || receivedCmd[0] == 0xb3) && len == 3) { //NACK - Request more time WTX
DbpString(_YELLOW_("!!") "NACK - time extension request?");
if (resp == 2 && lenpacket == 0) {
DbpString(_YELLOW_("!!") "Requesting more time - WTX");
dynamic_response_info.response_n = 2;
dynamic_response_info.response[0] = 0xf2;
dynamic_response_info.response[1] = 0x0b; // Requesting the maximum amount of time
} else if (lenpacket == 0) {
DbpString(_YELLOW_("!!") "NACK - ACK - Resend last command!"); // To burn some time as well
dynamic_response_info.response[0] = 0xa3;
dynamic_response_info.response_n = 1;
} else {
DbpString(_YELLOW_("!!") "Avoiding request - Bluetooth data already in memory!!");
}
} else {
DbpString(_GREEN_("[ ") "Card reader command" _GREEN_(" ]"));
Dbhexdump(len - 2, &receivedCmd[1], false);
if ((receivedCmd[0] == 0x02 || receivedCmd[0] == 0x03) && len > 3) { // Process reader commands
if (resp == 1) {
prevcmd = receivedCmd[0];
bufferlen = len - 3;
memcpy(&buffert[0], &bufferlen, 1);
memcpy(&buffert[1], &receivedCmd[1], bufferlen);
resp = 2;
}
if (lenpacket > 0) {
DbpString(_YELLOW_("[ ") "Answering using Bluetooth data!" _YELLOW_(" ]"));
memcpy(&dynamic_response_info.response[1], rpacket, lenpacket);
dynamic_response_info.response[0] = receivedCmd[0];
dynamic_response_info.response_n = lenpacket + 1;
lenpacket = 0;
resp = 1;
} else {
DbpString(_YELLOW_("[ ") "New command: sent it & waiting for Bluetooth response!" _YELLOW_(" ]"));
usart_writebuffer_sync(buffert, bufferlen + 1);
p_response = NULL;
}
} else {
if (lenpacket == 0) {
DbpString(_YELLOW_("!!") "Received unknown command!");
memcpy(dynamic_response_info.response, receivedCmd, len);
dynamic_response_info.response_n = len;
} else {
DbpString(_YELLOW_("!!") "Avoiding unknown command - Bluetooth data already in memory!!");
}
}
}
if (dynamic_response_info.response_n > 0) {
DbpString(_GREEN_("[ ") "Proxmark3 answer" _GREEN_(" ]"));
Dbhexdump(dynamic_response_info.response_n, dynamic_response_info.response, false);
DbpString("----");
if (lenpacket > 0) {
lenpacket = 0;
resp = 1;
}
// Add CRC bytes, always used in ISO 14443A-4 compliant cards
AddCrc14A(dynamic_response_info.response, dynamic_response_info.response_n);
dynamic_response_info.response_n += 2;
if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE) == false) {
Dbprintf(_YELLOW_("[ ") "Buffer size: %d "_YELLOW_(" ]"), dynamic_response_info.response_n);
SpinDelay(500);
DbpString(_YELLOW_("!!") "Error preparing Proxmark to answer!");
continue;
}
p_response = &dynamic_response_info;
}
if (p_response != NULL) {
EmSendPrecompiledCmd(p_response);
}
}
switch_off();
set_tracing(false);
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, retval, NULL, 0);
}
}
DbpString(_YELLOW_("[=]") "exiting");
LEDsoff();
}

View file

@ -0,0 +1,343 @@
//-----------------------------------------------------------------------------
// BOSCA Maxime, RIOUX Guilhem 2021
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// main code for Nexwatch ID / Magic number collector.
//-----------------------------------------------------------------------------
#include <inttypes.h>
#include "standalone.h" // standalone definitions
#include "proxmark3_arm.h"
#include "appmain.h"
#include "lfops.h"
#include "lfsampling.h"
#include "BigBuf.h"
#include "fpgaloader.h"
#include "util.h"
#include "dbprint.h"
#include "printf.h"
#include "spiffs.h"
#include "ticks.h"
#include "lfdemod.h"
#include "commonutil.h"
/*
* `lf_nexid` sniffs after LF Nexwatch ID credentials, and stores them in internal
* flash. It requires RDV4 hardware (for flash and battery).
*
* On entering stand-alone mode, this module will start reading/record LF Nexwatch ID credentials.
* Every found / collected credential will be written/appended to the logfile in flash
* as a text string.
*
* LEDs:
* - LED A: reading / record
* - LED B: writing to flash
* - LED C: unmounting/sync'ing flash (normally < 100ms)
*
* To retrieve log file from flash:
*
* 1. mem spiffs dump -s lf_nexcollect.log -d lf_nexcollect.log
* Copies log file from flash to your client.
*
* 2. exit the Proxmark3 client
*
* 3. more lf_nexcollect.log
*
* This module emits debug strings during normal operation -- so try it out in
* the lab connected to PM3 client before taking it into the field.
*
* To delete the log file from flash:
*
* 1. mem spiffs remove -f lf_nexcollect.log
*/
#define LF_NEXCOLLECT_LOGFILE "lf_nexcollect.log"
typedef enum {
SCRAMBLE,
DESCRAMBLE
} NexWatchScramble_t;
static void DownloadLogInstructions(void) {
Dbprintf("");
Dbprintf("[=] To get the logfile from flash and display it:");
Dbprintf("[=] " _YELLOW_("1.") " mem spiffs dump -s "LF_NEXCOLLECT_LOGFILE" -d "LF_NEXCOLLECT_LOGFILE);
Dbprintf("[=] " _YELLOW_("2.") " exit proxmark3 client");
Dbprintf("[=] " _YELLOW_("3.") " cat "LF_NEXCOLLECT_LOGFILE);
}
bool log_exists;
// scramble parity (1234) -> (4231)
static uint8_t nexwatch_parity_swap(uint8_t parity) {
uint8_t a = (((parity >> 3) & 1));
a |= (((parity >> 1) & 1) << 1);
a |= (((parity >> 2) & 1) << 2);
a |= ((parity & 1) << 3);
return a;
}
// parity check
// from 32b hex id, 4b mode,
static uint8_t nexwatch_parity(uint8_t hexid[5]) {
uint8_t p = 0;
for (uint8_t i = 0; i < 5; i++) {
p ^= NIBBLE_HIGH(hexid[i]);
p ^= NIBBLE_LOW(hexid[i]);
}
return nexwatch_parity_swap(p);
}
/// NETWATCH checksum
/// @param magic = 0xBE Quadrakey, 0x88 Nexkey
/// @param id = descrambled id (printed card number)
/// @param parity = the parity based upon the scrambled raw id.
static uint8_t nexwatch_checksum(uint8_t magic, uint32_t id, uint8_t parity) {
uint8_t a = ((id >> 24) & 0xFF);
a -= ((id >> 16) & 0xFF);
a -= ((id >> 8) & 0xFF);
a -= (id & 0xFF);
a -= magic;
a -= (reflect8(parity) >> 4);
return reflect8(a);
}
// Scrambled id ( 88 bit cardnumber format)
// ref:: http://www.proxmark.org/forum/viewtopic.php?pid=14662#p14662
static int nexwatch_scamble(NexWatchScramble_t action, uint32_t *id, uint32_t *scambled) {
// 255 = Not used/Unknown other values are the bit offset in the ID/FC values
uint8_t hex_2_id [] = {
31, 27, 23, 19, 15, 11, 7, 3,
30, 26, 22, 18, 14, 10, 6, 2,
29, 25, 21, 17, 13, 9, 5, 1,
28, 24, 20, 16, 12, 8, 4, 0
};
switch (action) {
case DESCRAMBLE: {
*id = 0;
for (uint8_t idx = 0; idx < 32; idx++) {
if (hex_2_id[idx] == 255)
continue;
bool bit_state = (*scambled >> hex_2_id[idx]) & 1;
*id |= (bit_state << (31 - idx));
}
break;
}
case SCRAMBLE: {
*scambled = 0;
for (uint8_t idx = 0; idx < 32; idx++) {
if (hex_2_id[idx] == 255)
continue;
bool bit_state = (*id >> idx) & 1;
*scambled |= (bit_state << (31 - hex_2_id[idx]));
}
break;
}
default:
break;
}
return PM3_SUCCESS;
}
static void append(uint8_t *entry, size_t entry_len) {
LED_B_ON();
if (log_exists == false) {
rdv40_spiffs_write(LF_NEXCOLLECT_LOGFILE, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE);
log_exists = true;
} else {
rdv40_spiffs_append(LF_NEXCOLLECT_LOGFILE, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE);
}
LED_B_OFF();
}
static int detectNexWatch(uint8_t *dest, size_t *size, bool *invert) {
uint8_t preamble[28] = {0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// sanity check.
if (*size < 96) return -1;
size_t startIdx = 0;
if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx)) {
// if didn't find preamble try again inverting
uint8_t preamble_i[28] = {1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
if (!preambleSearch(dest, preamble_i, sizeof(preamble_i), size, &startIdx)) return -4;
*invert ^= 1;
}
// size tests?
return (int) startIdx;
}
static uint32_t PSKDemod(uint8_t *dest, size_t *size, int *startIdx) {
//buffer for result
int clk = 0, invert = 0;
//checks if the signal is just noise
if (getSignalProperties()->isnoise) {
return PM3_ESOFT;
}
//int pskRawDemod_ext(uint8_t *dest, size_t *size, int *clock, int *invert, int *startIdx)
int errCnt = pskRawDemod_ext(dest, size, &clk, &invert, startIdx);
if (errCnt > 100) {
BigBuf_free();
return PM3_ESOFT;
}
return PM3_SUCCESS;
}
static int demodNexWatch(void) {
uint8_t *dest = BigBuf_get_addr();
size_t size = MIN(16385, BigBuf_max_traceLen());
int startIdx = 0;
if (PSKDemod(dest, &size, &startIdx) != PM3_SUCCESS) {
return PM3_ESOFT;
}
bool invert = false;
int idx = detectNexWatch(dest, &size, &invert);
if (idx < 0) {
return PM3_ESOFT;
}
// skip the 4 first bits from the nexwatch preamble identification (we use 4 extra zeros..)
idx += 4;
// size = size -idx;
dest = dest + idx;
Dbprintf("[+] Id: %d, Size: %d", idx, size);
//setClockGrid(g_DemodClock, g_DemodStartIdx + (idx * g_DemodClock));
if (invert) {
Dbprintf("Inverted the demodulated data");
for (size_t i = 0; i < size; i++)
dest[i] ^= 1;
}
//got a good demod
uint32_t raw1 = bytebits_to_byte(dest, 32);
uint32_t raw2 = bytebits_to_byte(dest + 32, 32);
uint32_t raw3 = bytebits_to_byte(dest + 32 + 32, 32);
// get rawid
uint32_t rawid = 0;
for (uint8_t k = 0; k < 4; k++) {
for (uint8_t m = 0; m < 8; m++) {
rawid = (rawid << 1) | dest[m + k + (m * 4)];
}
}
// descrambled id
uint32_t cn = 0;
uint32_t scambled = bytebits_to_byte(dest + 8 + 32, 32);
nexwatch_scamble(DESCRAMBLE, &cn, &scambled);
uint8_t mode = bytebits_to_byte(dest + 72, 4);
uint8_t chk = bytebits_to_byte(dest + 80, 8);
// parity check
// from 32b hex id, 4b mode
uint8_t hex[5] = {0};
for (uint8_t i = 0; i < 5; i++) {
hex[i] = bytebits_to_byte(dest + 8 + 32 + (i * 8), 8);
}
// mode is only 4 bits.
hex[4] &= 0xf0;
uint8_t calc_parity = nexwatch_parity(hex);
uint8_t magic = 0;
// output
Dbprintf(" NexWatch raw id : " _YELLOW_("0x%08"PRIx32), rawid);
uint8_t temp_checksum;
for (; magic < 255; magic++) {
temp_checksum = nexwatch_checksum(magic, cn, calc_parity);
if (temp_checksum == chk) {
Dbprintf(" Magic number : " _GREEN_("0x%X"), magic);
break;
}
}
Dbprintf(" 88bit id : " _YELLOW_("%"PRIu32) " (" _YELLOW_("0x%08"PRIx32)")", cn, cn);
Dbprintf(" mode : %x", mode);
Dbprintf(" Raw : " _YELLOW_("%08"PRIX32"%08"PRIX32"%08"PRIX32), raw1, raw2, raw3);
uint8_t entry[81];
memset(entry, 0, sizeof(entry));
sprintf((char *)entry, "Nexwatch ID: %"PRIu32", Magic bytes: 0x%X, Mode: %x\n",
cn,
magic,
mode);
append(entry, strlen((char *)entry));
Dbprintf("%s", entry);
BigBuf_free();
return PM3_SUCCESS;
}
void ModInfo(void) {
DbpString(_YELLOW_(" Nexwatch credentials detection mode") " - a.k.a NexID (jrjgjk & Zolorah)");
}
void RunMod(void) {
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
LFSetupFPGAForADC(LF_DIVISOR_125, true);
BigBuf_Clear();
StandAloneMode();
Dbprintf(_YELLOW_("[=] Standalone mode nexid started"));
rdv40_spiffs_lazy_mount();
log_exists = exists_in_spiffs(LF_NEXCOLLECT_LOGFILE);
// the main loop for your standalone mode
for (;;) {
WDT_HIT();
// exit from IceHID, send a usbcommand.
if (data_available()) break;
// Was our button held down or pressed?
int button_pressed = BUTTON_HELD(280);
if (button_pressed == BUTTON_HOLD)
break;
LED_A_ON();
uint32_t res;
size_t size = MIN(16385, BigBuf_max_traceLen());
DoAcquisition_config(false, size);
res = demodNexWatch();
if (res == PM3_SUCCESS) {
LED_A_OFF();
continue;
}
}
LED_C_ON();
rdv40_spiffs_lazy_unmount();
LED_C_OFF();
LEDsoff();
DownloadLogInstructions();
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
}

View file

@ -1114,15 +1114,9 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_LF_HITAG_ELOAD: {
/*
struct p {
uint16_t len;
uint8_t *data;
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
lf_hitag_t *payload = (lf_hitag_t *) packet->data.asBytes;
uint8_t *mem = BigBuf_get_EM_addr();
memcpy((uint8_t *)mem.sectors, payload->data, payload->len);
*/
memcpy((uint8_t *)mem, payload->data, payload->len);
break;
}
#endif
@ -1574,6 +1568,14 @@ static void PacketReceived(PacketCommandNG *packet) {
MifareGen3Freez();
break;
}
case CMD_HF_MIFARE_G3_RDBL: {
struct p {
uint8_t blockno;
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
MifareG3ReadBlk(payload->blockno);
break;
}
case CMD_HF_MIFARE_PERSONALIZE_UID: {
struct p {
uint8_t keytype;

View file

@ -103,8 +103,9 @@ void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, un
uint8_t *tout = (uint8_t *) out;
while (length > 0) {
for (i = 0; i < 8; i++)
for (i = 0; i < 8; i++) {
tin[i] = (unsigned char)(tin[i] ^ iv[i]);
}
mbedtls_des3_crypt_ecb(&ctx3, tin, tout);
@ -121,8 +122,9 @@ void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, un
void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key) {
uint8_t data[8];
memcpy(data, value, 8);
for (int n = 0; n < 8; n++)
data[n] &= 0xfe;
for (int n = 0; n < 8; n++) {
data[n] &= 0xFE;
}
Desfire_des_key_new_with_version(data, key);
}
@ -138,10 +140,12 @@ void Desfire_des_key_new_with_version(const uint8_t value[8], desfirekey_t key)
void Desfire_3des_key_new(const uint8_t value[16], desfirekey_t key) {
uint8_t data[16];
memcpy(data, value, 16);
for (int n = 0; n < 8; n++)
data[n] &= 0xfe;
for (int n = 8; n < 16; n++)
for (int n = 0; n < 8; n++) {
data[n] &= 0xFE;
}
for (int n = 8; n < 16; n++) {
data[n] |= 0x01;
}
Desfire_3des_key_new_with_version(data, key);
}
@ -156,8 +160,9 @@ void Desfire_3des_key_new_with_version(const uint8_t value[16], desfirekey_t key
void Desfire_3k3des_key_new(const uint8_t value[24], desfirekey_t key) {
uint8_t data[24];
memcpy(data, value, 24);
for (int n = 0; n < 8; n++)
data[n] &= 0xfe;
for (int n = 0; n < 8; n++) {
data[n] &= 0xFE;
}
Desfire_3k3des_key_new_with_version(data, key);
}
@ -194,13 +199,13 @@ uint8_t Desfire_key_get_version(desfirekey_t key) {
void Desfire_key_set_version(desfirekey_t key, uint8_t version) {
for (int n = 0; n < 8; n++) {
uint8_t version_bit = ((version & (1 << (7 - n))) >> (7 - n));
key->data[n] &= 0xfe;
key->data[n] &= 0xFE;
key->data[n] |= version_bit;
if (key->type == T_DES) {
key->data[n + 8] = key->data[n];
} else {
// Write ~version to avoid turning a 3DES key into a DES key
key->data[n + 8] &= 0xfe;
key->data[n + 8] &= 0xFE;
key->data[n + 8] |= ~version_bit;
}
}
@ -267,23 +272,32 @@ void cmac_generate_subkeys(desfirekey_t key) {
// Used to compute CMAC on complete blocks
memcpy(key->cmac_sk1, l, kbs);
txor = l[0] & 0x80;
lsl(key->cmac_sk1, kbs);
if (txor)
if (txor) {
key->cmac_sk1[kbs - 1] ^= R;
}
// Used to compute CMAC on the last block if non-complete
memcpy(key->cmac_sk2, key->cmac_sk1, kbs);
txor = key->cmac_sk1[0] & 0x80;
lsl(key->cmac_sk2, kbs);
if (txor)
if (txor) {
key->cmac_sk2[kbs - 1] ^= R;
}
}
void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac) {
int kbs = key_block_size(key);
if (kbs == 0)
if (kbs == 0) {
return;
}
uint8_t *buffer = BigBuf_malloc(padded_data_length(len, kbs));
@ -306,8 +320,10 @@ void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t le
}
size_t key_block_size(const desfirekey_t key) {
if (key == NULL)
if (key == NULL) {
return 0;
}
size_t block_size = 8;
switch (key->type) {
case T_DES:
@ -830,10 +846,12 @@ void mifare_cypher_blocks_chained(desfiretag_t tag, desfirekey_t key, uint8_t *i
size_t block_size;
if (tag) {
if (!key)
if (key == NULL) {
key = DESFIRE(tag)->session_key;
if (!ivect)
}
if (ivect == NULL) {
ivect = DESFIRE(tag)->ivect;
}
switch (DESFIRE(tag)->authentication_scheme) {
case AS_LEGACY:

View file

@ -1313,7 +1313,7 @@ static bool em4x50_sim_read_word(uint32_t *word) {
}
}
*word = BYTES2UINT32(bytes);
*word = BYTES2UINT32_BE(bytes);
// check parities
if ((parities == parities_calculated) && (stop_bit == 0)) {

View file

@ -1148,6 +1148,9 @@ void SniffHitag2(void) {
// Enable and reset counter
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
// Assert a sync signal. This sets all timers to 0 on next active clock edge
AT91C_BASE_TCB->TCB_BCR = 1;
int frame_count = 0, response = 0, overflow = 0, lastbit = 1, tag_sof = 4;
bool rising_edge = false, reader_frame = false, bSkip = true;
uint8_t rx[HITAG_FRAME_LEN];
@ -1293,11 +1296,15 @@ void SniffHitag2(void) {
// Reset the timer to restart while-loop that receives frames
AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG;
// Assert a sync signal. This sets all timers to 0 on next active clock edge
AT91C_BASE_TCB->TCB_BCR = 1;
}
LEDsoff();
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
set_tracing(false);

View file

@ -246,6 +246,9 @@ void lf_init(bool reader, bool simulate) {
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
// Assert a sync signal. This sets all timers to 0 on next active clock edge
AT91C_BASE_TCB->TCB_BCR = 1;
// Prepare data trace
uint32_t bufsize = 10000;

View file

@ -2594,6 +2594,42 @@ OUT:
BigBuf_free();
}
void MifareG3ReadBlk(uint8_t blockno) {
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
clear_trace();
set_tracing(true);
int retval = PM3_SUCCESS;
uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE);
uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE);
uint8_t *uid = BigBuf_malloc(10);
if (iso14443a_select_card(uid, NULL, NULL, true, 0, true) == false) {
retval = PM3_ESOFT;
goto OUT;
}
LED_B_ON();
uint32_t save_iso14a_timeout = iso14a_get_timeout();
iso14a_set_timeout(13560000 / 1000 / (8 * 16) * 1000); // 2 seconds timeout
uint8_t cmd[] = { 0xCF, 0x00, 0x00, 0x00, 0x00, 0xCE, blockno, 0x00, 0x00};
AddCrc14A(cmd, sizeof(cmd) - 2);
ReaderTransmit(cmd, sizeof(cmd), NULL);
int res = ReaderReceive(buf, par);
if (res != 18) {
retval = PM3_ESOFT;
}
iso14a_set_timeout(save_iso14a_timeout);
LED_B_OFF();
OUT:
reply_ng(CMD_HF_MIFARE_G3_RDBL, retval, buf, 18);
// turns off
OnSuccessMagic();
BigBuf_free();
}
void MifareSetMod(uint8_t *datain) {
uint8_t mod = datain[0];

View file

@ -49,6 +49,9 @@ void MifareGen3UID(uint8_t uidlen, uint8_t *uid); // Gen 3 magic card set UID wi
void MifareGen3Blk(uint8_t block_len, uint8_t *block); // Gen 3 magic card overwrite manufacturer block
void MifareGen3Freez(void); // Gen 3 magic card lock further UID changes
// MFC GEN3 GTU
void MifareG3ReadBlk(uint8_t blockno);
void MifareSetMod(uint8_t *datain);
void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key);

View file

@ -14,13 +14,17 @@
#define ALLOC 16
size_t DemodPCF7931(uint8_t **outBlocks) {
// 2021 iceman, memor
uint8_t bits[256] = {0x00};
uint8_t blocks[8][16];
uint8_t *dest = BigBuf_get_addr();
int GraphTraceLen = BigBuf_max_traceLen();
if (GraphTraceLen > 18000)
if (GraphTraceLen > 18000) {
GraphTraceLen = 18000;
}
int i = 2, j, lastval, bitidx, half_switch;
int clock = 64;
@ -38,15 +42,17 @@ size_t DemodPCF7931(uint8_t **outBlocks) {
/* Find first local max/min */
if (dest[1] > dest[0]) {
while (i < GraphTraceLen) {
if (!(dest[i] > dest[i - 1]) && dest[i] > lmax)
if (!(dest[i] > dest[i - 1]) && dest[i] > lmax) {
break;
}
i++;
}
dir = 0;
} else {
while (i < GraphTraceLen) {
if (!(dest[i] < dest[i - 1]) && dest[i] < lmin)
if (!(dest[i] < dest[i - 1]) && dest[i] < lmin) {
break;
}
i++;
}
dir = 1;
@ -58,6 +64,7 @@ size_t DemodPCF7931(uint8_t **outBlocks) {
block_done = 0;
for (bitidx = 0; i < GraphTraceLen; i++) {
if ((dest[i - 1] > dest[i] && dir == 1 && dest[i] > lmax) || (dest[i - 1] < dest[i] && dir == 0 && dest[i] < lmin)) {
lc = i - lastval;
lastval = i;
@ -66,8 +73,8 @@ size_t DemodPCF7931(uint8_t **outBlocks) {
// Tolerance is 1/8 of clock rate (arbitrary)
if (ABS(lc - clock / 4) < tolerance) {
// 16T0
if ((i - pmc) == lc) { /* 16T0 was previous one */
/* It's a PMC ! */
if ((i - pmc) == lc) { // 16T0 was previous one
// It's a PMC
i += (128 + 127 + 16 + 32 + 33 + 16) - 1;
lastval = i;
pmc = 0;
@ -77,8 +84,8 @@ size_t DemodPCF7931(uint8_t **outBlocks) {
}
} else if (ABS(lc - clock / 2) < tolerance) {
// 32TO
if ((i - pmc) == lc) { /* 16T0 was previous one */
/* It's a PMC ! */
if ((i - pmc) == lc) { // 16T0 was previous one
// It's a PMC !
i += (128 + 127 + 16 + 32 + 33) - 1;
lastval = i;
pmc = 0;
@ -95,8 +102,9 @@ size_t DemodPCF7931(uint8_t **outBlocks) {
// Error
if (++warnings > 10) {
if (DBGLEVEL >= DBG_EXTENDED)
Dbprintf("Error: too many detection errors, aborting.");
if (DBGLEVEL >= DBG_EXTENDED) {
Dbprintf("Error: too many detection errors, aborting");
}
return 0;
}
@ -122,13 +130,19 @@ size_t DemodPCF7931(uint8_t **outBlocks) {
block_done = 0;
half_switch = 0;
}
if (i < GraphTraceLen)
if (i < GraphTraceLen) {
dir = (dest[i - 1] > dest[i]) ? 0 : 1;
}
}
if (bitidx == 255)
if (bitidx == 255) {
bitidx = 0;
warnings = 0;
if (num_blocks == 4) break;
}
if (num_blocks == 4) {
break;
}
}
memcpy(outBlocks, blocks, 16 * num_blocks);
return num_blocks;
@ -138,10 +152,11 @@ bool IsBlock0PCF7931(uint8_t *block) {
// assuming all RFU bits are set to 0
// if PAC is enabled password is set to 0
if (block[7] == 0x01) {
if (!memcmp(block, "\x00\x00\x00\x00\x00\x00\x00", 7)
&& !memcmp(block + 9, "\x00\x00\x00\x00\x00\x00\x00", 7)) {
if (!memcmp(block, "\x00\x00\x00\x00\x00\x00\x00", 7) &&
!memcmp(block + 9, "\x00\x00\x00\x00\x00\x00\x00", 7)) {
return true;
}
} else if (block[7] == 0x00) {
if (!memcmp(block + 9, "\x00\x00\x00\x00\x00\x00\x00", 7)) {
return true;
@ -211,8 +226,9 @@ void ReadPCF7931(void) {
// exit if too many errors during reading
if (tries > 50 && (2 * errors > tries)) {
if (DBGLEVEL >= DBG_INFO)
if (DBGLEVEL >= DBG_INFO) {
Dbprintf("[!!] Error reading the tag, only partial content");
}
goto end;
}
@ -461,27 +477,32 @@ void SendCmdPCF7931(uint32_t *tab) {
//initialization of the timer
AT91C_BASE_PMC->PMC_PCER |= (0x1 << AT91C_ID_TC0);
AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE;
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; //clock at 48/32 MHz
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; // clock at 48/32 MHz
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN;
// Assert a sync signal. This sets all timers to 0 on next active clock edge
AT91C_BASE_TCB->TCB_BCR = 1;
tempo = AT91C_BASE_TC0->TC_CV;
for (u = 0; tab[u] != 0; u += 3) {
// modulate antenna
HIGH(GPIO_SSC_DOUT);
while (tempo != tab[u])
while (tempo != tab[u]) {
tempo = AT91C_BASE_TC0->TC_CV;
}
// stop modulating antenna
LOW(GPIO_SSC_DOUT);
while (tempo != tab[u + 1])
while (tempo != tab[u + 1]) {
tempo = AT91C_BASE_TC0->TC_CV;
}
// modulate antenna
HIGH(GPIO_SSC_DOUT);
while (tempo != tab[u + 2])
while (tempo != tab[u + 2]) {
tempo = AT91C_BASE_TC0->TC_CV;
}
}
LED_A_OFF();

View file

@ -125,6 +125,8 @@ void StartCountUS(void) {
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
// Assert a sync signal. This sets all timers to 0 on next active clock edge
AT91C_BASE_TCB->TCB_BCR = 1;
while (AT91C_BASE_TC1->TC_CV > 0);

View file

@ -76,7 +76,9 @@ endif (NOT SKIPPYTHON EQUAL 1)
# If cross-compiled, we need to init source and build.
if (CMAKE_TOOLCHAIN_FILE)
set(CFLAGS_EXTERNAL_LIB "CFLAGS=--target=${CMAKE_C_COMPILER_TARGET} -w")
if (ANDROID)
set(CFLAGS_EXTERNAL_LIB "CFLAGS=--target=${CMAKE_C_COMPILER_TARGET} -w")
endif (ANDROID)
set(EMBED_READLINE ON)
set(EMBED_BZIP2 ON)
endif (CMAKE_TOOLCHAIN_FILE)
@ -213,6 +215,7 @@ set (TARGET_SOURCES
${PM3_ROOT}/client/src/iso7816/iso7816core.c
${PM3_ROOT}/client/src/cipurse/cipursecrypto.c
${PM3_ROOT}/client/src/cipurse/cipursecore.c
${PM3_ROOT}/client/src/cipurse/cipursetest.c
${PM3_ROOT}/client/src/loclass/cipher.c
${PM3_ROOT}/client/src/loclass/cipherutils.c
${PM3_ROOT}/client/src/loclass/elite_crack.c
@ -225,9 +228,14 @@ set (TARGET_SOURCES
${PM3_ROOT}/client/src/mifare/mifarehost.c
${PM3_ROOT}/client/src/nfc/ndef.c
${PM3_ROOT}/client/src/mifare/desfire_crypto.c
${PM3_ROOT}/client/src/mifare/desfirecrypto.c
${PM3_ROOT}/client/src/mifare/desfiresecurechan.c
${PM3_ROOT}/client/src/mifare/desfirecore.c
${PM3_ROOT}/client/src/mifare/desfiretest.c
${PM3_ROOT}/client/src/uart/uart_posix.c
${PM3_ROOT}/client/src/uart/uart_win32.c
${PM3_ROOT}/client/src/ui/overlays.ui
${PM3_ROOT}/client/src/ui/image.ui
${PM3_ROOT}/client/src/aiddesfire.c
${PM3_ROOT}/client/src/aidsearch.c
${PM3_ROOT}/client/src/cmdanalyse.c

View file

@ -14,10 +14,18 @@ vpath %.dic dictionaries
OBJDIR = obj
ifeq ($(USE_BREW),1)
INCLUDES += -I$(BREW_PREFIX)/include
LDLIBS += -L$(BREW_PREFIX)/lib
PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/opt/qt/lib/pkgconfig
PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/opt/qt5/lib/pkgconfig
ifdef MACPORTS_PREFIX
INCLUDES += -I$(MACPORTS_PREFIX)/include
LDLIBS += -L$(MACPORTS_PREFIX)/lib
PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(MACPORTS_PREFIX)/lib/pkgconfig
PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(MACPORTS_PREFIX)/libexec/qt/lib/pkgconfig
PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(MACPORTS_PREFIX)/libexec/qt5/lib/pkgconfig
else
INCLUDES += -I$(BREW_PREFIX)/include
LDLIBS += -L$(BREW_PREFIX)/lib
PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/opt/qt/lib/pkgconfig
PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/opt/qt5/lib/pkgconfig
endif
endif
###################
@ -117,8 +125,13 @@ INCLUDES += $(HARDNESTEDLIBINC)
## Lua
ifneq ($(SKIPLUASYSTEM),1)
LUAINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags lua5.2 2>/dev/null)
LUALDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs lua5.2 2>/dev/null)
ifdef MACPORTS_PREFIX
LUAINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags lua-5.2 2>/dev/null)
LUALDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs lua-5.2 2>/dev/null)
else
LUAINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags lua5.2 2>/dev/null)
LUALDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs lua5.2 2>/dev/null)
endif
ifneq ($(LUALDLIBS),)
LUALIB =
LUALIBLD = $(LUALDLIBS)
@ -279,8 +292,12 @@ CXXINCLUDES += $(QTINCLUDES)
## Readline
ifneq ($(SKIPREADLINE),1)
ifeq ($(USE_BREW),1)
LDLIBS += -L$(BREW_PREFIX)/opt/readline/lib
INCLUDES += -I$(BREW_PREFIX)/opt/readline/include
ifdef MACPORTS_PREFIX
INCLUDES += -I$(MACPORTS_PREFIX)/include/readline
else
LDLIBS += -L$(BREW_PREFIX)/opt/readline/lib
INCLUDES += -I$(BREW_PREFIX)/opt/readline/include
endif
endif
LDLIBS += -lreadline
READLINE_FOUND = 1
@ -559,6 +576,7 @@ SRCS = aiddesfire.c \
fido/fidocore.c \
cipurse/cipursecore.c \
cipurse/cipursecrypto.c \
cipurse/cipursetest.c \
fileutils.c \
flash.c \
generator.c \
@ -571,6 +589,10 @@ SRCS = aiddesfire.c \
loclass/elite_crack.c \
loclass/ikeys.c \
mifare/desfire_crypto.c \
mifare/desfirecrypto.c \
mifare/desfirecore.c \
mifare/desfiresecurechan.c \
mifare/desfiretest.c \
mifare/mad.c \
mifare/mfkey.c \
mifare/mifare4.c \
@ -639,9 +661,9 @@ OBJS += $(OBJCSRCS:%.m=$(OBJDIR)/%.o)
BINS = proxmark3
CLEAN = $(BINS) src/version_pm3.c src/*.moc.cpp src/ui/ui_overlays.h lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lua
CLEAN = $(BINS) src/version_pm3.c src/*.moc.cpp src/ui/ui_overlays.h src/ui/ui_image.h lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lua
# transition: cleaning also old path stuff
CLEAN += flasher *.moc.cpp ui/ui_overlays.h
CLEAN += flasher *.moc.cpp ui/ui_overlays.h ui/ui_image.h
###########
# targets #
@ -657,7 +679,7 @@ proxmark3: $(OBJS) $(STATICLIBS) lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lu
$(info [=] LD $@)
$(Q)$(LD) $(PM3LDFLAGS) $(OBJS) $(STATICLIBS) $(LDLIBS) -o $@
src/proxgui.cpp: src/ui/ui_overlays.h
src/proxgui.cpp: src/ui/ui_overlays.h src/ui/ui_image.h
src/proxguiqt.moc.cpp: src/proxguiqt.h
$(info [-] MOC $@)
@ -667,6 +689,10 @@ src/ui/ui_overlays.h: src/ui/overlays.ui
$(info [-] UIC $@)
$(Q)$(UIC) $^ > $@
src/ui/ui_image.h: src/ui/image.ui
$(info [-] UIC $@)
$(Q)$(UIC) $^ > $@
lualibs/pm3_cmd.lua: ../include/pm3_cmd.h
$(info [=] GEN $@)
$(Q)awk -f pm3_cmd_h2lua.awk $^ > $@

View file

@ -60,6 +60,12 @@ void HideGraphWindow(void) {}
void RepaintGraphWindow(void) {}
void ShowPictureWindow(char *fn) {}
void HidePictureWindow(void) {}
void RepaintPictureWindow(void) {}
int push_cmdscriptfile(char *path, bool stayafter) { return PM3_SUCCESS; }
static bool OpenPm3(void) {

View file

@ -298,6 +298,59 @@ int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int
return 0;
}
int CLIGetOptionList(struct arg_str *argstr, const CLIParserOption *option_array, int *value) {
char data[200] = {0};
int datalen = 0;
int res = CLIParamStrToBuf(argstr, (uint8_t *)data, sizeof(data), &datalen);
if (res)
return res;
// no data to check - we do not touch *value, just return
if (datalen == 0)
return 0;
str_lower(data);
int val = -1;
int cntr = 0;
for (int i = 0; i < CLI_MAX_OPTLIST_LEN && option_array[i].text != NULL; i++) {
// exact match
if (strcmp(option_array[i].text, data) == 0) {
*value = option_array[i].code;
return 0;
}
// partial match
if (strncmp(option_array[i].text, data, datalen) == 0) {
val = option_array[i].code;
cntr++;
}
}
// check partial match
if (cntr == 0) {
PrintAndLogEx(ERR, "Parameter error: No similar option to `%s`. Valid options: %s\n", argstr->sval[0], argstr->hdr.datatype);
return 20;
}
if (cntr > 1) {
PrintAndLogEx(ERR, "Parameter error: Several options fit to `%s`. Valid options: %s\n", argstr->sval[0], argstr->hdr.datatype);
return 21;
}
*value = val;
return 0;
}
const char *CLIGetOptionListStr(const CLIParserOption *option_array, int value) {
static const char *errmsg = "n/a";
for (int i = 0; i < CLI_MAX_OPTLIST_LEN && option_array[i].text != NULL; i++) {
if (option_array[i].code == value)
return option_array[i].text;
}
return errmsg;
}
// hexstr -> u64, w optional len input and default value fallback.
// 0 = failed
// 1 = OK

View file

@ -51,6 +51,8 @@
#define CLIGetStrWithReturn(ctx, paramnum, data, datalen) if (CLIParamStrToBuf(arg_get_str((ctx), (paramnum)), (data), (*datalen), (datalen))) {CLIParserFree((ctx)); return PM3_ESOFT;}
#define CLIGetOptionListWithReturn(ctx, paramnum, option_array, option_array_len, value) if (CLIGetOptionList(arg_get_str((ctx), (paramnum)), (option_array), (option_array_len), (value))) {CLIParserFree((ctx)); return PM3_ESOFT;}
typedef struct {
void **argtable;
size_t argtableLen;
@ -59,6 +61,14 @@ typedef struct {
const char *programHelp;
char buf[1024 + 60];
} CLIParserContext;
#define CLI_MAX_OPTLIST_LEN 50
// option list needs to have NULL at the last record int the field `text`
typedef struct {
int code;
const char *text;
} CLIParserOption;
int CLIParserInit(CLIParserContext **ctx, const char *vprogramName, const char *vprogramHint, const char *vprogramHelp);
void CLIParserPrintHelp(CLIParserContext *ctx);
int CLIParserParseString(CLIParserContext *ctx, const char *str, void *vargtable[], size_t vargtableLen, bool allowEmptyExec);
@ -69,6 +79,10 @@ int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int
int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen);
int CLIParamBinToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen);
// names in the CLIParserOption array must be in the lowercase format
int CLIGetOptionList(struct arg_str *argstr, const CLIParserOption *option_array, int *value);
const char *CLIGetOptionListStr(const CLIParserOption *option_array, int value);
uint64_t arg_get_u64_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint64_t def);
int arg_get_u64_hexstr_def_nlen(CLIParserContext *ctx, uint8_t paramnum, uint64_t def, uint64_t *out, uint8_t nlen, bool optional);
int arg_get_u32_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint32_t def, uint32_t *out);

View file

@ -6,7 +6,8 @@
ffffffffffff # Defaultkey(firstkeyusedbyprogramifnouserdefinedkey)
000000000000 # Blankkey
a0a1a2a3a4a5 # NFC Forum MADkey
A5A4A3A2A1A0 # MAD access key (reversed)
A5A4A3A2A1A0 # MAD access key A (reversed)
89ECA97F8C2A # MAD access key B
b0b1b2b3b4b5
c0c1c2c3c4c5
d0d1d2d3d4d5
@ -20,6 +21,9 @@ d3f7d3f7d3f7 # key A Wien
a0478cc39091
533cb6c723f6
8fd0a4f256e9
e00000000000 # iCopy-X
e7d6064c5860
b27ccab30dbd
#
d2ece8b9395e # lib / Nat Bieb
# NSCP default key
@ -289,6 +293,49 @@ c2b7ec7d4eb1
ac70ca327a04
eb0a8ff88ade
#
# Transport system Metromoney
2803bcb0c7e1
9c616585e26d
4fa9eb49f75e
2dade48942c5
a160fcd5ec4c
112233445566
361a62f35bc9
#
# Transport system Spain
83f3cb98c258
070d486bc555
a9b018868cc1
9dcdb136110c
749934cc8ed3
506db955f161
f088a85e71d7
72b458d60363
70c714869dc7
b32464412ee3
f253c30568c4
1c68315674ac
cfe63749080a
c1e6f8afc9ec
dd0de3ba08a6
3d923eb73534
ff94f86b09a6
d61707ffdfb1
8223205047b6
9951a273dee7
c9449301af93
66695a45c9fa
89aa9d743812
c41514defc07
c52876869800
5353b3aecb53
2e4169a5c79d
4bb747e48c2a
6285a1c8eb5c
5145c34dba19
25352912cd8d
81b20c274c3f
#
# Data from https://github.com/RadioWar/NFCGUI
44dd5a385aaf
21a600056cb0
@ -1369,6 +1416,17 @@ F678905568C3
D1417E431949
4BF6DE347FB6
#
#
3a471b2192bf
a297ceb7d34b
ae76242931f1
#
#
124578ABFEDC
ABFEDC124578
4578ABFEDC12
#
# Data from
# premier inn hotel chain
5e594208ef02
af9e38d36582

View file

@ -9,6 +9,7 @@
6AC292FAA1315B4D858AB3A3D7D5933A
404142434445464748494a4b4c4d4e4f
3112B738D8862CCD34302EB299AAB456 # Gallagher AES (https://pastebin.com/GkbGLz8r)
47454D5850524553534F53414D504C45 # Gemalto
00112233445566778899aabbccddeeff
2b7e151628aed2a6abf7158809cf4f3c
fbeed618357133667c85e08f7236a8de

View file

@ -1,3 +1,4 @@
47454D5850524553534F53414D504C45 # Gemalto
ffffffffffffffffffffffffffffffff
00000000000000000000000000000000
a0a1a2a3a4a5a6a7a0a1a2a3a4a5a6a7

View file

@ -38,6 +38,10 @@ E9920427
50520901
# iCopy-X
20206666
# ID/HID CARD COPER SK-663
65857569
# password found on discord
5469616E
# Default pwd, simple:
00000000
11111111

View file

@ -76,7 +76,9 @@ endif (NOT SKIPPYTHON EQUAL 1)
# If cross-compiled, we need to init source and build.
if (CMAKE_TOOLCHAIN_FILE)
set(CFLAGS_EXTERNAL_LIB "CFLAGS=--target=${CMAKE_C_COMPILER_TARGET} -w")
if (ANDROID)
set(CFLAGS_EXTERNAL_LIB "CFLAGS=--target=${CMAKE_C_COMPILER_TARGET} -w")
endif (ANDROID)
set(EMBED_READLINE ON)
set(EMBED_BZIP2 ON)
endif (CMAKE_TOOLCHAIN_FILE)

View file

@ -81,7 +81,7 @@ function getUnixTime(datetime)
local datetime_pattern = "(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)+(%d+):(%d+)"
local new_year, new_month, new_day, new_hour, new_minute, new_seconds, new_hour_offset, new_minute_offset = datetime:match(datetime_pattern)
if new_year == nil or new_month == nil or new_day == nil or
if new_year == nil or new_month == nil or new_day == nil or
new_hour == nil or new_minute == nil or new_seconds == nil or
new_hour_offset == nil or new_minute_offset == nil then
@ -111,7 +111,7 @@ function sendRaw(rawdata, options)
-- arg1 is the defined flags for sending "raw" ISO 14443A package
arg1 = flags,
-- arg2 contains the length, which is half the length of the ASCII
-- string data
arg2 = string.len(rawdata) / 2,
@ -137,7 +137,7 @@ function readOTP(show_output)
lib14a.disconnect()
return oops(err)
end
-- parse the response
local cmd_response = Command.parse(res)
local len = tonumber(cmd_response.arg1) * 2
@ -160,7 +160,7 @@ function readOTP(show_output)
if show_output then
print("[" .. ansicolors.green .. "+" .. ansicolors.reset .. "] OTP: " .. ansicolors.green .. otp_value .. ansicolors.reset)
end
else
else
print("[" .. ansicolors.red .. "-" .. ansicolors.reset .."] Error: Could not read the OTP")
otp_value = nil
end
@ -183,7 +183,7 @@ function readInfo(show_output)
lib14a.disconnect()
return oops(err)
end
-- parse the response
local cmd_response = Command.parse(res)
local len = tonumber(cmd_response.arg1) * 2
@ -233,7 +233,7 @@ function readInfo(show_output)
end
return otp_interval
else
else
print("[" .. ansicolors.red .. "-" .. ansicolors.reset .."] Error: Could not read the token info")
otp_value = nil
end
@ -261,7 +261,7 @@ function bruteforceCommands()
lib14a.disconnect()
return oops(err)
end
-- parse the response
local cmd_response = Command.parse(res)
local len = tonumber(cmd_response.arg1) * 2
@ -287,7 +287,7 @@ function setTime(time, otp_interval)
-- build the raw command data
local data = "120000" ..string.format("%02x", otp_interval) .. string.format("%08x", time_var1) .. string.format("%02x", time_var2)
-- calculate XOR checksum on data
local checksum = 0
for i = 1, #data, 2 do
@ -306,7 +306,7 @@ function setTime(time, otp_interval)
lib14a.disconnect()
return oops(err)
end
-- parse the response
local cmd_response = Command.parse(res)
local len = tonumber(cmd_response.arg1) * 2
@ -343,8 +343,8 @@ function timeTravelAttack(datetime_string, otp_interval)
-- read the OTP
local otp = readOTP(false)
print(string.format("[" .. ansicolors.green .. "+" .. ansicolors.reset .. "] The future OTP on " ..
ansicolors.yellow .. "%s (%d) " .. ansicolors.reset .. "is " ..
print(string.format("[" .. ansicolors.green .. "+" .. ansicolors.reset .. "] The future OTP on " ..
ansicolors.yellow .. "%s (%d) " .. ansicolors.reset .. "is " ..
ansicolors.green .. "%s" .. ansicolors.reset, datetime_string, future_time, otp))
-- reset the current time
@ -372,7 +372,7 @@ function main(args)
if o == 'h' then return help() end
if o == 'i' then operation = READ_INFO end
if o == 'r' then operation = READ_OTP end
if o == 't' then
if o == 't' then
operation = TIME_TRAVELER_ATTACK
target_time = a
end

View file

@ -68,10 +68,10 @@ end
--- Set UID on magic command enabled on a ICEMAN based REPO
local function magicUID_iceman(b0, b1)
print('Using backdoor Magic tag function')
core.console('hf 15 raw -2 -c 02213E00000000')
core.console('hf 15 raw -2 -c 02213F69960000')
core.console('hf 15 raw -2 -c 022138'..b1)
core.console('hf 15 raw -2 -c 022139'..b0)
core.console('hf 15 raw -2 -c -d 02213E00000000')
core.console('hf 15 raw -2 -c -d 02213F69960000')
core.console('hf 15 raw -2 -c -d 022138'..b1)
core.console('hf 15 raw -2 -c -d 022139'..b0)
end
--
--- Set UID on magic command enabled, OFFICAL REPO

View file

@ -12,29 +12,30 @@ local err_lock = 'use -k or change cfg0 block'
copyright = 'Copyright (c) 2017 IceSQL AB. All rights reserved.'
author = 'Christian Herrmann'
version = 'v1.1.3'
version = 'v1.1.4'
desc = 'This script enables easy programming of a MAGIC NTAG 21* card'
example = [[
-- wipe tag
script run hf_mfu_magicwrite -w
-- wipe a locked down tag by giving the password
script run hf_mfu_magicwrite -k ffffffff -w
--read magic tag configuration
script run hf_mfu_magicwrite -c
-- read magic tag configuration
]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -c ]]..ansicolors.reset..[[
-- set uid
script run hf_mfu_magicwrite -u 04112233445566
]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -u 04112233445566 ]]..ansicolors.reset..[[
-- set pwd / pack
script run hf_mfu_magicwrite -p 11223344 -a 8080
]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -p 11223344 -a 8080 ]]..ansicolors.reset..[[
-- set version to NTAG213
script run hf_mfu_magicwrite -v 0004040201000f03
]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -v 0004040201000f03 ]]..ansicolors.reset..[[
-- set signature
script run hf_mfu_magicwrite -s 1122334455667788990011223344556677889900112233445566778899001122
]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -s 1122334455667788990011223344556677889900112233445566778899001122 ]]..ansicolors.reset..[[
-- wipe tag
]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -w ]]..ansicolors.reset..[[
-- wipe a locked down tag by giving the password
]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -k ffffffff -w ]]..ansicolors.reset..[[
]]
usage = [[
script run hf_mfu_easywrite -h -k <passwd> -c -w -u <uid> -t <type> -p <passwd> -a <pack> -s <signature> -o <otp> -v <version>
@ -190,11 +191,27 @@ local function read_config()
elseif cardtype == '02' then typestr = 'NTAG 216'
end
local versionstr = 'unknown'
if version == '0004030101000B03' then versionstr = 'UL EV1 48b'
elseif version == '0004030101000E03' then versionstr = 'UL EV1 128b'
elseif version == '0004040101000B03' then versionstr = 'NTAG 210'
elseif version == '0004040101000E03' then versionstr = 'NTAG 212'
elseif version == '0004040201000F03' then versionstr = 'NTAG 213'
elseif version == '0004040201001103' then versionstr = 'NTAG 215'
elseif version == '0004040201001303' then versionstr = 'NTAG 216'
elseif version == '0004040502011303' then versionstr = 'NTAG I2C 1K'
elseif version == '0004040502011503' then versionstr = 'NTAG I2C 2K'
elseif version == '0004040502021303' then versionstr = 'NTAG I2C 1K PLUS'
elseif version == '0004040502021503' then versionstr = 'NTAG I2C 2K PLUS'
elseif version == '0004040401000F03' then versionstr = 'NTAG 213F'
elseif version == '0004040401001303' then versionstr = 'NTAG 216F'
end
print('Magic NTAG 21* Configuration')
print(' - Type ', typestr, '(genuine cardtype)')
print(' - Password', pwd)
print(' - Pack ', pack)
print(' - Version ', version)
print(' - Version ', version, '(' .. versionstr .. ')')
print(' - Signature', signature1..signature2)
lib14a.disconnect()

View file

@ -5995,6 +5995,7 @@
"application": "(access control and security) VIGIK",
"company": "CDV International",
"mad": "0x4905",
"service_provider": "CDVI",
"system_integrator": "CDVI"
},
@ -11836,6 +11837,13 @@
"service_provider": "HUG-Witschi AG",
"system_integrator": "HUG-Witschi AG"
},
{
"application": "Access control - Hotel lodging system",
"company": "DORMA/KABA",
"mad": "0x9051",
"service_provider": "DORMA/KABA",
"system_integrator": "DORMA/KABA"
},
{
"application": "FIDELIO CRUISE ship property management system, - materials management system, - meal counting system",
"company": "FIDELIO CRUISE SOFTWARE GMBH",
@ -13558,4 +13566,4 @@
"service_provider": "Tech ID",
"system_integrator": "Tech ID"
}
]
]

View file

@ -18,23 +18,30 @@
#include "cmdhf14a.h"
#include "emv/emvcore.h"
#include "emv/emvjson.h"
#include "iso7816/apduinfo.h"
#include "ui.h"
#include "util.h"
// context for secure channel
CipurseContext cipurseContext;
static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
uint8_t data[APDU_RES_LEN] = {0};
uint8_t securedata[APDU_RES_LEN] = {0};
sAPDU secapdu;
static int CIPURSEExchangeEx(bool activate_field, bool leave_field_on, sAPDU apdu, bool include_le,
uint16_t le, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
*ResultLen = 0;
if (sw) *sw = 0;
if (result_len == NULL) {
PrintAndLogEx(FAILED, "CIPURSEExchangeEx, result_len is NULL");
return PM3_EINVARG;
}
*result_len = 0;
if (sw) {
*sw = 0;
}
uint16_t isw = 0;
int res = 0;
if (ActivateField) {
if (activate_field) {
DropField();
msleep(50);
}
@ -45,48 +52,58 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
// COMPUTE APDU
int datalen = 0;
uint16_t xle = IncludeLe ? 0x100 : 0x00;
if (xle == 0x100 && Le != 0)
xle = Le;
uint16_t xle = include_le ? 0x100 : 0x00;
if (xle == 0x100 && le != 0) {
xle = le;
}
CipurseCAPDUReqEncode(&cipurseContext, &apdu, &secapdu, securedata, IncludeLe, Le);
sAPDU secapdu;
uint8_t securedata[APDU_RES_LEN] = {0};
CipurseCAPDUReqEncode(&cipurseContext, &apdu, &secapdu, securedata, include_le, le);
uint8_t data[APDU_RES_LEN] = {0};
if (APDUEncodeS(&secapdu, false, xle, data, &datalen)) {
PrintAndLogEx(ERR, "APDU encoding error.");
return 201;
}
if (GetAPDULogging())
if (GetAPDULogging()) {
PrintAndLogEx(SUCCESS, ">>>> %s", sprint_hex(data, datalen));
}
res = ExchangeAPDU14a(data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
res = ExchangeAPDU14a(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len);
if (res) {
return res;
}
if (GetAPDULogging())
PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(Result, *ResultLen));
if (GetAPDULogging()) {
PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(result, *result_len));
}
if (*ResultLen < 2) {
if (*result_len < 2) {
return 200;
}
size_t rlen = 0;
if (*ResultLen == 2) {
if (cipurseContext.RequestSecurity == CPSMACed || cipurseContext.RequestSecurity == CPSEncrypted)
if (*result_len == 2) {
if (cipurseContext.RequestSecurity == CPSMACed || cipurseContext.RequestSecurity == CPSEncrypted) {
CipurseCClearContext(&cipurseContext);
}
isw = result[0] * 0x0100 + result[1];
isw = Result[0] * 0x0100 + Result[1];
} else {
CipurseCAPDURespDecode(&cipurseContext, Result, *ResultLen, securedata, &rlen, &isw);
memcpy(Result, securedata, rlen);
CipurseCAPDURespDecode(&cipurseContext, result, *result_len, securedata, &rlen, &isw);
memcpy(result, securedata, rlen);
}
if (ResultLen != NULL)
*ResultLen = rlen;
if (result_len != NULL) {
*result_len = rlen;
}
if (sw != NULL)
if (sw != NULL) {
*sw = isw;
}
if (isw != 0x9000) {
if (GetAPDULogging()) {
@ -102,69 +119,69 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
return PM3_SUCCESS;
}
static int CIPURSEExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return CIPURSEExchangeEx(false, true, apdu, true, 0, Result, MaxResultLen, ResultLen, sw);
static int CIPURSEExchange(sAPDU apdu, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
return CIPURSEExchangeEx(false, true, apdu, true, 0, result, max_result_len, result_len, sw);
}
int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
int CIPURSESelect(bool activate_field, bool leave_field_on, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
uint8_t data[] = {0x41, 0x44, 0x20, 0x46, 0x31};
CipurseCClearContext(&cipurseContext);
return EMVSelect(CC_CONTACTLESS, ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
return EMVSelect(CC_CONTACTLESS, activate_field, leave_field_on, data, sizeof(data), result, max_result_len, result_len, sw, NULL);
}
int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x84, 0x00, 0x00, 0x00, NULL}, true, 0x16, Result, MaxResultLen, ResultLen, sw);
int CIPURSEChallenge(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x84, 0x00, 0x00, 0x00, NULL}, true, 0x16, result, max_result_len, result_len, sw);
}
int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramslen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw);
int CIPURSEMutalAuthenticate(uint8_t keyindex, uint8_t *params, uint8_t paramslen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyindex, paramslen, params}, true, 0x10, result, max_result_len, result_len, sw);
}
int CIPURSECreateFile(uint8_t *attr, uint16_t attrlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xe4, 0x00, 0x00, attrlen, attr}, false, 0, Result, MaxResultLen, ResultLen, sw);
int CIPURSECreateFile(uint8_t *attr, uint16_t attrlen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xe4, 0x00, 0x00, attrlen, attr}, false, 0, result, max_result_len, result_len, sw);
}
int CIPURSEDeleteFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
uint8_t fileIdBin[] = {fileID >> 8, fileID & 0xff};
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xe4, 0x00, 0x00, 02, fileIdBin}, false, 0, Result, MaxResultLen, ResultLen, sw);
int CIPURSEDeleteFile(uint16_t fileid, uint8_t *Result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
uint8_t fileIdBin[] = {fileid >> 8, fileid & 0xff};
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xe4, 0x00, 0x00, 02, fileIdBin}, false, 0, Result, max_result_len, result_len, sw);
}
int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
uint8_t fileIdBin[] = {fileID >> 8, fileID & 0xff};
return CIPURSEExchange((sAPDU) {0x00, 0xa4, 0x00, 0x00, 02, fileIdBin}, Result, MaxResultLen, ResultLen, sw);
int CIPURSESelectFile(uint16_t fileid, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
uint8_t fileIdBin[] = {fileid >> 8, fileid & 0xff};
return CIPURSEExchange((sAPDU) {0x00, 0xa4, 0x00, 0x00, 02, fileIdBin}, result, max_result_len, result_len, sw);
}
int CIPURSESelectMFFile(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return CIPURSEExchange((sAPDU) {0x00, 0xa4, 0x00, 0x00, 0, NULL}, Result, MaxResultLen, ResultLen, sw);
int CIPURSESelectMFFile(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
return CIPURSEExchange((sAPDU) {0x00, 0xa4, 0x00, 0x00, 0, NULL}, result, max_result_len, result_len, sw);
}
int CIPURSEReadFileAttributes(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return CIPURSEExchange((sAPDU) {0x80, 0xce, 0x00, 0x00, 0, NULL}, Result, MaxResultLen, ResultLen, sw);
int CIPURSEReadFileAttributes(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
return CIPURSEExchange((sAPDU) {0x80, 0xce, 0x00, 0x00, 0, NULL}, result, max_result_len, result_len, sw);
}
int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return CIPURSEExchange((sAPDU) {0x00, 0xb0, (offset >> 8) & 0x7f, offset & 0xff, 0, NULL}, Result, MaxResultLen, ResultLen, sw);
int CIPURSEReadBinary(uint16_t offset, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
return CIPURSEExchange((sAPDU) {0x00, 0xb0, (offset >> 8) & 0x7f, offset & 0xff, 0, NULL}, result, max_result_len, result_len, sw);
}
int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return CIPURSEExchange((sAPDU) {0x00, 0xd6, (offset >> 8) & 0x7f, offset & 0xff, datalen, data}, Result, MaxResultLen, ResultLen, sw);
int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
return CIPURSEExchange((sAPDU) {0x00, 0xd6, (offset >> 8) & 0x7f, offset & 0xff, datalen, data}, result, max_result_len, result_len, sw);
}
bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
bool CIPURSEChannelAuthenticate(uint8_t keyindex, uint8_t *key, bool verbose) {
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
CipurseContext cpc = {0};
CipurseCSetKey(&cpc, keyIndex, key);
CipurseCSetKey(&cpc, keyindex, key);
// get RP, rP
int res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw);
if (res != 0 || len != 0x16) {
if (verbose)
PrintAndLogEx(ERR, "Cipurse get challenge " _RED_("error") ". Card returns 0x%04x.", sw);
if (verbose) {
PrintAndLogEx(ERR, "Cipurse get challenge ( " _RED_("fail") " ). Card returns 0x%04x", sw);
}
return false;
}
CipurseCSetRandomFromPICC(&cpc, buf);
@ -174,17 +191,20 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
CipurseCAuthenticateHost(&cpc, authparams);
// authenticate
res = CIPURSEMutalAuthenticate(keyIndex, authparams, sizeof(authparams), buf, sizeof(buf), &len, &sw);
res = CIPURSEMutalAuthenticate(keyindex, authparams, sizeof(authparams), buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000 || len != 16) {
if (sw == 0x6988) {
if (verbose)
PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Wrong key.");
if (verbose) {
PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " ). Wrong key");
}
} else if (sw == 0x6A88) {
if (verbose)
PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Wrong key number.");
if (verbose) {
PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " ). Wrong key number");
}
} else {
if (verbose)
PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Card returns 0x%04x.", sw);
if (verbose) {
PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " ). Card returns 0x%04x", sw);
}
}
CipurseCClearContext(&cipurseContext);
@ -192,15 +212,17 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
}
if (CipurseCCheckCT(&cpc, buf)) {
if (verbose)
PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
if (verbose) {
PrintAndLogEx(SUCCESS, "Authentication ( " _GREEN_("ok") " )");
}
CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
memcpy(&cipurseContext, &cpc, sizeof(CipurseContext));
return true;
} else {
if (verbose)
PrintAndLogEx(ERR, "Authentication " _RED_("ERROR") " card returned wrong CT");
if (verbose) {
PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " ) card returned wrong CT");
}
CipurseCClearContext(&cipurseContext);
return false;
@ -212,46 +234,47 @@ void CIPURSECSetActChannelSecurityLevels(CipurseChannelSecurityLevel req, Cipurs
}
static void CIPURSEPrintPersoMode(uint8_t data) {
if (data & 0x01)
PrintAndLogEx(INFO, "Perso: filesystem");
if (data & 0x02)
PrintAndLogEx(INFO, "Perso: EMV");
if (data & 0x04)
PrintAndLogEx(INFO, "Perso: transaction supported");
if ((data & 0x01) == 0x01)
PrintAndLogEx(INFO, "Perso... " _YELLOW_("filesystem"));
if ((data & 0x02) == 0x02)
PrintAndLogEx(INFO, "Perso... " _YELLOW_("EMV"));
if ((data & 0x04) == 0x04)
PrintAndLogEx(INFO, "Perso... " _YELLOW_("transaction supported"));
}
// 2021 iceman: what is the description text of profile L,S,T ?
static void CIPURSEPrintProfileInfo(uint8_t data) {
if (data & 0x01)
PrintAndLogEx(INFO, "Profile: L");
if (data & 0x02)
PrintAndLogEx(INFO, "Profile: S");
if (data & 0x04)
PrintAndLogEx(INFO, "Profile: T");
if ((data & 0x01) == 0x01)
PrintAndLogEx(INFO, "Profile... L");
if ((data & 0x02) == 0x02)
PrintAndLogEx(INFO, "Profile... S");
if ((data & 0x04) == 0x04)
PrintAndLogEx(INFO, "Profile... T");
}
static void CIPURSEPrintManufacturerInfo(uint8_t data) {
if (data == 0)
PrintAndLogEx(INFO, "Manufacturer: n/a");
else
PrintAndLogEx(INFO, "Manufacturer: %s", getTagInfo(data)); // getTagInfo from cmfhf14a.h
if (data == 0)
PrintAndLogEx(INFO, "Manufacturer... n/a");
else
PrintAndLogEx(INFO, "Manufacturer... %s", getTagInfo(data)); // getTagInfo from cmfhf14a.h
}
void CIPURSEPrintInfoFile(uint8_t *data, size_t len) {
if (len < 2) {
PrintAndLogEx(ERR, "Info file length " _RED_("ERROR"));
PrintAndLogEx(FAILED, "Info file length too short");
return;
}
PrintAndLogEx(INFO, "------------ INFO ------------");
PrintAndLogEx(INFO, "CIPURSE version %d revision %d", data[0], data[1]);
PrintAndLogEx(INFO, "--- " _CYAN_("CIPURSE Information") "---------------------");
PrintAndLogEx(INFO, "version.... " _YELLOW_("%d"), data[0]);
PrintAndLogEx(INFO, "revision... " _YELLOW_("%d"), data[1]);
if (len >= 3)
CIPURSEPrintPersoMode(data[2]);
if (len >= 4)
CIPURSEPrintProfileInfo(data[3]);
if (len >= 9)
CIPURSEPrintManufacturerInfo(data[8]);
}
@ -278,101 +301,111 @@ static void CIPURSEPrintFileDescriptor(uint8_t desc) {
}
static void CIPURSEPrintKeyAttrib(uint8_t *attr) {
PrintAndLogEx(INFO, "-------- KEY ATTRIBUTES --------");
PrintAndLogEx(INFO, "Additional info: 0x%02x", attr[0]);
PrintAndLogEx(INFO, "Key length: %d", attr[1]);
PrintAndLogEx(INFO, "Algorithm ID: 0x%02x", attr[2]);
PrintAndLogEx(INFO, "Security attr: 0x%02x", attr[3]);
PrintAndLogEx(INFO, "KVV: 0x%02x%02x%02x", attr[4], attr[5], attr[6]);
PrintAndLogEx(INFO, "-------------------------------");
PrintAndLogEx(INFO, "--- " _CYAN_("Key Attributes") "---------------------");
PrintAndLogEx(INFO, "Additional info... 0x%02x", attr[0]);
PrintAndLogEx(INFO, "Key length........ %d", attr[1]);
PrintAndLogEx(INFO, "Algorithm ID...... 0x%02x", attr[2]);
PrintAndLogEx(INFO, "Security attr..... 0x%02x", attr[3]);
PrintAndLogEx(INFO, "KVV............... 0x%02x%02x%02x", attr[4], attr[5], attr[6]);
PrintAndLogEx(NORMAL, "");
}
void CIPURSEPrintFileAttr(uint8_t *fileAttr, size_t len) {
void CIPURSEPrintFileAttr(uint8_t *attr, size_t len) {
if (len < 7) {
PrintAndLogEx(ERR, "Attributes length " _RED_("ERROR"));
PrintAndLogEx(FAILED, "Attributes length too short");
return;
}
PrintAndLogEx(INFO, "--------- FILE ATTRIBUTES ---------");
if (fileAttr[0] == 0x38) {
PrintAndLogEx(INFO, "Type: MF, ADF");
if (fileAttr[1] == 0x00) {
PrintAndLogEx(INFO, "Type: MF");
PrintAndLogEx(INFO, "--- " _CYAN_("File Attributes") "---------------------");
if (attr[0] == 0x38) {
PrintAndLogEx(INFO, "Type... MF, ADF");
if (attr[1] == 0x00) {
PrintAndLogEx(INFO, "Type... MF");
} else {
if ((fileAttr[1] & 0xe0) == 0x00)
PrintAndLogEx(INFO, "Type: Unknown");
if ((fileAttr[1] & 0xe0) == 0x20)
PrintAndLogEx(INFO, "Type: CIPURSE L");
if ((fileAttr[1] & 0xe0) == 0x40)
PrintAndLogEx(INFO, "Type: CIPURSE S");
if ((fileAttr[1] & 0xe0) == 0x60)
PrintAndLogEx(INFO, "Type: CIPURSE T");
if ((fileAttr[1] & 0x02) == 0x00)
if ((attr[1] & 0xe0) == 0x00)
PrintAndLogEx(INFO, "Type... Unknown");
if ((attr[1] & 0xe0) == 0x20)
PrintAndLogEx(INFO, "Type... CIPURSE L");
if ((attr[1] & 0xe0) == 0x40)
PrintAndLogEx(INFO, "Type... CIPURSE S");
if ((attr[1] & 0xe0) == 0x60)
PrintAndLogEx(INFO, "Type... CIPURSE T");
if ((attr[1] & 0x02) == 0x00)
PrintAndLogEx(INFO, "Autoselect on PxSE select OFF");
else
PrintAndLogEx(INFO, "Autoselect on PxSE select ON");
if ((fileAttr[1] & 0x01) == 0x00)
if ((attr[1] & 0x01) == 0x00)
PrintAndLogEx(INFO, "PxSE select returns FCPTemplate OFF");
else
PrintAndLogEx(INFO, "PxSE select returns FCPTemplate ON");
}
PrintAndLogEx(INFO, "File ID: 0x%02x%02x", fileAttr[2], fileAttr[3]);
PrintAndLogEx(INFO, "File ID................... 0x%02x%02x", attr[2], attr[3]);
PrintAndLogEx(INFO, "Maximum # custom EFs...... %d", attr[4]);
PrintAndLogEx(INFO, "Maximum # EFs with SFID... %d", attr[5]);
PrintAndLogEx(INFO, "Maximum number of custom EFs: %d", fileAttr[4]);
PrintAndLogEx(INFO, "Maximum number of EFs with SFID: %d", fileAttr[5]);
uint8_t keyNum = fileAttr[6];
PrintAndLogEx(INFO, "Keys assigned: %d", keyNum);
uint8_t keynum = attr[6];
PrintAndLogEx(INFO, "Keys assigned... %d", keynum);
if (len >= 9) {
PrintAndLogEx(INFO, "SMR entries: %02x%02x", fileAttr[7], fileAttr[8]);
PrintAndLogEx(INFO, "SMR entries... %02x%02x", attr[7], attr[8]);
}
if (len >= 10 + keyNum + 1) {
PrintAndLogEx(INFO, "ART: %s", sprint_hex(&fileAttr[9], keyNum + 1));
if (len >= 10 + keynum + 1) {
PrintAndLogEx(INFO, "ART... %s", sprint_hex(&attr[9], keynum + 1));
}
if (len >= 11 + keyNum + 1 + keyNum * 7) {
for (int i = 0; i < keyNum; i++) {
PrintAndLogEx(INFO, "Key %d Attributes: %s", i, sprint_hex(&fileAttr[11 + keyNum + 1 + i * 7], 7));
CIPURSEPrintKeyAttrib(&fileAttr[11 + keyNum + 1 + i * 7]);
if (len >= 11 + keynum + 1 + keynum * 7) {
for (int i = 0; i < keynum; i++) {
PrintAndLogEx(INFO, "Key %d Attributes... %s", i, sprint_hex(&attr[11 + keynum + 1 + i * 7], 7));
CIPURSEPrintKeyAttrib(&attr[11 + keynum + 1 + i * 7]);
}
}
// MF
if (fileAttr[1] == 0x00) {
PrintAndLogEx(INFO, "Total memory size: %d", (fileAttr[len - 6] << 16) + (fileAttr[len - 1] << 5) + fileAttr[len - 4]);
PrintAndLogEx(INFO, "Free memory size: %d", (fileAttr[len - 3] << 16) + (fileAttr[len - 2] << 8) + fileAttr[len - 1]);
if (attr[1] == 0x00) {
PrintAndLogEx(INFO, "Total memory size... %d", (attr[len - 6] << 16) + (attr[len - 1] << 5) + attr[len - 4]);
PrintAndLogEx(INFO, "Free memory size.... %d", (attr[len - 3] << 16) + (attr[len - 2] << 8) + attr[len - 1]);
} else {
int ptr = 11 + keyNum + 1 + keyNum * 7;
if (len > ptr)
PrintAndLogEx(INFO, "TLV file control: %s", sprint_hex(&fileAttr[ptr], len - ptr));
int ptr = 11 + keynum + 1 + keynum * 7;
if (len > ptr) {
PrintAndLogEx(INFO, "TLV file control... %s", sprint_hex(&attr[ptr], len - ptr));
}
}
} else {
PrintAndLogEx(INFO, "Type: EF");
CIPURSEPrintFileDescriptor(fileAttr[0]);
if (fileAttr[1] == 0)
PrintAndLogEx(INFO, "SFI: not assigned");
PrintAndLogEx(INFO, "Type... EF");
CIPURSEPrintFileDescriptor(attr[0]);
if (attr[1] == 0)
PrintAndLogEx(INFO, "SFI.... not assigned");
else
PrintAndLogEx(INFO, "SFI: 0x%02x", fileAttr[1]);
PrintAndLogEx(INFO, "SFI.... 0x%02x", attr[1]);
PrintAndLogEx(INFO, "File ID: 0x%02x%02x", fileAttr[2], fileAttr[3]);
PrintAndLogEx(INFO, "File ID... 0x%02x%02x", attr[2], attr[3]);
if (fileAttr[0] == 0x01 || fileAttr[0] == 0x11)
PrintAndLogEx(INFO, "File size: %d", (fileAttr[4] << 8) + fileAttr[5]);
if (attr[0] == 0x01 || attr[0] == 0x11)
PrintAndLogEx(INFO, "File size... %d", (attr[4] << 8) + attr[5]);
else
PrintAndLogEx(INFO, "Record num: %d record size: %d", fileAttr[4], fileAttr[5]);
PrintAndLogEx(INFO, "Record num " _YELLOW_("%d") " record size " _YELLOW_("%d"), attr[4], attr[5]);
PrintAndLogEx(INFO, "Keys assigned: %d", fileAttr[6]);
PrintAndLogEx(INFO, "Keys assigned... %d", attr[6]);
if (len >= 9) {
PrintAndLogEx(INFO, "SMR entries: %02x%02x", fileAttr[7], fileAttr[8]);
PrintAndLogEx(INFO, "SMR entries... %02x%02x", attr[7], attr[8]);
}
if (len >= 10) {
PrintAndLogEx(INFO, "ART: %s", sprint_hex(&fileAttr[9], len - 9));
if (fileAttr[6] + 1 != len - 9)
PrintAndLogEx(INFO, "ART... %s", sprint_hex(&attr[9], len - 9));
if (attr[6] + 1 != len - 9) {
PrintAndLogEx(WARNING, "ART length is wrong");
}
}
}

View file

@ -11,10 +11,10 @@
#ifndef __CIPURSECORE_H__
#define __CIPURSECORE_H__
#include "common.h"
#include <jansson.h>
#include "iso7816/apduinfo.h" // sAPDU
#include <stdbool.h>
#include "common.h"
#include "iso7816/apduinfo.h" // sAPDU
#include "cipurse/cipursecrypto.h"
@ -22,23 +22,23 @@
void CIPURSEPrintInfoFile(uint8_t *data, size_t len);
int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int CIPURSESelect(bool activate_field, bool leave_field_on, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw);
int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramslen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int CIPURSEChallenge(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw);
int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramslen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw);
int CIPURSECreateFile(uint8_t *attr, uint16_t attrlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int CIPURSEDeleteFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int CIPURSECreateFile(uint8_t *attr, uint16_t attrlen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw);
int CIPURSEDeleteFile(uint16_t fileid, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw);
int CIPURSESelectMFFile(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) ;
int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int CIPURSEReadFileAttributes(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int CIPURSESelectMFFile(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) ;
int CIPURSESelectFile(uint16_t fileid, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw);
int CIPURSEReadFileAttributes(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw);
int CIPURSEReadBinary(uint16_t offset, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw);
int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw);
bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose);
bool CIPURSEChannelAuthenticate(uint8_t keyindex, uint8_t *key, bool verbose);
void CIPURSECSetActChannelSecurityLevels(CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp);
void CIPURSEPrintFileAttr(uint8_t *fileAttr, size_t len);
void CIPURSEPrintFileAttr(uint8_t *attr, size_t len);
#endif /* __CIPURSECORE_H__ */

View file

@ -40,11 +40,6 @@ uint8_t CipurseCSecurityLevelEnc(CipurseChannelSecurityLevel lvl) {
}
}
static void bin_xor(uint8_t *d1, uint8_t *d2, size_t len) {
for (size_t i = 0; i < len; i++)
d1[i] = d1[i] ^ d2[i];
}
static void bin_ext(uint8_t *dst, size_t dstlen, uint8_t *src, size_t srclen) {
if (srclen > dstlen)
memcpy(dst, &src[srclen - dstlen], dstlen);
@ -218,24 +213,6 @@ bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT) {
return (memcmp(CT, ctx->CT, CIPURSE_AES_KEY_LENGTH) == 0);
}
void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen) {
*ddatalen = sdatalen + 1;
*ddatalen += blocklen - *ddatalen % blocklen;
memset(ddata, 0, *ddatalen);
memcpy(ddata, sdata, sdatalen);
ddata[sdatalen] = ISO9797_M2_PAD_BYTE;
}
size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen) {
for (int i = datalen; i > 0; i--) {
if (data[i - 1] == 0x80)
return i - 1;
if (data[i - 1] != 0x00)
return 0;
}
return 0;
}
static uint16_t CipurseCComputeMICCRC(uint8_t *data, size_t len) {
uint16_t initCRC = 0x6363;
for (size_t i = 0; i < len; i++) {
@ -506,6 +483,11 @@ void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdat
CipurseCChannelDecrypt(ctx, srcdata, srcdatalen, buf, &buflen);
//PrintAndLogEx(INFO, "data plain[%d]: %s", buflen, sprint_hex(buf, buflen));
if (buflen == 0) {
PrintAndLogEx(ERR, "APDU can't decode crypto stream");
break;
}
micdatalen = buflen - 2 - CIPURSE_MIC_LENGTH;
memcpy(micdata, buf, buflen);
memcpy(&micdata[micdatalen], &buf[buflen - 2], 2);

View file

@ -21,7 +21,6 @@
#define CIPURSE_MAC_LENGTH 8
#define CIPURSE_MIC_LENGTH 4
#define CIPURSE_POLY 0x35b088cce172UL
#define ISO9797_M2_PAD_BYTE 0x80
#define member_size(type, member) sizeof(((type *)0)->member)
@ -66,9 +65,6 @@ bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT);
void CipurseCChannelSetSecurityLevels(CipurseContext *ctx, CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp);
bool isCipurseCChannelSecuritySet(CipurseContext *ctx);
void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen);
size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen);
void CipurseCGenerateMAC(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac);
void CipurseCCalcMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac);
bool CipurseCCheckMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac);

View file

@ -0,0 +1,376 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2021 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// tests for crypto
//-----------------------------------------------------------------------------
#include "cipursetest.h"
#include <unistd.h>
#include <string.h> // memcpy memset
#include "fileutils.h"
#include "crypto/libpcrypto.h"
#include "cipurse/cipursecrypto.h"
#include "cipurse/cipursecore.h"
uint8_t Key[] = CIPURSE_DEFAULT_KEY;
uint8_t KeyKvv[CIPURSE_KVV_LENGTH] = {0x5f, 0xd6, 0x7b, 0xcb};
uint8_t TestRandom[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22};
uint8_t TestData[16] = {0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t TestDataPadded[16] = {0x11, 0x22, 0x33, 0x44, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static bool TestKVV(void) {
uint8_t kvv[CIPURSE_KVV_LENGTH] = {0};
CipurseCGetKVV(Key, kvv);
bool res = memcmp(KeyKvv, kvv, CIPURSE_KVV_LENGTH) == 0;
if (res)
PrintAndLogEx(INFO, "kvv.............. " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "kvv.............. " _RED_("fail"));
return res;
}
static bool TestISO9797M2(void) {
uint8_t data[32] = {0};
size_t ddatalen = 0;
AddISO9797M2Padding(data, &ddatalen, TestData, 4, 16);
bool res = (ddatalen == 16);
res = res && (memcmp(data, TestDataPadded, ddatalen) == 0);
res = res && (FindISO9797M2PaddingDataLen(data, ddatalen) == 4);
if (res)
PrintAndLogEx(INFO, "ISO9797M2........ " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "ISO9797M2........ " _RED_("fail"));
return res;
}
static bool TestSMI(void) {
CipurseContext ctx = {0};
CipurseCClearContext(&ctx);
bool res = (isCipurseCChannelSecuritySet(&ctx) == false);
CipurseCChannelSetSecurityLevels(&ctx, CPSPlain, CPSPlain);
res = res && (CipurseCGetSMI(&ctx, false) == 0x00);
res = res && (CipurseCGetSMI(&ctx, true) == 0x01);
CipurseCChannelSetSecurityLevels(&ctx, CPSPlain, CPSMACed);
res = res && (CipurseCGetSMI(&ctx, false) == 0x04);
res = res && (CipurseCGetSMI(&ctx, true) == 0x05);
CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed);
res = res && (CipurseCGetSMI(&ctx, false) == 0x44);
res = res && (CipurseCGetSMI(&ctx, true) == 0x45);
CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSEncrypted);
res = res && (CipurseCGetSMI(&ctx, false) == 0x48);
res = res && (CipurseCGetSMI(&ctx, true) == 0x49);
CipurseCChannelSetSecurityLevels(&ctx, CPSEncrypted, CPSEncrypted);
res = res && (CipurseCGetSMI(&ctx, false) == 0x88);
res = res && (CipurseCGetSMI(&ctx, true) == 0x89);
if (res)
PrintAndLogEx(INFO, "SMI.............. " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "SMI.............. " _RED_("fail"));
return res;
}
static bool TestMIC(void) {
uint8_t mic[4] = {0};
CipurseCGenerateMIC(TestData, 4, mic);
uint8_t valid_mic4[4] = {0xD4, 0x71, 0xA7, 0x73};
bool res = (memcmp(mic, valid_mic4, 4) == 0);
res = res && (CipurseCCheckMIC(TestData, 4, mic));
CipurseCGenerateMIC(TestData, 6, mic);
uint8_t valid_mic6[4] = {0xAA, 0x90, 0xFC, 0x5A};
res = res && (memcmp(mic, valid_mic6, 4) == 0);
res = res && (CipurseCCheckMIC(TestData, 6, mic));
if (res)
PrintAndLogEx(INFO, "MIC.............. " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "MIC.............. " _RED_("fail"));
return res;
}
static bool TestAuth(void) {
CipurseContext ctx = {0};
CipurseCClearContext(&ctx);
bool res = (isCipurseCChannelSecuritySet(&ctx) == false);
CipurseCSetKey(&ctx, 1, Key);
res = res && (memcmp(ctx.key, Key, 16) == 0);
res = res && (ctx.keyId == 1);
CipurseCSetRandomFromPICC(&ctx, TestRandom);
res = res && (memcmp(ctx.RP, TestRandom, 16) == 0);
res = res && (memcmp(ctx.rP, &TestRandom[16], 6) == 0);
uint8_t hrandom[] = {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};
CipurseCSetRandomHost(&ctx);
res = res && (memcmp(ctx.RT, hrandom, 16) == 0);
res = res && (memcmp(ctx.rT, &hrandom[16], 6) == 0);
uint8_t authparams[16 + 16 + 6] = {0};
CipurseCAuthenticateHost(&ctx, authparams);
uint8_t aparamstest[] = {0x12, 0xAA, 0x79, 0xA9, 0x03, 0xC5, 0xB4, 0x6A, 0x27, 0x1B, 0x13, 0xAE, 0x02, 0x50, 0x1C, 0x99, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
};
res = res && (memcmp(authparams, aparamstest, sizeof(authparams)) == 0);
uint8_t ct[] = {0xBE, 0x10, 0x6B, 0xB9, 0xAD, 0x84, 0xBC, 0xE1, 0x9F, 0xAE, 0x0C, 0x62, 0xCC, 0xC7, 0x0D, 0x41};
res = res && CipurseCCheckCT(&ctx, ct);
CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed);
res = res && (isCipurseCChannelSecuritySet(&ctx) == true);
uint8_t framekey[] = {0xCF, 0x6F, 0x3A, 0x47, 0xFC, 0xAC, 0x8D, 0x38, 0x25, 0x75, 0x8B, 0xFC, 0x8B, 0x61, 0x68, 0xF3};
res = res && (memcmp(ctx.frameKey, framekey, sizeof(framekey)) == 0);
if (res)
PrintAndLogEx(INFO, "Auth............. " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "Auth............. " _RED_("fail"));
return res;
}
static bool TestMAC(void) {
CipurseContext ctx = {0};
// authentication
CipurseCClearContext(&ctx);
CipurseCSetKey(&ctx, 1, Key);
CipurseCSetRandomFromPICC(&ctx, TestRandom);
uint8_t authparams[16 + 16 + 6] = {0};
CipurseCAuthenticateHost(&ctx, authparams);
uint8_t ct[] = {0xBE, 0x10, 0x6B, 0xB9, 0xAD, 0x84, 0xBC, 0xE1, 0x9F, 0xAE, 0x0C, 0x62, 0xCC, 0xC7, 0x0D, 0x41};
bool res = CipurseCCheckCT(&ctx, ct);
CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed);
res = res && (isCipurseCChannelSecuritySet(&ctx) == true);
// check MAC
uint8_t mac[8] = {0};
CipurseCGenerateMAC(&ctx, TestData, 4, mac);
uint8_t testmac1[8] = {0xAB, 0x5C, 0x86, 0x18, 0x7F, 0x73, 0xEC, 0x4E};
res = res && (memcmp(mac, testmac1, 8) == 0);
uint8_t framekey1[] = {0x7D, 0x6F, 0x31, 0x40, 0xC8, 0x47, 0xED, 0x3F, 0x0A, 0x21, 0xE6, 0xFB, 0xC7, 0xDB, 0x27, 0xB0};
res = res && (memcmp(ctx.frameKey, framekey1, sizeof(framekey1)) == 0);
CipurseCCalcMACPadded(&ctx, TestData, 4, mac);
uint8_t testmac2[8] = {0x9F, 0xE9, 0x54, 0xBF, 0xFC, 0xA0, 0x7D, 0x75};
res = res && (memcmp(mac, testmac2, 8) == 0);
uint8_t framekey2[] = {0x1E, 0xD4, 0xB6, 0x87, 0x85, 0x93, 0x5B, 0xAF, 0xA9, 0xF2, 0xF0, 0x8F, 0xA9, 0xF0, 0xA5, 0xFB};
res = res && (memcmp(ctx.frameKey, framekey2, sizeof(framekey2)) == 0);
CipurseCCalcMACPadded(&ctx, TestData, 4, mac);
uint8_t testmac3[8] = {0x15, 0x6F, 0x08, 0x5C, 0x0F, 0x80, 0xE7, 0x07};
res = res && (memcmp(mac, testmac3, 8) == 0);
uint8_t framekey3[] = {0x0C, 0x42, 0x93, 0x73, 0x88, 0x8F, 0x63, 0xB3, 0x10, 0x8E, 0xDF, 0xDB, 0xC1, 0x20, 0x63, 0x4C};
res = res && (memcmp(ctx.frameKey, framekey3, sizeof(framekey3)) == 0);
uint8_t testmac4[8] = {0x0E, 0xF0, 0x70, 0xA6, 0xA1, 0x15, 0x9A, 0xB6};
res = res && CipurseCCheckMACPadded(&ctx, TestData, 4, testmac4);
uint8_t framekey4[] = {0xA0, 0x65, 0x1A, 0x62, 0x56, 0x5D, 0xD7, 0xC9, 0x32, 0xAE, 0x1D, 0xE0, 0xCF, 0x8D, 0xC1, 0xB9};
res = res && (memcmp(ctx.frameKey, framekey4, sizeof(framekey4)) == 0);
if (res)
PrintAndLogEx(INFO, "channel MAC...... " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "channel MAC...... " _RED_("fail"));
return res;
}
static bool TestEncDec(void) {
CipurseContext ctx = {0};
// authentication
CipurseCClearContext(&ctx);
CipurseCSetKey(&ctx, 1, Key);
CipurseCSetRandomFromPICC(&ctx, TestRandom);
uint8_t authparams[16 + 16 + 6] = {0};
CipurseCAuthenticateHost(&ctx, authparams);
uint8_t ct[] = {0xBE, 0x10, 0x6B, 0xB9, 0xAD, 0x84, 0xBC, 0xE1, 0x9F, 0xAE, 0x0C, 0x62, 0xCC, 0xC7, 0x0D, 0x41};
bool res = CipurseCCheckCT(&ctx, ct);
CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed);
res = res && (isCipurseCChannelSecuritySet(&ctx) == true);
// check Encode-Decode
uint8_t dstdata[32] = {0};
size_t dstdatalen = 0;
CipurseCEncryptDecrypt(&ctx, TestData, 16, dstdata, true);
uint8_t tested1[16] = {0x5F, 0x01, 0x18, 0x79, 0xE0, 0x57, 0xA7, 0xE5, 0x34, 0x39, 0x6E, 0x32, 0x62, 0xF2, 0x71, 0x27};
res = res && (memcmp(dstdata, tested1, 16) == 0);
uint8_t tested2[16] = {0xA6, 0x22, 0xB5, 0xCF, 0xE8, 0x6E, 0x67, 0xF4, 0xAA, 0x88, 0xB1, 0x19, 0x87, 0xCF, 0xC9, 0xD2};
CipurseCEncryptDecrypt(&ctx, tested2, 16, dstdata, false);
res = res && (memcmp(dstdata, TestData, 16) == 0);
CipurseCChannelEncrypt(&ctx, TestData, 16, dstdata, &dstdatalen);
uint8_t tested3[32] = {0x1E, 0x0C, 0xD1, 0xF5, 0x8E, 0x0B, 0xAE, 0xF0, 0x06, 0xC6, 0xED, 0x73, 0x3F, 0x8A, 0x87, 0xCF,
0x36, 0xCC, 0xF2, 0xF4, 0x7D, 0x33, 0x50, 0xF1, 0x8E, 0xFF, 0xD1, 0x7D, 0x42, 0x88, 0xD5, 0xEE
};
res = res && (dstdatalen == 32);
res = res && (memcmp(dstdata, tested3, 32) == 0);
uint8_t tested4[32] = {0xC0, 0x42, 0xDB, 0xD9, 0x53, 0xFF, 0x01, 0xE5, 0xCC, 0x49, 0x8C, 0x9C, 0xDA, 0x60, 0x73, 0xA7,
0xE1, 0xEB, 0x14, 0x69, 0xF6, 0x39, 0xF3, 0xE1, 0x07, 0x03, 0x32, 0xF4, 0x27, 0xF9, 0x48, 0x3D
};
CipurseCChannelDecrypt(&ctx, tested4, 32, dstdata, &dstdatalen);
res = res && (dstdatalen == 16);
res = res && (memcmp(dstdata, TestData, 16) == 0);
if (res)
PrintAndLogEx(INFO, "channel EncDec... " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "channel EncDec... " _RED_("fail"));
return res;
}
//void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu, uint8_t *dstdatabuf, bool includeLe, uint8_t Le);
//void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen, uint16_t *sw);
static bool TestAPDU(void) {
CipurseContext ctx = {0};
// authentication
CipurseCClearContext(&ctx);
CipurseCSetKey(&ctx, 1, Key);
CipurseCSetRandomFromPICC(&ctx, TestRandom);
uint8_t authparams[16 + 16 + 6] = {0};
CipurseCAuthenticateHost(&ctx, authparams);
uint8_t ct[] = {0xBE, 0x10, 0x6B, 0xB9, 0xAD, 0x84, 0xBC, 0xE1, 0x9F, 0xAE, 0x0C, 0x62, 0xCC, 0xC7, 0x0D, 0x41};
bool res = CipurseCCheckCT(&ctx, ct);
CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed);
res = res && (isCipurseCChannelSecuritySet(&ctx) == true);
// check APDU formatting
sAPDU srcAPDU = {0};
sAPDU dstAPDU = {0};
uint8_t dstdata[256] = {0};
size_t dstdatalen = 0;
// MACED APDU
srcAPDU.CLA = 0x00;
srcAPDU.INS = 0x55;
srcAPDU.P1 = 0x11;
srcAPDU.P2 = 0x22;
srcAPDU.data = TestData;
srcAPDU.Lc = 5;
CipurseCAPDUReqEncode(&ctx, &srcAPDU, &dstAPDU, dstdata, true, 0x88);
uint8_t test1[] = {0x45, 0x11, 0x22, 0x33, 0x44, 0x00, 0x88, 0x79, 0x2B, 0xB7, 0xDD, 0xD1, 0x69, 0xA6, 0x66};
res = res && ((srcAPDU.CLA | 0x04) == dstAPDU.CLA);
res = res && (srcAPDU.INS == dstAPDU.INS);
res = res && (srcAPDU.P1 == dstAPDU.P1);
res = res && (srcAPDU.P2 == dstAPDU.P2);
res = res && (dstAPDU.Lc == sizeof(test1));
res = res && (memcmp(dstdata, test1, sizeof(test1)) == 0);
uint16_t sw = 0;
uint8_t test2[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x9D, 0x80, 0xE7, 0xE3, 0x34, 0xE9, 0x97, 0x82, 0xdd, 0xee};
CipurseCAPDURespDecode(&ctx, test2, sizeof(test2), dstdata, &dstdatalen, &sw);
res = res && (dstdatalen == 6);
res = res && (memcmp(test2, dstdata, dstdatalen) == 0);
res = res && (sw == 0xddee);
// Plain APDU
CipurseCChannelSetSecurityLevels(&ctx, CPSPlain, CPSPlain);
CipurseCAPDUReqEncode(&ctx, &srcAPDU, &dstAPDU, dstdata, true, 0x55);
uint8_t test3[] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x00, 0x55};
res = res && ((srcAPDU.CLA | 0x04) == dstAPDU.CLA);
res = res && (srcAPDU.INS == dstAPDU.INS);
res = res && (srcAPDU.P1 == dstAPDU.P1);
res = res && (srcAPDU.P2 == dstAPDU.P2);
res = res && (dstAPDU.Lc == sizeof(test3));
res = res && (memcmp(dstdata, test3, sizeof(test3)) == 0);
uint8_t test4[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xcc, 0xdd};
CipurseCAPDURespDecode(&ctx, test4, sizeof(test4), dstdata, &dstdatalen, &sw);
res = res && (dstdatalen == 6);
res = res && (memcmp(test4, dstdata, dstdatalen) == 0);
res = res && (sw == 0xccdd);
// Encrypted APDU
CipurseCChannelSetSecurityLevels(&ctx, CPSEncrypted, CPSEncrypted);
CipurseCAPDUReqEncode(&ctx, &srcAPDU, &dstAPDU, dstdata, true, 0x55);
uint8_t test5[] = {0x89, 0x7D, 0xED, 0x0D, 0x04, 0x8E, 0xE1, 0x99, 0x08, 0x70, 0x56, 0x7C, 0xEE, 0x67, 0xB3, 0x33, 0x6F, 0x00};
res = res && ((srcAPDU.CLA | 0x04) == dstAPDU.CLA);
res = res && (srcAPDU.INS == dstAPDU.INS);
res = res && (srcAPDU.P1 == dstAPDU.P1);
res = res && (srcAPDU.P2 == dstAPDU.P2);
res = res && (dstAPDU.Lc == sizeof(test5));
res = res && (memcmp(dstdata, test5, sizeof(test5)) == 0);
uint8_t test6[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x7E, 0x4B, 0xA0, 0xB7, 0xcc, 0xdd};
//CipurseCChannelEncrypt(&ctx, test6, sizeof(test6), dstdata, &dstdatalen);
//PrintAndLogEx(INFO, "dstdata[%d]: %s", dstdatalen, sprint_hex(dstdata, dstdatalen));
uint8_t test7[] = {0x07, 0xEF, 0x16, 0x91, 0xE7, 0x0F, 0xB5, 0x10, 0x63, 0xCE, 0x66, 0xDB, 0x3B, 0xC6, 0xD4, 0xE0, 0x90, 0x00};
CipurseCAPDURespDecode(&ctx, test7, sizeof(test7), dstdata, &dstdatalen, &sw);
res = res && (dstdatalen == 8);
res = res && (memcmp(test6, dstdata, dstdatalen) == 0);
res = res && (sw == 0xccdd);
if (res)
PrintAndLogEx(INFO, "apdu............. " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "apdu............. " _RED_("fail"));
return res;
}
bool CIPURSETest(bool verbose) {
bool res = true;
PrintAndLogEx(INFO, "------ " _CYAN_("CIPURSE Tests") " ------");
res = res && TestKVV();
res = res && TestISO9797M2();
res = res && TestSMI();
res = res && TestMIC();
res = res && TestAuth();
res = res && TestMAC();
res = res && TestEncDec();
res = res && TestAPDU();
PrintAndLogEx(INFO, "---------------------------");
if (res)
PrintAndLogEx(SUCCESS, " Tests [ %s ]", _GREEN_("ok"));
else
PrintAndLogEx(FAILED, " Tests [ %s ]", _RED_("fail"));
PrintAndLogEx(NORMAL, "");
return res;
}

View file

@ -0,0 +1,20 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2021 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// tests for crypto
//-----------------------------------------------------------------------------
#ifndef __CIPURSETEST_H__
#define __CIPURSETEST_H__
#include <stdbool.h>
#include "common.h"
#include "cipurse/cipursecrypto.h"
bool CIPURSETest(bool verbose);
#endif /* __CIPURSETEST_H__ */

View file

@ -2702,7 +2702,7 @@ typedef struct {
static int print_modulation(lf_modulation_t b) {
PrintAndLogEx(INFO, " Modulation........ " _GREEN_("%s"), GetSelectedModulationStr(b.modulation));
PrintAndLogEx(INFO, " Bit clock......... " _GREEN_("RF/%d"), b.bitrate);
PrintAndLogEx(INFO, " Approx baudrate... " _GREEN_("%.f") "bauds", (125000 / (float)b.bitrate));
PrintAndLogEx(INFO, " Approx baudrate... " _GREEN_("%.f") " baud", (125000 / (float)b.bitrate));
switch (b.modulation) {
case DEMOD_PSK1:
case DEMOD_PSK2:

View file

@ -59,9 +59,13 @@ int CmdHFSearch(const char *Cmd) {
);
void *argtable[] = {
arg_param_begin,
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool verbose = arg_get_lit(ctx, 1);
CLIParserFree(ctx);
int res = PM3_ESOFT;
@ -87,9 +91,13 @@ int CmdHFSearch(const char *Cmd) {
PROMPT_CLEARLINE;
PrintAndLogEx(INPLACE, " Searching for ISO14443-A tag...");
if (IfPm3Iso14443a()) {
if (infoHF14A(false, false, false) > 0) {
int sel_state = infoHF14A(false, false, false);
if (sel_state > 0) {
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO 14443-A tag") " found\n");
res = PM3_SUCCESS;
if (sel_state == 1)
infoHF14A4Applications(verbose);
}
}
@ -129,15 +137,6 @@ int CmdHFSearch(const char *Cmd) {
}
}
PROMPT_CLEARLINE;
PrintAndLogEx(INPLACE, " Searching for Cipurse tag...");
if (IfPm3Iso14443a()) {
if (CheckCardCipurse()) {
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Cipurse tag") " found\n");
res = PM3_SUCCESS;
}
}
// 14b is the longest test
PROMPT_CLEARLINE;
PrintAndLogEx(INPLACE, " Searching for ISO14443-B tag...");

View file

@ -169,8 +169,19 @@ const char *getTagInfo(uint8_t uid) {
return manufactureMapping[ARRAYLEN(manufactureMapping) - 1].desc;
}
static const hintAIDListT hintAIDList[] = {
// AID, AID len, name, hint - how to use
{ "\xA0\x00\x00\x06\x47\x2F\x00\x01", 8, "FIDO", "hf fido" },
{ "\xA0\x00\x00\x03\x08\x00\x00\x10\x00\x01\x00", 11, "PIV", "" },
{ "\xD2\x76\x00\x01\x24\x01", 8, "OpenPGP", "" },
{ "\x31\x50\x41\x59\x2E\x53\x59\x53\x2E\x44\x44\x46\x30\x31", 14, "EMV (pse)", "emv" },
{ "\x32\x50\x41\x59\x2E\x53\x59\x53\x2E\x44\x44\x46\x30\x31", 14, "EMV (ppse)", "emv" },
{ "\x41\x44\x20\x46\x31", 5, "CIPURSE", "hf cipurse" },
{ "\xd2\x76\x00\x00\x85\x01\x00", 7, "desfire", "hf mfdes" },
};
// iso14a apdu input frame length
static uint16_t frameLength = 0;
static uint16_t g_frame_len = 0;
uint16_t atsFSC[] = {16, 24, 32, 40, 48, 64, 96, 128, 256};
static int CmdHF14AList(const char *Cmd) {
@ -846,31 +857,35 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav
return 0;
}
int SelectCard14443A_4(bool disconnect, iso14a_card_select_t *card) {
PacketResponseNG resp;
int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card) {
frameLength = 0;
// global vars should be prefixed with g_
g_frame_len = 0;
if (card)
if (card) {
memset(card, 0, sizeof(iso14a_card_select_t));
}
DropField();
// Anticollision + SELECT card
PacketResponseNG resp;
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(ERR, "Proxmark3 connection timeout");
PrintAndLogEx(WARNING, "Command execute timeout");
return PM3_ETIMEOUT;
}
// check result
if (resp.oldarg[0] == 0) {
PrintAndLogEx(ERR, "No card in field");
if (verbose) {
PrintAndLogEx(FAILED, "No card in field");
}
return PM3_ECARDEXCHANGE;
}
if (resp.oldarg[0] != 1 && resp.oldarg[0] != 2) {
PrintAndLogEx(ERR, "Card not in iso14443-4, res=%" PRId64 ".", resp.oldarg[0]);
PrintAndLogEx(WARNING, "Card not in iso14443-4, res=%" PRId64 ".", resp.oldarg[0]);
return PM3_ECARDEXCHANGE;
}
@ -879,36 +894,44 @@ int SelectCard14443A_4(bool disconnect, iso14a_card_select_t *card) {
uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, sizeof(rats), 0, rats, sizeof(rats));
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(ERR, "Proxmark3 connection timeout");
PrintAndLogEx(WARNING, "Command execute timeout");
return PM3_ETIMEOUT;
}
if (resp.oldarg[0] == 0) { // ats_len
PrintAndLogEx(ERR, "Can't get ATS");
if (verbose) {
PrintAndLogEx(FAILED, "Can't get ATS");
}
return PM3_ECARDEXCHANGE;
}
// get frame length from ATS in data field
if (resp.oldarg[0] > 1) {
uint8_t fsci = resp.data.asBytes[1] & 0x0f;
if (fsci < ARRAYLEN(atsFSC))
frameLength = atsFSC[fsci];
if (fsci < ARRAYLEN(atsFSC)) {
g_frame_len = atsFSC[fsci];
}
}
} else {
// get frame length from ATS in card data structure
iso14a_card_select_t *vcard = (iso14a_card_select_t *) resp.data.asBytes;
if (vcard->ats_len > 1) {
uint8_t fsci = vcard->ats[1] & 0x0f;
if (fsci < ARRAYLEN(atsFSC))
frameLength = atsFSC[fsci];
if (fsci < ARRAYLEN(atsFSC)) {
g_frame_len = atsFSC[fsci];
}
}
if (card)
if (card) {
memcpy(card, vcard, sizeof(iso14a_card_select_t));
}
}
SetISODEPState(ISODEP_NFCA);
if (disconnect)
if (disconnect) {
DropField();
}
return PM3_SUCCESS;
}
@ -917,8 +940,8 @@ static int CmdExchangeAPDU(bool chainingin, uint8_t *datain, int datainlen, bool
*chainingout = false;
if (activateField) {
// select with no disconnect and set frameLength
int selres = SelectCard14443A_4(false, NULL);
// select with no disconnect and set g_frame_len
int selres = SelectCard14443A_4(false, true, NULL);
if (selres != PM3_SUCCESS)
return selres;
}
@ -954,7 +977,7 @@ static int CmdExchangeAPDU(bool chainingin, uint8_t *datain, int datainlen, bool
}
// I-block ACK
if ((res & 0xf2) == 0xa2) {
if ((res & 0xF2) == 0xA2) {
*dataoutlen = 0;
*chainingout = true;
return PM3_SUCCESS;
@ -1004,13 +1027,14 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea
// 3 byte here - 1b framing header, 2b crc16
if (APDUInFramingEnable &&
((frameLength && (datainlen > frameLength - 3)) || (datainlen > PM3_CMD_DATA_SIZE - 3))) {
((g_frame_len && (datainlen > g_frame_len - 3)) || (datainlen > PM3_CMD_DATA_SIZE - 3))) {
int clen = 0;
bool vActivateField = activateField;
do {
int vlen = MIN(frameLength - 3, datainlen - clen);
int vlen = MIN(g_frame_len - 3, datainlen - clen);
bool chainBlockNotLast = ((clen + vlen) < datainlen);
*dataoutlen = 0;
@ -1034,17 +1058,19 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea
clen += vlen;
vActivateField = false;
if (*dataoutlen) {
if (clen != datainlen)
if (clen != datainlen) {
PrintAndLogEx(ERR, "APDU: I-block/R-block sequence error. Data len=%d, Sent=%d, Last packet len=%d", datainlen, clen, *dataoutlen);
}
break;
}
} while (clen < datainlen);
} else {
res = CmdExchangeAPDU(false, datain, datainlen, activateField, dataout, maxdataoutlen, dataoutlen, &chaining);
if (res != PM3_SUCCESS) {
if (leaveSignalON == false)
if (leaveSignalON == false) {
DropField();
}
return res;
}
}
@ -1053,15 +1079,16 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea
// I-block with chaining
res = CmdExchangeAPDU(false, NULL, 0, false, &dataout[*dataoutlen], maxdataoutlen, dataoutlen, &chaining);
if (res != PM3_SUCCESS) {
if (leaveSignalON == false)
if (leaveSignalON == false) {
DropField();
}
return 100;
}
}
if (!leaveSignalON)
if (leaveSignalON == false) {
DropField();
}
return PM3_SUCCESS;
}
@ -1780,6 +1807,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]);
card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes
if (card.ats_len > 3)
select_status = 1;
}
if (card.ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes
@ -2070,6 +2099,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
if ((card.sak & 0x20) == 0x20) {
PrintAndLogEx(INFO, "--> SAK incorrectly claims that card supports RATS <--");
}
if (select_status == 1)
select_status = 2;
}
int isMagic = 0;
@ -2131,6 +2162,57 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
return select_status;
}
int infoHF14A4Applications(bool verbose) {
bool cardFound[ARRAYLEN(hintAIDList)] = {0};
bool ActivateField = true;
int found = 0;
for (int i = 0; i < ARRAYLEN(hintAIDList); i++) {
uint16_t sw = 0;
uint8_t result[1024] = {0};
size_t resultlen = 0;
int res = Iso7816Select(CC_CONTACTLESS, ActivateField, true, (uint8_t *)hintAIDList[i].aid, hintAIDList[i].aid_length, result, sizeof(result), &resultlen, &sw);
ActivateField = false;
if (res)
break;
if (sw == 0x9000 || sw == 0x6283 || sw == 0x6285) {
if (!found) {
if (verbose)
PrintAndLogEx(INFO, "----------------- " _CYAN_("Short AID search") " -----------------");
}
found++;
if (sw == 0x9000) {
if (verbose)
PrintAndLogEx(SUCCESS, "Application " _CYAN_("%s") " ( " _GREEN_("ok") " )", hintAIDList[i].desc);
cardFound[i] = true;
} else {
if (verbose)
PrintAndLogEx(WARNING, "Application " _CYAN_("%s") " ( " _RED_("blocked") " )", hintAIDList[i].desc);
}
}
}
if (found) {
if (verbose)
PrintAndLogEx(INFO, "---------------------------------------------------");
else
PrintAndLogEx(INFO, "Short AID search:");
if (found >= ARRAYLEN(hintAIDList) - 1) {
PrintAndLogEx(HINT, "Hint: card answers to all AID. It maybe the latest revision of plus/desfire/ultralight card.");
} else {
for (int i = 0; i < ARRAYLEN(hintAIDList); i++) {
if (cardFound[i] && strlen(hintAIDList[i].hint))
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("%s") " commands", hintAIDList[i].hint);
}
}
}
DropField();
return found;
}
static uint16_t get_sw(uint8_t *d, uint8_t n) {
if (n < 2) {
return 0;

View file

@ -22,6 +22,13 @@ typedef struct {
const char *desc;
} manufactureName;
typedef struct {
const char *aid;
const uint8_t aid_length;
const char *desc;
const char *hint;
} hintAIDListT;
int CmdHF14A(const char *Cmd);
int CmdHF14ASniff(const char *Cmd); // used by hf topaz sniff
int CmdHF14ASim(const char *Cmd); // used by hf mfu sim
@ -30,10 +37,11 @@ int CmdHF14ANdefRead(const char *Cmd);
int hf14a_getconfig(hf14a_config *config);
int hf14a_setconfig(hf14a_config *config, bool verbose);
int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search);
int infoHF14A4Applications(bool verbose);
const char *getTagInfo(uint8_t uid);
int Hf14443_4aGetCardData(iso14a_card_select_t *card);
int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool silentMode);
int SelectCard14443A_4(bool disconnect, iso14a_card_select_t *card);
int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card);
#endif

View file

@ -25,12 +25,17 @@
#include "cmdhfcipurse.h"
#include "cipurse/cipursecore.h"
#include "cipurse/cipursecrypto.h"
#include "cipurse/cipursetest.h"
#include "ui.h"
#include "cmdhf14a.h"
#include "cmdtrace.h"
#include "util.h"
#include "fileutils.h" // laodFileJSONroot
static uint8_t defaultKeyId = 1;
static uint8_t defaultKey[CIPURSE_AES_KEY_LENGTH] = CIPURSE_DEFAULT_KEY;
static uint16_t defaultFileId = 0x2ff7;
static int CmdHelp(const char *Cmd);
static int CmdHFCipurseInfo(const char *Cmd) {
@ -65,15 +70,15 @@ static int CmdHFCipurseInfo(const char *Cmd) {
if (sw != 0x9000) {
if (sw)
PrintAndLogEx(INFO, "Not a CIPURSE card! APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
PrintAndLogEx(INFO, "Not a CIPURSE card. APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
else
PrintAndLogEx(ERR, "APDU exchange error. Card returns 0x0000.");
PrintAndLogEx(ERR, "APDU exchange error. Card returns 0x0000");
DropField();
return PM3_SUCCESS;
}
PrintAndLogEx(INFO, "Cipurse card: " _GREEN_("OK"));
PrintAndLogEx(INFO, "Cipurse card ( " _GREEN_("ok") " )");
res = CIPURSESelectFile(0x2ff7, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
@ -88,8 +93,8 @@ static int CmdHFCipurseInfo(const char *Cmd) {
}
if (len > 0) {
PrintAndLogEx(INFO, "Info file: " _GREEN_("OK"));
PrintAndLogEx(INFO, "[%d]: %s", len, sprint_hex(buf, len));
PrintAndLogEx(INFO, "Info file ( " _GREEN_("ok") " )");
PrintAndLogEx(INFO, "[%zu]: %s", len, sprint_hex(buf, len));
CIPURSEPrintInfoFile(buf, len);
}
@ -98,83 +103,93 @@ static int CmdHFCipurseInfo(const char *Cmd) {
}
static int CmdHFCipurseAuth(const char *Cmd) {
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
uint8_t keyId = 1;
uint8_t key[] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73};
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf cipurse auth",
"Authenticate with key ID and key",
"hf cipurse auth -> Authenticate with keyID=1 and key = 7373...7373\n"
"hf cipurse auth -n 2 -k 65656565656565656565656565656565 -> Authenticate with key\n");
"Authenticate with key ID and key. If no key is supplied, default key of 737373...7373 will be used",
"hf cipurse auth -> Authenticate with keyID 1, default key\n"
"hf cipurse auth -n 2 -k 65656565656565656565656565656565 -> Authenticate keyID 2 with key\n");
void *argtable[] = {
arg_param_begin,
arg_lit0("a", "apdu", "show APDU requests and responses"),
arg_lit0("v", "verbose", "show technical data"),
arg_int0("n", "keyid", "<dec>", "key id"),
arg_str0("k", "key", "<hex>", "key for authenticate"),
arg_int0("n", NULL, "<dec>", "key ID"),
arg_str0("k", "key", "<hex>", "Auth key"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool APDULogging = arg_get_lit(ctx, 1);
bool verbose = arg_get_lit(ctx, 2);
keyId = arg_get_int_def(ctx, 3, 1);
uint8_t keyId = arg_get_int_def(ctx, 3, defaultKeyId);
uint8_t hdata[250] = {0};
int hdatalen = sizeof(hdata);
CLIGetHexWithReturn(ctx, 4, hdata, &hdatalen);
if (hdatalen && hdatalen != 16) {
PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only.");
PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only");
CLIParserFree(ctx);
return PM3_EINVARG;
}
uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0};
if (hdatalen)
memcpy(key, hdata, CIPURSE_AES_KEY_LENGTH);
else
memcpy(key, defaultKey, sizeof(defaultKey));
SetAPDULogging(APDULogging);
CLIParserFree(ctx);
size_t len = 0;
uint16_t sw = 0;
uint8_t buf[APDU_RES_LEN] = {0};
int res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x", sw);
DropField();
return PM3_ESOFT;
}
uint8_t kvv[CIPURSE_KVV_LENGTH] = {0};
CipurseCGetKVV(key, kvv);
if (verbose)
PrintAndLogEx(INFO, "Key id: %d key: %s KVV: %s", keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH), sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH));
if (verbose) {
PrintAndLogEx(INFO, "Key id " _YELLOW_("%d") " key " _YELLOW_("%s") " KVV " _YELLOW_("%s")
, keyId
, sprint_hex(key, CIPURSE_AES_KEY_LENGTH)
, sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH)
);
}
bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
if (verbose == false) {
if (bres)
PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
PrintAndLogEx(INFO, "Authentication ( " _GREEN_("ok") " )");
else
PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
PrintAndLogEx(ERR, "Authentication ( " _RED_("fail") " )");
}
DropField();
return bres ? PM3_SUCCESS : PM3_ESOFT;
return (bres) ? PM3_SUCCESS : PM3_ESOFT;
}
static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, size_t sreqid, size_t srespid, uint8_t *key, CipurseChannelSecurityLevel *sreq, CipurseChannelSecurityLevel *sresp) {
uint8_t hdata[250] = {0};
int hdatalen = sizeof(hdata);
CLIGetHexWithReturn(ctx, keyid, hdata, &hdatalen);
if (CLIParamHexToBuf(arg_get_str(ctx, keyid), hdata, hdatalen, &hdatalen))
return PM3_ESOFT;
if (hdatalen && hdatalen != 16) {
PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only.");
CLIParserFree(ctx);
PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only");
return PM3_EINVARG;
}
if (hdatalen)
memcpy(key, hdata, CIPURSE_AES_KEY_LENGTH);
else
memcpy(key, defaultKey, sizeof(defaultKey));
*sreq = CPSMACed;
*sresp = CPSMACed;
@ -182,7 +197,9 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz
char cdata[250] = {0};
int cdatalen = sizeof(cdata);
cdatalen--; // for trailer 0x00
CLIGetStrWithReturn(ctx, sreqid, (uint8_t *)cdata, &cdatalen);
if (CLIParamStrToBuf(arg_get_str(ctx, sreqid), (uint8_t *)cdata, cdatalen, &cdatalen))
return PM3_ESOFT;
if (cdatalen) {
str_lower(cdata);
if (strcmp(cdata, "plain") == 0)
@ -192,7 +209,7 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz
else if (strcmp(cdata, "enc") == 0 || strcmp(cdata, "encode") == 0 || strcmp(cdata, "encrypted") == 0)
*sreq = CPSEncrypted;
else {
PrintAndLogEx(ERR, _RED_("ERROR:") " security level can be only: plain|mac|encode.");
PrintAndLogEx(ERR, _RED_("ERROR:") " security level can be only: plain | mac | encode");
return PM3_EINVARG;
}
}
@ -200,7 +217,9 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz
cdatalen = sizeof(cdata);
memset(cdata, 0, cdatalen);
cdatalen--; // for trailer 0x00
CLIGetStrWithReturn(ctx, srespid, (uint8_t *)cdata, &cdatalen);
if (CLIParamStrToBuf(arg_get_str(ctx, srespid), (uint8_t *)cdata, cdatalen, &cdatalen))
return PM3_ESOFT;
if (cdatalen) {
str_lower(cdata);
if (strcmp(cdata, "plain") == 0)
@ -210,7 +229,7 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz
else if (strcmp(cdata, "enc") == 0 || strcmp(cdata, "encode") == 0 || strcmp(cdata, "encrypted") == 0)
*sresp = CPSEncrypted;
else {
PrintAndLogEx(ERR, _RED_("ERROR:") " security level can be only: plain|mac|encode.");
PrintAndLogEx(ERR, _RED_("ERROR:") " security level can be only: plain | mac | encode");
return PM3_EINVARG;
}
}
@ -219,24 +238,19 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz
}
static int CmdHFCipurseReadFile(const char *Cmd) {
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
uint8_t key[] = CIPURSE_DEFAULT_KEY;
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf cipurse read",
"Read file by file ID with key ID and key",
"hf cipurse read -f 2ff7 -> Authenticate with keyID=1 and key = 7373...7373 and read file with id 2ff7\n"
"hf cipurse read -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and read file\n");
"Read file by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used",
"hf cipurse read --fid 2ff7 -> Authenticate with keyID 1, read file with id 2ff7\n"
"hf cipurse read -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and read file\n");
void *argtable[] = {
arg_param_begin,
arg_lit0("a", "apdu", "show APDU requests and responses"),
arg_lit0("v", "verbose", "show technical data"),
arg_int0("n", "keyid", "<dec>", "key id"),
arg_str0("k", "key", "<hex>", "key for authenticate"),
arg_str0("f", "file", "<hex>", "file ID"),
arg_int0("n", NULL, "<dec>", "key ID"),
arg_str0("k", "key", "<hex>", "Auth key"),
arg_str0(NULL, "fid", "<hex>", "file ID"),
arg_int0("o", "offset", "<dec>", "offset for reading data from file"),
arg_lit0(NULL, "noauth", "read file without authentication"),
arg_str0(NULL, "sreq", "<plain|mac(default)|encode>", "communication reader-PICC security level"),
@ -245,28 +259,30 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool APDULogging = arg_get_lit(ctx, 1);
bool verbose = arg_get_lit(ctx, 2);
uint8_t keyId = arg_get_int_def(ctx, 3, 1);
uint8_t keyId = arg_get_int_def(ctx, 3, defaultKeyId);
CipurseChannelSecurityLevel sreq = CPSMACed;
CipurseChannelSecurityLevel sresp = CPSMACed;
uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0};
int res = CLIParseKeyAndSecurityLevels(ctx, 4, 8, 9, key, &sreq, &sresp);
if (res) {
CLIParserFree(ctx);
return PM3_EINVARG;
}
uint16_t fileId = 0x2ff7;
uint8_t hdata[250] = {0};
int hdatalen = sizeof(hdata);
CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
if (hdatalen && hdatalen != 2) {
PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only");
CLIParserFree(ctx);
return PM3_EINVARG;
}
uint16_t fileId = defaultFileId;
if (hdatalen)
fileId = (hdata[0] << 8) + hdata[1];
@ -278,21 +294,25 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
CLIParserFree(ctx);
size_t len = 0;
uint16_t sw = 0;
uint8_t buf[APDU_RES_LEN] = {0};
res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x", sw);
DropField();
return PM3_ESOFT;
}
if (verbose)
PrintAndLogEx(INFO, "File id: %x offset %d key id: %d key: %s", fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " offset " _YELLOW_("%zu") " key id " _YELLOW_("%d") " key " _YELLOW_("%s"), fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
if (noAuth == false) {
bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
if (bres == false) {
if (verbose == false)
PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
PrintAndLogEx(ERR, "Authentication ( " _RED_("fail") " )");
DropField();
return PM3_ESOFT;
}
@ -304,78 +324,75 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
if (verbose == false)
PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x.", sw);
PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x", sw);
DropField();
return PM3_ESOFT;
}
if (verbose)
PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId);
PrintAndLogEx(INFO, "Select file 0x%x ( " _GREEN_("ok") " )", fileId);
res = CIPURSEReadBinary(offset, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
if (verbose == false)
PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x.", sw);
PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x", sw);
DropField();
return PM3_ESOFT;
}
if (len == 0)
PrintAndLogEx(INFO, "File id: %x is empty", fileId);
PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " is empty", fileId);
else
PrintAndLogEx(INFO, "File id: %x data[%d]: %s", fileId, len, sprint_hex(buf, len));
PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " data[%zu]: %s", fileId, len, sprint_hex(buf, len));
DropField();
return PM3_SUCCESS;
}
static int CmdHFCipurseWriteFile(const char *Cmd) {
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
uint8_t key[] = CIPURSE_DEFAULT_KEY;
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf cipurse write",
"Write file by file ID with key ID and key",
"hf cipurse write -f 2ff7 -> Authenticate with keyID=1 and key = 7373...7373 and write file with id 2ff7\n"
"hf cipurse write -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and write file\n");
"Write file by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used",
"hf cipurse write --fid 2ff7 -> Authenticate with keyID 1, write file with id 2ff7\n"
"hf cipurse write -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and write file\n");
void *argtable[] = {
arg_param_begin,
arg_lit0("a", "apdu", "show APDU requests and responses"),
arg_lit0("v", "verbose", "show technical data"),
arg_int0("n", "keyid", "<dec>", "key id"),
arg_str0("k", "key", "<hex>", "key for authenticate"),
arg_str0("f", "file", "<hex>", "file ID"),
arg_int0("n", NULL, "<dec>", "key ID"),
arg_str0("k", "key", "<hex>", "Auth key"),
arg_str0(NULL, "fid", "<hex>", "file ID"),
arg_int0("o", "offset", "<dec>", "offset for reading data from file"),
arg_lit0(NULL, "noauth", "read file without authentication"),
arg_str0(NULL, "sreq", "<plain|mac(default)|encode>", "communication reader-PICC security level"),
arg_str0(NULL, "sresp", "<plain|mac(default)|encode>", "communication PICC-reader security level"),
arg_str0("c", "content", "<hex>", "new file content"),
arg_str0("d", "data", "<hex>", "hex data to write to new file"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool APDULogging = arg_get_lit(ctx, 1);
bool verbose = arg_get_lit(ctx, 2);
uint8_t keyId = arg_get_int_def(ctx, 3, 1);
uint8_t keyId = arg_get_int_def(ctx, 3, defaultKeyId);
CipurseChannelSecurityLevel sreq = CPSMACed;
CipurseChannelSecurityLevel sresp = CPSMACed;
uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0};
int res = CLIParseKeyAndSecurityLevels(ctx, 4, 8, 9, key, &sreq, &sresp);
if (res) {
CLIParserFree(ctx);
return PM3_EINVARG;
}
uint16_t fileId = 0x2ff7;
uint16_t fileId = defaultFileId;
uint8_t hdata[250] = {0};
int hdatalen = sizeof(hdata);
CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
if (hdatalen && hdatalen != 2) {
PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only");
CLIParserFree(ctx);
return PM3_EINVARG;
}
@ -389,7 +406,7 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
hdatalen = sizeof(hdata);
CLIGetHexWithReturn(ctx, 10, hdata, &hdatalen);
if (hdatalen == 0) {
PrintAndLogEx(ERR, _RED_("ERROR:") " file content length must be more 0.");
PrintAndLogEx(ERR, _RED_("ERROR:") " file content length must be more 0");
CLIParserFree(ctx);
return PM3_EINVARG;
}
@ -398,15 +415,24 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
CLIParserFree(ctx);
size_t len = 0;
uint16_t sw = 0;
uint8_t buf[APDU_RES_LEN] = {0};
res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x", sw);
DropField();
return PM3_ESOFT;
}
if (verbose) {
PrintAndLogEx(INFO, "File id: %x offset %d key id: %d key: %s", fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " offset " _YELLOW_("%zu") " key id " _YELLOW_("%d") " key " _YELLOW_("%s")
, fileId
, offset
, keyId
, sprint_hex(key, CIPURSE_AES_KEY_LENGTH)
);
PrintAndLogEx(INFO, "data[%d]: %s", hdatalen, sprint_hex(hdata, hdatalen));
}
@ -414,7 +440,7 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
if (bres == false) {
if (verbose == false)
PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
PrintAndLogEx(ERR, "Authentication ( " _RED_("fail") " )");
DropField();
return PM3_ESOFT;
}
@ -426,83 +452,78 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
if (verbose == false)
PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x.", sw);
PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x", sw);
DropField();
return PM3_ESOFT;
}
if (verbose)
PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId);
PrintAndLogEx(INFO, "Select file 0x%x ( " _GREEN_("ok") " )", fileId);
res = CIPURSEUpdateBinary(offset, hdata, hdatalen, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
if (verbose == false)
PrintAndLogEx(ERR, "File write " _RED_("ERROR") ". Card returns 0x%04x.", sw);
PrintAndLogEx(ERR, "File write " _RED_("ERROR") ". Card returns 0x%04x", sw);
DropField();
return PM3_ESOFT;
}
PrintAndLogEx(INFO, "File id: %x successfully written.", fileId);
PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " successfully written", fileId);
DropField();
return PM3_SUCCESS;
}
static int CmdHFCipurseReadFileAttr(const char *Cmd) {
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
uint8_t key[] = CIPURSE_DEFAULT_KEY;
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf cipurse aread",
"Read file attributes by file ID with key ID and key",
"hf cipurse aread -f 2ff7 -> Authenticate with keyID=1 and key = 7373...7373 and read file attributes with id 2ff7\n"
"hf cipurse aread -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and read file attributes\n");
"Read file attributes by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used",
"hf cipurse aread --fid 2ff7 -> Authenticate with keyID 1, read file attributes with id 2ff7\n"
"hf cipurse aread -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2, read file attributes\n");
void *argtable[] = {
arg_param_begin,
arg_lit0("a", "apdu", "show APDU requests and responses"),
arg_lit0("v", "verbose", "show technical data"),
arg_int0("n", "keyid", "<dec>", "key id"),
arg_str0("k", "key", "<hex>", "key for authenticate"),
arg_str0("f", "file", "<hex>", "file ID"),
arg_int0("n", NULL, "<dec>", "key ID"),
arg_str0("k", "key", "<hex>", "Auth key"),
arg_str0(NULL, "fid", "<hex>", "file ID"),
arg_lit0(NULL, "noauth", "read file attributes without authentication"),
arg_str0(NULL, "sreq", "<plain|mac(default)|encode>", "communication reader-PICC security level"),
arg_str0(NULL, "sresp", "<plain|mac(default)|encode>", "communication PICC-reader security level"),
arg_lit0(NULL, "sel-adf", "show info about ADF itself"),
arg_lit0(NULL, "sel-mf", "show info about master file"),
arg_lit0(NULL, "sel-adf", "show info about ADF itself"),
arg_lit0(NULL, "sel-mf", "show info about master file"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool APDULogging = arg_get_lit(ctx, 1);
bool verbose = arg_get_lit(ctx, 2);
uint8_t keyId = arg_get_int_def(ctx, 3, 1);
uint8_t keyId = arg_get_int_def(ctx, 3, defaultKeyId);
CipurseChannelSecurityLevel sreq = CPSMACed;
CipurseChannelSecurityLevel sresp = CPSMACed;
uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0};
int res = CLIParseKeyAndSecurityLevels(ctx, 4, 7, 8, key, &sreq, &sresp);
if (res) {
CLIParserFree(ctx);
return PM3_EINVARG;
}
uint16_t fileId = 0x2ff7;
uint8_t hdata[250] = {0};
int hdatalen = sizeof(hdata);
CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
if (hdatalen && hdatalen != 2) {
PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only");
CLIParserFree(ctx);
return PM3_EINVARG;
}
uint16_t fileId = defaultFileId;
if (hdatalen)
fileId = (hdata[0] << 8) + hdata[1];
bool noAuth = arg_get_lit(ctx, 6);
bool seladf = arg_get_lit(ctx, 9);
bool selmf = arg_get_lit(ctx, 10);
@ -510,21 +531,30 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
CLIParserFree(ctx);
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x", sw);
DropField();
return PM3_ESOFT;
}
if (verbose)
PrintAndLogEx(INFO, "File id: %x key id: %d key: %s", fileId, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
if (verbose) {
PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " key id " _YELLOW_("%d") " key " _YELLOW_("%s")
, fileId
, keyId
, sprint_hex(key, CIPURSE_AES_KEY_LENGTH)
);
}
if (noAuth == false) {
bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
if (bres == false) {
if (verbose == false)
PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
PrintAndLogEx(ERR, "Authentication ( " _RED_("fail") " )");
DropField();
return PM3_ESOFT;
}
@ -541,31 +571,31 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
if (res != 0 || sw != 0x9000) {
if (verbose == false)
PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x.", sw);
PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x", sw);
DropField();
return PM3_ESOFT;
}
}
if (verbose)
PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId);
PrintAndLogEx(INFO, "Select file 0x%x ( " _GREEN_("ok") " )", fileId);
res = CIPURSEReadFileAttributes(buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
if (verbose == false)
PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x.", sw);
PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x", sw);
DropField();
return PM3_ESOFT;
}
if (len == 0) {
PrintAndLogEx(WARNING, "File id: %x attributes is empty", fileId);
PrintAndLogEx(WARNING, "File id " _YELLOW_("%x") " attributes is empty", fileId);
DropField();
return PM3_SUCCESS;
}
if (verbose)
PrintAndLogEx(INFO, "File id: %x attributes[%d]: %s", fileId, len, sprint_hex(buf, len));
PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " attributes[%zu]: %s", fileId, len, sprint_hex(buf, len));
CIPURSEPrintFileAttr(buf, len);
@ -574,24 +604,19 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
}
static int CmdHFCipurseDeleteFile(const char *Cmd) {
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
uint8_t key[] = CIPURSE_DEFAULT_KEY;
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf cipurse delete",
"Read file by file ID with key ID and key",
"hf cipurse delete -f 2ff7 -> Authenticate with keyID=1 and key = 7373...7373 and delete file with id 2ff7\n"
"hf cipurse delete -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and delete file\n");
"Read file by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used",
"hf cipurse delete --fid 2ff7 -> Authenticate with keyID 1, delete file with id 2ff7\n"
"hf cipurse delete -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and delete file\n");
void *argtable[] = {
arg_param_begin,
arg_lit0("a", "apdu", "show APDU requests and responses"),
arg_lit0("v", "verbose", "show technical data"),
arg_int0("n", "keyid", "<dec>", "key id"),
arg_str0("k", "key", "<hex>", "key for authenticate"),
arg_str0("f", "file", "<hex>", "file ID"),
arg_int0("n", NULL, "<dec>", "key ID"),
arg_str0("k", "key", "<hex>", "Auth key"),
arg_str0(NULL, "fid", "<hex>", "file ID"),
arg_str0(NULL, "sreq", "<plain|mac(default)|encode>", "communication reader-PICC security level"),
arg_str0(NULL, "sresp", "<plain|mac(default)|encode>", "communication PICC-reader security level"),
arg_param_end
@ -600,26 +625,27 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) {
bool APDULogging = arg_get_lit(ctx, 1);
bool verbose = arg_get_lit(ctx, 2);
uint8_t keyId = arg_get_int_def(ctx, 3, 1);
uint8_t keyId = arg_get_int_def(ctx, 3, defaultKeyId);
CipurseChannelSecurityLevel sreq = CPSMACed;
CipurseChannelSecurityLevel sresp = CPSMACed;
uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0};
int res = CLIParseKeyAndSecurityLevels(ctx, 4, 6, 7, key, &sreq, &sresp);
if (res) {
CLIParserFree(ctx);
return PM3_EINVARG;
}
uint16_t fileId = 0x2ff7;
uint8_t hdata[250] = {0};
int hdatalen = sizeof(hdata);
CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
if (hdatalen && hdatalen != 2) {
PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only");
CLIParserFree(ctx);
return PM3_EINVARG;
}
uint16_t fileId = defaultFileId;
if (hdatalen)
fileId = (hdata[0] << 8) + hdata[1];
@ -627,20 +653,29 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) {
CLIParserFree(ctx);
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x", sw);
DropField();
return PM3_ESOFT;
}
if (verbose)
PrintAndLogEx(INFO, "File id: %x key id: %d key: %s", fileId, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
if (verbose) {
PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " key id " _YELLOW_("%d") " key " _YELLOW_("%s")
, fileId
, keyId
, sprint_hex(key, CIPURSE_AES_KEY_LENGTH)
);
}
bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
if (bres == false) {
if (verbose == false)
PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
PrintAndLogEx(ERR, "Authentication ( " _RED_("fail") " )");
DropField();
return PM3_ESOFT;
}
@ -651,28 +686,17 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) {
res = CIPURSEDeleteFile(fileId, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
if (verbose == false)
PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x.", sw);
PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x", sw);
DropField();
return PM3_ESOFT;
}
PrintAndLogEx(INFO, "File id: 04x deleted " _GREEN_("succesfully"), fileId);
PrintAndLogEx(INFO, "File id " _YELLOW_("%04x") " deleted " _GREEN_("succesfully"), fileId);
DropField();
return PM3_SUCCESS;
}
bool CheckCardCipurse(void) {
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
@ -682,14 +706,83 @@ bool CheckCardCipurse(void) {
return (res == 0 && sw == 0x9000);
}
static int CmdHFCipurseTest(const char *Cmd) {
CIPURSETest(true);
return PM3_SUCCESS;
}
static int CmdHFCipurseDefault(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf cipurse default",
"Set default parameters for access to cipurse card",
"hf cipurse default -n 1 -k 65656565656565656565656565656565 --fid 2ff7 -> Set key, key id and file id\n");
void *argtable[] = {
arg_param_begin,
arg_lit0(NULL, "clear", "resets to defaults"),
arg_int0("n", NULL, "<dec>", "Key ID"),
arg_str0("k", "key", "<hex>", "Authentication key"),
arg_str0(NULL, "fid", "<hex>", "File ID"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool clearing = arg_get_lit(ctx, 1);
if (clearing) {
defaultKeyId = 1;
defaultFileId = 0x2ff7;
uint8_t ckey[CIPURSE_AES_KEY_LENGTH] = CIPURSE_DEFAULT_KEY;
memcpy(defaultKey, ckey, CIPURSE_AES_KEY_LENGTH);
}
defaultKeyId = arg_get_int_def(ctx, 2, defaultKeyId);
uint8_t hdata[250] = {0};
int hdatalen = sizeof(hdata);
CLIGetHexWithReturn(ctx, 3, hdata, &hdatalen);
if (hdatalen && hdatalen != 16) {
PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only");
CLIParserFree(ctx);
return PM3_EINVARG;
}
if (hdatalen)
memcpy(defaultKey, hdata, CIPURSE_AES_KEY_LENGTH);
memset(hdata, 0, sizeof(hdata));
hdatalen = sizeof(hdata);
CLIGetHexWithReturn(ctx, 4, hdata, &hdatalen);
if (hdatalen && hdatalen != 2) {
PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only");
CLIParserFree(ctx);
return PM3_EINVARG;
}
if (hdatalen)
defaultFileId = (hdata[0] << 8) + hdata[1];
CLIParserFree(ctx);
PrintAndLogEx(INFO, "-----------" _CYAN_("Default parameters") "---------------------------------");
PrintAndLogEx(INFO, "Key ID : %d", defaultKeyId);
PrintAndLogEx(INFO, "Key : %s", sprint_hex(defaultKey, sizeof(defaultKey)));
PrintAndLogEx(INFO, "File ID: 0x%04x", defaultFileId);
return PM3_SUCCESS;
}
static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help."},
{"info", CmdHFCipurseInfo, IfPm3Iso14443a, "Info about Cipurse tag."},
{"auth", CmdHFCipurseAuth, IfPm3Iso14443a, "Authentication."},
{"read", CmdHFCipurseReadFile, IfPm3Iso14443a, "Read binary file."},
{"write", CmdHFCipurseWriteFile, IfPm3Iso14443a, "Write binary file."},
{"aread", CmdHFCipurseReadFileAttr, IfPm3Iso14443a, "Read file attributes."},
{"delete", CmdHFCipurseDeleteFile, IfPm3Iso14443a, "Delete file."},
{"info", CmdHFCipurseInfo, IfPm3Iso14443a, "Get info about CIPURSE tag"},
{"auth", CmdHFCipurseAuth, IfPm3Iso14443a, "Authenticate CIPURSE tag"},
{"read", CmdHFCipurseReadFile, IfPm3Iso14443a, "Read binary file"},
{"write", CmdHFCipurseWriteFile, IfPm3Iso14443a, "Write binary file"},
{"aread", CmdHFCipurseReadFileAttr, IfPm3Iso14443a, "Read file attributes"},
{"delete", CmdHFCipurseDeleteFile, IfPm3Iso14443a, "Delete file"},
{"default", CmdHFCipurseDefault, IfPm3Iso14443a, "Set default key and file id for all the other commands"},
{"test", CmdHFCipurseTest, AlwaysAvailable, "Tests"},
{NULL, NULL, 0, NULL}
};

View file

@ -260,7 +260,7 @@ static int CmdHFCryptoRFDump(const char *Cmd) {
void *argtable[] = {
arg_param_begin,
arg_str0("f", "file", "<filename>", "filename to save dump to"),
arg_str0("f", "file", "<fn>", "filename to save dump to"),
arg_lit0(NULL, "64", "64byte / 512bit memory"),
arg_lit0(NULL, "512", "512byte / 4096bit memory"),
arg_param_end
@ -486,7 +486,7 @@ static int CmdHFCryptoRFESave(const char *Cmd) {
);
void *argtable[] = {
arg_param_begin,
arg_str0("f", "file", "<filename>", "filename of dumpfile"),
arg_str0("f", "file", "<fn>", "filename of dumpfile"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);

View file

@ -19,12 +19,13 @@
#include "protocols.h" // definitions of ISO14A/7816 protocol
#include "iso7816/apduinfo.h" // GetAPDUCodeDescription
#include "iso7816/iso7816core.h" // Iso7816ExchangeEx etc
#include "crypto/libpcrypto.h" // Hash calculation (sha1, sha256, sha512)
#include "mifare/desfire_crypto.h" // des_encrypt/des_decrypt
#include "crypto/libpcrypto.h" // Hash calculation (sha1, sha256, sha512), des_encrypt/des_decrypt
#include "des.h" // mbedtls_des_key_set_parity
#include "crapto1/crapto1.h" // prng_successor
#include "commonutil.h" // num_to_bytes
#include "util_posix.h" // msclock
#include "ui.h" // searchhomedirectory
#include "proxgui.h" // Picture Window
// Max file size in bytes. Used in several places.
// Average EF_DG2 seems to be around 20-25kB or so, but ICAO doesn't set an upper limit
@ -54,6 +55,7 @@ static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const c
static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const char *path);
static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen);
static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen);
static int emrtd_print_ef_dg2_info(uint8_t *data, size_t datalen);
static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen);
static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen);
static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen);
@ -85,7 +87,7 @@ static emrtd_dg_t dg_table[] = {
// tag dg# fileid filename desc pace eac req fast parser dumper
{0x60, 0, 0x011E, "EF_COM", "Header and Data Group Presence Information", false, false, true, true, emrtd_print_ef_com_info, NULL},
{0x61, 1, 0x0101, "EF_DG1", "Details recorded in MRZ", false, false, true, true, emrtd_print_ef_dg1_info, NULL},
{0x75, 2, 0x0102, "EF_DG2", "Encoded Face", false, false, true, false, NULL, emrtd_dump_ef_dg2},
{0x75, 2, 0x0102, "EF_DG2", "Encoded Face", false, false, true, false, emrtd_print_ef_dg2_info, emrtd_dump_ef_dg2},
{0x63, 3, 0x0103, "EF_DG3", "Encoded Finger(s)", false, true, false, false, NULL, NULL},
{0x76, 4, 0x0104, "EF_DG4", "Encoded Eye(s)", false, true, false, false, NULL, NULL},
{0x65, 5, 0x0105, "EF_DG5", "Displayed Portrait", false, false, false, false, NULL, emrtd_dump_ef_dg5},
@ -263,20 +265,6 @@ static int emrtd_get_asn1_field_length(uint8_t *datain, int datainlen, int offse
return 0;
}
static void des_encrypt_ecb(uint8_t *key, uint8_t *input, uint8_t *output) {
mbedtls_des_context ctx_enc;
mbedtls_des_setkey_enc(&ctx_enc, key);
mbedtls_des_crypt_ecb(&ctx_enc, input, output);
mbedtls_des_free(&ctx_enc);
}
static void des_decrypt_ecb(uint8_t *key, uint8_t *input, uint8_t *output) {
mbedtls_des_context ctx_dec;
mbedtls_des_setkey_dec(&ctx_dec, key);
mbedtls_des_crypt_ecb(&ctx_dec, input, output);
mbedtls_des_free(&ctx_dec);
}
static void des3_encrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) {
mbedtls_des3_context ctx;
mbedtls_des3_set2key_enc(&ctx, key);
@ -345,15 +333,15 @@ static void retail_mac(uint8_t *key, uint8_t *input, int inputlen, uint8_t *outp
intermediate[x] = intermediate[x] ^ block[x];
}
des_encrypt_ecb(k0, intermediate, intermediate_des);
des_encrypt(intermediate_des, intermediate, k0);
memcpy(intermediate, intermediate_des, 8);
}
des_decrypt_ecb(k1, intermediate, intermediate_des);
des_decrypt(intermediate_des, intermediate, k1);
memcpy(intermediate, intermediate_des, 8);
des_encrypt_ecb(k0, intermediate, intermediate_des);
des_encrypt(intermediate_des, intermediate, k0);
memcpy(output, intermediate_des, 8);
}
@ -698,18 +686,18 @@ static bool emrtd_lds_get_data_by_tag(uint8_t *datain, size_t datainlen, uint8_t
static bool emrtd_select_and_read(uint8_t *dataout, size_t *dataoutlen, uint16_t file, uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, bool use_secure) {
if (use_secure) {
if (emrtd_secure_select_file_by_ef(ks_enc, ks_mac, ssc, file) == false) {
PrintAndLogEx(ERR, "Failed to secure select %s.", file);
PrintAndLogEx(ERR, "Failed to secure select %04X", file);
return false;
}
} else {
if (emrtd_select_file_by_ef(file) == false) {
PrintAndLogEx(ERR, "Failed to select %04X.", file);
PrintAndLogEx(ERR, "Failed to select %04X", file);
return false;
}
}
if (emrtd_read_file(dataout, dataoutlen, ks_enc, ks_mac, ssc, use_secure) == false) {
PrintAndLogEx(ERR, "Failed to read %04X.", file);
PrintAndLogEx(ERR, "Failed to read %04X", file);
return false;
}
return true;
@ -843,9 +831,13 @@ static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, uint
strncat(filepath, PATHSEP, 2);
strcat(filepath, name);
PrintAndLogEx(INFO, "Read %s, len: %i.", name, resplen);
PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen));
PrintAndLogEx(INFO, "Read " _YELLOW_("%s") " , len %zu", name, resplen);
PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars)");
PrintAndLogEx(DEBUG, "------------------------------------------");
PrintAndLogEx(DEBUG, "%s", sprint_hex_inrow(response, resplen));
PrintAndLogEx(DEBUG, "------------------------------------------");
saveFile(filepath, ".BIN", response, resplen);
emrtd_dg_t *dg = emrtd_fileid_to_dg(file);
if ((dg != NULL) && (dg->dumper != NULL)) {
dg->dumper(response, resplen, path);
@ -1030,8 +1022,8 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
// Dump EF_CardAccess (if available)
if (!emrtd_dump_file(ks_enc, ks_mac, ssc, dg_table[EF_CardAccess].fileid, dg_table[EF_CardAccess].filename, BAC, path)) {
PrintAndLogEx(INFO, "Couldn't dump EF_CardAccess, card does not support PACE.");
PrintAndLogEx(HINT, "This is expected behavior for cards without PACE, and isn't something to be worried about.");
PrintAndLogEx(INFO, "Couldn't dump EF_CardAccess, card does not support PACE");
PrintAndLogEx(HINT, "This is expected behavior for cards without PACE, and isn't something to be worried about");
}
// Authenticate with the eMRTD
@ -1042,7 +1034,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
// Select EF_COM
if (!emrtd_select_and_read(response, &resplen, dg_table[EF_COM].fileid, ks_enc, ks_mac, ssc, BAC)) {
PrintAndLogEx(ERR, "Failed to read EF_COM.");
PrintAndLogEx(ERR, "Failed to read EF_COM");
DropField();
return PM3_ESOFT;
}
@ -1051,11 +1043,12 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
char *filepath = calloc(strlen(path) + 100, sizeof(char));
if (filepath == NULL)
return PM3_EMALLOC;
strcpy(filepath, path);
strncat(filepath, PATHSEP, 2);
strcat(filepath, dg_table[EF_COM].filename);
PrintAndLogEx(INFO, "Read EF_COM, len: %i.", resplen);
PrintAndLogEx(INFO, "Read EF_COM, len: %zu", resplen);
PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen));
saveFile(filepath, ".BIN", response, resplen);
@ -1065,7 +1058,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
size_t filelistlen = 0;
if (emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0) == false) {
PrintAndLogEx(ERR, "Failed to read file list from EF_COM.");
PrintAndLogEx(ERR, "Failed to read file list from EF_COM");
DropField();
return PM3_ESOFT;
}
@ -1408,6 +1401,65 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) {
return PM3_SUCCESS;
}
static int emrtd_print_ef_dg2_info(uint8_t *data, size_t datalen) {
int offset = 0;
// This is a hacky impl that just looks for the image header. I'll improve it eventually.
// based on mrpkey.py
// Note: Doing datalen - 6 to account for the longest data we're checking.
// Checks first byte before the rest to reduce overhead
for (offset = 0; offset < datalen - 6; offset++) {
if ((data[offset] == 0xFF && memcmp(jpeg_header, data + offset, 4) == 0) ||
(data[offset] == 0x00 && memcmp(jpeg2k_header, data + offset, 6) == 0)) {
datalen = datalen - offset;
break;
}
}
// If we didn't get any data, return false.
if (datalen == 0) {
return PM3_ESOFT;
}
bool is_jpg = (data[offset] == 0xFF);
char *fn = calloc(strlen(dg_table[EF_DG2].filename) + 4 + 1, sizeof(uint8_t));
if (fn == NULL)
return PM3_EMALLOC;
sprintf(fn, "%s.%s", dg_table[EF_DG2].filename, (is_jpg) ? "jpg" : "jp2");
PrintAndLogEx(DEBUG, "image filename `" _YELLOW_("%s") "`", fn);
char *path;
if (searchHomeFilePath(&path, NULL, fn, false) != PM3_SUCCESS) {
free(fn);
return PM3_EFILE;
}
free(fn);
// remove old file
if (fileExists(path)) {
PrintAndLogEx(DEBUG, "Delete old temp file `" _YELLOW_("%s") "`", path);
remove(path);
}
// temp file.
PrintAndLogEx(DEBUG, "Save temp file `" _YELLOW_("%s") "`", path);
saveFile(path, "", data + offset, datalen);
PrintAndLogEx(DEBUG, "view temp file `" _YELLOW_("%s") "`", path);
ShowPictureWindow(path);
msleep(500);
// delete temp file
PrintAndLogEx(DEBUG, "Deleting temp file `" _YELLOW_("%s") "`", path);
remove(path);
//free(path);
return PM3_SUCCESS;
}
static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) {
uint8_t taglist[100] = { 0x00 };
size_t taglistlen = 0;
@ -1940,6 +1992,7 @@ int infoHF_EMRTD_offline(const char *path) {
if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) {
emrtd_print_ef_cardaccess_info(data, datalen);
free(data);
} else {
PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE.");
}
@ -2194,12 +2247,12 @@ static int CmdHFeMRTDInfo(const char *Cmd) {
}
}
uint8_t path[FILENAME_MAX] = { 0x00 };
bool offline = CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) == 0 && slen > 0;
bool is_offline = CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) == 0 && slen > 0;
CLIParserFree(ctx);
if (error) {
return PM3_ESOFT;
}
if (offline) {
if (is_offline) {
return infoHF_EMRTD_offline((const char *)path);
} else {
bool restore_apdu_logging = GetAPDULogging();

View file

@ -13,6 +13,10 @@
#include "common.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct emrtd_dg_s {
uint8_t tag;
uint8_t dgnum;
@ -53,4 +57,8 @@ int CmdHFeMRTD(const char *Cmd);
int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, const char *path);
int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available);
int infoHF_EMRTD_offline(const char *path);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -37,6 +37,9 @@
#define ICLASS_AUTH_RETRY 10
#define ICLASS_DECRYPTION_BIN "iclass_decryptionkey.bin"
static void print_picopass_info(const picopass_hdr_t *hdr);
void print_picopass_header(const picopass_hdr_t *hdr);
static picopass_hdr_t iclass_last_known_card;
static void iclass_set_last_known_card(picopass_hdr_t *card) {
memcpy(&iclass_last_known_card, card, sizeof(picopass_hdr_t));
@ -129,7 +132,7 @@ static void iclass_upload_emul(uint8_t *d, uint16_t n, uint16_t *bytes_sent) {
uint16_t bytes_remaining = n;
while (bytes_remaining > 0) {
uint32_t bytes_in_packet = MIN(PM3_CMD_DATA_SIZE, bytes_remaining);
uint32_t bytes_in_packet = MIN(PM3_CMD_DATA_SIZE - 4, bytes_remaining);
if (bytes_in_packet == bytes_remaining) {
// Disable fast mode on last packet
conn.block_after_ACK = false;
@ -244,8 +247,8 @@ static void print_config_cards(void) {
static void print_config_card(const iclass_config_card_item_t *o) {
if (check_config_card(o)) {
PrintAndLogEx(INFO, "description... %s", o->desc);
PrintAndLogEx(INFO, "data....... " _YELLOW_("%s"), sprint_hex_inrow(o->data, sizeof(o->data)));
PrintAndLogEx(INFO, "description... " _YELLOW_("%s"), o->desc);
PrintAndLogEx(INFO, "data.......... " _YELLOW_("%s"), sprint_hex_inrow(o->data, sizeof(o->data)));
}
}
@ -253,20 +256,37 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke
if (check_config_card(o) == false) {
return PM3_EINVARG;
}
// generated config card header
picopass_hdr_t configcard;
memset(&configcard, 0xFF, sizeof(picopass_hdr_t));
memcpy(configcard.csn, "\x41\x87\x66\x00\xFB\xFF\x12\xE0", 8);
memcpy(&configcard.conf, "\xFF\xFF\xFF\xFF\xF9\xFF\xFF\xBC", 8);
memcpy(&configcard.epurse, "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8);
// defaulting to known AA1 key
HFiClassCalcDivKey(configcard.csn, iClass_Key_Table[0], configcard.key_d, false);
// reference
picopass_hdr_t *cc = &configcard;
// get header from card
//bool have = memcmp(iclass_last_known_card.csn, "\x00\x00\x00\x00\x00\x00\x00\x00", 8);
PrintAndLogEx(INFO, "trying to read a card..");
int res = read_iclass_csn(false, false);
if (res != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "Put a card on antenna and try again...");
return res;
if (res == PM3_SUCCESS) {
cc = &iclass_last_known_card;
// calc diversified key for selected card
HFiClassCalcDivKey(cc->csn, iClass_Key_Table[0], cc->key_d, false);
} else {
PrintAndLogEx(INFO, "failed to read a card, will use default config card data");
}
// generate dump file
uint8_t app1_limit = iclass_last_known_card.conf.app_limit;
uint8_t app1_limit = cc->conf.app_limit;
uint8_t old_limit = app1_limit;
uint8_t tot_bytes = (app1_limit + 1) * 8;
uint16_t tot_bytes = (app1_limit + 1) * 8;
PrintAndLogEx(INFO, " APP1 limit: %u", app1_limit);
PrintAndLogEx(INFO, "total bytes: %u", tot_bytes);
// normal size
uint8_t *data = calloc(1, tot_bytes);
if (data == NULL) {
@ -274,12 +294,9 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke
return PM3_EMALLOC;
}
memcpy(data, cc, sizeof(picopass_hdr_t));
// calc diversified key for selected card
HFiClassCalcDivKey(iclass_last_known_card.csn, iClass_Key_Table[0], iclass_last_known_card.key_d, false);
memset(data, 0x00, tot_bytes);
memcpy(data, (uint8_t *)&iclass_last_known_card, sizeof(picopass_hdr_t));
print_picopass_header(cc);
// Keyrolling configuration cards are special.
if (strstr(o->desc, "Keyroll") != NULL) {
@ -295,7 +312,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke
PrintAndLogEx(WARNING, "Adapting applimit1 for KEY rolling..");
app1_limit = 0x16;
iclass_last_known_card.conf.app_limit = 0x16;
cc->conf.app_limit = 0x16;
tot_bytes = (app1_limit + 1) * 8;
uint8_t *p = realloc(data, tot_bytes);
@ -308,25 +325,22 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke
memset(data, 0xFF, tot_bytes);
}
// need to encrypt
PrintAndLogEx(INFO, "Detecting cardhelper...");
if (IsCardHelperPresent(false) == false) {
PrintAndLogEx(FAILED, "failed to detect cardhelper");
free(data);
return PM3_ENODATA;
}
// KEYROLL need to encrypt
uint8_t ffs[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
if (Encrypt(ffs, ffs) == false) {
PrintAndLogEx(WARNING, "failed to encrypt FF");
}
// local key copy
uint8_t lkey[8];
memcpy(lkey, key, sizeof(lkey));
uint8_t enckey1[8];
if (Encrypt(key, enckey1) == false) {
if (Encrypt(lkey, enckey1) == false) {
PrintAndLogEx(WARNING, "failed to encrypt key1");
}
memcpy(data, &iclass_last_known_card, sizeof(picopass_hdr_t));
memcpy(data, cc, sizeof(picopass_hdr_t));
memcpy(data + (6 * 8), o->data, sizeof(o->data));
// encrypted keyroll key 0D
@ -338,7 +352,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke
// encrypted partial keyroll key 14
uint8_t foo[8] = {0x15};
memcpy(foo + 1, key, 7);
memcpy(foo + 1, lkey, 7);
uint8_t enckey2[8];
if (Encrypt(foo, enckey2) == false) {
PrintAndLogEx(WARNING, "failed to encrypt partial 1");
@ -347,7 +361,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke
// encrypted partial keyroll key 15
memset(foo, 0xFF, sizeof(foo));
foo[0] = key[7];
foo[0] = lkey[7];
if (Encrypt(foo, enckey2) == false) {
PrintAndLogEx(WARNING, "failed to encrypt partial 2");
}
@ -358,12 +372,11 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke
memcpy(data + (i * 8), ffs, sizeof(ffs));
}
// revert potential modified app1_limit
iclass_last_known_card.conf.app_limit = old_limit;
cc->conf.app_limit = old_limit;
} else {
memcpy(data, &iclass_last_known_card, sizeof(picopass_hdr_t));
memcpy(data, cc, sizeof(picopass_hdr_t));
memcpy(data + (6 * 8), o->data, sizeof(o->data));
}
@ -532,13 +545,13 @@ static void mem_app_config(const picopass_hdr_t *hdr) {
}
}
static void print_picopass_info(const picopass_hdr_t *hdr) {
void print_picopass_info(const picopass_hdr_t *hdr) {
PrintAndLogEx(INFO, "-------------------- " _CYAN_("card configuration") " --------------------");
fuse_config(hdr);
mem_app_config(hdr);
}
static void print_picopass_header(const picopass_hdr_t *hdr) {
void print_picopass_header(const picopass_hdr_t *hdr) {
PrintAndLogEx(INFO, "--------------------------- " _CYAN_("card") " ---------------------------");
PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s") " uid", sprint_hex(hdr->csn, sizeof(hdr->csn)));
PrintAndLogEx(SUCCESS, " Config: %s Card configuration", sprint_hex((uint8_t *)&hdr->conf, sizeof(hdr->conf)));
@ -1640,6 +1653,9 @@ static int CmdHFiClassDump(const char *Cmd) {
app_limit1 = card_app2_limit[type];
app_limit2 = 0;
} else if (hdr->conf.app_limit >= hdr->conf.mem_config) {
PrintAndLogEx(WARNING, "AA1 config is >= card size, using card size as AA1 limit");
app_limit1 = card_app2_limit[type];
} else {
app_limit1 = hdr->conf.app_limit;
app_limit2 = card_app2_limit[type];
@ -1661,7 +1677,12 @@ static int CmdHFiClassDump(const char *Cmd) {
PrintAndLogEx(FAILED, "Run command with keys");
return PM3_ESOFT;
}
PrintAndLogEx(INFO, "Card has atleast 2 application areas. AA1 limit %u (0x%02X) AA2 limit %u (0x%02X)", app_limit1, app_limit1, app_limit2, app_limit2);
if (app_limit2 != 0) {
PrintAndLogEx(INFO, "Card has at least 2 application areas. AA1 limit %u (0x%02X) AA2 limit %u (0x%02X)", app_limit1, app_limit1, app_limit2, app_limit2);
} else {
PrintAndLogEx(INFO, "Card has 1 application area. AA1 limit %u (0x%02X)", app_limit1, app_limit1);
}
}
iclass_dump_req_t payload = {
@ -1737,7 +1758,7 @@ static int CmdHFiClassDump(const char *Cmd) {
// AIA data
memcpy(tag_data + (8 * 5), tempbuf + (8 * 5), 8);
// AA1 data
memcpy(tag_data + (8 * 6), tempbuf + (8 * 6), (blocks_read * 8));
memcpy(tag_data + (8 * 6), tempbuf + (8 * 6), ((blocks_read - 6) * 8));
}
uint16_t bytes_got = (app_limit1 + 1) * 8;
@ -1745,7 +1766,7 @@ static int CmdHFiClassDump(const char *Cmd) {
// try AA2 Kc, Credit
bool aa2_success = false;
if (have_credit_key && pagemap != 0x01) {
if (have_credit_key && pagemap != PICOPASS_NON_SECURE_PAGEMODE && app_limit2 != 0) {
// AA2 authenticate credit key
memcpy(payload.req.key, credit_key, 8);
@ -3859,6 +3880,8 @@ int CmdHFiClass(const char *Cmd) {
// SR | 6,7,8,9, | AA1, Access control payload | 2
// | 10,11,12,13,14,15,16 | AA1, Secure identity object (SIO) |
// SEOS | | |
// MFC SIO| | |
// DESFIRE| | |
//}
int info_iclass(void) {

View file

@ -762,7 +762,7 @@ static int CmdLegicDump(const char *Cmd) {
void *argtable[] = {
arg_param_begin,
arg_str0("f", "file", "<filename>", "specify a filename for dump file"),
arg_str0("f", "file", "<fn>", "specify a filename for dump file"),
arg_lit0(NULL, "de", "deobfuscate dump data (xor with MCC)"),
arg_param_end
};
@ -1032,7 +1032,7 @@ static int CmdLegicESave(const char *Cmd) {
void *argtable[] = {
arg_param_begin,
arg_str0("f", "file", "<filename>", "Specify a filename to save"),
arg_str0("f", "file", "<fn>", "Specify a filename to save"),
arg_int0("t", "type", "<dec>", "Tag type"),
arg_lit0(NULL, "deobfuscate", "De-obfuscate dump data (xor with MCC)"),
arg_param_end

View file

@ -650,7 +650,7 @@ static int CmdHfLTODump(const char *Cmd) {
void *argtable[] = {
arg_param_begin,
arg_str0("f", "file", "<filename>", "specify a filename for dumpfile"),
arg_str0("f", "file", "<fn>", "specify a filename for dumpfile"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);

View file

@ -24,7 +24,6 @@
#include "protocols.h"
#include "util_posix.h" // msclock
#include "cmdhfmfhard.h"
#include "des.h" // des ecb
#include "crapto1/crapto1.h" // prng_successor
#include "cmdhf14a.h" // exchange APDU
#include "crypto/libpcrypto.h"
@ -161,7 +160,7 @@ static void decode_print_st(uint16_t blockno, uint8_t *data) {
PrintAndLogEx(INFO, "----------------------- " _CYAN_("Sector trailer decoder") " -----------------------");
PrintAndLogEx(INFO, "key A........ " _GREEN_("%s"), sprint_hex_inrow(data, 6));
PrintAndLogEx(INFO, "acr.......... " _GREEN_("%s"), sprint_hex_inrow(data + 6, 3));
PrintAndLogEx(INFO, "user / gdb... " _GREEN_("%02x"), data[9]);
PrintAndLogEx(INFO, "user / gpb... " _GREEN_("%02x"), data[9]);
PrintAndLogEx(INFO, "key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6));
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, " # | Access rights");
@ -2577,9 +2576,9 @@ static int CmdHF14AMfChk_fast(const char *Cmd) {
"hf mf fchk --2k -k FFFFFFFFFFFF --> Key recovery against MIFARE 2k\n"
"hf mf fchk --4k -k FFFFFFFFFFFF --> Key recovery against MIFARE 4k\n"
"hf mf fchk --1k -f mfc_default_keys.dic --> Target 1K using default dictionary file\n"
"hf mf fchk --1k --emu --> Target 1K, write to emulator memory\n"
"hf mf fchk --1k --dump --> Target 1K, write to file\n"
"hf mf fchk --1k --mem --> Target 1K, use dictionary from flashmemory");
"hf mf fchk --1k --emu --> Target 1K, write keys to emulator memory\n"
"hf mf fchk --1k --dump --> Target 1K, write keys to file\n"
"hf mf fchk --1k --mem --> Target 1K, use dictionary from flash memory");
void *argtable[] = {
arg_param_begin,
@ -2885,7 +2884,7 @@ static int CmdHF14AMfChk(const char *Cmd) {
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_lit0(NULL, "emu", "Fill simulator keys from found keys"),
arg_lit0(NULL, "dump", "Dump found keys to binary file"),
arg_str0("f", "file", "<filename>", "filename of dictionary"),
arg_str0("f", "file", "<fn>", "filename of dictionary"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
@ -5031,7 +5030,7 @@ static int CmdHF14AMfice(const char *Cmd) {
void *argtable[] = {
arg_param_begin,
arg_str0("f", "file", "<filename>", "filename of nonce dump"),
arg_str0("f", "file", "<fn>", "filename of nonce dump"),
arg_u64_0(NULL, "limit", "<dec>", "nonces to be collected"),
arg_param_end
};
@ -5645,12 +5644,6 @@ static int CmdHf14AGen3Freeze(const char *Cmd) {
return res;
}
static void des_decrypt(void *out, const void *in, const void *key) {
mbedtls_des_context ctx;
mbedtls_des_setkey_dec(&ctx, key);
mbedtls_des_crypt_ecb(&ctx, in, out);
}
static int CmdHf14AMfSuperCard(const char *Cmd) {
CLIParserContext *ctx;
@ -6007,6 +6000,110 @@ static int CmdHF14AMfView(const char *Cmd) {
return PM3_SUCCESS;
}
static int CmdHF14AGen3View(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf gview",
"View `magic gen3 gtu` card memory",
"hf mf gview\n"
"hf mf gview --4k"
);
void *argtable[] = {
arg_param_begin,
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool m0 = arg_get_lit(ctx, 1);
bool m1 = arg_get_lit(ctx, 2);
bool m2 = arg_get_lit(ctx, 3);
bool m4 = arg_get_lit(ctx, 4);
CLIParserFree(ctx);
// validations
if ((m0 + m1 + m2 + m4) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
m1 = true;
}
char s[6];
memset(s, 0, sizeof(s));
uint16_t block_cnt = MIFARE_1K_MAXBLOCK;
if (m0) {
block_cnt = MIFARE_MINI_MAXBLOCK;
strncpy(s, "Mini", 5);
} else if (m1) {
block_cnt = MIFARE_1K_MAXBLOCK;
strncpy(s, "1K", 3);
} else if (m2) {
block_cnt = MIFARE_2K_MAXBLOCK;
strncpy(s, "2K", 3);
} else if (m4) {
block_cnt = MIFARE_4K_MAXBLOCK;
strncpy(s, "4K", 3);
} else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG;
}
PrintAndLogEx(SUCCESS, "View magic gen3 GTU MIFARE Classic " _GREEN_("%s"), s);
PrintAndLogEx(INFO, "." NOLF);
// Select card to get UID/UIDLEN information
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "iso14443a card select timeout");
return PM3_ETIMEOUT;
}
/*
0: couldn't read
1: OK, with ATS
2: OK, no ATS
3: proprietary Anticollision
*/
uint64_t select_status = resp.oldarg[0];
if (select_status == 0) {
PrintAndLogEx(WARNING, "iso14443a card select failed");
return select_status;
}
iso14a_card_select_t card;
memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
// reserve memory
uint16_t bytes = block_cnt * MFBLOCK_SIZE;
uint8_t *dump = calloc(bytes, sizeof(uint8_t));
if (dump == NULL) {
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
return PM3_EMALLOC;
}
for (uint16_t i = 0; i < block_cnt; i++) {
if (mfG3GetBlock(i, dump + (i * MFBLOCK_SIZE)) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Can't get magic card block: %u", i);
PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position");
free(dump);
return PM3_ESOFT;
}
PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
}
PrintAndLogEx(NORMAL, "");
mf_print_blocks(block_cnt, dump);
free(dump);
return PM3_SUCCESS;
}
static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"list", CmdHF14AMfList, AlwaysAvailable, "List MIFARE history"},
@ -6059,7 +6156,8 @@ static command_t CommandTable[] = {
{"gen3uid", CmdHf14AGen3UID, IfPm3Iso14443a, "Set UID without changing manufacturer block"},
{"gen3blk", CmdHf14AGen3Block, IfPm3Iso14443a, "Overwrite manufacturer block"},
{"gen3freeze", CmdHf14AGen3Freeze, IfPm3Iso14443a, "Perma lock UID changes. irreversible"},
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("magic gen3 GTU") " -----------------------"},
{"gview", CmdHF14AGen3View, IfPm3Iso14443a, "View card"},
// {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("i") " -----------------------"},
// {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"},
{NULL, NULL, NULL, NULL}

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
//-----------------------------------------------------------------------------
// Iceman, 2014
// Copyright (C) Iceman, 2014
// Copyright (C) 2021 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of

View file

@ -133,7 +133,7 @@ static int topaz_rall(uint8_t *uid, uint8_t *response) {
static int topaz_read_block(uint8_t *uid, uint8_t blockno, uint8_t *block_data) {
uint16_t resp_len = 0;
uint8_t read8_cmd[] = {TOPAZ_READ8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
uint8_t read8_response[11];
uint8_t read8_response[11] = {0};
read8_cmd[1] = blockno;
memcpy(&read8_cmd[10], uid, 4);
@ -571,10 +571,10 @@ int CmdHFTopaz(const char *Cmd) {
int readTopazUid(bool verbose) {
uint8_t atqa[2];
uint8_t rid_response[8];
uint8_t atqa[2] = {0};
uint8_t rid_response[8] = {0};
uint8_t *uid_echo = &rid_response[2];
uint8_t rall_response[124];
uint8_t rall_response[124] = {0};
int status = topaz_select(atqa, sizeof(atqa), rid_response, sizeof(rid_response), verbose);
if (status == PM3_ESOFT) {

View file

@ -92,8 +92,10 @@ static model_t models[] = {
static int CmdHelp(const char *Cmd);
static int picture_bit_depth(const uint8_t *bmp, const size_t bmpsize, const uint8_t model_nr) {
if (bmpsize < sizeof(BMP_HEADER))
if (bmpsize < sizeof(BMP_HEADER)) {
return PM3_ESOFT;
}
BMP_HEADER *pbmpheader = (BMP_HEADER *)bmp;
PrintAndLogEx(DEBUG, "colorsused = %d", pbmpheader->colorsused);
PrintAndLogEx(DEBUG, "pbmpheader->bpp = %d", pbmpheader->bpp);
@ -403,14 +405,14 @@ static int read_bmp_rgb(uint8_t *bmp, const size_t bmpsize, uint8_t model_nr, ui
if ((model_nr == M1in54B) || (model_nr == M2in13B)) {
// for BW+Red screens:
uint8_t *mapBlack = calloc(width * height, sizeof(uint8_t));
uint8_t *mapBlack = calloc((uint32_t)(width * height), sizeof(uint8_t));
if (mapBlack == NULL) {
free(chanR);
free(chanG);
free(chanB);
return PM3_EMALLOC;
}
uint8_t *mapRed = calloc(width * height, sizeof(uint8_t));
uint8_t *mapRed = calloc((uint32_t)(width * height), sizeof(uint8_t));
if (mapRed == NULL) {
free(chanR);
free(chanG);

View file

@ -539,7 +539,7 @@ static int CmdSetMux(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hw setmux",
"Set the ADC mux to a specific value",
"hw setmux --hiraw -> set HIGH RAW"
"hw setmux --hipkd -> set HIGH PEAK\n"
);
void *argtable[] = {
@ -562,6 +562,13 @@ static int CmdSetMux(const char *Cmd) {
return PM3_EINVARG;
}
#ifdef WITH_FPC_USART
if (loraw || hiraw) {
PrintAndLogEx(INFO, "this ADC mux option is unavailable on RDV4 compiled with FPC USART");
return PM3_EINVARG;
}
#endif
uint8_t arg = 0;
if (lopkd)
arg = 0;

View file

@ -686,9 +686,12 @@ int lf_sniff(bool verbose, uint32_t samples) {
int CmdLFSniff(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf sniff",
"Sniff low frequency signal.\n"
"Sniff low frequency signal. You need to configure the LF part on the Proxmark3 device manually.\n"
"Usually a trigger and skip samples is a good thing to set before doing a low frequency sniff.\n"
"\n"
" - use " _YELLOW_("`lf config`") _CYAN_(" to set parameters.\n")
_CYAN_(" - use ") _YELLOW_("`data plot`") _CYAN_(" to look at it"),
_CYAN_(" - use ") _YELLOW_("`data plot`") _CYAN_(" to look at sniff signal.\n")
_CYAN_(" - use ") _YELLOW_("`lf search -1`") _CYAN_(" to see if signal can be automatic decoded\n"),
"lf sniff -v\n"
"lf sniff -s 3000 -@ --> oscilloscope style \n"
);

View file

@ -1319,13 +1319,13 @@ int CmdEM4x05Chk(const char *Cmd) {
CLIParserInit(&ctx, "lf em 4x05 chk",
"This command uses a dictionary attack against EM4205/4305/4469/4569",
"lf em 4x05 chk\n"
"lf em 4x05 chk -e 000022B8 -> remember to use 0x for hex\n"
"lf em 4x05 chk -e 000022B8 -> check password 000022B8\n"
"lf em 4x05 chk -f t55xx_default_pwds -> use T55xx default dictionary"
);
void *argtable[] = {
arg_param_begin,
arg_strx0("f", "file", "<*.dic>", "loads a default keys dictionary file <*.dic>"),
arg_str0("f", "file", "<fn>", "loads a default keys dictionary file <*.dic>"),
arg_str0("e", "em", "<EM4100>", "try the calculated password from some cloners based on EM4100 ID"),
arg_param_end
};
@ -1334,7 +1334,18 @@ int CmdEM4x05Chk(const char *Cmd) {
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
uint64_t card_id = arg_get_u64_hexstr_def(ctx, 2, 0);
uint64_t card_id = 0;
int res = arg_get_u64_hexstr_def_nlen(ctx, 2, 0, &card_id, 5, true);
if (res == 2) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "EM4100 ID must be 5 hex bytes");
return PM3_EINVARG;
}
if (res == 0) {
CLIParserFree(ctx);
return PM3_EINVARG;
}
CLIParserFree(ctx);
if (strlen(filename) == 0) {
@ -1366,7 +1377,7 @@ int CmdEM4x05Chk(const char *Cmd) {
uint32_t keycount = 0;
int res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock, 4, &keycount);
res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock, 4, &keycount);
if (res != PM3_SUCCESS || keycount == 0 || keyBlock == NULL) {
PrintAndLogEx(WARNING, "no keys found in file");
if (keyBlock != NULL)
@ -1418,22 +1429,30 @@ int CmdEM4x05Chk(const char *Cmd) {
int CmdEM4x05Brute(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x05 brute",
"This command tries to bruteforce the password of a EM4205/4305/4469/4569\n",
"This command tries to bruteforce the password of a EM4205/4305/4469/4569\n"
"The loop is running on device side, press Proxmark3 button to abort\n",
"Note: if you get many false positives, change position on the antenna"
"lf em 4x05 brute\n"
"lf em 4x05 brute -n 1 -> stop after first candidate found\n"
"lf em 4x05 brute -s 000022B8 -> remember to use 0x for hex"
"lf em 4x05 brute -n 1 -> stop after first candidate found\n"
"lf em 4x05 brute -s 000022AA -> start at 000022AA"
);
void *argtable[] = {
arg_param_begin,
arg_u64_0("s", "start", "<pwd>", "Start bruteforce enumeration from this password value"),
arg_int0("n", NULL, "<digits>", "Stop after having found n candidates. Default: 0 => infinite"),
arg_str0("s", "start", "<hex>", "Start bruteforce enumeration from this password value"),
arg_u64_0("n", NULL, "<dec>", "Stop after having found n candidates. Default: 0 (infinite)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
uint32_t start_pwd = arg_get_u64_hexstr_def(ctx, 1, 0);
uint32_t n = arg_get_int_def(ctx, 2, 0);
uint32_t start_pwd = 0;
int res = arg_get_u32_hexstr_def(ctx, 1, 0, &start_pwd);
if (res != 1) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "check `start_pwd` parameter");
return PM3_EINVARG;
}
uint32_t n = arg_get_u32_def(ctx, 2, 0);
CLIParserFree(ctx);
PrintAndLogEx(NORMAL, "");
@ -1449,7 +1468,7 @@ int CmdEM4x05Brute(const char *Cmd) {
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X_BF, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_LF_EM4X_BF, &resp, 1000)) {
if (WaitForResponseTimeout(CMD_LF_EM4X_BF, &resp, 1000) == false) {
PrintAndLogEx(WARNING, "(EM4x05 Bruteforce) timeout while waiting for reply.");
return PM3_ETIMEOUT;
}
@ -1857,7 +1876,7 @@ int CmdEM4x05Unlock(const char *Cmd) {
// compute number of bits flipped
PrintAndLogEx(INFO, "Bitflips: %2u events => %s", bitcount32(bitflips), bitstring);
PrintAndLogEx(INFO, "New protection word => " _CYAN_("%08X") "\n", word14b);
PrintAndLogEx(INFO, "Try " _YELLOW_("`lf em 4x05_dump`"));
PrintAndLogEx(INFO, "Try " _YELLOW_("`lf em 4x05 dump`"));
}
if (verbose) {

View file

@ -14,6 +14,7 @@
#include "cmdlfem4x50.h"
#include <ctype.h>
#include "cmdparser.h" // command_t
#include "util_posix.h" // msclock
#include "fileutils.h"
#include "commonutil.h"
#include "pmflash.h"
@ -100,8 +101,6 @@ static void print_info_result(uint8_t *data, bool verbose) {
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
// data section
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, _YELLOW_("EM4x50 data:"));
if (verbose) {
print_result(words, 0, EM4X50_NO_WORDS - 1);
} else {
@ -112,16 +111,16 @@ static void print_info_result(uint8_t *data, bool verbose) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "---- " _CYAN_("Configuration") " ----");
PrintAndLogEx(INFO, "first word read %3i", fwr);
PrintAndLogEx(INFO, "last word read %3i", lwr);
PrintAndLogEx(INFO, "password check %3s", (bpwc) ? _RED_("on") : _GREEN_("off"));
PrintAndLogEx(INFO, "read after write %3s", (braw) ? "on" : "off");
PrintAndLogEx(INFO, "first word read.... " _YELLOW_("%i"), fwr);
PrintAndLogEx(INFO, "last word read..... " _YELLOW_("%i"), lwr);
PrintAndLogEx(INFO, "password check..... %s", (bpwc) ? _RED_("on") : _GREEN_("off"));
PrintAndLogEx(INFO, "read after write... %s", (braw) ? "on" : "off");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--------- " _CYAN_("Protection") " ---------");
PrintAndLogEx(INFO, "first word read protected %3i", fwrp);
PrintAndLogEx(INFO, "last word read protected %3i", lwrp);
PrintAndLogEx(INFO, "first word write inhibited %3i", fwwi);
PrintAndLogEx(INFO, "last word write inhibited %3i", lwwi);
PrintAndLogEx(INFO, "--------- " _CYAN_("Protection") " ------------");
PrintAndLogEx(INFO, "first word read protected.... %i", fwrp);
PrintAndLogEx(INFO, "last word read protected..... %i", lwrp);
PrintAndLogEx(INFO, "first word write inhibited... %i", fwwi);
PrintAndLogEx(INFO, "last word write inhibited.... %i", lwwi);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "zero values may indicate read protection");
PrintAndLogEx(NORMAL, "");
@ -238,7 +237,7 @@ int CmdEM4x50ESave(const char *Cmd) {
void *argtable[] = {
arg_param_begin,
arg_str0("f", "filename", "<fn>", "data filename"),
arg_str0("f", "file", "<fn>", "save filename"),
arg_param_end
};
@ -344,7 +343,7 @@ int CmdEM4x50Login(const char *Cmd) {
return PM3_EINVARG;
}
uint32_t password = BYTES2UINT32(pwd);
uint32_t password = BYTES2UINT32_BE(pwd);
// start
clearCommandBuffer();
@ -364,9 +363,9 @@ int CmdEM4x50Login(const char *Cmd) {
int CmdEM4x50Brute(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 brute",
"Tries to bruteforce the password of a EM4x50.\n"
"Tries to bruteforce the password of a EM4x50 card.\n"
"Function can be stopped by pressing pm3 button.",
"lf em 4x50 brute --first 12330000 --last 12340000 -> tries pwds from 0x12330000 to 0x1234000000\n"
"lf em 4x50 brute --first 12330000 --last 12340000 -> tries pwds from 0x12330000 to 0x1234000000\n"
);
void *argtable[] = {
@ -395,8 +394,8 @@ int CmdEM4x50Brute(const char *Cmd) {
}
em4x50_data_t etd;
etd.password1 = BYTES2UINT32(first);
etd.password2 = BYTES2UINT32(last);
etd.password1 = BYTES2UINT32_BE(first);
etd.password2 = BYTES2UINT32_BE(last);
// 27 passwords/second (empirical value)
const int speed = 27;
@ -408,12 +407,12 @@ int CmdEM4x50Brute(const char *Cmd) {
int dur_m = (dur_s - dur_h * 3600) / 60;
dur_s -= dur_h * 3600 + dur_m * 60;
PrintAndLogEx(INFO, "Trying %i passwords in range [0x%08x, 0x%08x]"
PrintAndLogEx(INFO, "Trying " _YELLOW_("%i") " passwords in range [0x%08x, 0x%08x]"
, no_iter
, etd.password1
, etd.password2
);
PrintAndLogEx(INFO, "Estimated duration: %ih%im%is", dur_h, dur_m, dur_s);
PrintAndLogEx(INFO, "Estimated duration: %ih %im %is", dur_h, dur_m, dur_s);
// start
clearCommandBuffer();
@ -423,9 +422,9 @@ int CmdEM4x50Brute(const char *Cmd) {
// print response
if (resp.status == PM3_SUCCESS)
PrintAndLogEx(SUCCESS, "Password " _GREEN_("found") ": 0x%08x", resp.data.asDwords[0]);
PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%08"PRIX32) " ]", resp.data.asDwords[0]);
else
PrintAndLogEx(FAILED, "Password: " _RED_("not found"));
PrintAndLogEx(WARNING, "brute pwd failed");
return PM3_SUCCESS;
}
@ -435,14 +434,14 @@ int CmdEM4x50Brute(const char *Cmd) {
int CmdEM4x50Chk(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 chk",
"Dictionary attack against EM4x50.",
"Run dictionary key recovery against EM4x50 card.",
"lf em 4x50 chk -> uses T55xx default dictionary\n"
"lf em 4x50 chk -f my.dic"
);
void *argtable[] = {
arg_param_begin,
arg_str0("f", "filename", "<fn>", "dictionary filename"),
arg_str0("f", "file", "<fn>", "dictionary filename"),
arg_param_end
};
@ -463,30 +462,34 @@ int CmdEM4x50Chk(const char *Cmd) {
PrintAndLogEx(INFO, "treating file as T55xx keys");
}
size_t datalen = 0;
uint8_t data[100000] = {0x0};
uint8_t *keys = data;
// load keys
uint8_t *keys = NULL;
uint32_t key_count = 0;
int res = loadFileDICTIONARY_safe(filename, (void **)&keys, 4, &key_count);
if (res != PM3_SUCCESS || key_count == 0) {
free(keys);
return res;
}
int res = loadFileDICTIONARY(filename, data, &datalen, 4, &key_count);
if ((res != PM3_SUCCESS) || (key_count == 0))
return PM3_EFILE;
uint8_t *pkeys = keys;
uint64_t t1 = msclock();
PrintAndLogEx(INFO, "You can cancel this operation by pressing the pm3 button");
int status = PM3_EFAILED;
int keyblock = 2000; // block with 2000 bytes -> 500 keys
// block with 2000 bytes -> 500 keys
uint8_t destfn[32] = "em4x50_chk.bin";
PacketResponseNG resp;
int bytes_remaining = datalen;
int bytes_remaining = key_count * 4;
int status = PM3_EFAILED;
while (bytes_remaining > 0) {
PrintAndLogEx(INPLACE, "Remaining keys: %i ", bytes_remaining / 4);
// upload to flash.
datalen = MIN(bytes_remaining, keyblock);
res = flashmem_spiffs_load((char *)destfn, keys, datalen);
size_t n = MIN(bytes_remaining, 2000);
res = flashmem_spiffs_load((char *)destfn, keys, n);
if (res != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "SPIFFS upload failed");
return res;
@ -500,25 +503,22 @@ int CmdEM4x50Chk(const char *Cmd) {
if ((status == PM3_SUCCESS) || (status == PM3_EOPABORTED))
break;
bytes_remaining -= keyblock;
keys += keyblock;
bytes_remaining -= n;
keys += n;
}
free(pkeys);
PrintAndLogEx(NORMAL, "");
// print response
if (status == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "Key " _GREEN_("found: %02x %02x %02x %02x"),
resp.data.asBytes[3],
resp.data.asBytes[2],
resp.data.asBytes[1],
resp.data.asBytes[0]
);
uint32_t pwd = BYTES2UINT32(resp.data.asBytes);
PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%08"PRIX32) " ]", pwd);
} else {
PrintAndLogEx(FAILED, "No key found");
}
PrintAndLogEx(INFO, "Done");
t1 = msclock() - t1;
PrintAndLogEx(SUCCESS, "\ntime in check pwd " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0);
return PM3_SUCCESS;
}
@ -622,7 +622,7 @@ int CmdEM4x50Read(const char *Cmd) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
return PM3_EINVARG;
} else {
etd.password1 = BYTES2UINT32(pwd);
etd.password1 = BYTES2UINT32_BE(pwd);
etd.pwd_given = true;
}
}
@ -637,8 +637,8 @@ int CmdEM4x50Info(const char *Cmd) {
CLIParserInit(&ctx, "lf em 4x50 info",
"Tag information EM4x50.",
"lf em 4x50 info\n"
"lf em 4x50 info -v -> show data section\n"
"lf em 4x50 info -p 12345678 -> uses pwd 0x12345678\n"
"lf em 4x50 info -v -> show data section\n"
"lf em 4x50 info -p 12345678 -> uses pwd 0x12345678\n"
);
void *argtable[] = {
@ -661,7 +661,7 @@ int CmdEM4x50Info(const char *Cmd) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
return PM3_EINVARG;
} else {
etd.password1 = BYTES2UINT32(pwd);
etd.password1 = BYTES2UINT32_BE(pwd);
etd.pwd_given = true;
}
}
@ -750,7 +750,7 @@ int CmdEM4x50Dump(const char *Cmd) {
void *argtable[] = {
arg_param_begin,
arg_str0("f", "filename", "<fn>", "dump filename (bin/eml/json)"),
arg_str0("f", "file", "<fn>", "dump filename (bin/eml/json)"),
arg_str0("p", "pwd", "<hex>", "password, 4 hex bytes, lsb"),
arg_param_end
};
@ -773,7 +773,7 @@ int CmdEM4x50Dump(const char *Cmd) {
CLIParserFree(ctx);
return PM3_EINVARG;
} else {
etd.password1 = BYTES2UINT32(pwd);
etd.password1 = BYTES2UINT32_BE(pwd);
etd.pwd_given = true;
}
}
@ -865,14 +865,14 @@ int CmdEM4x50Write(const char *Cmd) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
return PM3_EINVARG;
} else {
etd.password1 = BYTES2UINT32(pwd);
etd.password1 = BYTES2UINT32_BE(pwd);
etd.pwd_given = true;
}
}
etd.addresses = (addr << 8) | addr;
etd.addr_given = true;
etd.word = BYTES2UINT32(word);
etd.word = BYTES2UINT32_BE(word);
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
@ -906,9 +906,9 @@ int CmdEM4x50Write(const char *Cmd) {
// envokes changing the password of EM4x50 tag
int CmdEM4x50WritePwd(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 writepwd",
CLIParserInit(&ctx, "lf em 4x50 wrpwd",
"Writes EM4x50 password.",
"lf em 4x50 writepwd -p 4f22e7ff -n 12345678"
"lf em 4x50 wrpwd -p 4f22e7ff -n 12345678"
);
void *argtable[] = {
@ -934,14 +934,14 @@ int CmdEM4x50WritePwd(const char *Cmd) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
return PM3_EINVARG;
} else {
etd.password1 = BYTES2UINT32(pwd);
etd.password1 = BYTES2UINT32_BE(pwd);
}
if (npwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", npwd_len);
return PM3_EINVARG;
} else {
etd.password2 = BYTES2UINT32(npwd);
etd.password2 = BYTES2UINT32_BE(npwd);
}
PacketResponseNG resp;
@ -997,7 +997,7 @@ int CmdEM4x50Wipe(const char *Cmd) {
em4x50_data_t etd = {.pwd_given = false, .word = 0x0, .password2 = 0x0};
etd.password1 = BYTES2UINT32(pwd);
etd.password1 = BYTES2UINT32_BE(pwd);
etd.pwd_given = true;
// clear password
@ -1061,7 +1061,7 @@ int CmdEM4x50Restore(const char *Cmd) {
void *argtable[] = {
arg_param_begin,
arg_str0("u", "uid", "<hex>", "uid, 4 hex bytes, msb"),
arg_str0("f", "filename", "<fn>", "dump filename (bin/eml/json)"),
arg_str0("f", "file", "<fn>", "dump filename (bin/eml/json)"),
arg_str0("p", "pwd", "<hex>", "password, 4 hex bytes, lsb"),
arg_param_end
};
@ -1093,7 +1093,7 @@ int CmdEM4x50Restore(const char *Cmd) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
return PM3_EINVARG;
} else {
etd.password1 = BYTES2UINT32(pwd);
etd.password1 = BYTES2UINT32_BE(pwd);
etd.pwd_given = true;
// if password is available protection and control word can be restored
startblock = EM4X50_PROTECTION;
@ -1120,7 +1120,7 @@ int CmdEM4x50Restore(const char *Cmd) {
PrintAndLogEx(INPLACE, "Restoring block %i", i);
etd.addresses = i << 8 | i;
etd.word = reflect32(BYTES2UINT32((data + 4 * i)));
etd.word = reflect32(BYTES2UINT32_BE((data + 4 * i)));
PacketResponseNG resp;
clearCommandBuffer();
@ -1170,7 +1170,7 @@ int CmdEM4x50Sim(const char *Cmd) {
PrintAndLogEx(FAILED, "password length must be 4 bytes, got %d", pwd_len);
return PM3_EINVARG;
} else {
password = BYTES2UINT32(pwd);
password = BYTES2UINT32_BE(pwd);
}
}
@ -1208,21 +1208,23 @@ int CmdEM4x50Sim(const char *Cmd) {
static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"brute", CmdEM4x50Brute, IfPm3EM4x50, "guess password of EM4x50"},
{"chk", CmdEM4x50Chk, IfPm3EM4x50, "check passwords from dictionary"},
{"dump", CmdEM4x50Dump, IfPm3EM4x50, "dump EM4x50 tag"},
{"info", CmdEM4x50Info, IfPm3EM4x50, "tag information EM4x50"},
{"login", CmdEM4x50Login, IfPm3EM4x50, "login into EM4x50"},
{"rdbl", CmdEM4x50Read, IfPm3EM4x50, "read word data from EM4x50"},
{"wrbl", CmdEM4x50Write, IfPm3EM4x50, "write word data to EM4x50"},
{"writepwd", CmdEM4x50WritePwd, IfPm3EM4x50, "change password of EM4x50"},
{"wipe", CmdEM4x50Wipe, IfPm3EM4x50, "wipe EM4x50 tag"},
{"reader", CmdEM4x50Reader, IfPm3EM4x50, "show standard read mode data of EM4x50"},
{"restore", CmdEM4x50Restore, IfPm3EM4x50, "restore EM4x50 dump to tag"},
{"sim", CmdEM4x50Sim, IfPm3EM4x50, "simulate EM4x50 tag"},
{"eload", CmdEM4x50ELoad, IfPm3EM4x50, "upload dump of EM4x50 to emulator memory"},
{"esave", CmdEM4x50ESave, IfPm3EM4x50, "save emulator memory to file"},
{"eview", CmdEM4x50EView, IfPm3EM4x50, "view EM4x50 content in emulator memory"},
{"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("operations") " ---------------------"},
{"brute", CmdEM4x50Brute, IfPm3EM4x50, "Simple bruteforce attack to find password"},
{"chk", CmdEM4x50Chk, IfPm3EM4x50, "Check passwords from dictionary"},
{"dump", CmdEM4x50Dump, IfPm3EM4x50, "Dump EM4x50 tag"},
{"info", CmdEM4x50Info, IfPm3EM4x50, "Tag information"},
{"login", CmdEM4x50Login, IfPm3EM4x50, "Login into EM4x50 tag"},
{"rdbl", CmdEM4x50Read, IfPm3EM4x50, "Read EM4x50 word data"},
{"reader", CmdEM4x50Reader, IfPm3EM4x50, "Show standard read mode data"},
{"restore", CmdEM4x50Restore, IfPm3EM4x50, "Restore EM4x50 dump to tag"},
{"wrbl", CmdEM4x50Write, IfPm3EM4x50, "Write EM4x50 word data"},
{"wrpwd", CmdEM4x50WritePwd, IfPm3EM4x50, "Change EM4x50 password"},
{"wipe", CmdEM4x50Wipe, IfPm3EM4x50, "Wipe EM4x50 tag"},
{"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("simulation") " ---------------------"},
{"eload", CmdEM4x50ELoad, IfPm3EM4x50, "Upload EM4x50 dump to emulator memory"},
{"esave", CmdEM4x50ESave, IfPm3EM4x50, "Save emulator memory to file"},
{"eview", CmdEM4x50EView, IfPm3EM4x50, "View EM4x50 content in emulator memory"},
{"sim", CmdEM4x50Sim, IfPm3EM4x50, "Simulate EM4x50 tag"},
{NULL, NULL, NULL, NULL}
};

View file

@ -210,14 +210,15 @@ static int CmdLFHitagEload(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag eload",
"Loads hitag tag dump into emulator memory on device",
"lf hitag eload -f lf-hitag-11223344-dump.bin\n");
"lf hitag eload -2 -f lf-hitag-11223344-dump.bin\n");
void *argtable[] = {
arg_param_begin,
arg_str1("f", "file", "<filename>", "filename of dump"),
arg_lit0("1", NULL, "simulate Hitag1"),
arg_lit0("2", NULL, "simulate Hitag2"),
arg_lit0("s", NULL, "simulate HitagS"),
arg_lit0("1", NULL, "Card type Hitag1"),
arg_lit0("2", NULL, "Card type Hitag2"),
arg_lit0("s", NULL, "Card type HitagS"),
arg_lit0("m", NULL, "Card type HitagM"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -229,9 +230,10 @@ static int CmdLFHitagEload(const char *Cmd) {
bool use_ht1 = arg_get_lit(ctx, 2);
bool use_ht2 = arg_get_lit(ctx, 3);
bool use_hts = arg_get_lit(ctx, 4);
bool use_htm = arg_get_lit(ctx, 5);
CLIParserFree(ctx);
uint8_t n = (use_ht1 + use_ht2 + use_hts);
uint8_t n = (use_ht1 + use_ht2 + use_hts + use_htm);
if (n != 1) {
PrintAndLogEx(ERR, "error, only specify one Hitag type");
return PM3_EINVARG;
@ -274,15 +276,24 @@ static int CmdLFHitagEload(const char *Cmd) {
// check dump len..
if (dumplen == 48 || dumplen == 4 * 64) {
struct {
uint16_t len;
uint8_t *data;
} PACKED payload;
payload.len = dumplen;
memcpy(payload.data, dump, dumplen);
lf_hitag_t *payload = calloc(1, sizeof(lf_hitag_t) + dumplen);
if (use_ht1)
payload->type = 1;
if (use_ht2)
payload->type = 2;
if (use_hts)
payload->type = 3;
if (use_htm)
payload->type = 4;
payload->len = dumplen;
memcpy(payload->data, dump, dumplen);
clearCommandBuffer();
SendCommandNG(CMD_LF_HITAG_ELOAD, (uint8_t *)&payload, 2 + dumplen);
SendCommandNG(CMD_LF_HITAG_ELOAD, (uint8_t *)payload, 3 + dumplen);
free(payload);
} else {
PrintAndLogEx(ERR, "error, wrong dump file size. got %zu", dumplen);
}
@ -559,8 +570,8 @@ static int CmdLFHitagReader(const char *Cmd) {
}
// sanity checks
if (keylen != 0 && keylen != 4) {
PrintAndLogEx(WARNING, "Wrong KEY len expected 0 or 4, got %d", keylen);
if (keylen != 0 && keylen != 4 && keylen != 6) {
PrintAndLogEx(WARNING, "Wrong KEY len expected 0, 4 or 6, got %d", keylen);
return PM3_EINVARG;
}
@ -646,7 +657,7 @@ static int CmdLFHitagCheckChallenges(const char *Cmd) {
void *argtable[] = {
arg_param_begin,
arg_str0("f", "filename", "<fn w/o ext>", "filename to load from"),
arg_str0("f", "file", "<fn>", "filename to load ( w/o ext )"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);

View file

@ -154,7 +154,7 @@ int demodKeri(bool verbose) {
raw1 = bytebits_to_byte(DemodBuffer, 32);
raw2 = bytebits_to_byte(DemodBuffer + 32, 32);
CmdPrintDemodBuff("x");
CmdPrintDemodBuff("-x");
}
//get internal id

View file

@ -24,6 +24,8 @@
#include "cmdlft55xx.h" // clone..
#include "cmdlfem4x05.h" //
#include "cliparser.h"
#include "util.h"
typedef enum {
SCRAMBLE,
@ -52,7 +54,7 @@ static uint8_t nexwatch_parity(uint8_t hexid[5]) {
}
/// NETWATCH checksum
/// @param magic = 0xBE Quadrakey, 0x88 Nexkey
/// @param magic = 0xBE Quadrakey, 0x88 Nexkey, 0x86 Honeywell
/// @param id = descrambled id (printed card number)
/// @param parity = the parity based upon the scrambled raw id.
static uint8_t nexwatch_checksum(uint8_t magic, uint32_t id, uint8_t parity) {
@ -108,6 +110,20 @@ static int nexwatch_scamble(NexWatchScramble_t action, uint32_t *id, uint32_t *s
return PM3_SUCCESS;
}
static int nexwatch_magic_bruteforce(uint32_t cn, uint8_t calc_parity, uint8_t chk) {
for (uint8_t magic = 0; magic < 255; magic++) {
uint8_t temp_checksum;
temp_checksum = nexwatch_checksum(magic, cn, calc_parity);
if (temp_checksum == chk) {
PrintAndLogEx(SUCCESS, " Magic number : " _GREEN_("0x%X"), magic);
return PM3_SUCCESS;
}
}
PrintAndLogEx(DEBUG, "DEBUG: Error - Magic number not found");
return PM3_ESOFT;
}
int demodNexWatch(bool verbose) {
(void) verbose; // unused so far
if (PSKDemod(0, 0, 100, false) != PM3_SUCCESS) {
@ -204,10 +220,13 @@ int demodNexWatch(bool verbose) {
if (m_idx < ARRAYLEN(items)) {
PrintAndLogEx(SUCCESS, " fingerprint : " _GREEN_("%s"), items[m_idx].desc);
} else {
nexwatch_magic_bruteforce(cn, calc_parity, chk);
}
PrintAndLogEx(SUCCESS, " 88bit id : " _YELLOW_("%"PRIu32) " (" _YELLOW_("0x%08"PRIx32)")", cn, cn);
PrintAndLogEx(SUCCESS, " mode : %x", mode);
if (parity == calc_parity) {
PrintAndLogEx(DEBUG, " parity : %s (0x%X)", _GREEN_("ok"), parity);
} else {
@ -285,6 +304,8 @@ static int CmdNexWatchClone(const char *Cmd) {
arg_lit0(NULL, "hc", "Honeywell credential"),
arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"),
arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"),
arg_str0(NULL, "magic", "<hex>", "optional - magic hex data. 1 byte"),
arg_lit0(NULL, "psk2", "optional - specify writing a tag in psk2 modulation"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -298,9 +319,16 @@ static int CmdNexWatchClone(const char *Cmd) {
uint32_t mode = arg_get_u32_def(ctx, 3, -1);
bool use_nexkey = arg_get_lit(ctx, 4);
bool use_quadrakey = arg_get_lit(ctx, 5);
bool use_unk = arg_get_lit(ctx, 6);
bool use_honeywell = arg_get_lit(ctx, 6);
bool q5 = arg_get_lit(ctx, 7);
bool em = arg_get_lit(ctx, 8);
uint8_t magic_arg[2];
int mlen = 0;
CLIGetHexWithReturn(ctx, 9, magic_arg, &mlen);
bool use_psk2 = arg_get_lit(ctx, 10);
CLIParserFree(ctx);
if (use_nexkey && use_quadrakey) {
@ -316,6 +344,13 @@ static int CmdNexWatchClone(const char *Cmd) {
// 56000000 00213C9F 8F150C00
bool use_raw = (raw_len != 0);
bool use_custom_magic = (mlen != 0);
if (mlen > 1) {
PrintAndLogEx(FAILED, "Can't specify a magic number bigger than one byte");
return PM3_EINVARG;
}
if (use_raw && cn != -1) {
PrintAndLogEx(FAILED, "Can't specify both Raw and Card id at the same time");
return PM3_EINVARG;
@ -336,20 +371,27 @@ static int CmdNexWatchClone(const char *Cmd) {
}
uint8_t magic = 0xBE;
if (use_nexkey)
magic = 0x88;
if (use_custom_magic) {
magic = magic_arg[0];
} else {
if (use_nexkey)
magic = 0x88;
if (use_quadrakey)
magic = 0xBE;
if (use_quadrakey)
magic = 0xBE;
if (use_unk)
magic = 0x86;
if (use_honeywell)
magic = 0x86;
}
PrintAndLogEx(INFO, "Magic byte selected... " _YELLOW_("0x%X"), magic);
uint32_t blocks[4];
//Nexwatch - compat mode, PSK, data rate 40, 3 data blocks
blocks[0] = T55x7_MODULATION_PSK1 | T55x7_BITRATE_RF_32 | 3 << T55x7_MAXBLOCK_SHIFT;
char cardtype[16] = {"T55x7"};
// Q5
if (q5) {
blocks[0] = T5555_FIXED | T5555_MODULATION_MANCHESTER | T5555_SET_BITRATE(64) | T5555_ST_TERMINATOR | 3 << T5555_MAXBLOCK_SHIFT;
@ -368,6 +410,21 @@ static int CmdNexWatchClone(const char *Cmd) {
raw[10] |= nexwatch_checksum(magic, cn, parity);
}
if (use_psk2) {
blocks[0] = 0x00042080;
uint8_t *res_shifted = calloc(96, sizeof(uint8_t));
uint8_t *res = calloc(96, sizeof(uint8_t));
bytes_to_bytebits(raw, 12, res);
psk1TOpsk2(res, 96);
memcpy(res_shifted, &res[1], 95 * sizeof(uint8_t));
free(res);
bits_to_array(res_shifted, 96, raw);
free(res_shifted);
}
for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) {
blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t));
}
@ -408,6 +465,8 @@ static int CmdNexWatchSim(const char *Cmd) {
arg_lit0(NULL, "nc", "Nexkey credential"),
arg_lit0(NULL, "qc", "Quadrakey credential"),
arg_lit0(NULL, "hc", "Honeywell credential"),
arg_str0(NULL, "magic", "<hex>", "optional - magic hex data. 1 byte"),
arg_lit0(NULL, "psk2", "optional - specify writing a tag in psk2 modulation"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);

View file

@ -2210,7 +2210,7 @@ static int CmdT55xxDump(const char *Cmd) {
// 1 (help) + 3 (two user specified params) + (5 T55XX_DLMODE_SINGLE)
void *argtable[4 + 5] = {
arg_param_begin,
arg_str0("f", "filename", "<fn>", "filename (default is generated on blk 0)"),
arg_str0("f", "file", "<fn>", "filename (default is generated on blk 0)"),
arg_lit0("o", "override", "override, force pwd read despite danger to card"),
arg_str0("p", "pwd", "<hex>", "password (4 hex bytes)"),
};
@ -3301,7 +3301,7 @@ static int CmdT55xxBruteForce(const char *Cmd) {
uint32_t curr = 0;
uint8_t found = 0; // > 0 if found xx1 xx downlink needed, 1 found
if (start_password >= end_password) {
if (start_password > end_password) {
PrintAndLogEx(FAILED, "Error, start larger then end password");
return PM3_EINVARG;
}
@ -3331,7 +3331,10 @@ static int CmdT55xxBruteForce(const char *Cmd) {
PrintAndLogEx(NORMAL, "");
if (found) {
PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") " ]", curr - 1);
if (curr != end_password) {
PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") " ]", curr - 1);
} else
PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") " ]", curr);
T55xx_Print_DownlinkMode((found >> 1) & 3);
} else
PrintAndLogEx(WARNING, "Bruteforce failed, last tried: [ " _YELLOW_("%08X") " ]", curr);

View file

@ -17,7 +17,7 @@
#include <string.h>
#include <ctype.h>
#include <time.h> // MingW
#include <time.h> // MingW
#include <stdlib.h> // calloc
#include "comms.h"

View file

@ -15,6 +15,7 @@
#include "ui.h"
#include "comms.h"
#include "util_posix.h" // msleep
bool AlwaysAvailable(void) {
return true;
@ -193,6 +194,11 @@ void CmdsHelp(const command_t Commands[]) {
}
int CmdsParse(const command_t Commands[], const char *Cmd) {
if (session.client_exe_delay != 0) {
msleep(session.client_exe_delay);
}
// Help dump children
if (strcmp(Cmd, "XX_internal_command_dump_XX") == 0) {
dumpCommandsRecursive(Commands, 0, false);

View file

@ -498,7 +498,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
static int download_trace(void) {
if (IfPm3Present() == false) {
PrintAndLogEx(FAILED, "You requested a trace upload in offline mode, consider using parameter '1' for working from Tracebuffer");
PrintAndLogEx(FAILED, "You requested a trace upload in offline mode, consider using parameter '-1' for working from Tracebuffer");
return PM3_EINVARG;
}
@ -753,7 +753,7 @@ int CmdTraceList(const char *Cmd) {
if (use_buffer == false) {
download_trace();
} else if (g_traceLen == 0) {
PrintAndLogEx(FAILED, "You requested a trace list in offline mode but there is no trace, consider using 'trace load' or removing parameter '1'");
PrintAndLogEx(FAILED, "You requested a trace list in offline mode but there is no trace, consider using 'trace load' or removing parameter '-1'");
return PM3_EINVARG;
}

View file

@ -1,4 +1,4 @@
//-----------------------------------------------------------------------------
// //-----------------------------------------------------------------------------
// Copyright (C) 2018 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
@ -197,14 +197,14 @@ static unsigned long asn1_value_integer(const struct tlv *tlv, unsigned start, u
i = start;
for (; i < end - 1; i += 2) {
ret *= 10;
ret = ret << 4; // was: ret*=10
ret += tlv->value[i / 2] >> 4;
ret *= 10;
ret = ret << 4; // was: ret*=10
ret += tlv->value[i / 2] & 0xf;
}
if (end & 1) {
ret *= 10;
ret = ret << 4; // was: ret*=10
ret += tlv->value[end / 2] >> 4;
}
@ -227,10 +227,11 @@ static void asn1_tag_dump_integer(const struct tlv *tlv, const struct asn1_tag *
for (size_t i = 0; i < tlv->len; i++) {
val = (val << 8) + tlv->value[i];
}
PrintAndLogEx(NORMAL, " value4b: %d", val);
PrintAndLogEx(NORMAL, " value: %d (0x%08X)", val, val);
return;
}
PrintAndLogEx(NORMAL, " value: %lu", asn1_value_integer(tlv, 0, tlv->len * 2));
uint32_t val = asn1_value_integer(tlv, 0, tlv->len * 2);
PrintAndLogEx(NORMAL, " value: %" PRIu32 " (0x%X)", val, val);
}
static char *asn1_oid_description(const char *oid, bool with_group_desc) {
@ -326,7 +327,7 @@ bool asn1_tag_dump(const struct tlv *tlv, int level, bool *candump) {
*/
PrintAndLogEx(INFO,
"%*s-- %2x [%02zx] '"_YELLOW_("%s") "'" NOLF
"%*s-- %02X [%02ZX] '"_YELLOW_("%s") "'" NOLF
, (level * 4)
, " "
, tlv->tag

View file

@ -15,6 +15,7 @@
#include <unistd.h>
#include <string.h>
#include <mbedtls/asn1.h>
#include <mbedtls/des.h>
#include <mbedtls/aes.h>
#include <mbedtls/cmac.h>
#include <mbedtls/pk.h>
@ -27,6 +28,43 @@
#include <mbedtls/error.h>
#include "util.h"
#include "ui.h"
void des_encrypt(void *out, const void *in, const void *key) {
mbedtls_des_context ctx;
mbedtls_des_setkey_enc(&ctx, key);
mbedtls_des_crypt_ecb(&ctx, in, out);
mbedtls_des_free(&ctx);
}
void des_decrypt(void *out, const void *in, const void *key) {
mbedtls_des_context ctx;
mbedtls_des_setkey_dec(&ctx, key);
mbedtls_des_crypt_ecb(&ctx, in, out);
mbedtls_des_free(&ctx);
}
void des_encrypt_ecb(void *out, const void *in, const int length, const void *key) {
for (int i = 0; i < length; i += 8)
des_encrypt((uint8_t *)out + i, (uint8_t *)in + i, key);
}
void des_decrypt_ecb(void *out, const void *in, const int length, const void *key) {
for (int i = 0; i < length; i += 8)
des_decrypt((uint8_t *)out + i, (uint8_t *)in + i, key);
}
void des_encrypt_cbc(void *out, const void *in, const int length, const void *key, uint8_t *iv) {
mbedtls_des_context ctx;
mbedtls_des_setkey_enc(&ctx, key);
mbedtls_des_crypt_cbc(&ctx, MBEDTLS_DES_ENCRYPT, length, iv, in, out);
}
void des_decrypt_cbc(void *out, const void *in, const int length, const void *key, uint8_t *iv) {
mbedtls_des_context ctx;
mbedtls_des_setkey_dec(&ctx, key);
mbedtls_des_crypt_cbc(&ctx, MBEDTLS_DES_DECRYPT, length, iv, in, out);
}
// NIST Special Publication 800-38A — Recommendation for block cipher modes of operation: methods and techniques, 2001.
int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length) {
uint8_t iiv[16] = {0};
@ -498,3 +536,26 @@ exit:
PrintAndLogEx(NORMAL, _RED_("failed\n"));
return res;
}
void bin_xor(uint8_t *d1, uint8_t *d2, size_t len) {
for (size_t i = 0; i < len; i++)
d1[i] = d1[i] ^ d2[i];
}
void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen) {
*ddatalen = sdatalen + 1;
*ddatalen += blocklen - *ddatalen % blocklen;
memset(ddata, 0, *ddatalen);
memcpy(ddata, sdata, sdatalen);
ddata[sdatalen] = ISO9797_M2_PAD_BYTE;
}
size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen) {
for (int i = datalen; i > 0; i--) {
if (data[i - 1] == 0x80)
return i - 1;
if (data[i - 1] != 0x00)
return 0;
}
return 0;
}

View file

@ -16,6 +16,13 @@
#include <stddef.h>
#include <mbedtls/pk.h>
void des_encrypt(void *out, const void *in, const void *key);
void des_decrypt(void *out, const void *in, const void *key);
void des_encrypt_ecb(void *out, const void *in, const int length, const void *key);
void des_decrypt_ecb(void *out, const void *in, const int length, const void *key);
void des_encrypt_cbc(void *out, const void *in, const int length, const void *key, uint8_t *iv);
void des_decrypt_cbc(void *out, const void *in, const int length, const void *key, uint8_t *iv);
int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length);
int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length);
int aes_cmac(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length);
@ -35,4 +42,11 @@ char *ecdsa_get_error(int ret);
int ecdsa_nist_test(bool verbose);
void bin_xor(uint8_t *d1, uint8_t *d2, size_t len);
#define ISO9797_M2_PAD_BYTE 0x80
void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen);
size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen);
#endif /* libpcrypto.h */

View file

@ -758,6 +758,7 @@ int trDDA(Iso7816CommandChannel channel, bool decodeTLV, struct tlvdb *tlv) {
tlvdb_free(atc_db);
return 9;
}
tlvdb_free(atc_db);
} else {
struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv);

View file

@ -1292,12 +1292,13 @@ out:
}
int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, uint8_t keylen, uint32_t *keycnt) {
// t5577 == 4bytes
// t5577 == 4 bytes
// mifare == 6 bytes
// mf plus == 16 bytes
// mf desfire == 3des3k 24 bytes
// iclass == 8 bytes
// default to 6 bytes.
if (keylen != 4 && keylen != 6 && keylen != 8 && keylen != 16) {
if (keylen != 4 && keylen != 6 && keylen != 8 && keylen != 16 && keylen != 24) {
keylen = 6;
}
@ -1404,9 +1405,10 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key
// t5577 == 4bytes
// mifare == 6 bytes
// mf plus == 16 bytes
// mf desfire == 3des3k 24 bytes
// iclass == 8 bytes
// default to 6 bytes.
if (keylen != 4 && keylen != 6 && keylen != 8 && keylen != 16) {
if (keylen != 4 && keylen != 6 && keylen != 8 && keylen != 16 && keylen != 24) {
keylen = 6;
}

View file

@ -123,7 +123,8 @@ static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs,
if (paddr < FLASH_START || (paddr + filesz) > flash_end) {
PrintAndLogEx(ERR, "Error: PHDR is not contained in Flash");
if ((paddr + filesz) > flash_end) {
PrintAndLogEx(ERR, "Firmware probably too big for your device");
PrintAndLogEx(ERR, "Firmware is probably too big for your device");
PrintAndLogEx(ERR, "See README.md for information on compiling for platforms with 256KB of flash memory");
}
return PM3_EFILE;
}

View file

@ -21,6 +21,18 @@ extern "C" void ShowGraphWindow(void) {
extern "C" void HideGraphWindow(void) {}
extern "C" void RepaintGraphWindow(void) {}
extern "C" void ShowPictureWindow(char *fn) {
static int warned = 0;
if (!warned) {
printf("No GUI in this build!\n");
warned = 1;
}
}
extern "C" void HidePictureWindow(void) {}
extern "C" void RepaintPictureWindow(void) {}
extern "C" void MainGraphics() {}
extern "C" void InitGraphics(int argc, char **argv) {}
extern "C" void ExitGraphics(void) {}

View file

@ -37,7 +37,11 @@ static isodep_state_t isodep_state = ISODEP_INACTIVE;
void SetISODEPState(isodep_state_t state) {
isodep_state = state;
if (APDULogging) {
PrintAndLogEx(SUCCESS, ">>>> ISODEP -> %s%s%s", isodep_state == ISODEP_INACTIVE ? "inactive" : "", isodep_state == ISODEP_NFCA ? "NFC-A" : "", isodep_state == ISODEP_NFCB ? "NFC-B" : "");
PrintAndLogEx(SUCCESS, "Setting ISODEP -> %s%s%s"
, isodep_state == ISODEP_INACTIVE ? "inactive" : ""
, isodep_state == ISODEP_NFCA ? _GREEN_("NFC-A") : ""
, isodep_state == ISODEP_NFCB ? _GREEN_("NFC-B") : ""
);
}
}
@ -51,46 +55,50 @@ int Iso7816Connect(Iso7816CommandChannel channel) {
}
// Try to 14a
// select with no disconnect and set frameLength
int selres = SelectCard14443A_4(false, NULL);
if (selres == PM3_SUCCESS) {
int res = SelectCard14443A_4(false, false, NULL);
if (res == PM3_SUCCESS) {
SetISODEPState(ISODEP_NFCA);
return PM3_SUCCESS;
}
PrintAndLogEx(DEBUG, "No 14a tag spotted, trying 14b");
// If not 14a, try to 14b
selres = select_card_14443b_4(false, NULL);
if (selres == PM3_SUCCESS) {
res = select_card_14443b_4(false, NULL);
if (res == PM3_SUCCESS) {
SetISODEPState(ISODEP_NFCB);
return PM3_SUCCESS;
}
PrintAndLogEx(DEBUG, "No 14b tag spotted, failed to find any tag.");
return selres;
return res;
}
int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool includeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
uint8_t data[APDU_RES_LEN] = {0};
int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool activate_field, bool leave_field_on,
sAPDU apdu, bool include_le, uint16_t le, uint8_t *result,
size_t max_result_len, size_t *result_len, uint16_t *sw) {
*ResultLen = 0;
if (sw) *sw = 0;
uint16_t isw = 0;
int res = 0;
*result_len = 0;
if (sw) {
*sw = 0;
}
if (ActivateField) {
if (activate_field) {
DropFieldEx(channel);
msleep(50);
}
// COMPUTE APDU
int datalen = 0;
if (includeLe) {
if (Le == 0) {
Le = 0x100;
if (include_le) {
if (le == 0) {
le = 0x100;
}
} else {
Le = 0;
le = 0;
}
if (APDUEncodeS(&apdu, false, Le, data, &datalen)) {
uint8_t data[APDU_RES_LEN] = {0};
if (APDUEncodeS(&apdu, false, le, data, &datalen)) {
PrintAndLogEx(ERR, "APDU encoding error.");
return 201;
}
@ -98,59 +106,68 @@ int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool ActivateField, bool Le
if (APDULogging)
PrintAndLogEx(SUCCESS, ">>>> %s", sprint_hex(data, datalen));
int res = 0;
switch (channel) {
case CC_CONTACTLESS:
case CC_CONTACTLESS: {
switch (GetISODEPState()) {
case ISODEP_NFCA:
res = ExchangeAPDU14a(data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
res = ExchangeAPDU14a(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len);
break;
case ISODEP_NFCB:
res = exchange_14b_apdu(data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen, 4000);
res = exchange_14b_apdu(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len, 4000);
break;
case ISODEP_INACTIVE:
if (! ActivateField) {
if (activate_field == false) {
PrintAndLogEx(FAILED, "Field currently inactive, cannot send an APDU");
return PM3_EIO;
}
res = ExchangeAPDU14a(data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
res = ExchangeAPDU14a(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len);
if (res != PM3_SUCCESS) {
res = exchange_14b_apdu(data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen, 4000);
res = exchange_14b_apdu(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len, 4000);
}
break;
}
if (res != PM3_SUCCESS) {
return res;
}
break;
case CC_CONTACT:
}
case CC_CONTACT: {
res = 1;
if (IfPm3Smartcard())
res = ExchangeAPDUSC(false, data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
if (IfPm3Smartcard()) {
res = ExchangeAPDUSC(false, data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len);
}
if (res) {
return res;
}
break;
}
}
if (APDULogging)
PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(Result, *ResultLen));
PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(result, *result_len));
if (*ResultLen < 2) {
if (*result_len < 2) {
return 200;
}
*ResultLen -= 2;
isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1];
if (sw)
*result_len -= 2;
uint16_t isw = (result[*result_len] * 0x0100) + result[*result_len + 1];
if (sw) {
*sw = isw;
}
if (isw != 0x9000) {
if (APDULogging) {
if (*sw >> 8 == 0x61) {
PrintAndLogEx(ERR, "APDU chaining len:%02x -->", *sw & 0xff);
PrintAndLogEx(ERR, "APDU chaining len %02x", *sw & 0xFF);
} else {
PrintAndLogEx(ERR, "APDU(%02x%02x) ERROR: [%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff));
PrintAndLogEx(ERR, "APDU(%02x%02x) ERROR: [%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xFF));
return 5;
}
}
@ -158,10 +175,32 @@ int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool ActivateField, bool Le
return PM3_SUCCESS;
}
int Iso7816Exchange(Iso7816CommandChannel channel, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return Iso7816ExchangeEx(channel, false, LeaveFieldON, apdu, false, 0, Result, MaxResultLen, ResultLen, sw);
int Iso7816Exchange(Iso7816CommandChannel channel, bool leave_field_on, sAPDU apdu, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
return Iso7816ExchangeEx(channel
, false
, leave_field_on
, apdu
, false
, 0
, result
, max_result_len
, result_len
, sw
);
}
int Iso7816Select(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return Iso7816ExchangeEx(channel, ActivateField, LeaveFieldON, (sAPDU) {0x00, 0xa4, 0x04, 0x00, AIDLen, AID}, (channel == CC_CONTACTLESS), 0, Result, MaxResultLen, ResultLen, sw);
int Iso7816Select(Iso7816CommandChannel channel, bool activate_field, bool leave_field_on, uint8_t *aid, size_t aid_len,
uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
return Iso7816ExchangeEx(channel
, activate_field
, leave_field_on
, (sAPDU) {0x00, 0xa4, 0x04, 0x00, aid_len, aid}
, (channel == CC_CONTACTLESS)
, 0
, result
, max_result_len
, result_len
, sw
);
}

View file

@ -12,9 +12,7 @@
#define ISO7816CORE_H__
#include "common.h"
#include <inttypes.h>
#include "apduinfo.h"
#define APDU_RES_LEN 260
@ -41,9 +39,14 @@ isodep_state_t GetISODEPState(void);
int Iso7816Connect(Iso7816CommandChannel channel);
// exchange
int Iso7816Exchange(Iso7816CommandChannel channel, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int Iso7816Exchange(Iso7816CommandChannel channel, bool leave_field_on, sAPDU apdu, uint8_t *result, size_t max_result_len,
size_t *result_len, uint16_t *sw);
int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool activate_field, bool leave_field_on, sAPDU apdu, bool include_le,
uint16_t le, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw);
// search application
int Iso7816Select(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int Iso7816Select(Iso7816CommandChannel channel, bool activate_field, bool leave_field_on, uint8_t *aid, size_t aid_len,
uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw);
#endif

View file

@ -1,5 +1,6 @@
/*-
* Copyright (C) 2010, Romain Tartiere.
* Copyright (C) 2021 Merlok
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
@ -30,6 +31,7 @@
#include <string.h>
#include <util.h>
#include "commonutil.h"
#include "crypto/libpcrypto.h"
#include "aes.h"
#include "des.h"
#include "ui.h"
@ -51,25 +53,31 @@ static inline void update_key_schedules(desfirekey_t key) {
// }
}
int desfire_get_key_length(enum DESFIRE_CRYPTOALGO key_type) {
switch (key_type) {
case T_DES:
return 8;
case T_3DES:
return 16;
case T_3K3DES:
return 24;
case T_AES:
return 16;
}
return 0;
}
/******************************************************************************/
void des_encrypt(void *out, const void *in, const void *key) {
mbedtls_des_context ctx;
mbedtls_des_setkey_enc(&ctx, key);
mbedtls_des_crypt_ecb(&ctx, in, out);
}
void des_decrypt(void *out, const void *in, const void *key) {
mbedtls_des_context ctx;
mbedtls_des_setkey_dec(&ctx, key);
mbedtls_des_crypt_ecb(&ctx, in, out);
}
void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode) {
if (length % 8) return;
if (length % 8)
return;
mbedtls_des3_context ctx3;
if (keymode == 2) mbedtls_des3_set2key_dec(&ctx3, key);
else mbedtls_des3_set3key_dec(&ctx3, key);
if (keymode == 2)
mbedtls_des3_set2key_dec(&ctx3, key);
else
mbedtls_des3_set3key_dec(&ctx3, key);
uint8_t i;
unsigned char temp[8];
@ -81,8 +89,9 @@ void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key,
mbedtls_des3_crypt_ecb(&ctx3, tin, tout);
for (i = 0; i < 8; i++)
for (i = 0; i < 8; i++) {
tout[i] = (unsigned char)(tout[i] ^ iv[i]);
}
memcpy(iv, temp, 8);
@ -93,18 +102,24 @@ void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key,
}
void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode) {
if (length % 8) return;
if (length % 8)
return;
mbedtls_des3_context ctx3;
if (keymode == 2) mbedtls_des3_set2key_enc(&ctx3, key);
else mbedtls_des3_set3key_enc(&ctx3, key);
if (keymode == 2)
mbedtls_des3_set2key_enc(&ctx3, key);
else
mbedtls_des3_set3key_enc(&ctx3, key);
uint8_t i;
uint8_t *tin = (uint8_t *) in;
uint8_t *tout = (uint8_t *) out;
while (length > 0) {
for (i = 0; i < 8; i++)
for (i = 0; i < 8; i++) {
tin[i] = (unsigned char)(tin[i] ^ iv[i]);
}
mbedtls_des3_crypt_ecb(&ctx3, tin, tout);
@ -120,8 +135,9 @@ void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, un
void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key) {
uint8_t data[8];
memcpy(data, value, 8);
for (int n = 0; n < 8; n++)
data[n] &= 0xfe;
for (int n = 0; n < 8; n++) {
data[n] &= 0xFE;
}
Desfire_des_key_new_with_version(data, key);
}
@ -137,10 +153,12 @@ void Desfire_des_key_new_with_version(const uint8_t value[8], desfirekey_t key)
void Desfire_3des_key_new(const uint8_t value[16], desfirekey_t key) {
uint8_t data[16];
memcpy(data, value, 16);
for (int n = 0; n < 8; n++)
data[n] &= 0xfe;
for (int n = 8; n < 16; n++)
for (int n = 0; n < 8; n++) {
data[n] &= 0xFE;
}
for (int n = 8; n < 16; n++) {
data[n] |= 0x01;
}
Desfire_3des_key_new_with_version(data, key);
}
@ -155,8 +173,9 @@ void Desfire_3des_key_new_with_version(const uint8_t value[16], desfirekey_t key
void Desfire_3k3des_key_new(const uint8_t value[24], desfirekey_t key) {
uint8_t data[24];
memcpy(data, value, 24);
for (int n = 0; n < 8; n++)
data[n] &= 0xfe;
for (int n = 0; n < 8; n++) {
data[n] &= 0xFE;
}
Desfire_3k3des_key_new_with_version(data, key);
}
@ -173,7 +192,6 @@ void Desfire_aes_key_new(const uint8_t value[16], desfirekey_t key) {
}
void Desfire_aes_key_new_with_version(const uint8_t value[16], uint8_t version, desfirekey_t key) {
if (key != NULL) {
memcpy(key->data, value, 16);
key->type = T_AES;
@ -193,13 +211,15 @@ uint8_t Desfire_key_get_version(desfirekey_t key) {
void Desfire_key_set_version(desfirekey_t key, uint8_t version) {
for (int n = 0; n < 8; n++) {
uint8_t version_bit = ((version & (1 << (7 - n))) >> (7 - n));
key->data[n] &= 0xfe;
key->data[n] &= 0xFE;
key->data[n] |= version_bit;
if (key->type == T_DES) {
key->data[n + 8] = key->data[n];
} else {
// Write ~version to avoid turning a 3DES key into a DES key
key->data[n + 8] &= 0xfe;
key->data[n + 8] &= 0xFE;
key->data[n + 8] |= ~version_bit;
}
}
@ -268,15 +288,17 @@ void cmac_generate_subkeys(desfirekey_t key, MifareCryptoDirection direction) {
memcpy(key->cmac_sk1, l, kbs);
txor = l[0] & 0x80;
lsl(key->cmac_sk1, kbs);
if (txor)
if (txor) {
key->cmac_sk1[kbs - 1] ^= R;
}
// Used to compute CMAC on the last block if non-complete
memcpy(key->cmac_sk2, key->cmac_sk1, kbs);
txor = key->cmac_sk1[0] & 0x80;
lsl(key->cmac_sk2, kbs);
if (txor)
if (txor) {
key->cmac_sk2[kbs - 1] ^= R;
}
}
void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac) {
@ -286,6 +308,10 @@ void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t le
}
uint8_t *buffer = calloc(padded_data_length(len, kbs), sizeof(uint8_t));
if (buffer == NULL) {
PrintAndLogEx(WARNING, "failed to allocate memory");
return;
}
memcpy(buffer, data, len);
@ -313,12 +339,24 @@ void mifare_kdf_an10922(const desfirekey_t key, const uint8_t *data, size_t len)
return;
}
// AES uses 16 byte IV
if (kbs < 16)
kbs = 16;
cmac_generate_subkeys(key, MCD_SEND);
uint8_t *buffer = calloc(kbs2, sizeof(uint8_t));
// reserv atleast 32bytes.
uint8_t *buffer = calloc(len, sizeof(uint8_t));
if (buffer == NULL) {
PrintAndLogEx(WARNING, "failed to allocate memory");
return;
}
uint8_t *ivect = calloc(kbs, sizeof(uint8_t));
memset(ivect, 0, kbs);
if (ivect == NULL) {
PrintAndLogEx(WARNING, "failed to allocate memory");
free(buffer);
return;
}
buffer[0] = 0x01;
memcpy(&buffer[1], data, len++);
@ -344,11 +382,9 @@ void mifare_kdf_an10922(const desfirekey_t key, const uint8_t *data, size_t len)
free(buffer);
}
size_t key_block_size(const desfirekey_t key) {
if (key == NULL)
return 0;
size_t desfire_get_key_block_length(enum DESFIRE_CRYPTOALGO key_type) {
size_t block_size = 8;
switch (key->type) {
switch (key_type) {
case T_DES:
case T_3DES:
case T_3K3DES:
@ -361,6 +397,13 @@ size_t key_block_size(const desfirekey_t key) {
return block_size;
}
size_t key_block_size(const desfirekey_t key) {
if (key == NULL) {
return 0;
}
return desfire_get_key_block_length(key->type);
}
/*
* Size of MACing produced with the key.
*/
@ -924,19 +967,3 @@ void mifare_cypher_blocks_chained(desfiretag_t tag, desfirekey_t key, uint8_t *i
offset += block_size;
}
}
void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc) {
crc32_ex(data, len, crc);
}
void desfire_crc32_append(uint8_t *data, const size_t len) {
crc32_ex(data, len, data + len);
}
void iso14443a_crc_append(uint8_t *data, size_t len) {
return compute_crc(CRC_14443_A, data, len, data + len, data + len + 1);
}
void iso14443a_crc(uint8_t *data, size_t len, uint8_t *pbtCrc) {
return compute_crc(CRC_14443_A, data, len, pbtCrc, pbtCrc + 1);
}

View file

@ -1,12 +1,33 @@
/*-
* Copyright (C) 2010, Romain Tartiere.
* Copyright (C) 2021 Merlok
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
* $Id$
*/
#ifndef __DESFIRE_CRYPTO_H
#define __DESFIRE_CRYPTO_H
#include "common.h"
#include "mifare.h" // structs
#include "crc32.h"
#include "crypto/libpcrypto.h"
#include "mifare/desfirecrypto.h"
#define MAX_CRYPTO_BLOCK_SIZE 16
/* Mifare DESFire EV1 Application crypto operations */
#define APPLICATION_CRYPTO_DES 0x00
#define APPLICATION_CRYPTO_3K3DES 0x40
@ -53,12 +74,8 @@ typedef enum {
/* Error code managed by the library */
#define CRYPTO_ERROR 0x01
enum DESFIRE_CRYPTOALGO {
T_DES = 0x00,
T_3DES = 0x01, //aka 2K3DES
T_3K3DES = 0x02,
T_AES = 0x03
};
int desfire_get_key_length(enum DESFIRE_CRYPTOALGO key_type);
size_t desfire_get_key_block_length(enum DESFIRE_CRYPTOALGO key_type);
enum DESFIRE_AUTH_SCHEME {
AS_LEGACY,
@ -102,8 +119,6 @@ typedef unsigned long DES3_KS[48][2]; /* Triple-DES key schedule */
extern int Asmversion; /* 1 if we're linked with an asm version, 0 if C */
void des_encrypt(void *out, const void *in, const void *key);
void des_decrypt(void *out, const void *in, const void *key);
void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode);
void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode);
void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key);
@ -132,8 +147,4 @@ void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t l
void mifare_kdf_an10922(const desfirekey_t key, const uint8_t *data, size_t len);
void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc);
void desfire_crc32_append(uint8_t *data, const size_t len);
void iso14443a_crc_append(uint8_t *data, size_t len);
void iso14443a_crc(uint8_t *data, size_t len, uint8_t *pbtCrc);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,58 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2010 Romain Tartiere.
// Copyright (C) 2014 Iceman
// Copyright (C) 2021 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// High frequency Desfire core functions
//-----------------------------------------------------------------------------
#ifndef __DESFIRECORE_H
#define __DESFIRECORE_H
#include "common.h"
#include "cliparser.h"
#include "mifare/desfirecrypto.h"
#include "mifare/desfire_crypto.h"
#include "mifare/mifare4.h"
extern const CLIParserOption DesfireAlgoOpts[];
extern const CLIParserOption DesfireKDFAlgoOpts[];
extern const CLIParserOption DesfireCommunicationModeOpts[];
extern const CLIParserOption DesfireCommandSetOpts[];
extern const CLIParserOption DesfireSecureChannelOpts[];
const char *DesfireGetErrorString(int res, uint16_t *sw);
uint32_t DesfireAIDByteToUint(uint8_t *data);
void DesfireAIDUintToByte(uint32_t aid, uint8_t *data);
void DesfirePrintContext(DesfireContext *ctx);
int DesfireExchange(DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen);
int DesfireExchangeEx(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen, bool enable_chaining, size_t splitbysize);
int DesfireSelectAID(DesfireContext *ctx, uint8_t *aid1, uint8_t *aid2);
int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uint32_t aid2);
int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool verbose);
int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel);
int DesfireFormatPICC(DesfireContext *dctx);
int DesfireGetFreeMem(DesfireContext *dctx, uint32_t *freemem);
int DesfireGetUID(DesfireContext *dctx, uint8_t *resp, size_t *resplen);
int DesfireGetAIDList(DesfireContext *dctx, uint8_t *resp, size_t *resplen);
int DesfireGetDFList(DesfireContext *dctx, uint8_t *resp, size_t *resplen);
int DesfireCreateApplication(DesfireContext *dctx, uint8_t *appdata, size_t appdatalen);
int DesfireDeleteApplication(DesfireContext *dctx, uint32_t aid);
int DesfireGetKeyVersion(DesfireContext *dctx, uint8_t *data, size_t len, uint8_t *resp, size_t *resplen);
int DesfireGetKeySettings(DesfireContext *dctx, uint8_t *resp, size_t *resplen);
int DesfireChangeKeySettings(DesfireContext *dctx, uint8_t *data, size_t len);
void PrintKeySettings(uint8_t keysettings, uint8_t numkeys, bool applevel, bool print2ndbyte);
uint8_t DesfireKeyAlgoToType(DesfireCryptoAlgorythm keyType);
#endif // __DESFIRECORE_H

View file

@ -0,0 +1,341 @@
/*-
* Copyright (C) 2010, Romain Tartiere.
* Copyright (C) 2021 Merlok
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
* $Id$
*/
#include "desfirecrypto.h"
#include <stdlib.h>
#include <string.h>
#include <util.h>
#include "ui.h"
#include "aes.h"
#include "des.h"
#include <mbedtls/cmac.h>
#include "crc.h"
#include "crc16.h" // crc16 ccitt
#include "crc32.h"
#include "commonutil.h"
#include "mifare/desfire_crypto.h"
void DesfireClearContext(DesfireContext *ctx) {
ctx->keyNum = 0;
ctx->keyType = T_DES;
memset(ctx->key, 0, sizeof(ctx->key));
ctx->secureChannel = DACNone;
ctx->cmdSet = DCCNative;
ctx->commMode = DCMNone;
ctx->kdfAlgo = 0;
ctx->kdfInputLen = 0;
memset(ctx->kdfInput, 0, sizeof(ctx->kdfInput));
DesfireClearSession(ctx);
}
void DesfireClearSession(DesfireContext *ctx) {
ctx->secureChannel = DACNone; // here none - not authenticared
memset(ctx->IV, 0, sizeof(ctx->IV));
memset(ctx->sessionKeyMAC, 0, sizeof(ctx->sessionKeyMAC));
memset(ctx->sessionKeyEnc, 0, sizeof(ctx->sessionKeyEnc));
memset(ctx->lastIV, 0, sizeof(ctx->lastIV));
ctx->lastCommand = 0;
ctx->lastRequestZeroLen = false;
ctx->cntrTx = 0;
ctx->cntrRx = 0;
memset(ctx->TI, 0, sizeof(ctx->TI));
}
void DesfireSetKey(DesfireContext *ctx, uint8_t keyNum, enum DESFIRE_CRYPTOALGO keyType, uint8_t *key) {
DesfireClearContext(ctx);
ctx->keyNum = keyNum;
ctx->keyType = keyType;
memcpy(ctx->key, key, desfire_get_key_length(keyType));
}
void DesfireSetCommandSet(DesfireContext *ctx, DesfireCommandSet cmdSet) {
ctx->cmdSet = cmdSet;
}
void DesfireSetCommMode(DesfireContext *ctx, DesfireCommunicationMode commMode) {
ctx->commMode = commMode;
}
void DesfireSetKdf(DesfireContext *ctx, uint8_t kdfAlgo, uint8_t *kdfInput, uint8_t kdfInputLen) {
ctx->kdfAlgo = kdfAlgo;
ctx->kdfInputLen = kdfInputLen;
if (kdfInputLen)
memcpy(ctx->kdfInput, kdfInput, kdfInputLen);
}
bool DesfireIsAuthenticated(DesfireContext *dctx) {
return dctx->secureChannel != DACNone;
}
size_t DesfireGetMACLength(DesfireContext *ctx) {
size_t mac_length = MAC_LENGTH;
switch (ctx->secureChannel) {
case DACNone:
mac_length = 0;
break;
case DACd40:
mac_length = 4;
break;
case DACEV1:
mac_length = 8;
break;
case DACEV2:
mac_length = 8;
break;
}
return mac_length;
}
size_t DesfireSearchCRCPos(uint8_t *data, size_t datalen, uint8_t respcode, uint8_t crclen) {
size_t crcpos = datalen - 1;
while (crcpos > 0)
if (data[crcpos] == 0)
crcpos--;
else
break;
crcpos++; // crc may be 0x00000000 or 0x0000
if (crcpos < crclen) {
PrintAndLogEx(WARNING, "No space for crc. pos: %zu", crcpos);
return 0;
}
uint8_t crcdata[1024] = {0};
size_t crcposfound = 0;
for (int i = 0; i < crclen + 1; i++) {
if (crcpos - i == 0)
break;
if (crcpos - i + crclen > datalen)
continue;
memcpy(crcdata, data, crcpos - i);
crcdata[crcpos - i] = respcode;
bool res;
if (crclen == 4)
res = desfire_crc32_check(crcdata, crcpos - i + 1, &data[crcpos - i]);
else
res = iso14443a_crc_check(data, crcpos - i, &data[crcpos - i]);
if (res) {
crcposfound = crcpos - i;
}
}
return crcposfound;
}
static void DesfireCryptoEncDecSingleBlock(uint8_t *key, DesfireCryptoAlgorythm keyType, uint8_t *data, uint8_t *dstdata, uint8_t *ivect, bool dir_to_send, bool encode) {
size_t block_size = desfire_get_key_block_length(keyType);
uint8_t sdata[MAX_CRYPTO_BLOCK_SIZE] = {0};
memcpy(sdata, data, block_size);
if (dir_to_send) {
bin_xor(sdata, ivect, block_size);
}
uint8_t edata[MAX_CRYPTO_BLOCK_SIZE] = {0};
switch (keyType) {
case T_DES:
if (encode)
des_encrypt(edata, sdata, key);
else
des_decrypt(edata, sdata, key);
break;
case T_3DES:
if (encode) {
mbedtls_des3_context ctx3;
mbedtls_des3_set2key_enc(&ctx3, key);
mbedtls_des3_crypt_ecb(&ctx3, sdata, edata);
} else {
mbedtls_des3_context ctx3;
mbedtls_des3_set2key_dec(&ctx3, key);
mbedtls_des3_crypt_ecb(&ctx3, sdata, edata);
}
break;
case T_3K3DES:
if (encode) {
mbedtls_des3_context ctx3;
mbedtls_des3_set3key_enc(&ctx3, key);
mbedtls_des3_crypt_ecb(&ctx3, sdata, edata);
} else {
mbedtls_des3_context ctx3;
mbedtls_des3_set3key_dec(&ctx3, key);
mbedtls_des3_crypt_ecb(&ctx3, sdata, edata);
}
break;
case T_AES:
if (encode) {
mbedtls_aes_context actx;
mbedtls_aes_init(&actx);
mbedtls_aes_setkey_enc(&actx, key, 128);
mbedtls_aes_crypt_ecb(&actx, MBEDTLS_AES_ENCRYPT, sdata, edata);
mbedtls_aes_free(&actx);
} else {
mbedtls_aes_context actx;
mbedtls_aes_init(&actx);
mbedtls_aes_setkey_dec(&actx, key, 128);
mbedtls_aes_crypt_ecb(&actx, MBEDTLS_AES_DECRYPT, sdata, edata);
mbedtls_aes_free(&actx);
}
break;
}
if (dir_to_send) {
memcpy(ivect, edata, block_size);
} else {
bin_xor(edata, ivect, block_size);
memcpy(ivect, data, block_size);
}
memcpy(dstdata, edata, block_size);
}
void DesfireCryptoEncDecEx(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode, uint8_t *iv) {
uint8_t data[1024] = {0};
uint8_t xiv[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0};
bool xencode = encode;
if (ctx->secureChannel == DACd40) {
memset(ctx->IV, 0, DESFIRE_MAX_CRYPTO_BLOCK_SIZE);
xencode = false;
}
size_t block_size = desfire_get_key_block_length(ctx->keyType);
if (iv == NULL)
memcpy(xiv, ctx->IV, block_size);
else
memcpy(xiv, iv, block_size);
size_t offset = 0;
while (offset < srcdatalen) {
if (use_session_key)
DesfireCryptoEncDecSingleBlock(ctx->sessionKeyMAC, ctx->keyType, srcdata + offset, data + offset, xiv, encode, xencode);
else
DesfireCryptoEncDecSingleBlock(ctx->key, ctx->keyType, srcdata + offset, data + offset, xiv, encode, xencode);
offset += block_size;
}
if (iv == NULL)
memcpy(ctx->IV, xiv, block_size);
else
memcpy(iv, xiv, block_size);
if (dstdata)
memcpy(dstdata, data, srcdatalen);
}
void DesfireCryptoEncDec(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode) {
DesfireCryptoEncDecEx(ctx, use_session_key, srcdata, srcdatalen, dstdata, encode, NULL);
}
static void DesfireCMACGenerateSubkeys(DesfireContext *ctx, uint8_t *sk1, uint8_t *sk2) {
int kbs = desfire_get_key_block_length(ctx->keyType);
const uint8_t R = (kbs == 8) ? 0x1B : 0x87;
uint8_t l[kbs];
memset(l, 0, kbs);
uint8_t ivect[kbs];
memset(ivect, 0, kbs);
DesfireCryptoEncDecEx(ctx, true, l, kbs, l, true, ivect);
bool txor = false;
// Used to compute CMAC on complete blocks
memcpy(sk1, l, kbs);
txor = l[0] & 0x80;
lsl(sk1, kbs);
if (txor) {
sk1[kbs - 1] ^= R;
}
// Used to compute CMAC on the last block if non-complete
memcpy(sk2, sk1, kbs);
txor = sk1[0] & 0x80;
lsl(sk2, kbs);
if (txor) {
sk2[kbs - 1] ^= R;
}
}
void DesfireCryptoCMAC(DesfireContext *ctx, uint8_t *data, size_t len, uint8_t *cmac) {
int kbs = desfire_get_key_block_length(ctx->keyType);
if (kbs == 0)
return;
uint8_t buffer[padded_data_length(len, kbs)];
memset(buffer, 0, sizeof(buffer));
uint8_t sk1[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0};
uint8_t sk2[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0};
DesfireCMACGenerateSubkeys(ctx, sk1, sk2);
memcpy(buffer, data, len);
if ((!len) || (len % kbs)) {
buffer[len++] = 0x80;
while (len % kbs) {
buffer[len++] = 0x00;
}
bin_xor(buffer + len - kbs, sk2, kbs);
} else {
bin_xor(buffer + len - kbs, sk1, kbs);
}
DesfireCryptoEncDec(ctx, true, buffer, len, NULL, true);
if (cmac != NULL)
memcpy(cmac, ctx->IV, kbs);
}
void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc) {
crc32_ex(data, len, crc);
}
void desfire_crc32_append(uint8_t *data, const size_t len) {
crc32_ex(data, len, data + len);
}
bool desfire_crc32_check(uint8_t *data, const size_t len, uint8_t *crc) {
uint8_t ccrc[4] = {0};
desfire_crc32(data, len, ccrc);
return (memcmp(ccrc, crc, 4) == 0);
}
void iso14443a_crc_append(uint8_t *data, size_t len) {
return compute_crc(CRC_14443_A, data, len, data + len, data + len + 1);
}
void iso14443a_crc(uint8_t *data, size_t len, uint8_t *pbtCrc) {
return compute_crc(CRC_14443_A, data, len, pbtCrc, pbtCrc + 1);
}
bool iso14443a_crc_check(uint8_t *data, const size_t len, uint8_t *crc) {
uint8_t ccrc[2] = {0};
iso14443a_crc(data, len, ccrc);
return (memcmp(ccrc, crc, 2) == 0);
}

View file

@ -0,0 +1,111 @@
/*-
* Copyright (C) 2010, Romain Tartiere.
* Copyright (C) 2021 Merlok
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
* $Id$
*/
#ifndef __DESFIRECRYPTO_H
#define __DESFIRECRYPTO_H
#include "common.h"
#include "mifare/mifare4.h"
#define MAX_CRYPTO_BLOCK_SIZE 16
#define DESFIRE_MAX_CRYPTO_BLOCK_SIZE 16
#define DESFIRE_MAX_KEY_SIZE 24
#define DESFIRE_GET_ISO_STATUS(x) ( ((uint16_t)(0x91<<8)) + (uint16_t)x )
enum DESFIRE_CRYPTOALGO {
T_DES = 0x00,
T_3DES = 0x01, //aka 2K3DES
T_3K3DES = 0x02,
T_AES = 0x03
};
typedef enum DESFIRE_CRYPTOALGO DesfireCryptoAlgorythm;
typedef enum {
DACNone,
DACd40,
DACEV1,
DACEV2
} DesfireSecureChannel;
typedef enum {
DCCNative,
DCCNativeISO,
DCCISO
} DesfireCommandSet;
typedef enum {
DCMNone,
DCMPlain,
DCMMACed,
DCMEncrypted
} DesfireCommunicationMode;
typedef struct DesfireContextS {
uint8_t keyNum;
DesfireCryptoAlgorythm keyType; // des/2tdea/3tdea/aes
uint8_t key[DESFIRE_MAX_KEY_SIZE];
// KDF finction
uint8_t kdfAlgo;
uint8_t kdfInputLen;
uint8_t kdfInput[31];
DesfireSecureChannel secureChannel; // none/d40/ev1/ev2
DesfireCommandSet cmdSet; // native/nativeiso/iso
DesfireCommunicationMode commMode; // plain/mac/enc
uint8_t IV[DESFIRE_MAX_KEY_SIZE];
uint8_t sessionKeyMAC[DESFIRE_MAX_KEY_SIZE];
uint8_t sessionKeyEnc[DESFIRE_MAX_KEY_SIZE]; // look at mifare4.h - mf4Session_t
uint8_t lastIV[DESFIRE_MAX_KEY_SIZE];
uint8_t lastCommand;
bool lastRequestZeroLen;
//mf4Session_t AESSession;
uint16_t cntrTx; // for AES
uint16_t cntrRx; // for AES
uint8_t TI[4]; // for AES
} DesfireContext;
void DesfireClearContext(DesfireContext *ctx);
void DesfireClearSession(DesfireContext *ctx);
void DesfireSetKey(DesfireContext *ctx, uint8_t keyNum, enum DESFIRE_CRYPTOALGO keyType, uint8_t *key);
void DesfireSetCommandSet(DesfireContext *ctx, DesfireCommandSet cmdSet);
void DesfireSetCommMode(DesfireContext *ctx, DesfireCommunicationMode commMode);
void DesfireSetKdf(DesfireContext *ctx, uint8_t kdfAlgo, uint8_t *kdfInput, uint8_t kdfInputLen);
bool DesfireIsAuthenticated(DesfireContext *dctx);
size_t DesfireGetMACLength(DesfireContext *ctx);
size_t DesfireSearchCRCPos(uint8_t *data, size_t datalen, uint8_t respcode, uint8_t crclen);
void DesfireCryptoEncDec(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode);
void DesfireCryptoEncDecEx(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode, uint8_t *iv);
void DesfireCryptoCMAC(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *cmac);
void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc);
void desfire_crc32_append(uint8_t *data, const size_t len);
bool desfire_crc32_check(uint8_t *data, const size_t len, uint8_t *crc);
void iso14443a_crc_append(uint8_t *data, size_t len);
void iso14443a_crc(uint8_t *data, size_t len, uint8_t *pbtCrc);
bool iso14443a_crc_check(uint8_t *data, const size_t len, uint8_t *crc);
#endif // __DESFIRECRYPTO_H

View file

@ -0,0 +1,298 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2010 Romain Tartiere.
// Copyright (C) 2014 Iceman
// Copyright (C) 2021 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// High frequency Desfire secure channel functions
//-----------------------------------------------------------------------------
#include "desfiresecurechan.h"
#include <stdlib.h>
#include <string.h>
#include <util.h>
#include "ui.h"
#include "crc.h"
#include "crc16.h" // crc16 ccitt
#include "crc32.h"
#include "commonutil.h"
#include "protocols.h"
#include "mifare/desfire_crypto.h"
AllowedChannelModesS AllowedChannelModes[] = {
{MFDES_CREATE_APPLICATION, DACd40, DCCNative, DCMPlain},
{MFDES_DELETE_APPLICATION, DACd40, DCCNative, DCMPlain},
{MFDES_GET_APPLICATION_IDS, DACd40, DCCNative, DCMPlain},
{MFDES_GET_DF_NAMES, DACd40, DCCNative, DCMPlain},
{MFDES_GET_KEY_SETTINGS, DACd40, DCCNative, DCMPlain},
{MFDES_GET_KEY_VERSION, DACd40, DCCNative, DCMPlain},
{MFDES_GET_FREE_MEMORY, DACd40, DCCNative, DCMPlain},
{MFDES_READ_DATA, DACd40, DCCNative, DCMMACed},
{MFDES_WRITE_DATA, DACd40, DCCNative, DCMMACed},
{MFDES_GET_VALUE, DACd40, DCCNative, DCMMACed},
{MFDES_CREDIT, DACd40, DCCNative, DCMMACed},
{MFDES_DEBIT, DACd40, DCCNative, DCMMACed},
{MFDES_LIMITED_CREDIT, DACd40, DCCNative, DCMMACed},
{MFDES_READ_RECORDS, DACd40, DCCNative, DCMMACed},
{MFDES_WRITE_RECORD, DACd40, DCCNative, DCMMACed},
{MFDES_UPDATE_RECORD1, DACd40, DCCNative, DCMMACed},
{MFDES_UPDATE_RECORD2, DACd40, DCCNativeISO, DCMMACed},
{MFDES_INIT_KEY_SETTINGS, DACd40, DCCNative, DCMMACed},
{MFDES_FINALIZE_KEY_SETTINGS, DACd40, DCCNative, DCMMACed},
{MFDES_ROLL_KEY_SETTINGS, DACd40, DCCNative, DCMMACed},
{MFDES_COMMIT_READER_ID, DACd40, DCCNative, DCMMACed},
{MFDES_GET_UID, DACd40, DCCNative, DCMEncrypted},
{MFDES_CHANGE_KEY_SETTINGS, DACd40, DCCNative, DCMEncrypted},
{MFDES_READ_DATA, DACd40, DCCNative, DCMEncrypted},
{MFDES_WRITE_DATA, DACd40, DCCNative, DCMEncrypted},
{MFDES_GET_KEY_VERSION, DACEV1, DCCNative, DCMPlain},
{MFDES_GET_FREE_MEMORY, DACEV1, DCCNative, DCMPlain},
{MFDES_CREATE_APPLICATION, DACEV1, DCCNative, DCMMACed},
{MFDES_DELETE_APPLICATION, DACEV1, DCCNative, DCMMACed},
{MFDES_GET_APPLICATION_IDS, DACEV1, DCCNative, DCMMACed},
{MFDES_GET_DF_NAMES, DACEV1, DCCNative, DCMMACed},
{MFDES_GET_KEY_SETTINGS, DACEV1, DCCNative, DCMMACed},
{MFDES_GET_UID, DACEV1, DCCNative, DCMEncrypted},
{MFDES_CHANGE_KEY_SETTINGS, DACEV1, DCCNative, DCMEncrypted},
};
static void DesfireSecureChannelEncodeD40(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) {
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
uint8_t data[1024] = {0};
size_t rlen = 0;
switch (ctx->commMode) {
case DCMPlain:
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
break;
case DCMMACed:
if (srcdatalen == 0)
break;
rlen = srcdatalen + DesfireGetMACLength(ctx);
memcpy(data, srcdata, srcdatalen);
DesfireCryptoEncDec(ctx, true, data, srcdatalen, NULL, true);
memcpy(dstdata, srcdata, srcdatalen);
memcpy(&dstdata[srcdatalen], ctx->IV, 4);
*dstdatalen = rlen;
break;
case DCMEncrypted:
if (srcdatalen == 0)
break;
rlen = padded_data_length(srcdatalen + 2, desfire_get_key_block_length(ctx->keyType)); // 2 - crc16
memcpy(data, srcdata, srcdatalen);
compute_crc(CRC_14443_A, data, srcdatalen, &data[srcdatalen], &data[srcdatalen + 1]);
DesfireCryptoEncDec(ctx, true, data, rlen, dstdata, true);
*dstdatalen = rlen;
break;
case DCMNone:
;
}
}
static void DesfireSecureChannelEncodeEV1(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) {
uint8_t data[1024] = {0};
size_t rlen = 0;
// we calc MAC anyway
// if encypted channel and no data - we only calc MAC
if (ctx->commMode == DCMPlain || ctx->commMode == DCMMACed || (ctx->commMode == DCMEncrypted && srcdatalen == 0)) {
data[0] = cmd;
memcpy(&data[1], srcdata, srcdatalen);
uint8_t cmac[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0};
DesfireCryptoCMAC(ctx, data, srcdatalen + 1, cmac);
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
if (srcdatalen != 0 && ctx->commMode == DCMMACed) {
memcpy(&dstdata[srcdatalen], cmac, DesfireGetMACLength(ctx));
*dstdatalen = srcdatalen + DesfireGetMACLength(ctx);
}
} else if (ctx->commMode == DCMEncrypted) {
rlen = padded_data_length(srcdatalen + 4, desfire_get_key_block_length(ctx->keyType));
data[0] = cmd;
memcpy(&data[1], srcdata, srcdatalen);
desfire_crc32_append(data, srcdatalen + 1);
DesfireCryptoEncDec(ctx, true, &data[1], rlen, dstdata, true);
*dstdatalen = rlen;
} else {
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
}
}
void DesfireSecureChannelEncode(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) {
ctx->lastCommand = cmd;
ctx->lastRequestZeroLen = (srcdatalen == 0);
switch (ctx->secureChannel) {
case DACd40:
DesfireSecureChannelEncodeD40(ctx, cmd, srcdata, srcdatalen, dstdata, dstdatalen);
break;
case DACEV1:
DesfireSecureChannelEncodeEV1(ctx, cmd, srcdata, srcdatalen, dstdata, dstdatalen);
break;
case DACEV2:
break;
case DACNone:
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
break;
}
}
static void DesfireSecureChannelDecodeD40(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) {
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
switch (ctx->commMode) {
case DCMMACed:
break;
case DCMEncrypted:
if (srcdatalen < desfire_get_key_block_length(ctx->keyType)) {
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
return;
}
DesfireCryptoEncDec(ctx, true, srcdata, srcdatalen, dstdata, false);
//PrintAndLogEx(INFO, "decoded[%d]: %s", srcdatalen, sprint_hex(dstdata, srcdatalen));
size_t puredatalen = DesfireSearchCRCPos(dstdata, srcdatalen, respcode, 2);
if (puredatalen != 0) {
*dstdatalen = puredatalen;
} else {
PrintAndLogEx(WARNING, "CRC16 error.");
*dstdatalen = srcdatalen;
}
break;
case DCMPlain:
case DACNone:
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
break;
}
}
static void DesfireSecureChannelDecodeEV1(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) {
uint8_t data[1024] = {0};
// if comm mode = plain --> response with MAC
// if request is not zero length --> response MAC
if (ctx->commMode == DCMPlain || ctx->commMode == DCMMACed || (ctx->commMode == DCMEncrypted && !ctx->lastRequestZeroLen)) {
if (srcdatalen < DesfireGetMACLength(ctx)) {
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
return;
}
memcpy(dstdata, srcdata, srcdatalen - DesfireGetMACLength(ctx));
*dstdatalen = srcdatalen - DesfireGetMACLength(ctx);
memcpy(data, srcdata, *dstdatalen);
data[*dstdatalen] = respcode;
uint8_t cmac[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0};
DesfireCryptoCMAC(ctx, data, *dstdatalen + 1, cmac);
if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) {
PrintAndLogEx(WARNING, "Received MAC is not match with calculated");
PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], desfire_get_key_block_length(ctx->keyType)));
PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, desfire_get_key_block_length(ctx->keyType)));
}
} else if (ctx->commMode == DCMEncrypted) {
if (srcdatalen < desfire_get_key_block_length(ctx->keyType)) {
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
return;
}
DesfireCryptoEncDec(ctx, true, srcdata, srcdatalen, dstdata, false);
//PrintAndLogEx(INFO, "decoded[%d]: %s", srcdatalen, sprint_hex(dstdata, srcdatalen));
size_t puredatalen = DesfireSearchCRCPos(dstdata, srcdatalen, respcode, 4);
if (puredatalen != 0) {
*dstdatalen = puredatalen;
} else {
PrintAndLogEx(WARNING, "CRC32 error.");
*dstdatalen = srcdatalen;
}
} else {
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
}
}
void DesfireSecureChannelDecode(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) {
switch (ctx->secureChannel) {
case DACd40:
DesfireSecureChannelDecodeD40(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen);
break;
case DACEV1:
DesfireSecureChannelDecodeEV1(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen);
break;
case DACEV2:
break;
case DACNone:
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
break;
}
}
bool PrintChannelModeWarning(uint8_t cmd, DesfireSecureChannel secureChannel, DesfireCommandSet cmdSet, DesfireCommunicationMode commMode) {
if (commMode == DCMNone) {
PrintAndLogEx(WARNING, "Communication mode can't be NONE. command: %02x", cmd);
return false;
}
// no security set
if (secureChannel == DACNone)
return true;
bool found = false;
for (int i = 0; i < ARRAY_LENGTH(AllowedChannelModes); i++)
if (AllowedChannelModes[i].cmd == cmd) {
// full compare
if (AllowedChannelModes[i].secureChannel == secureChannel &&
(AllowedChannelModes[i].cmdSet == cmdSet || (AllowedChannelModes[i].cmdSet == DCCNative && cmdSet == DCCNativeISO)) &&
AllowedChannelModes[i].commMode == commMode) {
found = true;
break;
}
// ev1 plain and mac are the same
if (AllowedChannelModes[i].secureChannel == secureChannel &&
AllowedChannelModes[i].secureChannel == DACEV1 &&
(AllowedChannelModes[i].cmdSet == cmdSet || (AllowedChannelModes[i].cmdSet == DCCNative && cmdSet == DCCNativeISO)) &&
(commMode == DCMPlain || commMode == DCMMACed)) {
found = true;
break;
}
}
if (!found)
PrintAndLogEx(WARNING, "Wrong communication mode. Check settings. command: %02x", cmd);
return found;
}

View file

@ -0,0 +1,34 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2010 Romain Tartiere.
// Copyright (C) 2014 Iceman
// Copyright (C) 2021 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// High frequency Desfire secure channel functions
//-----------------------------------------------------------------------------
#ifndef __DESFIRESECURECHAN_H
#define __DESFIRESECURECHAN_H
#include "common.h"
#include "mifare/desfirecore.h"
#include "mifare/desfirecrypto.h"
#include "mifare/desfire_crypto.h"
#include "mifare/mifare4.h"
typedef struct {
uint8_t cmd;
DesfireSecureChannel secureChannel;
DesfireCommandSet cmdSet;
DesfireCommunicationMode commMode;
} AllowedChannelModesS;
void DesfireSecureChannelEncode(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen);
void DesfireSecureChannelDecode(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen);
bool PrintChannelModeWarning(uint8_t cmd, DesfireSecureChannel secureChannel, DesfireCommandSet cmdSet, DesfireCommunicationMode commMode);
#endif // __DESFIRESECURECHAN_H

View file

@ -0,0 +1,228 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2021 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// tests for desfire
//-----------------------------------------------------------------------------
#include "desfiretest.h"
#include <unistd.h>
#include <string.h> // memcpy memset
#include "fileutils.h"
#include "crypto/libpcrypto.h"
#include "mifare/desfirecrypto.h"
static uint8_t CMACData[] = {0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96,
0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A,
0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C,
0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51
};
static bool TestCRC16(void) {
uint8_t data[] = {0x04, 0x44, 0x0F, 0x32, 0x76, 0x31, 0x80, 0x27, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
bool res = true;
size_t len = DesfireSearchCRCPos(data, 16, 0x00, 2);
res = res && (len == 7);
len = DesfireSearchCRCPos(data, 7 + 2, 0x00, 2);
res = res && (len == 7);
len = DesfireSearchCRCPos(data, 7, 0x00, 2);
res = res && (len == 0);
len = DesfireSearchCRCPos(data, 3, 0x00, 2);
res = res && (len == 0);
len = DesfireSearchCRCPos(data, 1, 0x00, 2);
res = res && (len == 0);
if (res)
PrintAndLogEx(INFO, "crc16............ " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "crc16............ " _RED_("fail"));
return res;
}
static bool TestCRC32(void) {
uint8_t data[] = {0x04, 0x44, 0x0F, 0x32, 0x76, 0x31, 0x80, 0x99, 0xCE, 0x1A, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00};
bool res = true;
size_t len = DesfireSearchCRCPos(data, 16, 0x00, 4);
res = res && (len == 7);
len = DesfireSearchCRCPos(data, 7 + 4, 0x00, 4);
res = res && (len == 7);
len = DesfireSearchCRCPos(data, 5, 0x00, 4);
res = res && (len == 0);
len = DesfireSearchCRCPos(data, 4, 0x00, 4);
res = res && (len == 0);
len = DesfireSearchCRCPos(data, 2, 0x00, 4);
res = res && (len == 0);
if (res)
PrintAndLogEx(INFO, "crc32............ " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "crc32............ " _RED_("fail"));
return res;
}
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/TDES_CMAC.pdf
static bool TestCMAC3TDEA(void) {
bool res = true;
uint8_t key[DESFIRE_MAX_KEY_SIZE] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01,
0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23
};
DesfireContext dctx;
DesfireSetKey(&dctx, 0, T_3K3DES, key);
memcpy(dctx.sessionKeyMAC, key, DESFIRE_MAX_KEY_SIZE);
uint8_t cmac[DESFIRE_MAX_KEY_SIZE] = {0};
uint8_t cmac1[] = {0x7D, 0xB0, 0xD3, 0x7D, 0xF9, 0x36, 0xC5, 0x50};
memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE);
DesfireCryptoCMAC(&dctx, CMACData, 0, cmac);
res = res && (memcmp(cmac, cmac1, sizeof(cmac1)) == 0);
uint8_t cmac2[] = {0x30, 0x23, 0x9C, 0xF1, 0xF5, 0x2E, 0x66, 0x09};
memset(cmac, 0, sizeof(cmac));
memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE);
DesfireCryptoCMAC(&dctx, CMACData, 16, cmac);
res = res && (memcmp(cmac, cmac2, sizeof(cmac1)) == 0);
uint8_t cmac3[] = {0x6C, 0x9F, 0x3E, 0xE4, 0x92, 0x3F, 0x6B, 0xE2};
memset(cmac, 0, sizeof(cmac));
memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE);
DesfireCryptoCMAC(&dctx, CMACData, 20, cmac);
res = res && (memcmp(cmac, cmac3, sizeof(cmac1)) == 0);
uint8_t cmac4[] = {0x99, 0x42, 0x9B, 0xD0, 0xBF, 0x79, 0x04, 0xE5};
memset(cmac, 0, sizeof(cmac));
memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE);
DesfireCryptoCMAC(&dctx, CMACData, 32, cmac);
res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0);
if (res)
PrintAndLogEx(INFO, "CMAC 3TDEA....... " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "CMAC 3TDEA....... " _RED_("fail"));
return res;
}
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/TDES_CMAC.pdf
static bool TestCMAC2TDEA(void) {
bool res = true;
uint8_t key[DESFIRE_MAX_KEY_SIZE] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01
};
DesfireContext dctx;
DesfireSetKey(&dctx, 0, T_3DES, key);
memcpy(dctx.sessionKeyMAC, key, DESFIRE_MAX_KEY_SIZE);
uint8_t cmac[DESFIRE_MAX_KEY_SIZE] = {0};
uint8_t cmac1[] = {0x79, 0xCE, 0x52, 0xA7, 0xF7, 0x86, 0xA9, 0x60};
memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE);
DesfireCryptoCMAC(&dctx, CMACData, 0, cmac);
// PrintAndLogEx(INFO, "cmac: %s", sprint_hex(cmac, 16));
res = res && (memcmp(cmac, cmac1, sizeof(cmac1)) == 0);
uint8_t cmac2[] = {0xCC, 0x18, 0xA0, 0xB7, 0x9A, 0xF2, 0x41, 0x3B};
memset(cmac, 0, sizeof(cmac));
memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE);
DesfireCryptoCMAC(&dctx, CMACData, 16, cmac);
res = res && (memcmp(cmac, cmac2, sizeof(cmac1)) == 0);
uint8_t cmac3[] = {0xC0, 0x6D, 0x37, 0x7E, 0xCD, 0x10, 0x19, 0x69};
memset(cmac, 0, sizeof(cmac));
memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE);
DesfireCryptoCMAC(&dctx, CMACData, 20, cmac);
res = res && (memcmp(cmac, cmac3, sizeof(cmac1)) == 0);
uint8_t cmac4[] = {0x9C, 0xD3, 0x35, 0x80, 0xF9, 0xB6, 0x4D, 0xFB};
memset(cmac, 0, sizeof(cmac));
memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE);
DesfireCryptoCMAC(&dctx, CMACData, 32, cmac);
res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0);
if (res)
PrintAndLogEx(INFO, "CMAC 2TDEA....... " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "CMAC 2TDEA....... " _RED_("fail"));
return res;
}
static bool TestCMACDES(void) {
bool res = true;
uint8_t key[DESFIRE_MAX_KEY_SIZE] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};
DesfireContext dctx;
DesfireSetKey(&dctx, 0, T_DES, key);
memcpy(dctx.sessionKeyMAC, key, DESFIRE_MAX_KEY_SIZE);
uint8_t cmac[DESFIRE_MAX_KEY_SIZE] = {0};
uint8_t cmac1[] = {0x86, 0xF7, 0x9C, 0x13, 0xFD, 0x30, 0x6E, 0x67};
memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE);
DesfireCryptoCMAC(&dctx, CMACData, 0, cmac);
res = res && (memcmp(cmac, cmac1, sizeof(cmac1)) == 0);
uint8_t cmac2[] = {0xBE, 0xA4, 0x21, 0x22, 0x92, 0x46, 0x2A, 0x85};
memset(cmac, 0, sizeof(cmac));
memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE);
DesfireCryptoCMAC(&dctx, CMACData, 16, cmac);
res = res && (memcmp(cmac, cmac2, sizeof(cmac1)) == 0);
uint8_t cmac3[] = {0x3E, 0x2F, 0x83, 0x10, 0xC5, 0x69, 0x27, 0x5E};
memset(cmac, 0, sizeof(cmac));
memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE);
DesfireCryptoCMAC(&dctx, CMACData, 20, cmac);
res = res && (memcmp(cmac, cmac3, sizeof(cmac1)) == 0);
uint8_t cmac4[] = {0x9D, 0x1F, 0xC4, 0xD4, 0xC0, 0x25, 0x91, 0x32};
memset(cmac, 0, sizeof(cmac));
memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE);
DesfireCryptoCMAC(&dctx, CMACData, 32, cmac);
res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0);
if (res)
PrintAndLogEx(INFO, "CMAC DES......... " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "CMAC DES......... " _RED_("fail"));
return res;
}
bool DesfireTest(bool verbose) {
bool res = true;
PrintAndLogEx(INFO, "------ " _CYAN_("Desfire Tests") " ------");
res = res && TestCRC16();
res = res && TestCRC32();
res = res && TestCMAC3TDEA();
res = res && TestCMAC2TDEA();
res = res && TestCMACDES();
PrintAndLogEx(INFO, "---------------------------");
if (res)
PrintAndLogEx(SUCCESS, " Tests [ %s ]", _GREEN_("ok"));
else
PrintAndLogEx(FAILED, " Tests [ %s ]", _RED_("fail"));
PrintAndLogEx(NORMAL, "");
return res;
}

View file

@ -0,0 +1,19 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2021 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// tests for desfire
//-----------------------------------------------------------------------------
#ifndef __CIPURSETEST_H__
#define __CIPURSETEST_H__
#include <stdbool.h>
#include "common.h"
bool DesfireTest(bool verbose);
#endif /* __CIPURSETEST_H__ */

View file

@ -56,6 +56,8 @@ int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) {
return PM3_EOPABORTED;
}
PrintAndLogEx(INFO, "." NOLF);
// wait cycle
while (true) {
PrintAndLogEx(NORMAL, "." NOLF);
@ -197,16 +199,20 @@ int mfCheckKeys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk,
uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory) {
uint64_t t2 = msclock();
uint32_t timeout = 0;
// send keychunk
clearCommandBuffer();
SendCommandOLD(CMD_HF_MIFARE_CHKKEYS_FAST, (sectorsCnt | (firstChunk << 8) | (lastChunk << 12)), ((use_flashmemory << 8) | strategy), size, keyBlock, 6 * size);
PacketResponseNG resp;
uint32_t timeout = 0;
while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) {
PrintAndLogEx((timeout == 0) ? INFO : NORMAL, "." NOLF);
fflush(stdout);
timeout++;
PrintAndLogEx(NORMAL, "." NOLF);
// max timeout for one chunk of 85keys, 60*3sec = 180seconds
// s70 with 40*2 keys to check, 80*85 = 6800 auth.
// takes about 97s, still some margin before abort
@ -217,6 +223,10 @@ int mfCheckKeys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk,
}
t2 = msclock() - t2;
if (timeout) {
PrintAndLogEx(NORMAL, "");
}
// time to convert the returned data.
uint8_t curr_keys = resp.oldarg[0];
@ -538,7 +548,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo,
PrintAndLogEx(SUCCESS, "\ntarget block:%3u key type: %c -- found valid key [ " _GREEN_("%s") "]",
package->block,
package->keytype ? 'B' : 'A',
sprint_hex(resultKey, 6)
sprint_hex_inrow(resultKey, 6)
);
return PM3_SUCCESS;
}
@ -717,7 +727,7 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl
PrintAndLogEx(SUCCESS, "target block:%3u key type: %c -- found valid key [ " _GREEN_("%s") "]",
package->block,
package->keytype ? 'B' : 'A',
sprint_hex(resultKey, 6)
sprint_hex_inrow(resultKey, 6)
);
return PM3_SUCCESS;
} else if (res == PM3_ETIMEOUT || res == PM3_EOPABORTED) {
@ -1042,6 +1052,27 @@ int mfGen3Freeze(void) {
}
}
int mfG3GetBlock(uint8_t blockno, uint8_t *data) {
struct p {
uint8_t blockno;
} PACKED payload;
payload.blockno = blockno;
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_G3_RDBL, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_G3_RDBL, &resp, 1500)) {
if (resp.status != PM3_SUCCESS)
return PM3_EUNDEF;
memcpy(data, resp.data.asBytes, 16);
} else {
PrintAndLogEx(WARNING, "command execute timeout");
return PM3_ETIMEOUT;
}
return PM3_SUCCESS;
}
// variables
uint32_t cuid = 0; // uid part used for crypto1.

View file

@ -85,6 +85,8 @@ int mfGen3UID(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid);
int mfGen3Block(uint8_t *block, int blockLen, uint8_t *newBlock);
int mfGen3Freeze(void);
int mfG3GetBlock(uint8_t blockno, uint8_t *data);
int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len);
int detect_classic_prng(void);

View file

@ -15,9 +15,17 @@
#include "ui.h"
#include "util.h" // sprint_hex...
#include "crypto/asn1utils.h"
#include "crypto/libpcrypto.h"
#include "ecp.h"
#include "commonutil.h" // ARRAYLEN
#include "pm3_cmd.h"
#define STRBOOL(p) ((p) ? "+" : "-")
#define STRBOOL(p) ((p) ? "1" : "0")
#define NDEF_WIFIAPPL "application/vnd.wfa"
#define NDEF_BLUEAPPL "application/vnd.bluetooth"
#define NDEF_VCARDTEXT "text/vcard"
#define NDEF_XVCARDTEXT "text/x-vcard"
static const char *TypeNameFormat_s[] = {
"Empty Record",
@ -91,6 +99,9 @@ static const char *URI_s[] = {
"urn:nfc:" // 0x23
};
static int ndefRecordDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen);
static int ndefDecodePayload(NDEFHeader_t *ndef);
static uint16_t ndefTLVGetLength(uint8_t *data, size_t *indx) {
uint16_t len = 0;
if (data[0] == 0xff) {
@ -142,20 +153,122 @@ static int ndefDecodeHeader(uint8_t *data, size_t datalen, NDEFHeader_t *header)
}
static int ndefPrintHeader(NDEFHeader_t *header) {
PrintAndLogEx(INFO, "Header:");
PrintAndLogEx(INFO, _CYAN_("Header info"));
PrintAndLogEx(SUCCESS, "\tMessage Begin: %s", STRBOOL(header->MessageBegin));
PrintAndLogEx(SUCCESS, "\tMessage End: %s", STRBOOL(header->MessageEnd));
PrintAndLogEx(SUCCESS, "\tChunk Flag: %s", STRBOOL(header->ChunkFlag));
PrintAndLogEx(SUCCESS, "\tShort Record Bit: %s", STRBOOL(header->ShortRecordBit));
PrintAndLogEx(SUCCESS, "\tID Len Present: %s", STRBOOL(header->IDLenPresent));
PrintAndLogEx(SUCCESS, "\tType Name Format: [0x%02x] %s", header->TypeNameFormat, TypeNameFormat_s[header->TypeNameFormat]);
PrintAndLogEx(SUCCESS, " %s ....... Message begin", STRBOOL(header->MessageBegin));
PrintAndLogEx(SUCCESS, " %s ...... Message end", STRBOOL(header->MessageEnd));
PrintAndLogEx(SUCCESS, " %s ..... Chunk flag", STRBOOL(header->ChunkFlag));
PrintAndLogEx(SUCCESS, " %s .... Short record bit", STRBOOL(header->ShortRecordBit));
PrintAndLogEx(SUCCESS, " %s ... ID Len present", STRBOOL(header->IDLenPresent));
PrintAndLogEx(SUCCESS, "");
PrintAndLogEx(SUCCESS, "\tHeader length : %zu", header->len);
PrintAndLogEx(SUCCESS, "\tType length : %zu", header->TypeLen);
PrintAndLogEx(SUCCESS, "\tPayload length : %zu", header->PayloadLen);
PrintAndLogEx(SUCCESS, "\tID length : %zu", header->IDLen);
PrintAndLogEx(SUCCESS, "\tRecord length : %zu", header->RecLen);
PrintAndLogEx(SUCCESS, " Header length...... %zu", header->len);
PrintAndLogEx(SUCCESS, " Type length........ %zu", header->TypeLen);
PrintAndLogEx(SUCCESS, " Payload length..... %zu", header->PayloadLen);
PrintAndLogEx(SUCCESS, " ID length.......... %zu", header->IDLen);
PrintAndLogEx(SUCCESS, " Record length...... %zu", header->RecLen);
PrintAndLogEx(SUCCESS, " Type name format... [ 0x%02x ] " _YELLOW_("%s"), header->TypeNameFormat, TypeNameFormat_s[header->TypeNameFormat]);
return PM3_SUCCESS;
}
static const char *get_curve_name(mbedtls_ecp_group_id grp_id) {
switch (grp_id) {
case MBEDTLS_ECP_DP_NONE:
return "";
case MBEDTLS_ECP_DP_SECP192R1:
return "SECP192R1"; // Domain parameters for the 192-bit curve defined by FIPS 186-4 and SEC1
case MBEDTLS_ECP_DP_SECP224R1:
return "SECP224R1"; // Domain parameters for the 224-bit curve defined by FIPS 186-4 and SEC1
case MBEDTLS_ECP_DP_SECP256R1:
return "SECP256R1"; // Domain parameters for the 256-bit curve defined by FIPS 186-4 and SEC1
case MBEDTLS_ECP_DP_SECP384R1:
return "SECP384R1"; // Domain parameters for the 384-bit curve defined by FIPS 186-4 and SEC1
case MBEDTLS_ECP_DP_SECP521R1:
return "SECP521R1"; // Domain parameters for the 521-bit curve defined by FIPS 186-4 and SEC1
case MBEDTLS_ECP_DP_BP256R1:
return "BP256R1"; // Domain parameters for 256-bit Brainpool curve
case MBEDTLS_ECP_DP_BP384R1:
return "BP384R1"; // Domain parameters for 384-bit Brainpool curve
case MBEDTLS_ECP_DP_BP512R1:
return "BP512R1"; // Domain parameters for 512-bit Brainpool curve
case MBEDTLS_ECP_DP_CURVE25519:
return "CURVE25519"; // Domain parameters for Curve25519
case MBEDTLS_ECP_DP_SECP192K1:
return "SECP192K1"; // Domain parameters for 192-bit "Koblitz" curve
case MBEDTLS_ECP_DP_SECP224K1:
return "SECP224K1"; // Domain parameters for 224-bit "Koblitz" curve
case MBEDTLS_ECP_DP_SECP256K1:
return "SECP256K1"; // Domain parameters for 256-bit "Koblitz" curve
case MBEDTLS_ECP_DP_CURVE448:
return "CURVE448"; // Domain parameters for Curve448
case MBEDTLS_ECP_DP_SECP128R1:
return "SECP128R1"; // Domain parameters for the 128-bit curve used for NXP originality check
default :
return "";
}
return "";
}
typedef struct {
mbedtls_ecp_group_id grp_id;
uint8_t keylen;
const char *desc;
const char *value;
} PACKED ndef_publickey_t;
static int ndef_print_signature(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t sign_len) {
const ndef_publickey_t ndef_public_keys[] = {
{ MBEDTLS_ECP_DP_SECP256R1, 65, "Minecraft Earth", "04760200b60315f31ff7951d0892b87930c34967dfbf57763afc775fc56a22b601f7b8fd9e47519524505322435b07d0782463f39400a39a9dbc06bab2225c082a"},
};
uint8_t i;
int reason = 0;
bool is_valid = false;
for (i = 0; i < ARRAYLEN(ndef_public_keys); i++) {
int dl = 0;
uint8_t key[ndef_public_keys[i].keylen];
param_gethex_to_eol(ndef_public_keys[i].value, 0, key, ndef_public_keys[i].keylen, &dl);
int res = ecdsa_signature_r_s_verify(ndef_public_keys[i].grp_id, key, data, data_len, signature, sign_len, false);
is_valid = (res == 0);
if (is_valid) {
reason = 1;
break;
}
// try with sha256
res = ecdsa_signature_r_s_verify(ndef_public_keys[i].grp_id, key, data, data_len, signature, sign_len, true);
is_valid = (res == 0);
if (is_valid) {
reason = 2;
break;
}
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("NDEF Signature"));
if (is_valid == false || i == ARRAYLEN(ndef_public_keys)) {
PrintAndLogEx(INFO, " NDEF Signature: %s", sprint_hex_inrow(signature, 32));
PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed"));
return PM3_ESOFT;
}
PrintAndLogEx(INFO, " IC signature public key name: %s", ndef_public_keys[i].desc);
PrintAndLogEx(INFO, "IC signature public key value: %s", ndef_public_keys[i].value);
PrintAndLogEx(INFO, " Elliptic curve parameters: %s", get_curve_name(ndef_public_keys[i].grp_id));
PrintAndLogEx(INFO, " NDEF Signature: %s", sprint_hex_inrow(signature, 32));
PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful"));
switch (reason) {
case 1:
PrintAndLogEx(INFO, " Params used: signature, plain");
break;
case 2:
PrintAndLogEx(INFO, " Params used: signature, SHA256");
break;
}
return PM3_SUCCESS;
}
@ -251,6 +364,8 @@ static int ndefDecodeSig2(uint8_t *sig, size_t siglen) {
PrintAndLogEx(SUCCESS, "\tsignature : " _GREEN_("ECDSA-%d"), slen * 8);
PrintAndLogEx(SUCCESS, "\t\tr: %s", sprint_hex(&sig[indx], slen));
PrintAndLogEx(SUCCESS, "\t\ts: %s", sprint_hex(&sig[indx + slen], slen));
ndef_print_signature(NULL, 0, NULL, 0);
}
} else {
PrintAndLogEx(INFO, "\tsignature: unknown type");
@ -301,18 +416,169 @@ static int ndefDecodeSig(uint8_t *sig, size_t siglen) {
return PM3_ESOFT;
}
static int ndefDecodePayloadDeviceInfo(uint8_t *payload, size_t len) {
if (payload == NULL)
return PM3_EINVARG;
if (len < 1)
return PM3_EINVARG;
PrintAndLogEx(INFO, _CYAN_("Device information"));
uint8_t *p = payload;
p++;
uint8_t n = *(p++);
PrintAndLogEx(INFO, "Vendor........ " _YELLOW_("%.*s"), n, p);
p += n + 1;
n = *(p++);
PrintAndLogEx(INFO, "Model......... " _YELLOW_("%.*s"), n, p);
p += n + 1;
n = *(p++);
PrintAndLogEx(INFO, "Unique name... " _YELLOW_("%.*s"), n, p);
p += n + 1;
n = *(p++);
//uuid string
// record.uuid_string = '123e4567-e89b-12d3-a456-426655440000'
// 8-4-4-4-12
char uuid[37] = {0};
sprintf(uuid, "%s-", sprint_hex_inrow(p, 4));
p += 4;
sprintf(uuid + strlen(uuid), "%s-", sprint_hex_inrow(p, 2));
p += 2;
sprintf(uuid + strlen(uuid), "%s-", sprint_hex_inrow(p, 2));
p += 2;
sprintf(uuid + strlen(uuid), "%s-", sprint_hex_inrow(p, 2));
p += 2;
sprintf(uuid + strlen(uuid), "%s", sprint_hex_inrow(p, 6));
p += 6;
PrintAndLogEx(INFO, "UUID.......... " _YELLOW_("%s"), uuid);
p++;
n = *(p++);
PrintAndLogEx(INFO, "Version....... " _YELLOW_("%.*s"), n, p);
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
static int ndefDecodePayloadSmartPoster(uint8_t *ndef, size_t ndeflen, bool print, bool verbose) {
if (print) {
PrintAndLogEx(INFO, _YELLOW_("Well Known Record - Smartposter {"));
}
NDEFHeader_t NDEFHeader = {0};
int res = ndefDecodeHeader(ndef, ndeflen, &NDEFHeader);
if (res != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "decode header failed..");
return res;
}
if (verbose) {
ndefPrintHeader(&NDEFHeader);
}
if (NDEFHeader.TypeLen && NDEFHeader.PayloadLen) {
ndefDecodePayload(&NDEFHeader);
}
if (NDEFHeader.TypeLen) {
PrintAndLogEx(INFO, "Type data");
print_buffer(NDEFHeader.Type, NDEFHeader.TypeLen, 1);
}
if (NDEFHeader.IDLen) {
PrintAndLogEx(INFO, "ID data");
print_buffer(NDEFHeader.ID, NDEFHeader.IDLen, 1);
}
if (NDEFHeader.PayloadLen) {
PrintAndLogEx(INFO, "Payload data");
print_buffer(NDEFHeader.Payload, NDEFHeader.PayloadLen, 1);
}
// recursive
if (NDEFHeader.MessageEnd == false) {
ndefDecodePayloadSmartPoster(ndef + NDEFHeader.RecLen, ndeflen - NDEFHeader.RecLen, false, false);
}
if (print) {
PrintAndLogEx(INFO, _YELLOW_("}"));
}
return PM3_SUCCESS;
}
static int ndefDecodeMime_wifi(NDEFHeader_t *ndef) {
PrintAndLogEx(INFO, _CYAN_("WiFi details"));
if (ndef->PayloadLen > 1) {
PrintAndLogEx(INFO, ">>> decorder, to be implemented <<<");
}
return PM3_SUCCESS;
}
static int ndefDecodeMime_vcard(NDEFHeader_t *ndef) {
PrintAndLogEx(INFO, _CYAN_("VCARD details"));
if (ndef->PayloadLen > 1) {
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "%.*s", (int)ndef->PayloadLen, ndef->Payload);
}
return PM3_SUCCESS;
}
static int ndefDecodeMime_bt(NDEFHeader_t *ndef) {
PrintAndLogEx(INFO, "Type............ " _YELLOW_("%.*s"), (int)ndef->TypeLen, ndef->Type);
if (ndef->PayloadLen > 1) {
uint16_t ooblen = (ndef->Payload[1] << 8 | ndef->Payload[0]);
PrintAndLogEx(INFO, "OOB data len.... %u", ooblen);
PrintAndLogEx(INFO, "BT MAC.......... " _YELLOW_("%s"), sprint_hex(ndef->Payload + 2, 6));
// Let's check payload[8]. Tells us a bit about the UUID's. If 0x07 then it tells us a service UUID is 128bit
switch (ndef->Payload[8]) {
case 0x02:
PrintAndLogEx(INFO, "Optional Data... incomplete list 16-bit UUID's");
break;
case 0x03:
PrintAndLogEx(INFO, "Optional Data... complete list 16-bit UUID's");
break;
case 0x04:
PrintAndLogEx(INFO, "Optional Data... incomplete list 32-bit UUID's");
break;
case 0x05:
PrintAndLogEx(INFO, "Optional Data... complete list 32-bit UUID's");
break;
case 0x06:
PrintAndLogEx(INFO, "Optional Data... incomplete list 128-bit UUID's");
break;
case 0x07:
PrintAndLogEx(INFO, "Optional Data... complete list 128-bit UUID's");
break;
default:
PrintAndLogEx(INFO, "Optional Data... [ %02x ]", ndef->Payload[8]);
break;
}
// Let's check payload[9]. If 0x08 then SHORT_NAME or if 0x09 then COMPLETE_NAME
if (ndef->Payload[9] == 0x08) {
PrintAndLogEx(INFO, "Short name...... " _YELLOW_("%.*s"), (int)(ndef->PayloadLen - 10), ndef->Payload + 10);
} else if (ndef->Payload[9] == 0x09) {
PrintAndLogEx(INFO, "Complete name... " _YELLOW_("%.*s"), (int)(ndef->PayloadLen - 10), ndef->Payload + 10);
} else {
PrintAndLogEx(INFO, "[ %02x ]", ndef->Payload[9]);
}
PrintAndLogEx(NORMAL, "");
}
return PM3_SUCCESS;
}
static int ndefDecodePayload(NDEFHeader_t *ndef) {
PrintAndLogEx(INFO, "");
switch (ndef->TypeNameFormat) {
case tnfEmptyRecord:
PrintAndLogEx(INFO, "Empty Record");
if (ndef->TypeLen != 0 || ndef->IDLen != 0 || ndef->PayloadLen != 0) {
PrintAndLogEx(FAILED, "unexpected data in TNF_EMPTY record");
break;
}
break;
case tnfWellKnownRecord:
PrintAndLogEx(INFO, "Well Known Record");
PrintAndLogEx(INFO, "\ttype\t: %.*s", (int)ndef->TypeLen, ndef->Type);
if (!strncmp((char *)ndef->Type, "T", ndef->TypeLen)) {
PrintAndLogEx(INFO, _CYAN_("Text"));
uint8_t utf8 = (ndef->Payload[0] >> 7);
uint8_t lc_len = ndef->Payload[0] & 0x3F;
PrintAndLogEx(INFO,
"\tUTF %d\t: " _GREEN_("%.*s") ", " _GREEN_("%.*s"),
" UTF %d... " _GREEN_("%.*s") ", " _GREEN_("%.*s"),
(utf8 == 0) ? 8 : 16,
lc_len,
ndef->Payload + 1,
@ -322,8 +588,9 @@ static int ndefDecodePayload(NDEFHeader_t *ndef) {
}
if (!strncmp((char *)ndef->Type, "U", ndef->TypeLen)) {
PrintAndLogEx(INFO, _CYAN_("URL"));
PrintAndLogEx(INFO
, "\turi\t: " _GREEN_("%s%.*s")
, " uri... " _GREEN_("%s%.*s")
, (ndef->Payload[0] <= 0x23 ? URI_s[ndef->Payload[0]] : "[err]")
, (int)(ndef->PayloadLen - 1)
, &ndef->Payload[1]
@ -331,34 +598,76 @@ static int ndefDecodePayload(NDEFHeader_t *ndef) {
}
if (!strncmp((char *)ndef->Type, "Sig", ndef->TypeLen)) {
PrintAndLogEx(INFO, _CYAN_("Signature"));
ndefDecodeSig(ndef->Payload, ndef->PayloadLen);
}
if (!strncmp((char *)ndef->Type, "Sp", ndef->TypeLen)) {
ndefDecodePayloadSmartPoster(ndef->Payload, ndef->PayloadLen, true, false);
}
if (!strncmp((char *)ndef->Type, "Di", ndef->TypeLen)) {
ndefDecodePayloadDeviceInfo(ndef->Payload, ndef->PayloadLen);
}
if (!strncmp((char *)ndef->Type, "Hc", ndef->TypeLen)) {
PrintAndLogEx(INFO, _CYAN_("Handover carrier"));
PrintAndLogEx(INFO, "- decoder to be impl -");
}
if (!strncmp((char *)ndef->Type, "Hr", ndef->TypeLen)) {
PrintAndLogEx(INFO, _CYAN_("Handover request"));
PrintAndLogEx(INFO, "- decoder to be impl -");
}
if (!strncmp((char *)ndef->Type, "Hs", ndef->TypeLen)) {
PrintAndLogEx(INFO, _CYAN_("Handover select"));
PrintAndLogEx(INFO, "- decoder to be impl -");
}
if (!strncmp((char *)ndef->Type, "ac", ndef->TypeLen)) {
PrintAndLogEx(INFO, _CYAN_("Alternative carrier"));
PrintAndLogEx(INFO, "- decoder to be impl -");
}
break;
case tnfMIMEMediaRecord: {
PrintAndLogEx(INFO, "MIME Media Record");
if (ndef->TypeLen == 0) {
PrintAndLogEx(INFO, "type length is zero");
break;
}
char begin[ndef->TypeLen];
memcpy(begin, ndef->Type, ndef->TypeLen);
str_lower(begin);
if (str_startswith(begin, NDEF_WIFIAPPL)) {
ndefDecodeMime_wifi(ndef);
}
if (str_startswith(begin, NDEF_VCARDTEXT) || str_startswith(begin, NDEF_XVCARDTEXT)) {
ndefDecodeMime_vcard(ndef);
}
if (str_startswith(begin, NDEF_BLUEAPPL)) {
ndefDecodeMime_bt(ndef);
}
break;
}
case tnfAbsoluteURIRecord:
PrintAndLogEx(INFO, "Absolute URI Record");
PrintAndLogEx(INFO, "\ttype : %.*s", (int)ndef->TypeLen, ndef->Type);
PrintAndLogEx(INFO, "\tpayload : %.*s", (int)ndef->PayloadLen, ndef->Payload);
break;
case tnfEmptyRecord:
PrintAndLogEx(INFO, "Empty Record");
PrintAndLogEx(INFO, "\t -to be impl-");
break;
case tnfMIMEMediaRecord:
PrintAndLogEx(INFO, "MIME Media Record");
PrintAndLogEx(INFO, "\t -to be impl-");
PrintAndLogEx(INFO, " payload : %.*s", (int)ndef->PayloadLen, ndef->Payload);
break;
case tnfExternalRecord:
PrintAndLogEx(INFO, "External Record");
PrintAndLogEx(INFO, "\t -to be impl-");
break;
case tnfUnchangedRecord:
PrintAndLogEx(INFO, "Unchanged Record");
PrintAndLogEx(INFO, "\t -to be impl-");
PrintAndLogEx(INFO, "- decoder to be impl -");
break;
case tnfUnknownRecord:
PrintAndLogEx(INFO, "Unknown Record");
PrintAndLogEx(INFO, "\t -to be impl-");
PrintAndLogEx(INFO, "- decoder to be impl -");
break;
case tnfUnchangedRecord:
PrintAndLogEx(INFO, "Unchanged Record");
PrintAndLogEx(INFO, "- decoder to be impl -");
break;
}
return PM3_SUCCESS;
@ -371,20 +680,23 @@ static int ndefRecordDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen) {
return res;
ndefPrintHeader(&NDEFHeader);
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, _CYAN_("Payload info"));
if (NDEFHeader.TypeLen) {
PrintAndLogEx(INFO, "Type data:");
PrintAndLogEx(INFO, "Type data");
print_buffer(NDEFHeader.Type, NDEFHeader.TypeLen, 1);
}
if (NDEFHeader.IDLen) {
PrintAndLogEx(INFO, "ID data:");
PrintAndLogEx(INFO, "ID data");
print_buffer(NDEFHeader.ID, NDEFHeader.IDLen, 1);
}
if (NDEFHeader.PayloadLen) {
PrintAndLogEx(INFO, "Payload data:");
PrintAndLogEx(INFO, "Payload data");
print_buffer(NDEFHeader.Payload, NDEFHeader.PayloadLen, 1);
if (NDEFHeader.TypeLen)
ndefDecodePayload(&NDEFHeader);
}
if (NDEFHeader.TypeLen && NDEFHeader.PayloadLen) {
ndefDecodePayload(&NDEFHeader);
}
return PM3_SUCCESS;
@ -438,8 +750,6 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("NDEF parsing") " ----------------");
while (indx < ndefLen) {
PrintAndLogEx(INFO, "-----------------------------------------------------");
switch (ndef[indx]) {
case 0x00: {
indx++;
@ -462,11 +772,11 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) {
uint8_t Size = ndef[indx + 1];
uint8_t BytesLockedPerLockBit = (ndef[indx + 2] >> 4) & 0x0f;
uint8_t bytes_per_page = ndef[indx + 2] & 0x0f;
PrintAndLogEx(SUCCESS, " Pages addr (number of pages) : %d", pages_addr);
PrintAndLogEx(SUCCESS, "Byte offset (number of bytes) : %d", byte_offset);
PrintAndLogEx(SUCCESS, "Size in bits of the lock area : %d. bytes approx: %d", Size, Size / 8);
PrintAndLogEx(SUCCESS, " Number of bytes / page : %d", bytes_per_page);
PrintAndLogEx(SUCCESS, "Bytes Locked Per LockBit.");
PrintAndLogEx(SUCCESS, " Pages addr (number of pages)... %d", pages_addr);
PrintAndLogEx(SUCCESS, "Byte offset (number of bytes)... %d", byte_offset);
PrintAndLogEx(SUCCESS, "Size in bits of the lock area %d. bytes approx %d", Size, Size / 8);
PrintAndLogEx(SUCCESS, " Number of bytes / page... %d", bytes_per_page);
PrintAndLogEx(SUCCESS, "Bytes Locked Per LockBit");
PrintAndLogEx(SUCCESS, " number of bytes that each dynamic lock bit is able to lock: %d", BytesLockedPerLockBit);
}
indx += len;

View file

@ -243,6 +243,8 @@ void preferences_save_callback(json_t *root) {
JsonSaveStr(root, "logging.level", "NORMAL");
}
*/
JsonSaveInt(root, "client.exe.delay", session.client_exe_delay);
}
void preferences_load_callback(json_t *root) {
json_error_t up_error = {0};
@ -331,6 +333,9 @@ void preferences_load_callback(json_t *root) {
if (strncmp(tempStr, "extended", 8) == 0) session.device_debug_level = ddbEXTENDED;
}
*/
// client command execution delay
if (json_unpack_ex(root, &up_error, 0, "{s:i}", "client.exe.delay", &i1) == 0)
session.client_exe_delay = i1;
}
// Help Functions
@ -502,6 +507,11 @@ static void showBarModeState(prefShowOpt_t opt) {
}
}
static void showClientExeDelayState(void) {
PrintAndLogEx(INFO, " Cmd execution delay.... "_GREEN_("%u"), session.client_exe_delay);
}
static int setCmdEmoji(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "prefs set emoji ",
@ -718,6 +728,35 @@ static int setCmdDeviceDebug (const char *Cmd)
}
*/
static int setCmdExeDelay(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "prefs set clientdelay",
"Set presistent preference of delay before executing a command in the client",
"prefs set clientdelay --ms 0 --> unsets any delay\n"
"prefs set clientdelay --ms 1000 --> sets 1000ms delay"
);
void *argtable[] = {
arg_param_begin,
arg_int0(NULL, "ms", "<ms>", "delay in micro seconds"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
uint16_t new_value = (uint16_t)arg_get_int_def(ctx, 1, 0);
CLIParserFree(ctx);
if (session.client_exe_delay != new_value) {
showClientExeDelayState();
session.client_exe_delay = new_value;
showClientExeDelayState();
preferences_save();
} else {
showClientExeDelayState();
}
return PM3_SUCCESS;
}
static int setCmdHint(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "prefs set hints ",
@ -1045,9 +1084,26 @@ static int getCmdSavePaths(const char *Cmd) {
return PM3_SUCCESS;
}
static int getCmdExeDelay(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "prefs get clientdelay",
"Get preference of delay time before execution of a command in the client",
"prefs get clientdelay"
);
void *argtable[] = {
arg_param_begin,
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIParserFree(ctx);
showClientExeDelayState();
return PM3_SUCCESS;
}
static command_t CommandTableGet[] = {
{"barmode", getCmdBarMode, AlwaysAvailable, "Get bar mode preference"},
{"clientdebug", getCmdDebug, AlwaysAvailable, "Get client debug level preference"},
{"clientdelay", getCmdExeDelay, AlwaysAvailable, "Get client execution delay preference"},
{"color", getCmdColor, AlwaysAvailable, "Get color support preference"},
{"savepaths", getCmdSavePaths, AlwaysAvailable, "Get file folder "},
// {"devicedebug", getCmdDeviceDebug, AlwaysAvailable, "Get device debug level"},
@ -1061,6 +1117,7 @@ static command_t CommandTableSet[] = {
{"help", setCmdHelp, AlwaysAvailable, "This help"},
{"barmode", setCmdBarMode, AlwaysAvailable, "Set bar mode"},
{"clientdebug", setCmdDebug, AlwaysAvailable, "Set client debug level"},
{"clientdelay", setCmdExeDelay, AlwaysAvailable, "Set client execution delay"},
{"color", setCmdColor, AlwaysAvailable, "Set color support"},
{"emoji", setCmdEmoji, AlwaysAvailable, "Set emoji display"},
{"hints", setCmdHint, AlwaysAvailable, "Set hint display"},
@ -1120,8 +1177,9 @@ static int CmdPrefShow(const char *Cmd) {
showClientDebugState(prefShowNone);
showPlotSliderState(prefShowNone);
// showDeviceDebugState(prefShowNone);
showBarModeState(prefShowNone);
showClientExeDelayState();
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}

View file

@ -40,6 +40,7 @@ extern "C" void ShowGraphWindow(void) {
}
gui->ShowGraphWindow();
}
extern "C" void HideGraphWindow(void) {
@ -56,6 +57,36 @@ extern "C" void RepaintGraphWindow(void) {
gui->RepaintGraphWindow();
}
// hook up picture viewer
extern "C" void ShowPictureWindow(char *fn) {
if (!gui) {
// Show a notice if X11/XQuartz isn't available
#if defined(__MACH__) && defined(__APPLE__)
PrintAndLogEx(WARNING, "You appear to be on a MacOS device without XQuartz.\nYou may need to install XQuartz (https://www.xquartz.org/) to make the plot work.");
#else
PrintAndLogEx(WARNING, "You appear to be on an environment without an X11 server or without DISPLAY environment variable set.\nPlot may not work until you resolve these issues.");
#endif
return;
}
gui->ShowPictureWindow(fn);
}
extern "C" void HidePictureWindow(void) {
if (!gui)
return;
gui->HidePictureWindow();
}
extern "C" void RepaintPictureWindow(void) {
if (!gui)
return;
gui->RepaintPictureWindow();
}
extern "C" void MainGraphics(void) {
if (!gui)
return;

View file

@ -22,6 +22,12 @@ extern "C" {
void ShowGraphWindow(void);
void HideGraphWindow(void);
void RepaintGraphWindow(void);
// hook up picture viewer
void ShowPictureWindow(char *fn);
void HidePictureWindow(void);
void RepaintPictureWindow(void);
void MainGraphics(void);
void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool stayInCommandLoop);
void ExitGraphics(void);

View file

@ -12,6 +12,7 @@
#include <inttypes.h>
#include <stdbool.h>
#include <iostream>
//#include <QtCore>
#include <QPainterPath>
#include <QBrush>
#include <QPen>
@ -32,6 +33,7 @@
#include "graph.h"
#include "cmddata.h"
#include "util_darwin.h"
//#include "fileutils.h"
extern "C" int preferences_save(void);
@ -54,6 +56,19 @@ void ProxGuiQT::HideGraphWindow(void) {
emit HideGraphWindowSignal();
}
// emit picture viewer signals
void ProxGuiQT::ShowPictureWindow(char *fn) {
emit ShowPictureWindowSignal(fn);
}
void ProxGuiQT::RepaintPictureWindow(void) {
emit RepaintPictureWindowSignal();
}
void ProxGuiQT::HidePictureWindow(void) {
emit HidePictureWindowSignal();
}
void ProxGuiQT::Exit(void) {
emit ExitSignal();
}
@ -71,6 +86,7 @@ void ProxGuiQT::_ShowGraphWindow(void) {
plotwidget = new ProxWidget();
}
plotwidget->show();
}
void ProxGuiQT::_RepaintGraphWindow(void) {
@ -87,6 +103,70 @@ void ProxGuiQT::_HideGraphWindow(void) {
plotwidget->hide();
}
// picture viewer
void ProxGuiQT::_ShowPictureWindow(char *fn) {
if (!plotapp)
return;
if (fn == NULL)
return;
size_t slen = strlen(fn);
if (slen == 0)
return;
char *myfn = (char *)calloc(slen + 1, sizeof(uint8_t));
if (myfn == NULL)
return;
memcpy(myfn, fn, slen);
if (!pictureWidget) {
#if defined(__MACH__) && defined(__APPLE__)
makeFocusable();
#endif
pictureWidget = new PictureWidget();
}
QPixmap pm;
if (pm.load(myfn) == false) {
qWarning("Failed to load %s", myfn);
}
free(myfn);
free(fn);
//QPixmap newPixmap = pm.scaled(QSize(50,50), Qt::KeepAspectRatio);
//pm = pm.scaled(pictureController->lbl_pm->size(), Qt::KeepAspectRatio);
pictureController->lbl_pm->setPixmap(pm);
pictureController->lbl_pm->setScaledContents(false);
pictureController->lbl_pm->setAlignment(Qt::AlignCenter);
QString s = QString("w: %1 h: %2")
.arg(pm.size().width())
.arg(pm.size().height()
);
pictureController->lbl_sz->setText(s);
pictureWidget->show();
}
void ProxGuiQT::_RepaintPictureWindow(void) {
if (!plotapp || !pictureWidget)
return;
pictureWidget->update();
}
void ProxGuiQT::_HidePictureWindow(void) {
if (!plotapp || !pictureWidget)
return;
pictureWidget->hide();
}
void ProxGuiQT::_Exit(void) {
delete this;
}
@ -105,11 +185,30 @@ void ProxGuiQT::_StartProxmarkThread(void) {
void ProxGuiQT::MainLoop() {
plotapp = new QApplication(argc, argv);
// Setup the picture widget
pictureWidget = new PictureWidget();
pictureController = new Ui::PictureForm();
pictureController->setupUi(pictureWidget);
// pictureWidget->setAttribute(Qt::WA_DeleteOnClose,true);
// Set picture widget position if no settings.
if (session.preferences_loaded == false) {
// Move controller widget below plot
//pictureController->move(x(), y() + frameSize().height());
//pictureController->resize(size().width(), 200);
}
connect(this, SIGNAL(ShowGraphWindowSignal()), this, SLOT(_ShowGraphWindow()));
connect(this, SIGNAL(RepaintGraphWindowSignal()), this, SLOT(_RepaintGraphWindow()));
connect(this, SIGNAL(HideGraphWindowSignal()), this, SLOT(_HideGraphWindow()));
connect(this, SIGNAL(ExitSignal()), this, SLOT(_Exit()));
// hook up picture viewer signals
connect(this, SIGNAL(ShowPictureWindowSignal(char *)), this, SLOT(_ShowPictureWindow(char *)));
connect(this, SIGNAL(RepaintPictureWindowSignal()), this, SLOT(_RepaintPictureWindow()));
connect(this, SIGNAL(HidePictureWindowSignal()), this, SLOT(_HidePictureWindow()));
//start proxmark thread after starting event loop
QTimer::singleShot(200, this, SLOT(_StartProxmarkThread()));
@ -118,21 +217,51 @@ void ProxGuiQT::MainLoop() {
makeUnfocusable();
#endif
plotapp->exec();
}
ProxGuiQT::ProxGuiQT(int argc, char **argv, WorkerThread *wthread) : plotapp(NULL), plotwidget(NULL),
argc(argc), argv(argv), proxmarkThread(wthread) {
ProxGuiQT::ProxGuiQT(int argc, char **argv, WorkerThread *wthread) :
plotapp(NULL), plotwidget(NULL), pictureController(NULL), pictureWidget(NULL), argc(argc), argv(argv), proxmarkThread(wthread) {
}
ProxGuiQT::~ProxGuiQT(void) {
if (pictureController) {
delete pictureController;
pictureController = NULL;
}
if (pictureWidget) {
pictureWidget->close();
delete pictureWidget;
pictureWidget = NULL;
}
if (plotapp) {
plotapp->quit();
plotapp = NULL;
}
}
// -------------------------------------------------
// Slider Widget form based on a class to enable
// Event override functions
// -------------------------------------------------
PictureWidget::PictureWidget() {
// Set the initail postion and size from settings
// if (session.preferences_loaded)
// setGeometry(session.pw.x, session.pw.y, session.pw.w, session.pw.h);
// else
resize(400, 400);
}
void PictureWidget::closeEvent(QCloseEvent *event) {
this->hide();
event->ignore();
}
// -------------------------------------------------
// Slider Widget form based on a class to enable
// Event override functions
@ -197,6 +326,8 @@ void ProxWidget::vchange_dthr_down(int v) {
g_useOverlays = true;
RepaintGraphWindow();
}
ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) {
this->master = master;
// Set the initail postion and size from settings
@ -206,7 +337,7 @@ ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) {
resize(800, 400);
// Setup the controller widget
controlWidget = new SliderWidget(); //new QWidget();
controlWidget = new SliderWidget();
opsController = new Ui::Form();
opsController->setupUi(controlWidget);
//Due to quirks in QT Designer, we need to fiddle a bit
@ -242,7 +373,7 @@ ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) {
show();
// Set Slider/Overlay position if no settings.
if (!session.preferences_loaded) {
if (session.preferences_loaded == false) {
// Move controller widget below plot
controlWidget->move(x(), y() + frameSize().height());
controlWidget->resize(size().width(), 200);
@ -295,6 +426,7 @@ void ProxWidget::showEvent(QShowEvent *event) {
controlWidget->show();
else
controlWidget->hide();
plot->show();
}
void ProxWidget::moveEvent(QMoveEvent *event) {

View file

@ -22,6 +22,7 @@
#include <QtGui>
#include "ui/ui_overlays.h"
#include "ui/ui_image.h"
class ProxWidget;
@ -70,6 +71,14 @@ class SliderWidget : public QWidget {
SliderWidget();
};
// Added class for SliderWidget to allow move/resize event override
class PictureWidget : public QWidget {
protected:
void closeEvent(QCloseEvent *event);
public:
PictureWidget();
};
/**
* The window with plot and controls
*/
@ -81,8 +90,8 @@ class ProxWidget : public QWidget {
ProxGuiQT *master;
Plot *plot;
Ui::Form *opsController;
// QWidget *controlWidget;
SliderWidget *controlWidget;
public:
ProxWidget(QWidget *parent = 0, ProxGuiQT *master = NULL);
~ProxWidget(void);
@ -125,6 +134,9 @@ class ProxGuiQT : public QObject {
private:
QApplication *plotapp;
ProxWidget *plotwidget;
Ui::PictureForm *pictureController;
PictureWidget *pictureWidget;
int argc;
char **argv;
//void (*main_func)(void);
@ -136,6 +148,12 @@ class ProxGuiQT : public QObject {
void ShowGraphWindow(void);
void RepaintGraphWindow(void);
void HideGraphWindow(void);
// hook up picture viewer
void ShowPictureWindow(char *fn);
void HidePictureWindow(void);
void RepaintPictureWindow(void);
void MainLoop(void);
void Exit(void);
@ -143,6 +161,12 @@ class ProxGuiQT : public QObject {
void _ShowGraphWindow(void);
void _RepaintGraphWindow(void);
void _HideGraphWindow(void);
// hook up picture viewer
void _ShowPictureWindow(char *fn);
void _HidePictureWindow(void);
void _RepaintPictureWindow(void);
void _Exit(void);
void _StartProxmarkThread(void);
@ -151,6 +175,11 @@ class ProxGuiQT : public QObject {
void RepaintGraphWindowSignal(void);
void HideGraphWindowSignal(void);
void ExitSignal(void);
// hook up picture viewer signals
void ShowPictureWindowSignal(char *fn);
void HidePictureWindowSignal(void);
void RepaintPictureWindowSignal(void);
};
#endif // PROXGUI_QT

View file

@ -342,12 +342,12 @@ static int l_GetFromFlashMemSpiffs(lua_State *L) {
return returnToLuaWithError(L, "Filename missing or invalid");
// get size from spiffs itself !
SendCommandMIX(CMD_SPIFFS_STAT, 0, 0, 0, (uint8_t *)destfilename, 32);
SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)destfilename, 32);
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000))
if (!WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000))
return returnToLuaWithError(L, "No response from the device");
len = resp.oldarg[0];
len = resp.data.asDwords[0];
if (len == 0)
return returnToLuaWithError(L, "Filename invalid or empty");

View file

@ -48,6 +48,7 @@ typedef struct {
clientdebugLevel_t client_debug_level;
barMode_t bar_mode;
// uint8_t device_debug_level;
uint16_t client_exe_delay;
char *history_path;
pm3_device *current_device;
} session_arg_t;

33
client/src/ui/image.ui Normal file
View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PictureForm</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>Picture Viewer</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_1">
<item>
<widget class="QLabel" name="lbl_pm">
</widget>
</item>
<item>
<widget class="QLabel" name="lbl_sz">
<property name="text">
<string>Image size</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -25,7 +25,7 @@
#include "ui.h" // PrintAndLog
#define UTIL_BUFFER_SIZE_SPRINT 4097
#define UTIL_BUFFER_SIZE_SPRINT 8193
// global client debug variable
uint8_t g_debugMode = 0;
// global client disable logging variable
@ -450,7 +450,7 @@ void num_to_bytebits(uint64_t n, size_t len, uint8_t *dest) {
}
}
//least significant bit first
// least significant bit (lsb) first
void num_to_bytebitsLSBF(uint64_t n, size_t len, uint8_t *dest) {
for (size_t i = 0 ; i < len ; ++i) {
dest[i] = n & 1;

View file

@ -16,6 +16,8 @@
#include <endian.h>
#endif
#define ARRAY_LENGTH( array ) ( sizeof( array ) / sizeof( *( array ) ) )
// used for save/load files
#ifndef FILE_PATH_SIZE
# define FILE_PATH_SIZE 1000

View file

@ -190,10 +190,11 @@ The first time, your OS will ask you for pairing. The default PIN is
1234. If PIN is not typed in quickly, the client might timeout. Simply
restart it again after pairing.
If your OS doesn't prompt you for pairing, you can do it in command line, e.g. (again, replace with your addon MAC address):
If your OS doesn't prompt you for pairing or if the device connects and immediately disconnects, you can pair it in command line, e.g. (again, replace with your addon MAC address):
```sh
bluetoothctl
[bluetooth]# remove aa:bb:cc:dd:ee:ff
[bluetooth]# pairable on
[bluetooth]# scan on
Discovery started

View file

@ -973,7 +973,7 @@
},
"help": {
"command": "help",
"description": "help use `<command> help` for details of a command prefs { edit client/device preferences... } -------- ----------------------- technology ----------------------- analyse { analyse utils... } data { plot window / data buffer manipulation... } emv { emv iso-14443 / iso-7816... } hf { high frequency commands... } hw { hardware commands... } lf { low frequency commands... } nfc { nfc commands... } reveng { crc calculations from reveng software... } smart { smart card iso-7816 commands... } script { scripting commands... } trace { trace manipulation... } wiegand { wiegand format manipulation... } -------- ----------------------- general ----------------------- clear clear screen hints turn hints on / off msleep add a pause in milliseconds rem add a text line in log file quit exit exit program [=] session log e:\\proxspace\\pm3/.proxmark3/logs/log_20210615.txt --------------------------------------------------------------------------------------- auto available offline: no run lf search / hf search / data plot / data save",
"description": "help use `<command> help` for details of a command prefs { edit client/device preferences... } -------- ----------------------- technology ----------------------- analyse { analyse utils... } data { plot window / data buffer manipulation... } emv { emv iso-14443 / iso-7816... } hf { high frequency commands... } hw { hardware commands... } lf { low frequency commands... } nfc { nfc commands... } reveng { crc calculations from reveng software... } smart { smart card iso-7816 commands... } script { scripting commands... } trace { trace manipulation... } wiegand { wiegand format manipulation... } -------- ----------------------- general ----------------------- clear clear screen hints turn hints on / off msleep add a pause in milliseconds rem add a text line in log file quit exit exit program [=] session log e:\\proxspace\\pm3/.proxmark3/logs/log_20210709.txt --------------------------------------------------------------------------------------- auto available offline: no run lf search / hf search / data plot / data save",
"notes": [
"auto"
],
@ -1684,67 +1684,83 @@
},
"hf cipurse aread": {
"command": "hf cipurse aread",
"description": "read file attributes by file id with key id and key",
"description": "read file attributes by file id with key id and key. if no key is supplied, default key of 737373...7373 will be used",
"notes": [
"hf cipurse aread -f 2ff7 -> authenticate with keyid=1 and key = 7373...7373 and read file attributes with id 2ff7",
"hf cipurse aread -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> authenticate with specified key and read file attributes"
"hf cipurse aread --fid 2ff7 -> authenticate with keyid 1, read file attributes with id 2ff7",
"hf cipurse aread -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> authenticate keyid 2, read file attributes"
],
"offline": false,
"options": [
"-h, --help this help",
"-a, --apdu show apdu requests and responses",
"-v, --verbose show technical data",
"-n, --keyid <dec> key id",
"-k, --key <hex> key for authenticate",
"-f, --file <hex> file id",
"-n <dec> key id",
"-k, --key <hex> auth key",
"--fid <hex> file id",
"--noauth read file attributes without authentication",
"--sreq <plain|mac(default)|encode> communication reader-picc security level",
"--sresp <plain|mac(default)|encode> communication picc-reader security level",
"--sel-adf show info about adf itself",
"--sel-mf show info about master file"
],
"usage": "hf cipurse aread [-hav] [-n <dec>] [-k <hex>] [-f <hex>] [--noauth] [--sreq <plain|mac(default)|encode>] [--sresp <plain|mac(default)|encode>] [--sel-adf] [--sel-mf]"
"usage": "hf cipurse aread [-hav] [-n <dec>] [-k <hex>] [--fid <hex>] [--noauth] [--sreq <plain|mac(default)|encode>] [--sresp <plain|mac(default)|encode>] [--sel-adf] [--sel-mf]"
},
"hf cipurse auth": {
"command": "hf cipurse auth",
"description": "authenticate with key id and key",
"description": "authenticate with key id and key. if no key is supplied, default key of 737373...7373 will be used",
"notes": [
"hf cipurse auth -> authenticate with keyid=1 and key = 7373...7373",
"hf cipurse auth -n 2 -k 65656565656565656565656565656565 -> authenticate with key"
"hf cipurse auth -> authenticate with keyid 1, default key",
"hf cipurse auth -n 2 -k 65656565656565656565656565656565 -> authenticate keyid 2 with key"
],
"offline": false,
"options": [
"-h, --help this help",
"-a, --apdu show apdu requests and responses",
"-v, --verbose show technical data",
"-n, --keyid <dec> key id",
"-k, --key <hex> key for authenticate"
"-n <dec> key id",
"-k, --key <hex> auth key"
],
"usage": "hf cipurse auth [-hav] [-n <dec>] [-k <hex>]"
},
"hf cipurse default": {
"command": "hf cipurse default",
"description": "set default parameters for access to cipurse card",
"notes": [
"hf cipurse default -n 1 -k 65656565656565656565656565656565 --fid 2ff7 -> set key, key id and file id"
],
"offline": false,
"options": [
"-h, --help this help",
"--clear resets to defaults",
"-n <dec> key id",
"-k, --key <hex> authentication key",
"--fid <hex> file id"
],
"usage": "hf cipurse default [-h] [--clear] [-n <dec>] [-k <hex>] [--fid <hex>]"
},
"hf cipurse delete": {
"command": "hf cipurse delete",
"description": "read file by file id with key id and key",
"description": "read file by file id with key id and key. if no key is supplied, default key of 737373...7373 will be used",
"notes": [
"hf cipurse delete -f 2ff7 -> authenticate with keyid=1 and key = 7373...7373 and delete file with id 2ff7",
"hf cipurse delete -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> authenticate with specified key and delete file"
"hf cipurse delete --fid 2ff7 -> authenticate with keyid 1, delete file with id 2ff7",
"hf cipurse delete -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> authenticate keyid 2 and delete file"
],
"offline": false,
"options": [
"-h, --help this help",
"-a, --apdu show apdu requests and responses",
"-v, --verbose show technical data",
"-n, --keyid <dec> key id",
"-k, --key <hex> key for authenticate",
"-f, --file <hex> file id",
"-n <dec> key id",
"-k, --key <hex> auth key",
"--fid <hex> file id",
"--sreq <plain|mac(default)|encode> communication reader-picc security level",
"--sresp <plain|mac(default)|encode> communication picc-reader security level"
],
"usage": "hf cipurse delete [-hav] [-n <dec>] [-k <hex>] [-f <hex>] [--sreq <plain|mac(default)|encode>] [--sresp <plain|mac(default)|encode>]"
"usage": "hf cipurse delete [-hav] [-n <dec>] [-k <hex>] [--fid <hex>] [--sreq <plain|mac(default)|encode>] [--sresp <plain|mac(default)|encode>]"
},
"hf cipurse help": {
"command": "hf cipurse help",
"description": "help this help. --------------------------------------------------------------------------------------- hf cipurse info available offline: no get info from cipurse tags",
"description": "help this help. test tests --------------------------------------------------------------------------------------- hf cipurse info available offline: no get info from cipurse tags",
"notes": [
"hf cipurse info"
],
@ -1756,48 +1772,63 @@
},
"hf cipurse read": {
"command": "hf cipurse read",
"description": "read file by file id with key id and key",
"description": "read file by file id with key id and key. if no key is supplied, default key of 737373...7373 will be used",
"notes": [
"hf cipurse read -f 2ff7 -> authenticate with keyid=1 and key = 7373...7373 and read file with id 2ff7",
"hf cipurse read -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> authenticate with specified key and read file"
"hf cipurse read --fid 2ff7 -> authenticate with keyid 1, read file with id 2ff7",
"hf cipurse read -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> authenticate keyid 2 and read file"
],
"offline": false,
"options": [
"-h, --help this help",
"-a, --apdu show apdu requests and responses",
"-v, --verbose show technical data",
"-n, --keyid <dec> key id",
"-k, --key <hex> key for authenticate",
"-f, --file <hex> file id",
"-n <dec> key id",
"-k, --key <hex> auth key",
"--fid <hex> file id",
"-o, --offset <dec> offset for reading data from file",
"--noauth read file without authentication",
"--sreq <plain|mac(default)|encode> communication reader-picc security level",
"--sresp <plain|mac(default)|encode> communication picc-reader security level"
],
"usage": "hf cipurse read [-hav] [-n <dec>] [-k <hex>] [-f <hex>] [-o <dec>] [--noauth] [--sreq <plain|mac(default)|encode>] [--sresp <plain|mac(default)|encode>]"
"usage": "hf cipurse read [-hav] [-n <dec>] [-k <hex>] [--fid <hex>] [-o <dec>] [--noauth] [--sreq <plain|mac(default)|encode>] [--sresp <plain|mac(default)|encode>]"
},
"hf cipurse test": {
"command": "hf cipurse test",
"description": "[=] ------ cipurse tests ------ [=] kvv.............. passed [=] iso9797m2........ passed [=] smi.............. passed [=] mic.............. passed [=] auth............. passed [=] channel mac...... passed [=] channel encdec... passed [=] apdu............. passed [=] --------------------------- [+] tests [ ok ] ======================================================================================= hf epa { german identification card... } --------------------------------------------------------------------------------------- hf epa help available offline: yes help this help --------------------------------------------------------------------------------------- hf epa cnonces available offline: no tries to collect nonces when doing part of pace protocol.",
"notes": [
"hf epa cnonces --size 4 --num 4 --delay 1"
],
"offline": true,
"options": [
"-h, --help this help",
"--size <dec> nonce size",
"--num <dec> number of nonces to collect",
"-d, --delay <dec> delay between attempts"
],
"usage": "hf epa cnonces [-h] --size <dec> --num <dec> -d <dec>"
},
"hf cipurse write": {
"command": "hf cipurse write",
"description": "write file by file id with key id and key",
"description": "write file by file id with key id and key. if no key is supplied, default key of 737373...7373 will be used",
"notes": [
"hf cipurse write -f 2ff7 -> authenticate with keyid=1 and key = 7373...7373 and write file with id 2ff7",
"hf cipurse write -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> authenticate with specified key and write file"
"hf cipurse write --fid 2ff7 -> authenticate with keyid 1, write file with id 2ff7",
"hf cipurse write -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> authenticate keyid 2 and write file"
],
"offline": false,
"options": [
"-h, --help this help",
"-a, --apdu show apdu requests and responses",
"-v, --verbose show technical data",
"-n, --keyid <dec> key id",
"-k, --key <hex> key for authenticate",
"-f, --file <hex> file id",
"-n <dec> key id",
"-k, --key <hex> auth key",
"--fid <hex> file id",
"-o, --offset <dec> offset for reading data from file",
"--noauth read file without authentication",
"--sreq <plain|mac(default)|encode> communication reader-picc security level",
"--sresp <plain|mac(default)|encode> communication picc-reader security level",
"-c, --content <hex> new file content"
"-d, --data <hex> hex data to write to new file"
],
"usage": "hf cipurse write [-hav] [-n <dec>] [-k <hex>] [-f <hex>] [-o <dec>] [--noauth] [--sreq <plain|mac(default)|encode>] [--sresp <plain|mac(default)|encode>] [-c <hex>]"
"usage": "hf cipurse write [-hav] [-n <dec>] [-k <hex>] [--fid <hex>] [-o <dec>] [--noauth] [--sreq <plain|mac(default)|encode>] [--sresp <plain|mac(default)|encode>] [-d <hex>]"
},
"hf emrtd help": {
"command": "hf emrtd help",
@ -1854,21 +1885,6 @@
],
"usage": "hf emrtd list [-h1fcrux] [--dict <file>]..."
},
"hf epa help": {
"command": "hf epa help",
"description": "help this help --------------------------------------------------------------------------------------- hf epa cnonces available offline: no tries to collect nonces when doing part of pace protocol.",
"notes": [
"hf epa cnonces --size 4 --num 4 --delay 1"
],
"offline": true,
"options": [
"-h, --help this help",
"--size <dec> nonce size",
"--num <dec> number of nonces to collect",
"-d, --delay <dec> delay between attempts"
],
"usage": "hf epa cnonces [-h] --size <dec> --num <dec> -d <dec>"
},
"hf epa preplay": {
"command": "hf epa preplay",
"description": "perform pace protocol by replaying given apdus",
@ -2826,10 +2842,10 @@
"offline": false,
"options": [
"-h, --help this help",
"-f, --file <filename> specify a filename for dump file",
"-f, --file <fn> specify a filename for dump file",
"--de deobfuscate dump data (xor with mcc)"
],
"usage": "hf legic dump [-h] [-f <filename>] [--de]"
"usage": "hf legic dump [-h] [-f <fn>] [--de]"
},
"hf legic eload": {
"command": "hf legic eload",
@ -2860,11 +2876,11 @@
"offline": true,
"options": [
"-h, --help this help",
"-f, --file <filename> specify a filename to save",
"-f, --file <fn> specify a filename to save",
"-t, --type <dec> tag type",
"--deobfuscate de-obfuscate dump data (xor with mcc)"
],
"usage": "hf legic esave [-h] [-f <filename>] [-t <dec>] [--deobfuscate]"
"usage": "hf legic esave [-h] [-f <fn>] [-t <dec>] [--deobfuscate]"
},
"hf legic help": {
"command": "hf legic help",
@ -2996,9 +3012,9 @@
"offline": true,
"options": [
"-h, --help this help",
"-f, --file <filename> specify a filename for dumpfile"
"-f, --file <fn> specify a filename for dumpfile"
],
"usage": "hf lto dump [-h] [-f <filename>]"
"usage": "hf lto dump [-h] [-f <fn>]"
},
"hf lto info": {
"command": "hf lto info",
@ -3175,9 +3191,9 @@
"--4k mifare classic 4k / s70",
"--emu fill simulator keys from found keys",
"--dump dump found keys to binary file",
"-f, --file <filename> filename of dictionary"
"-f, --file <fn> filename of dictionary"
],
"usage": "hf mf chk [-hab*] [-k <hex>]... [--blk <dec>] [--mini] [--1k] [--2k] [--4k] [--emu] [--dump] [-f <filename>]"
"usage": "hf mf chk [-hab*] [-k <hex>]... [--blk <dec>] [--mini] [--1k] [--2k] [--4k] [--emu] [--dump] [-f <fn>]"
},
"hf mf cload": {
"command": "hf mf cload",
@ -3491,9 +3507,9 @@
"hf mf fchk --2k -k ffffffffffff -> key recovery against mifare 2k",
"hf mf fchk --4k -k ffffffffffff -> key recovery against mifare 4k",
"hf mf fchk --1k -f mfc_default_keys.dic -> target 1k using default dictionary file",
"hf mf fchk --1k --emu -> target 1k, write to emulator memory",
"hf mf fchk --1k --dump -> target 1k, write to file",
"hf mf fchk --1k --mem -> target 1k, use dictionary from flashmemory"
"hf mf fchk --1k --emu -> target 1k, write keys to emulator memory",
"hf mf fchk --1k --dump -> target 1k, write keys to file",
"hf mf fchk --1k --mem -> target 1k, use dictionary from flash memory"
],
"offline": false,
"options": [
@ -3553,6 +3569,23 @@
],
"usage": "hf mf gen3uid [-h] [-u <hex>]"
},
"hf mf gview": {
"command": "hf mf gview",
"description": "view `magic gen3 gtu` card memory",
"notes": [
"hf mf gview",
"hf mf gview --4k"
],
"offline": false,
"options": [
"-h, --help this help",
"--mini mifare classic mini / s20",
"--1k mifare classic 1k / s50 (def)",
"--2k mifare classic/plus 2k",
"--4k mifare classic 4k / s70"
],
"usage": "hf mf gview [-h] [--mini] [--1k] [--2k] [--4k]"
},
"hf mf hardnested": {
"command": "hf mf hardnested",
"description": "nested attack for hardened mifare classic cards. `--i<x>` set type of simd instructions. without this flag programs autodetect it. or hf mf hardnested -r --tk [known target key] add the known target key to check if it is present in the remaining key space hf mf hardnested --blk 0 -a -k a0a1a2a3a4a5 --tblk 4 --ta --tk ffffffffffff",
@ -3905,6 +3938,31 @@
],
"usage": "hf mf wrbl [-hab] --blk <dec> [-k <hex>] [-d <hex>]"
},
"hf mfdes auth": {
"command": "hf mfdes auth",
"description": "authenticates mifare desfire using key",
"notes": [
"hf mfdes auth -m 3 -t 4 -a 808301 -n 0 -k 00000000000000000000000000000000 -> aes,keynumber 0, aid 0x803201",
"hf mfdes auth -m 2 -t 2 -a 000000 -n 1 -k 00000000000000000000000000000000 -> 3des,keynumber 1, aid 0x000000",
"hf mfdes auth -m 1 -t 1 -a 000000 -n 2 -k 0000000000000000 -> des,keynumber 2, aid 0x000000",
"hf mfdes auth -m 1 -t 1 -a 000000 -n 0 -> des, defaultkey, aid 0x000000",
"hf mfdes auth -m 2 -t 2 -a 000000 -n 0 -> 3des, defaultkey, aid 0x000000",
"hf mfdes auth -m 3 -t 4 -a 000000 -n 0 -> 3k3des, defaultkey, aid 0x000000",
"hf mfdes auth -m 3 -t 4 -a 000000 -n 0 -> aes, defaultkey, aid 0x000000"
],
"offline": false,
"options": [
"-h, --help this help",
"-m, --type <type> auth type (1=normal, 2=iso, 3=aes)",
"-t, --algo <algo> crypt algo (1=des, 2=3des(2k2des), 3=3k3des, 4=aes)",
"-a, --aid <aid> aid used for authentification (hex 3 bytes)",
"-n, --keyno <keyno> key number used for authentification",
"-k, --key <key> key for checking (hex 8-24 bytes)",
"-d, --kdf <kdf> key derivation function (kdf) (0=none, 1=an10922, 2=gallagher)",
"-i, --kdfi <kdfi> kdf input (hex 1-31 bytes)"
],
"usage": "hf mfdes auth [-h] [-m <type>] [-t <algo>] [-a <aid>]... [-n <keyno>] [-k <key>] [-d <kdf>] [-i <kdfi>]"
},
"hf mfdes bruteaid": {
"command": "hf mfdes bruteaid",
"description": "recover aids by bruteforce. warning: this command takes a long time",
@ -4135,6 +4193,50 @@
],
"usage": "hf mfdes formatpicc [-h]"
},
"hf mfdes getaids": {
"command": "hf mfdes getaids",
"description": "get application ids list from card. master key needs to be provided.",
"notes": [
"hf mfdes getaids -n 0 -t des -k 0000000000000000 -f none -> execute with default factory setup"
],
"offline": false,
"options": [
"-h, --help this help",
"-a, --apdu show apdu requests and responses",
"-v, --verbose show technical data",
"-n, --keyno <keyno> key number",
"-t, --algo <des/2tdea/3tdea/aes> crypt algo: des, 2tdea, 3tdea, aes",
"-k, --key <key> key for authenticate (hex 8(des), 16(2tdea or aes) or 24(3tdea) bytes)",
"-f, --kdf <none/an10922/gallagher> key derivation function (kdf): none, an10922, gallagher",
"-i, --kdfi <kdfi> kdf input (hex 1-31 bytes)",
"-m, --cmode <plain/mac/encrypt> communicaton mode: plain/mac/encrypt",
"-c, --ccset <native/niso/iso> communicaton command set: native/niso/iso",
"-s, --schann <d40/ev1/ev2> secure channel: d40/ev1/ev2"
],
"usage": "hf mfdes getaids [-hav] [-n <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>]"
},
"hf mfdes getappnames": {
"command": "hf mfdes getappnames",
"description": "get application ids, iso ids and df names from card. master key needs to be provided.",
"notes": [
"hf mfdes getappnames -n 0 -t des -k 0000000000000000 -f none -> execute with default factory setup"
],
"offline": false,
"options": [
"-h, --help this help",
"-a, --apdu show apdu requests and responses",
"-v, --verbose show technical data",
"-n, --keyno <keyno> key number",
"-t, --algo <des/2tdea/3tdea/aes> crypt algo: des, 2tdea, 3tdea, aes",
"-k, --key <key> key for authenticate (hex 8(des), 16(2tdea or aes) or 24(3tdea) bytes)",
"-f, --kdf <none/an10922/gallagher> key derivation function (kdf): none, an10922, gallagher",
"-i, --kdfi <kdfi> kdf input (hex 1-31 bytes)",
"-m, --cmode <plain/mac/encrypt> communicaton mode: plain/mac/encrypt",
"-c, --ccset <native/niso/iso> communicaton command set: native/niso/iso",
"-s, --schann <d40/ev1/ev2> secure channel: d40/ev1/ev2"
],
"usage": "hf mfdes getappnames [-hav] [-n <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>]"
},
"hf mfdes getuid": {
"command": "hf mfdes getuid",
"description": "get uid from a mifare desfire tag",
@ -4163,28 +4265,23 @@
},
"hf mfdes help": {
"command": "hf mfdes help",
"description": "help this help list list desfire (iso 14443a) history --------------------------------------------------------------------------------------- hf mfdes auth available offline: no authenticates mifare desfire using key",
"description": "help this help list list desfire (iso 14443a) history --------------------------------------------------------------------------------------- hf mfdes default available offline: no get application ids, iso ids and df names from card. master key needs to be provided.",
"notes": [
"hf mfdes auth -m 3 -t 4 -a 808301 -n 0 -k 00000000000000000000000000000000 -> aes,keynumber 0, aid 0x803201",
"hf mfdes auth -m 2 -t 2 -a 000000 -n 1 -k 00000000000000000000000000000000 -> 3des,keynumber 1, aid 0x000000",
"hf mfdes auth -m 1 -t 1 -a 000000 -n 2 -k 0000000000000000 -> des,keynumber 2, aid 0x000000",
"hf mfdes auth -m 1 -t 1 -a 000000 -n 0 -> des, defaultkey, aid 0x000000",
"hf mfdes auth -m 2 -t 2 -a 000000 -n 0 -> 3des, defaultkey, aid 0x000000",
"hf mfdes auth -m 3 -t 4 -a 000000 -n 0 -> 3k3des, defaultkey, aid 0x000000",
"hf mfdes auth -m 3 -t 4 -a 000000 -n 0 -> aes, defaultkey, aid 0x000000"
"hf mfdes getappnames -n 0 -t des -k 0000000000000000 -f none -> execute with default factory setup"
],
"offline": true,
"options": [
"-h, --help this help",
"-m, --type <type> auth type (1=normal, 2=iso, 3=aes)",
"-t, --algo <algo> crypt algo (1=des, 2=3des(2k2des), 3=3k3des, 4=aes)",
"-a, --aid <aid> aid used for authentification (hex 3 bytes)",
"-n, --keyno <keyno> key number used for authentification",
"-k, --key <key> key for checking (hex 8-24 bytes)",
"-d, --kdf <kdf> key derivation function (kdf) (0=none, 1=an10922, 2=gallagher)",
"-i, --kdfi <kdfi> kdf input (hex 1-31 bytes)"
"-n, --keyno <keyno> key number",
"-t, --algo <des/2tdea/3tdea/aes> crypt algo: des, 2tdea, 3tdea, aes",
"-k, --key <key> key for authenticate (hex 8(des), 16(2tdea or aes) or 24(3tdea) bytes)",
"-f, --kdf <none/an10922/gallagher> key derivation function (kdf): none, an10922, gallagher",
"-i, --kdfi <kdfi> kdf input (hex 1-31 bytes)",
"-m, --cmode <plain/mac/encrypt> communicaton mode: plain/mac/encrypt",
"-c, --ccset <native/niso/iso> communicaton command set: native/niso/iso",
"-s, --schann <d40/ev1/ev2> secure channel: d40/ev1/ev2"
],
"usage": "hf mfdes auth [-h] [-m <type>] [-t <algo>] [-a <aid>]... [-n <keyno>] [-k <key>] [-d <kdf>] [-i <kdfi>]"
"usage": "hf mfdes default [-h] [-n <keyno>] [-t <des/2tdea/3tdea/aes>] [-k <key>] [-f <none/an10922/gallagher>] [-i <kdfi>] [-m <plain/mac/encrypt>] [-c <native/niso/iso>] [-s <d40/ev1/ev2>]"
},
"hf mfdes info": {
"command": "hf mfdes info",
@ -4219,12 +4316,12 @@
],
"usage": "hf mfdes list [-h1fcrux] [--dict <file>]..."
},
"hf mfdes readdata": {
"command": "hf mfdes readdata",
"hf mfdes read": {
"command": "hf mfdes read",
"description": "read data from file make sure to select aid or authenticate aid before running this command.",
"notes": [
"hf mfdes readdata -n 01 -t 0 -o 000000 -l 000000 -a 123456",
"hf mfdes readdata -n 01 -t 0 -> read all data from standard file, fileno 01"
"hf mfdes read -n 1 -t 0 -o 000000 -l 000000 -a 123456",
"hf mfdes read -n 1 -t 0 -> read all data from standard file, fileno 1"
],
"offline": false,
"options": [
@ -4235,7 +4332,7 @@
"-t, --type <dec> file type (0 = standard / backup, 1 = record)",
"-a, --aid <hex> app id to select (3 hex bytes, big endian)"
],
"usage": "hf mfdes readdata [-h] [-n <dec>] [-o <hex>]... [-l <hex>]... [-t <dec>] [-a <hex>]..."
"usage": "hf mfdes read [-h] [-n <dec>] [-o <hex>]... [-l <hex>]... [-t <dec>] [-a <hex>]..."
},
"hf mfdes selectaid": {
"command": "hf mfdes selectaid",
@ -4250,11 +4347,23 @@
],
"usage": "hf mfdes selectaid [-h] [-a <hex>]..."
},
"hf mfdes writedata": {
"command": "hf mfdes writedata",
"hf mfdes test": {
"command": "hf mfdes test",
"description": "[=] key num: 0 key algo: des key[8]: 00 00 00 00 00 00 00 00 [=] secure channel: n/a command set: niso communication mode: encrypt [+] setting isodep -> inactive [=] sending bytes to proxmark failed - offline [+] >>>> 90 5a 00 00 03 00 00 00 00 [+] setting isodep -> inactive [=] sending bytes to proxmark failed - offline [=] sending bytes to proxmark failed - offline [!] command execute timeout [+] setting isodep -> inactive [=] sending bytes to proxmark failed - offline ======================================================================================= hf seos { seos rfids... } --------------------------------------------------------------------------------------- hf seos help available offline: yes help this help list list seos history --------------------------------------------------------------------------------------- hf seos info available offline: no get info from seos tags",
"notes": [
"hf seos info"
],
"offline": false,
"options": [
"-h, --help this help"
],
"usage": "hf seos info [-h]"
},
"hf mfdes write": {
"command": "hf mfdes write",
"description": "write data to file make sure to select aid or authenticate aid before running this command.",
"notes": [
"hf mfdes writedata -n 01 -t 0 -o 000000 -d 3132333435363738"
"hf mfdes write -n 01 -t 0 -o 000000 -d 3132333435363738"
],
"offline": false,
"options": [
@ -4265,7 +4374,7 @@
"-t, --type <dec> file type (0 = standard / backup, 1 = record)",
"-a, --aid <hex> app id to select as hex bytes (3 bytes, big endian)"
],
"usage": "hf mfdes writedata [-h] [-n <dec>] [-o <hex>]... [-d <hex>]... [-t <dec>] [-a <hex>]..."
"usage": "hf mfdes write [-h] [-n <dec>] [-o <hex>]... [-d <hex>]... [-t <dec>] [-a <hex>]..."
},
"hf mfp auth": {
"command": "hf mfp auth",
@ -4726,21 +4835,10 @@
],
"offline": true,
"options": [
"-h, --help this help"
"-h, --help this help",
"-v, --verbose verbose output"
],
"usage": "hf search [-h]"
},
"hf seos help": {
"command": "hf seos help",
"description": "help this help list list seos history --------------------------------------------------------------------------------------- hf seos info available offline: no get info from seos tags",
"notes": [
"hf seos info"
],
"offline": true,
"options": [
"-h, --help this help"
],
"usage": "hf seos info [-h]"
"usage": "hf search [-hv]"
},
"hf seos list": {
"command": "hf seos list",
@ -5208,7 +5306,7 @@
"command": "hw setmux",
"description": "set the adc mux to a specific value",
"notes": [
"hw setmux --hiraw -> set high raw"
"hw setmux --hipkd -> set high peak"
],
"offline": false,
"options": [
@ -5602,16 +5700,16 @@
"description": "this command uses a dictionary attack against em4205/4305/4469/4569",
"notes": [
"lf em 4x05 chk",
"lf em 4x05 chk -e 000022b8 -> remember to use 0x for hex",
"lf em 4x05 chk -e 000022b8 -> check password 000022b8",
"lf em 4x05 chk -f t55xx_default_pwds -> use t55xx default dictionary"
],
"offline": false,
"options": [
"-h, --help this help",
"-f, --file <*.dic> loads a default keys dictionary file <*.dic>",
"-f, --file <fn> loads a default keys dictionary file <*.dic>",
"-e, --em <em4100> try the calculated password from some cloners based on em4100 id"
],
"usage": "lf em 4x05 chk [-h] [-f <*.dic>]... [-e <em4100>]"
"usage": "lf em 4x05 chk [-h] [-f <fn>] [-e <em4100>]"
},
"lf em 4x05 demod": {
"command": "lf em 4x05 demod",
@ -5643,19 +5741,19 @@
},
"lf em 4x05 help": {
"command": "lf em 4x05 help",
"description": "help this help demod demodulate a em4x05/em4x69 tag from the graphbuffer sniff attempt to recover em4x05 commands from sample buffer --------------------------------------------------------------------------------------- lf em 4x05 brute available offline: no this command tries to bruteforce the password of a em4205/4305/4469/4569",
"description": "help this help demod demodulate a em4x05/em4x69 tag from the graphbuffer sniff attempt to recover em4x05 commands from sample buffer --------------------------------------------------------------------------------------- lf em 4x05 brute available offline: no this command tries to bruteforce the password of a em4205/4305/4469/4569 the loop is running on device side, press proxmark3 button to abort",
"notes": [
"note: if you get many false positives, change position on the antennalf em 4x05 brute",
"lf em 4x05 brute -n 1 -> stop after first candidate found",
"lf em 4x05 brute -s 000022b8 -> remember to use 0x for hex"
"lf em 4x05 brute -s 000022aa -> start at 000022aa"
],
"offline": true,
"options": [
"-h, --help this help",
"-s, --start <pwd> start bruteforce enumeration from this password value",
"-n <digits> stop after having found n candidates. default: 0 => infinite"
"-s, --start <hex> start bruteforce enumeration from this password value",
"-n <dec> stop after having found n candidates. default: 0 (infinite)"
],
"usage": "lf em 4x05 brute [-h] [-s <pwd>] [-n <digits>]"
"usage": "lf em 4x05 brute [-h] [-s <hex>] [-n <dec>]"
},
"lf em 4x05 info": {
"command": "lf em 4x05 info",
@ -5760,7 +5858,7 @@
},
"lf em 4x50 chk": {
"command": "lf em 4x50 chk",
"description": "dictionary attack against em4x50.",
"description": "run dictionary key recovery against em4x50 card.",
"notes": [
"lf em 4x50 chk -> uses t55xx default dictionary",
"lf em 4x50 chk -f my.dic"
@ -5768,7 +5866,7 @@
"offline": false,
"options": [
"-h, --help this help",
"-f, --filename <fn> dictionary filename"
"-f, --file <fn> dictionary filename"
],
"usage": "lf em 4x50 chk [-h] [-f <fn>]"
},
@ -5784,7 +5882,7 @@
"offline": false,
"options": [
"-h, --help this help",
"-f, --filename <fn> dump filename (bin/eml/json)",
"-f, --file <fn> dump filename (bin/eml/json)",
"-p, --pwd <hex> password, 4 hex bytes, lsb"
],
"usage": "lf em 4x50 dump [-h] [-f <fn>] [-p <hex>]"
@ -5812,7 +5910,7 @@
"offline": false,
"options": [
"-h, --help this help",
"-f, --filename <fn> data filename"
"-f, --file <fn> save filename"
],
"usage": "lf em 4x50 esave [-h] [-f <fn>]"
},
@ -5830,7 +5928,7 @@
},
"lf em 4x50 help": {
"command": "lf em 4x50 help",
"description": "help this help --------------------------------------------------------------------------------------- lf em 4x50 brute available offline: no tries to bruteforce the password of a em4x50. function can be stopped by pressing pm3 button.",
"description": "help this help ----------- --------------------- operations --------------------- ----------- --------------------- simulation --------------------- --------------------------------------------------------------------------------------- lf em 4x50 brute available offline: no tries to bruteforce the password of a em4x50 card. function can be stopped by pressing pm3 button.",
"notes": [
"lf em 4x50 brute --first 12330000 --last 12340000 -> tries pwds from 0x12330000 to 0x1234000000"
],
@ -5913,7 +6011,7 @@
"options": [
"-h, --help this help",
"-u, --uid <hex> uid, 4 hex bytes, msb",
"-f, --filename <fn> dump filename (bin/eml/json)",
"-f, --file <fn> dump filename (bin/eml/json)",
"-p, --pwd <hex> password, 4 hex bytes, lsb"
],
"usage": "lf em 4x50 restore [-h] [-u <hex>] [-f <fn>] [-p <hex>]"
@ -5961,11 +6059,11 @@
],
"usage": "lf em 4x50 wrbl [-h] -b <dec> -d <hex> [-p <hex>]"
},
"lf em 4x50 writepwd": {
"command": "lf em 4x50 writepwd",
"lf em 4x50 wrpwd": {
"command": "lf em 4x50 wrpwd",
"description": "writes em4x50 password.",
"notes": [
"lf em 4x50 writepwd -p 4f22e7ff -n 12345678"
"lf em 4x50 wrpwd -p 4f22e7ff -n 12345678"
],
"offline": false,
"options": [
@ -5973,7 +6071,7 @@
"-p, --pwd <hex> password, 4 hex bytes, lsb",
"-n, --new <hex> new password, 4 hex bytes, lsb"
],
"usage": "lf em 4x50 writepwd [-h] -p <hex> -n <hex>"
"usage": "lf em 4x50 wrpwd [-h] -p <hex> -n <hex>"
},
"lf em 4x70 auth": {
"command": "lf em 4x70 auth",
@ -6416,9 +6514,9 @@
"offline": false,
"options": [
"-h, --help this help",
"-f, --filename <fn w/o ext> filename to load from"
"-f, --file <fn> filename to load ( w/o ext )"
],
"usage": "lf hitag cc [-h] [-f <fn w/o ext>]"
"usage": "lf hitag cc [-h] [-f <fn>]"
},
"lf hitag dump": {
"command": "lf hitag dump",
@ -6440,17 +6538,18 @@
"command": "lf hitag help",
"description": "help this help list list hitag trace history --------------------------------------------------------------------------------------- lf hitag eload available offline: no loads hitag tag dump into emulator memory on device",
"notes": [
"lf hitag eload -f lf-hitag-11223344-dump.bin"
"lf hitag eload -2 -f lf-hitag-11223344-dump.bin"
],
"offline": true,
"options": [
"-h, --help this help",
"-f, --file <filename> filename of dump",
"-1 simulate hitag1",
"-2 simulate hitag2",
"-s simulate hitags"
"-1 card type hitag1",
"-2 card type hitag2",
"-s card type hitags",
"-m card type hitagm"
],
"usage": "lf hitag eload [-h12s] -f <filename>"
"usage": "lf hitag eload [-h12sm] -f <filename>"
},
"lf hitag info": {
"command": "lf hitag info",
@ -7015,9 +7114,11 @@
"--qc quadrakey credential",
"--hc honeywell credential",
"--q5 optional - specify writing to q5/t5555 tag",
"--em optional - specify writing to em4305/4469 tag"
"--em optional - specify writing to em4305/4469 tag",
"--magic <hex> optional - magic hex data. 1 byte",
"--psk2 optional - specify writing a tag in psk2 modulation"
],
"usage": "lf nexwatch clone [-h] [-r <hex>] [--cn <dec>] [-m <dec>] [--nc] [--qc] [--hc] [--q5] [--em]"
"usage": "lf nexwatch clone [-h] [-r <hex>] [--cn <dec>] [-m <dec>] [--nc] [--qc] [--hc] [--q5] [--em] [--magic <hex>] [--psk2]"
},
"lf nexwatch help": {
"command": "lf nexwatch help",
@ -7061,9 +7162,11 @@
"-m, --mode <dec> mode (decimal) (0-15, defaults to 1)",
"--nc nexkey credential",
"--qc quadrakey credential",
"--hc honeywell credential"
"--hc honeywell credential",
"--magic <hex> optional - magic hex data. 1 byte",
"--psk2 optional - specify writing a tag in psk2 modulation"
],
"usage": "lf nexwatch sim [-h] [-r <hex>] [--cn <dec>] [-m <dec>] [--nc] [--qc] [--hc]"
"usage": "lf nexwatch sim [-h] [-r <hex>] [--cn <dec>] [-m <dec>] [--nc] [--qc] [--hc] [--magic <hex>] [--psk2]"
},
"lf noralsy clone": {
"command": "lf noralsy clone",
@ -7585,7 +7688,7 @@
},
"lf sniff": {
"command": "lf sniff",
"description": "sniff low frequency signal. - use `lf config` to set parameters. - use `data plot` to look at it",
"description": "sniff low frequency signal. you need to configure the lf part on the proxmark3 device manually. usually a trigger and skip samples is a good thing to set before doing a low frequency sniff. - use `lf config` to set parameters. - use `data plot` to look at sniff signal. - use `lf search -1` to see if signal can be automatic decoded",
"notes": [
"lf sniff -v",
"lf sniff -s 3000 -@ -> oscilloscope style"
@ -7748,7 +7851,7 @@
"offline": false,
"options": [
"-h, --help this help",
"-f, --filename <fn> filename (default is generated on blk 0)",
"-f, --file <fn> filename (default is generated on blk 0)",
"-o, --override override, force pwd read despite danger to card",
"-p, --pwd <hex> password (4 hex bytes)",
"--r0 downlink - fixed bit length",
@ -9178,8 +9281,8 @@
}
},
"metadata": {
"commands_extracted": 570,
"commands_extracted": 575,
"extracted_by": "PM3Help2JSON v1.00",
"extracted_on": "2021-06-15T12:04:40"
"extracted_on": "2021-07-09T11:42:14"
}
}

View file

@ -241,12 +241,14 @@ Check column "offline" for their availability.
|command |offline |description
|------- |------- |-----------
|`hf cipurse help `|Y |`This help.`
|`hf cipurse info `|N |`Info about Cipurse tag.`
|`hf cipurse auth `|N |`Authentication.`
|`hf cipurse read `|N |`Read binary file.`
|`hf cipurse write `|N |`Write binary file.`
|`hf cipurse aread `|N |`Read file attributes.`
|`hf cipurse delete `|N |`Delete file.`
|`hf cipurse info `|N |`Get info about CIPURSE tag`
|`hf cipurse auth `|N |`Authenticate CIPURSE tag`
|`hf cipurse read `|N |`Read binary file`
|`hf cipurse write `|N |`Write binary file`
|`hf cipurse aread `|N |`Read file attributes`
|`hf cipurse delete `|N |`Delete file`
|`hf cipurse default `|N |`Set default key and file id for all the other commands`
|`hf cipurse test `|Y |`Tests`
### hf epa
@ -445,6 +447,7 @@ Check column "offline" for their availability.
|`hf mf gen3uid `|N |`Set UID without changing manufacturer block`
|`hf mf gen3blk `|N |`Overwrite manufacturer block`
|`hf mf gen3freeze `|N |`Perma lock UID changes. irreversible`
|`hf mf gview `|N |`View card`
### hf mfp
@ -498,6 +501,7 @@ Check column "offline" for their availability.
|command |offline |description
|------- |------- |-----------
|`hf mfdes help `|Y |`This help`
|`hf mfdes default `|N |`[new]Set defaults for all the commands`
|`hf mfdes auth `|N |`Tries a MIFARE DesFire Authentication`
|`hf mfdes changekey `|N |`Change Key`
|`hf mfdes chk `|N |`Check keys`
@ -510,6 +514,8 @@ Check column "offline" for their availability.
|`hf mfdes createaid `|N |`Create Application ID`
|`hf mfdes deleteaid `|N |`Delete Application ID`
|`hf mfdes selectaid `|N |`Select Application ID`
|`hf mfdes getaids `|N |`[new]Get Application IDs list`
|`hf mfdes getappnames `|N |`[new]Get Applications list`
|`hf mfdes changevalue `|N |`Write value of a value file (credit/debit/clear)`
|`hf mfdes clearfile `|N |`Clear record File`
|`hf mfdes createfile `|N |`Create Standard/Backup File`
@ -518,8 +524,9 @@ Check column "offline" for their availability.
|`hf mfdes deletefile `|N |`Create Delete File`
|`hf mfdes dump `|N |`Dump all files`
|`hf mfdes getvalue `|N |`Get value of file`
|`hf mfdes readdata `|N |`Read data from standard/backup/record file`
|`hf mfdes writedata `|N |`Write data to standard/backup/record file`
|`hf mfdes read `|N |`Read data from standard/backup/record file`
|`hf mfdes write `|N |`Write data to standard/backup/record file`
|`hf mfdes test `|N |`Test crypto`
### hf seos
@ -722,21 +729,21 @@ Check column "offline" for their availability.
|command |offline |description
|------- |------- |-----------
|`lf em 4x50 help `|Y |`This help`
|`lf em 4x50 brute `|N |`guess password of EM4x50`
|`lf em 4x50 chk `|N |`check passwords from dictionary`
|`lf em 4x50 dump `|N |`dump EM4x50 tag`
|`lf em 4x50 info `|N |`tag information EM4x50`
|`lf em 4x50 login `|N |`login into EM4x50`
|`lf em 4x50 rdbl `|N |`read word data from EM4x50`
|`lf em 4x50 wrbl `|N |`write word data to EM4x50`
|`lf em 4x50 writepwd `|N |`change password of EM4x50`
|`lf em 4x50 wipe `|N |`wipe EM4x50 tag`
|`lf em 4x50 reader `|N |`show standard read mode data of EM4x50`
|`lf em 4x50 restore `|N |`restore EM4x50 dump to tag`
|`lf em 4x50 sim `|N |`simulate EM4x50 tag`
|`lf em 4x50 eload `|N |`upload dump of EM4x50 to emulator memory`
|`lf em 4x50 esave `|N |`save emulator memory to file`
|`lf em 4x50 eview `|N |`view EM4x50 content in emulator memory`
|`lf em 4x50 brute `|N |`Simple bruteforce attack to find password`
|`lf em 4x50 chk `|N |`Check passwords from dictionary`
|`lf em 4x50 dump `|N |`Dump EM4x50 tag`
|`lf em 4x50 info `|N |`Tag information`
|`lf em 4x50 login `|N |`Login into EM4x50 tag`
|`lf em 4x50 rdbl `|N |`Read EM4x50 word data`
|`lf em 4x50 reader `|N |`Show standard read mode data`
|`lf em 4x50 restore `|N |`Restore EM4x50 dump to tag`
|`lf em 4x50 wrbl `|N |`Write EM4x50 word data`
|`lf em 4x50 wrpwd `|N |`Change EM4x50 password`
|`lf em 4x50 wipe `|N |`Wipe EM4x50 tag`
|`lf em 4x50 eload `|N |`Upload EM4x50 dump to emulator memory`
|`lf em 4x50 esave `|N |`Save emulator memory to file`
|`lf em 4x50 eview `|N |`View EM4x50 content in emulator memory`
|`lf em 4x50 sim `|N |`Simulate EM4x50 tag`
### lf em 4x70

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