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,10 +52,14 @@ 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
|
||||
|
@ -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
|
||||
#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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -464,25 +480,30 @@ void SendCmdPCF7931(uint32_t *tab) {
|
|||
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();
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_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)
|
||||
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,11 +14,19 @@ vpath %.dic dictionaries
|
|||
OBJDIR = obj
|
||||
|
||||
ifeq ($(USE_BREW),1)
|
||||
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
|
||||
|
||||
###################
|
||||
# local libraries #
|
||||
|
@ -117,8 +125,13 @@ INCLUDES += $(HARDNESTEDLIBINC)
|
|||
|
||||
## Lua
|
||||
ifneq ($(SKIPLUASYSTEM),1)
|
||||
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,9 +292,13 @@ CXXINCLUDES += $(QTINCLUDES)
|
|||
## Readline
|
||||
ifneq ($(SKIPREADLINE),1)
|
||||
ifeq ($(USE_BREW),1)
|
||||
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
|
||||
endif
|
||||
|
@ -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)
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
]]..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",
|
||||
|
|
|
@ -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];
|
||||
} else {
|
||||
CipurseCAPDURespDecode(&cipurseContext, Result, *ResultLen, securedata, &rlen, &isw);
|
||||
memcpy(Result, securedata, rlen);
|
||||
}
|
||||
|
||||
if (ResultLen != NULL)
|
||||
*ResultLen = rlen;
|
||||
isw = result[0] * 0x0100 + result[1];
|
||||
|
||||
if (sw != NULL)
|
||||
} else {
|
||||
CipurseCAPDURespDecode(&cipurseContext, result, *result_len, securedata, &rlen, &isw);
|
||||
memcpy(result, securedata, rlen);
|
||||
}
|
||||
|
||||
if (result_len != NULL) {
|
||||
*result_len = rlen;
|
||||
}
|
||||
|
||||
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,39 +234,40 @@ 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");
|
||||
PrintAndLogEx(INFO, "Manufacturer... n/a");
|
||||
else
|
||||
PrintAndLogEx(INFO, "Manufacturer: %s", getTagInfo(data)); // getTagInfo from cmfhf14a.h
|
||||
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]);
|
||||
|
@ -278,102 +301,112 @@ 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,9 +11,9 @@
|
|||
#ifndef __CIPURSECORE_H__
|
||||
#define __CIPURSECORE_H__
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <jansson.h>
|
||||
#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,47 +452,42 @@ 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"),
|
||||
|
@ -478,31 +499,31 @@ static int CmdHFCipurseReadFileAttr(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, 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;
|
||||
}
|
||||
|
||||
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,8 +2576,8 @@ 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 --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[] = {
|
||||
|
@ -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 -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,7 +363,7 @@ 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"
|
||||
);
|
||||
|
@ -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,7 +407,7 @@ 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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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_custom_magic) {
|
||||
magic = magic_arg[0];
|
||||
} else {
|
||||
if (use_nexkey)
|
||||
magic = 0x88;
|
||||
|
||||
if (use_quadrakey)
|
||||
magic = 0xBE;
|
||||
|
||||
if (use_unk)
|
||||
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) {
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1295,9 +1295,10 @@ int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, u
|
|||
// 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,16 +288,18 @@ 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) {
|
||||
int kbs = key_block_size(key);
|
||||
|
@ -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,19 +680,22 @@ 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)
|
||||
}
|
||||
if (NDEFHeader.TypeLen && NDEFHeader.PayloadLen) {
|
||||
ndefDecodePayload(&NDEFHeader);
|
||||
}
|
||||
|
||||
|
@ -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,8 +3507,8 @@
|
|||
"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 --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,
|
||||
|
@ -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
|
||||
|
|
|
@ -14,7 +14,8 @@ Useful docs:
|
|||
* [MIFARE Classic DirectWrite, FUID version aka 1-write](#mifare-classic-directwrite-fuid-version-aka-1-write)
|
||||
* [MIFARE Classic DirectWrite, UFUID version](#mifare-classic-directwrite-ufuid-version)
|
||||
* [MIFARE Classic, other versions](#mifare-classic-other-versions)
|
||||
* [MIFARE Classic APDU aka Gen3](#mifare-classic-apdu-aka-gen3)
|
||||
* [MIFARE Classic Gen3 aka APDU](#mifare-classic-gen3-aka-apdu)
|
||||
* [MIFARE Classic Gen3 aka GTU](#mifare-classic-gen3-aka-gtu)
|
||||
* [MIFARE Classic Super](#mifare-classic-super)
|
||||
- [MIFARE Ultralight](#mifare-ultralight)
|
||||
* [MIFARE Ultralight blocks 0..2](#mifare-ultralight-blocks-02)
|
||||
|
@ -400,7 +401,7 @@ hf 14a raw -c 85000000000000000000000000000008
|
|||
* ZXUID, EUID, ICUID ?
|
||||
* Some cards exhibit a specific SAK=28 ??
|
||||
|
||||
## MIFARE Classic APDU aka Gen3
|
||||
## MIFARE Classic Gen3 aka APDU
|
||||
|
||||
### Identify
|
||||
|
||||
|
@ -467,6 +468,154 @@ hf 14a raw -s -c -t 2000 90F0CCCC10 041219c3219316984200e32000000000
|
|||
# lock (uid/block0?) forever:
|
||||
hf 14a raw -s -c 90FD111100
|
||||
```
|
||||
---------
|
||||
## MIFARE Classic Gen3 aka GTU
|
||||
|
||||
### Identify
|
||||
|
||||
Tag doesn't get identified by latest Proxmark3 client correct but instead mislabled as Gen2/CUID
|
||||
|
||||
```
|
||||
hf 14a info
|
||||
...
|
||||
[+] Magic capabilities : Gen 2 / CUID
|
||||
```
|
||||
|
||||
|
||||
### Magic commands
|
||||
|
||||
Android compatible - unknown status
|
||||
|
||||
* issue special APDUs
|
||||
|
||||
```
|
||||
cla ins p1 p2 len
|
||||
CF 00 00 00 00 32 <param>
|
||||
CF 00 00 00 00 35 <ATQA><SAK>
|
||||
CF 00 00 00 00 34 <length><ATS>
|
||||
CF 00 00 00 00 68 <param>
|
||||
CF 00 00 00 00 69 <00>
|
||||
CF 00 00 00 00 CE <block number> // Backdoor read card
|
||||
CF 00 00 00 00 CD <block number><single block data> // Backdoor write card
|
||||
CF 00 00 00 00 FE aabbccdd // set password to AABBCCDD
|
||||
CF 00 00 00 00 F1 // fuse is set ???
|
||||
CF 00 00 00 00 F0 // fuse ???
|
||||
```
|
||||
Note: It doesn't seem to follow a APDU structure per default,
|
||||
|
||||
|
||||
### Characteristics
|
||||
|
||||
* UID: 4b, 7b and 10b versions
|
||||
* ATQA/SAK: changeable
|
||||
* BCC:
|
||||
* ATS: changable
|
||||
* Card Type: changeable
|
||||
* Shadow mode: GTU
|
||||
* Backdoor password mode:
|
||||
|
||||
#### Possible card types
|
||||
Known Preset Change Available:
|
||||
* MIFARE Mini
|
||||
* MIFARE 1k S50 4 byte UID
|
||||
* MIFARE 1k S50 7 byte UID
|
||||
* MIFARE 1k S50 10 byte UID
|
||||
* MIFARE 4k S70 4 byte UID
|
||||
* MIFARE 4k S70 7 byte UID
|
||||
* MIFARE 4k S70 10 byte UID
|
||||
* Ultralight
|
||||
* Ultralight-C
|
||||
* Ultralight Ev1
|
||||
* NTAG
|
||||
|
||||
|
||||
|
||||
### Proxmark3 commands
|
||||
|
||||
```
|
||||
# view contents of tag memory:
|
||||
hf mf gview
|
||||
```
|
||||
|
||||
Equivalent:
|
||||
|
||||
|
||||
change ATQA / SAK
|
||||
=================
|
||||
```
|
||||
hf 14a raw -s -c -t 1000 cf0000000035<ATQA><SAK>
|
||||
hf 14a raw -s -c -t 1000 cf0000000035440028 // ATQA 00 44 SAK 28
|
||||
```
|
||||
|
||||
change ATS
|
||||
==========
|
||||
It should be noted that when SAK=20,28, ATS must be turned on, otherwise the card will not be recognized!
|
||||
|
||||
```
|
||||
hf 14a raw -s -c -t 1000 cf0000000034<length><ATS>
|
||||
hf 14a raw -s -c -t 1000 cf000000003406067577810280 // ATS to 0606757781028002F0
|
||||
```
|
||||
* length 0, ATS is not sent
|
||||
* when SAK is 20 or 28, ATS must be set, otherwise the card cannot be read
|
||||
* the last two digits of ATS are CRC and cannot be counted as length
|
||||
|
||||
set UID length (4, 7, 10)
|
||||
=========================
|
||||
```
|
||||
hf 14a raw -s -c -t 1000 cf0000000068<Param>
|
||||
hf 14a raw -s -c -t 1000 cf000000006801 // set UID length to 7 bytes
|
||||
```
|
||||
* param=00: 4 bytes 01: 7 bytes 02: 10 bytes
|
||||
|
||||
Set 14443A/B-UID
|
||||
================
|
||||
--> missing instructions.
|
||||
|
||||
|
||||
|
||||
Set Ultralight mode
|
||||
===================
|
||||
```
|
||||
hf 14a raw -s -c -t 1000 cf0000000069<00>
|
||||
```
|
||||
* 01: UL agreement opened
|
||||
* 00: UL agreement closed
|
||||
* in this mode, if SAK=00 ATQA=0044 (Pm3 sequence), it can become an Ultralight card
|
||||
|
||||
|
||||
set shadow mode (GTU)
|
||||
=====================
|
||||
|
||||
This mode is divided into four states: off (pre-write), on (on restore), don’t care, and high-speed read and write.
|
||||
If you use it, please enter the pre-write mode first. At this time, write the full card data.
|
||||
After writing, set it to on. At this time, after writing the data, the first time you read the data just written, the next time you read It is the pre-written data. All modes support this operation. It should be noted that using any block to read and write in this mode may give wrong results.
|
||||
|
||||
```
|
||||
hf 14a raw -s -c -t 1000 cf0000000032<param>
|
||||
```
|
||||
Param | Description
|
||||
00 | Closed, shadow data can be written at this time
|
||||
01 | Open, start restore
|
||||
02 | Turn it off completely, as a normal card
|
||||
03 | High-speed read and write mode
|
||||
|
||||
Any block read and write
|
||||
========================
|
||||
Using the backdoor command can read and write any area without password, similar to UID card, it should be noted that this command must be used to modify UID.
|
||||
|
||||
```
|
||||
hf 14a raw -s -c -t 1000 cf00000000CE<block number> // Backdoor read card
|
||||
hf 14a raw -s -c -t 1000 cf00000000CD<block number><single block data> // Backdoor write card
|
||||
```
|
||||
|
||||
set backdoor password
|
||||
=====================
|
||||
All backdoor operations are protected by passwords. If password is forgotten, the card will be scrapped
|
||||
default password is 00000000
|
||||
```
|
||||
hf 14a raw -s -c -t 1000 cf00000000feaabbccdd // set password to AABBCCDD
|
||||
```
|
||||
|
||||
|
||||
## MIFARE Classic Super
|
||||
|
||||
|
|
|
@ -30,11 +30,12 @@ For further questions about Mac & Homebrew, contact [\@Chrisfu on Twitter](https
|
|||
2. Tap this repo: `brew tap RfidResearchGroup/proxmark3`
|
||||
|
||||
3. Install Proxmark3:
|
||||
- (Optional) `export HOMEBREW_PROXMARK3_PLATFORM=xxxxxx` to specify [platform](https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md#platform), default value is `PM3RDV4` if none
|
||||
- `brew install proxmark3` for stable release
|
||||
- `brew install --HEAD proxmark3` for latest non-stable from GitHub (use this if previous command fails)
|
||||
- `brew install --with-blueshark proxmark3` for blueshark support, stable release
|
||||
- `brew install --HEAD --with-blueshark proxmark3` for blueshark support, latest non-stable from GitHub (use this if previous command fails)
|
||||
- `brew install --with-generic proxmark3`: for generic (non-RDV4) devices ([platform](https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md#platform)), stable release
|
||||
- `brew install --HEAD --with-generic proxmark3`: for generic (non-RDV4) devices ([platform](https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md#platform)), latest non-stable from GitHub (use this if previous command fails)
|
||||
|
||||
For more info, go to https://github.com/RfidResearchGroup/homebrew-proxmark3
|
||||
|
||||
|
|
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