mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 13:53:55 -07:00
Merge branch 'master' into patch-1
This commit is contained in:
commit
f027a1fce6
125 changed files with 8299 additions and 1546 deletions
12
.github/ISSUE_TEMPLATE/checklist-for-release.md
vendored
12
.github/ISSUE_TEMPLATE/checklist-for-release.md
vendored
|
@ -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`
|
||||
|
|
38
CHANGELOG.md
38
CHANGELOG.md
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
12
README.md
12
README.md
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
|
|
415
armsrc/Standalone/hf_reblay.c
Normal file
415
armsrc/Standalone/hf_reblay.c
Normal 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();
|
||||
}
|
343
armsrc/Standalone/lf_nexid.c
Normal file
343
armsrc/Standalone/lf_nexid.c
Normal 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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 $^ > $@
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
6AC292FAA1315B4D858AB3A3D7D5933A
|
||||
404142434445464748494a4b4c4d4e4f
|
||||
3112B738D8862CCD34302EB299AAB456 # Gallagher AES (https://pastebin.com/GkbGLz8r)
|
||||
47454D5850524553534F53414D504C45 # Gemalto
|
||||
00112233445566778899aabbccddeeff
|
||||
2b7e151628aed2a6abf7158809cf4f3c
|
||||
fbeed618357133667c85e08f7236a8de
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
47454D5850524553534F53414D504C45 # Gemalto
|
||||
ffffffffffffffffffffffffffffffff
|
||||
00000000000000000000000000000000
|
||||
a0a1a2a3a4a5a6a7a0a1a2a3a4a5a6a7
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
376
client/src/cipurse/cipursetest.c
Normal file
376
client/src/cipurse/cipursetest.c
Normal 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;
|
||||
}
|
20
client/src/cipurse/cipursetest.h
Normal file
20
client/src/cipurse/cipursetest.h
Normal 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__ */
|
|
@ -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:
|
||||
|
|
|
@ -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...");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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}
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
1076
client/src/mifare/desfirecore.c
Normal file
1076
client/src/mifare/desfirecore.c
Normal file
File diff suppressed because it is too large
Load diff
58
client/src/mifare/desfirecore.h
Normal file
58
client/src/mifare/desfirecore.h
Normal 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
|
341
client/src/mifare/desfirecrypto.c
Normal file
341
client/src/mifare/desfirecrypto.c
Normal 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);
|
||||
}
|
111
client/src/mifare/desfirecrypto.h
Normal file
111
client/src/mifare/desfirecrypto.h
Normal 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
|
298
client/src/mifare/desfiresecurechan.c
Normal file
298
client/src/mifare/desfiresecurechan.c
Normal 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;
|
||||
}
|
||||
|
34
client/src/mifare/desfiresecurechan.h
Normal file
34
client/src/mifare/desfiresecurechan.h
Normal 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
|
228
client/src/mifare/desfiretest.c
Normal file
228
client/src/mifare/desfiretest.c
Normal 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;
|
||||
}
|
19
client/src/mifare/desfiretest.h
Normal file
19
client/src/mifare/desfiretest.h
Normal 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__ */
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
33
client/src/ui/image.ui
Normal 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>
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue