Merge branch 'master' into 4x50_fix

update 210223
This commit is contained in:
tharexde 2021-02-23 22:35:13 +01:00
commit b37c32677c
47 changed files with 1515 additions and 828 deletions

View file

@ -25,6 +25,12 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: brew install readline qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc run: brew install readline qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc
- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install setuptools ansicolors sslcrypto
if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi
- name: make clean - name: make clean
run: make clean run: make clean
@ -60,6 +66,12 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: brew install readline qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc run: brew install readline qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc
- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install setuptools ansicolors sslcrypto
if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi
- name: make clean - name: make clean
run: make clean run: make clean
@ -96,6 +108,12 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: brew install readline qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc run: brew install readline qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc
- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install setuptools ansicolors sslcrypto
if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi
- name: Prepare Build Folders - name: Prepare Build Folders
run: mkdir -p client/build run: mkdir -p client/build

View file

@ -15,6 +15,13 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0-dbg liblua5.2-0 lua5.2 sed run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0-dbg liblua5.2-0 lua5.2 sed
- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install setuptools
python3 -m pip install ansicolors sslcrypto
if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi
- name: make clean - name: make clean
run: make clean run: make clean
@ -40,6 +47,13 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0-dbg liblua5.2-0 lua5.2 sed run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0-dbg liblua5.2-0 lua5.2 sed
- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install setuptools
python3 -m pip install ansicolors sslcrypto
if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi
- name: make clean - name: make clean
run: make clean run: make clean
@ -66,6 +80,13 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0-dbg liblua5.2-0 lua5.2 sed run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0-dbg liblua5.2-0 lua5.2 sed
- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install setuptools
python3 -m pip install ansicolors sslcrypto
if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi
- name: Prepare Build Folders - name: Prepare Build Folders
run: mkdir -p client/build run: mkdir -p client/build

View file

@ -3,11 +3,20 @@ 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... 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] ## [unreleased][unreleased]
- Added parsing of EF_CardAccess to display PACE algorithm, version and parameter in `hf emrtd info` (@aveao)
- Change, numerous commands more uses cliparser (@tcprst, @iceman1001)
- Added more originality public keys (@anon)
- Added `hf 14a info` - now also verify MFC Ev1 signatures (@iceman1001)
- Added `LF_THAREXDE` standalone mode which simulates and reads EM4x50 cards (@tharexde) - Added `LF_THAREXDE` standalone mode which simulates and reads EM4x50 cards (@tharexde)
- Added `hf jooki` commands (@iceman1001) - Added `hf jooki` commands (@iceman1001)
- Changed `lf hid clone` - also accepts binary wiegand (@iceman1001)
- Changed `wiegand encode` - format param is now optional, w/o it will try encode all formats (@iceman1001) - Changed `wiegand encode` - format param is now optional, w/o it will try encode all formats (@iceman1001)
- Fix cppchecker warnings (@iceman1001) - Fix cppchecker warnings (@iceman1001)
- Added `trace list -t mf` - now can use external dictionary keys file - Added `trace list -t mf` - now can use external dictionary keys file (@McEloff)
- Fix `lf gallagher read` - now correctly decodes card data
- Add support to `lf gallagher clone` and `lf gallagher sim` for writing specific card region, facility, card & issue numbers (@DarkMatterMatt)
- Added support for older vid/pid detection (@Gator96100)
- Added `hf mfdes bruteaid` - proper bruteforce of DESFire AID when no reading of files is possible (@craftbyte)
- Added support for bidirectional communication for `lf em 4x50 sim` (@tharexde) - Added support for bidirectional communication for `lf em 4x50 sim` (@tharexde)
- Change `PLATFORM=PM3OTHER` to `PLATFORM=PM3GENERIC` (@iceman1001) - Change `PLATFORM=PM3OTHER` to `PLATFORM=PM3GENERIC` (@iceman1001)
- Added `tools/hitag2crack/crack5opencl`, an optimized version of `crack5gpu` (@matrix) - Added `tools/hitag2crack/crack5opencl`, an optimized version of `crack5gpu` (@matrix)
@ -133,8 +142,6 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Change, use system Whereami library if available (@doegox) - Change, use system Whereami library if available (@doegox)
- Change, use system Zlib library if available (@doegox) - Change, use system Zlib library if available (@doegox)
- Fix release version information (@doegox) - Fix release version information (@doegox)
- Fix `lf gallagher read` - now correctly decodes card data
- Add support to `lf gallagher clone` and `lf gallagher sim` for writing specific card region, facility, card & issue numbers (@DarkMatterMatt)
## [ice coffee.4.9237][2020-05-21] ## [ice coffee.4.9237][2020-05-21]
- Updated documentation (@doegox, @iceman1001) - Updated documentation (@doegox, @iceman1001)

View file

@ -77,7 +77,7 @@ quite a lot, see the [Changelog file](CHANGELOG.md) which we try to keep updated
This repo compiles nicely on This repo compiles nicely on
- Proxspace v3.x - Proxspace v3.x
- [latest release v3.7.2](https://github.com/Gator96100/ProxSpace/releases) - [latest release v3.7.3](https://github.com/Gator96100/ProxSpace/releases)
- Windows/mingw environment with Qt5.6.1 & GCC 4.9 - Windows/mingw environment with Qt5.6.1 & GCC 4.9
- Ubuntu 16.04 -> 20.04 - Ubuntu 16.04 -> 20.04
- ParrotOS, Gentoo, Pentoo, Kali, Nethunter, Archlinux, Fedora, Debian - ParrotOS, Gentoo, Pentoo, Kali, Nethunter, Archlinux, Fedora, Debian

View file

@ -14,10 +14,18 @@ add_library(pm3rrg_rdv4_amiibo STATIC
if (NOT TARGET pm3rrg_rdv4_mbedtls) if (NOT TARGET pm3rrg_rdv4_mbedtls)
include(mbedtls.cmake) include(mbedtls.cmake)
endif() endif()
target_link_libraries(pm3rrg_rdv4_amiibo PRIVATE target_link_libraries(pm3rrg_rdv4_amiibo PRIVATE
m m
pm3rrg_rdv4_mbedtls) pm3rrg_rdv4_mbedtls)
target_include_directories(pm3rrg_rdv4_amiibo PRIVATE ../../include ../../common)
target_include_directories(pm3rrg_rdv4_amiibo INTERFACE amiitool)
target_compile_options(pm3rrg_rdv4_amiibo PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_amiibo PRIVATE -Wall -Werror -O3)
set_property(TARGET pm3rrg_rdv4_amiibo PROPERTY POSITION_INDEPENDENT_CODE ON) set_property(TARGET pm3rrg_rdv4_amiibo PROPERTY POSITION_INDEPENDENT_CODE ON)
target_include_directories(pm3rrg_rdv4_amiibo PRIVATE amiitool
../../common
../../include
../src
jansson)
target_include_directories(pm3rrg_rdv4_amiibo INTERFACE amiitool)

View file

@ -1,5 +1,5 @@
MYSRCPATHS = MYSRCPATHS =
MYINCLUDES = -I. -I.. -I../jansson -I../../../common -I../../../common/mbedtls -I../../../include MYINCLUDES = -I. -I.. -I../../../common -I../../../common/mbedtls -I../../../include -I../../src -I../../../include -I../jansson
MYCFLAGS = MYCFLAGS =
MYDEFS = MYDEFS =
MYSRCS = \ MYSRCS = \

View file

@ -9,6 +9,7 @@
#include "md.h" #include "md.h"
#include "aes.h" #include "aes.h"
#include "commonutil.h" #include "commonutil.h"
#include "../src/fileutils.h"
#define HMAC_POS_DATA 0x008 #define HMAC_POS_DATA 0x008
#define HMAC_POS_TAG 0x1B4 #define HMAC_POS_TAG 0x1B4
@ -131,24 +132,29 @@ void nfc3d_amiibo_pack(const nfc3d_amiibo_keys *amiiboKeys, const uint8_t *plain
nfc3d_amiibo_internal_to_tag(cipher, tag); nfc3d_amiibo_internal_to_tag(cipher, tag);
} }
bool nfc3d_amiibo_load_keys(nfc3d_amiibo_keys *amiiboKeys, const char *path) { bool nfc3d_amiibo_load_keys(nfc3d_amiibo_keys *amiiboKeys) {
FILE *f = fopen(path, "rb");
if (!f) { #define amiboo_key_fn "key_retail.bin"
uint8_t *dump = NULL;
size_t bytes_read = 0;
if (loadFile_safe(amiboo_key_fn, "", (void **)&dump, &bytes_read) != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", amiboo_key_fn);
return false; return false;
} }
size_t len = fread(amiiboKeys, sizeof(*amiiboKeys), 1, f); if (bytes_read != sizeof(*amiiboKeys)) {
fclose(f); free(dump);
if (len != sizeof(*amiiboKeys)) {
return false; return false;
} }
if ((amiiboKeys->data.magicBytesSize > 16) || if ((amiiboKeys->data.magicBytesSize > 16) || (amiiboKeys->tag.magicBytesSize > 16)) {
(amiiboKeys->tag.magicBytesSize > 16)) { free(dump);
return false; return false;
} }
memcpy(amiiboKeys, dump, bytes_read);
free(dump);
return true; return true;
} }

View file

@ -25,7 +25,7 @@ typedef struct {
bool nfc3d_amiibo_unpack(const nfc3d_amiibo_keys *amiiboKeys, const uint8_t *tag, uint8_t *plain); bool nfc3d_amiibo_unpack(const nfc3d_amiibo_keys *amiiboKeys, const uint8_t *tag, uint8_t *plain);
void nfc3d_amiibo_pack(const nfc3d_amiibo_keys *amiiboKeys, const uint8_t *plain, uint8_t *tag); void nfc3d_amiibo_pack(const nfc3d_amiibo_keys *amiiboKeys, const uint8_t *plain, uint8_t *tag);
bool nfc3d_amiibo_load_keys(nfc3d_amiibo_keys *amiiboKeys, const char *path); bool nfc3d_amiibo_load_keys(nfc3d_amiibo_keys *amiiboKeys);
void nfc3d_amiibo_copy_app_data(const uint8_t *src, uint8_t *dst); void nfc3d_amiibo_copy_app_data(const uint8_t *src, uint8_t *dst);
#endif #endif

View file

@ -33,15 +33,6 @@ void amiitool_usage(void) {
); );
} }
static bool LoadAmiikey(nfc3d_amiibo_keys keys, char *keyfile) {
if (!nfc3d_amiibo_load_keys(&keys, keyfile)) {
PrintAndLogEx(ERR, "Could not load keys from '%s'", keyfile);
return false;
}
return true;
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
self = argv[0]; self = argv[0];

View file

@ -27,6 +27,8 @@ A5B4C3D2
00434343 00434343
44B44CAE 44B44CAE
88661858 88661858
# MKF fobs
E9920427
# paxton bullit? # paxton bullit?
575F4F4B 575F4F4B
# #

View file

@ -115,8 +115,7 @@ local function main(args)
local dumpdata = readdump(infile) local dumpdata = readdump(infile)
-- The hex-data is now in ascii-format, -- The hex-data is now in ascii-format,
if dumpdata == NIL then return oops('Dumpfle not loaded') end if dumpdata == nil then return oops('Dumpfle not loaded') end
-- But first, check the uid -- But first, check the uid
local uid = string.sub(dumpdata, 1, 8) local uid = string.sub(dumpdata, 1, 8)
@ -124,8 +123,7 @@ local function main(args)
-- Format some linebreaks -- Format some linebreaks
dumpdata = convert_to_emulform(dumpdata) dumpdata = convert_to_emulform(dumpdata)
if dumpdata == NIL then return oops('Dumpfle not loaded') end if dumpdata == nil then return oops('Dumpfle not loaded') end
local outfile = io.open(output, 'w') local outfile = io.open(output, 'w')
if outfile == nil then if outfile == nil then

View file

@ -0,0 +1,212 @@
local getopt = require('getopt')
local utils = require('utils')
local ac = require('ansicolors')
copyright = ''
author = "Christian Herrmann"
version = 'v1.0.2'
desc = [[
Perform bulk EM410x enrollment of T5577 RFID tags. It keeps track of last card id used.
If called with -s, this value resets "session".
if press <enter> it defaults to Y, which writes a ID.
Any other input char will exit the script.
You can supply a password, which will set the config block / block 7 on the T5577.
The verify option will issue a 'lf em 410x reader' command, so you can manually verify
that the write worked.
]]
example = [[
-- resets and start enrolling EM410x id 11CC334455
script run lf_em4100_bulk.lua -s 11CC334455
-- continue enrolling from where last iteration
script run lf_em4100_bulk.lua -c
-- reset and start enrolling from 11223344,
-- protecting the tag with password 010203
-- and verify the em id write.
script run lf_em4100_bulk.lua -s 1122334455 -p 01020304 -v
]]
usage = [[
script run lf_en4100_bulk.lua [-h] [-c] [-p password] [-s <start cn>] [-v]
]]
arguments = [[
-h : this help
-c : continue from last card number used
-p : Password protecting the T5577.
-s : starting card number
-v : verify write by executing a `lf em 410x reader`
]]
-- Some globals
local DEBUG = false
local ENROLL_STATUS_FN = 'lf_em4100_status.txt'
---
-- A debug printout-function
local function dbg(args)
if not DEBUG then return end
if type(args) == 'table' then
local i = 1
while args[i] do
dbg(args[i])
i = i+1
end
else
print('###', args)
end
end
---
-- This is only meant to be used when errors occur
local function oops(err)
print('ERROR:', err)
core.clearCommandBuffer()
return nil, errr
end
---
-- Usage help
local function help()
print(copyright)
print(author)
print(version)
print(desc)
print(ac.cyan..'Usage'..ac.reset)
print(usage)
print(ac.cyan..'Arguments'..ac.reset)
print(arguments)
print(ac.cyan..'Example usage'..ac.reset)
print(example)
end
---
-- Exit message
local function exitMsg(msg)
print( string.rep('--',20) )
print( string.rep('--',20) )
print(msg)
print()
end
---
--
local function readfile()
local f = io.open(ENROLL_STATUS_FN, "r")
if f == nil then
return nil, string.format("Could not read file %s", ENROLL_STATUS_FN)
end
local t = f:read("*all")
f:close()
local cn_hi = tonumber(t:sub(1, 2), 16)
local cn_low = tonumber(t:sub(3, 10), 16)
print(('Using EM4100 ID '..ac.green..'%02X%08X'..ac.reset..' from `'..ac.yellow..'%s'..ac.reset..'`'):format(cn_hi, cn_low, ENROLL_STATUS_FN))
return cn_hi, cn_low
end
---
--
local function writefile(cn_hi, cn_low)
local f = io.open(ENROLL_STATUS_FN, "w")
if f == nil then
return nil, string.format("Could not write to file %s", ENROLL_STATUS_FN)
end
f:write(("%02X%08X\n"):format(cn_hi, cn_low))
f:close()
print(('Wrote EM4100 ID '..ac.green..'%02X%08X'..ac.reset..' to `'..ac.yellow..'%s'..ac.reset..'`'):format(cn_hi, cn_low, ENROLL_STATUS_FN))
return true, 'Ok'
end
---
-- main
local function main(args)
print( string.rep('--',20) )
print( string.rep('--',20) )
print()
if #args == 0 then return help() end
local shall_verify = false
local shall_continue = false
local got_pwd = false
local startid = ''
local ipwd = ''
for o, a in getopt.getopt(args, 'cp:s:hv') do
if o == 'h' then return help() end
if o == 'c' then shall_continue = true end
if o == 's' then startid = a end
if o == 'p' then
ipwd = a
got_pwd = true
end
if o == 'v' then shall_verify = true end
end
-- if reset/start over, check -s
if not shall_continue then
if startid == nil then return oops('empty card number string') end
if #startid == 0 then return oops('empty card number string') end
if #startid ~= 10 then return oops('card number wrong length. Must be 5 hex bytes') end
end
if got_pwd then
if ipwd == nil then return oops('empty password') end
if #ipwd == 0 then return oops('empty password') end
if #ipwd ~= 8 then return oops('password wrong length. Must be 4 hex bytes') end
end
core.console('clear')
print(ac.red..'disable hints for less output'..ac.reset)
core.console('pref set hint --off')
print('')
local hi = tonumber(startid:sub(1, 2), 16)
local low = tonumber(startid:sub(3, 10), 16)
local pwd = tonumber(ipwd, 16)
if got_pwd then
print(('Will protect T5577 with password '..ac.green..'%08X'..ac.reset):format(pwd))
end
if shall_verify then
print('Will verify write afterwards')
end
if shall_continue then
print('Continue enrolling from last save')
hi, low = readfile()
else
print('reset & starting enrolling from refresh')
end
local template = 'EM4100 ID '..ac.green..'%02X%08X'..ac.reset
for i = low, low + 10000, 1 do
print('')
print( string.rep('--',20) )
local msg = (template):format(hi, i)
local ans = utils.input(msg, 'y'):lower()
if ans == 'y' then
core.console( ('lf em 410x clone --id %02X%08X'):format(hi, i) )
-- print ( ('lf em 410x clone --id %02X%08X'):format(hi, i) )
if got_pwd then
core.console('lf t55 detect')
core.console(('lf t55 protect -n %08x'):format(pwd))
end
if shall_verify then
core.console('lf em 410x reader')
end
else
print(ac.red..'User aborted'..ac.reset)
low = i
break
end
end
writefile(hi, low)
print('enabling hints again')
core.console('pref set hint --on')
end
main(args)

View file

@ -11,7 +11,7 @@
#include <ctype.h> #include <ctype.h>
#include "cmdparser.h" // command_t #include "cmdparser.h" // command_t
#include "cliparser.h" #include "cliparser.h"
#include "pmflash.h" #include "pmflash.h" // rdv40validation_t
#include "fileutils.h" // saveFile #include "fileutils.h" // saveFile
#include "comms.h" // getfromdevice #include "comms.h" // getfromdevice
#include "cmdflashmemspiffs.h" // spiffs commands #include "cmdflashmemspiffs.h" // spiffs commands
@ -26,6 +26,123 @@
static int CmdHelp(const char *Cmd); static int CmdHelp(const char *Cmd);
//-------------------------------------------------------------------------------------
// RRG Public RSA Key
#define RRG_RSA_KEY_LEN 128
// public key Exponent E
#define RRG_RSA_E "010001"
// public key modulus N
#define RRG_RSA_N "E28D809BF323171D11D1ACA4C32A5B7E0A8974FD171E75AD120D60E9B76968FF" \
"4B0A6364AE50583F9555B8EE1A725F279E949246DF0EFCE4C02B9F3ACDCC623F" \
"9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33" \
"DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5"
// Following example RSA-1024 keypair, for test purposes (from common/polarssl/rsa.c)
// private key - Exponent D
#define RSA_D "24BF6185468786FDD303083D25E64EFC" \
"66CA472BC44D253102F8B4A9D3BFA750" \
"91386C0077937FE33FA3252D28855837" \
"AE1B484A8A9A45F7EE8C0C634F99E8CD" \
"DF79C5CE07EE72C7F123142198164234" \
"CABB724CF78B8173B9F880FC86322407" \
"AF1FEDFDDE2BEB674CA15F3E81A1521E" \
"071513A1E85B5DFA031F21ECAE91A34D"
// prime P
#define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \
"2C01CAD19EA484A87EA4377637E75500" \
"FCB2005C5C7DD6EC4AC023CDA285D796" \
"C3D9E75E1EFC42488BB4F1D13AC30A57"
// prime Q
#define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \
"E211C2B9E5DB1ED0BF61D0D9899620F4" \
"910E4168387E3C30AA1E00C339A79508" \
"8452DD96A9A5EA5D9DCA68DA636032AF"
#define RSA_DP "C1ACF567564274FB07A0BBAD5D26E298" \
"3C94D22288ACD763FD8E5600ED4A702D" \
"F84198A5F06C2E72236AE490C93F07F8" \
"3CC559CD27BC2D1CA488811730BB5725"
#define RSA_DQ "4959CBF6F8FEF750AEE6977C155579C7" \
"D8AAEA56749EA28623272E4F7D0592AF" \
"7C1F1313CAC9471B5C523BFE592F517B" \
"407A1BD76C164B93DA2D32A383E58357"
#define RSA_QP "9AE7FBC99546432DF71896FC239EADAE" \
"F38D18D2B2F0E2DD275AA977E2BF4411" \
"F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \
"A74206CEC169D74BF5A8C50D6F48EA08"
int rdv4_get_signature(rdv40_validation_t *out) {
if (out == NULL) {
return PM3_EINVARG;
}
clearCommandBuffer();
SendCommandNG(CMD_FLASHMEM_INFO, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
uint8_t isok = resp.oldarg[0] & 0xFF;
if (isok == false) {
PrintAndLogEx(FAILED, "fail reading from flashmemory");
return PM3_EFLASH;
}
//rdv40_validation_t mem;
memcpy(out, (rdv40_validation_t *)resp.data.asBytes, sizeof(rdv40_validation_t));
return PM3_SUCCESS;
}
// validate signature
int rdv4_validate(rdv40_validation_t *mem) {
// Flash ID hash (sha1)
uint8_t sha_hash[20] = {0};
mbedtls_sha1(mem->flashid, sizeof(mem->flashid), sha_hash);
// set up RSA
mbedtls_rsa_context rsa;
mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0);
rsa.len = RRG_RSA_KEY_LEN;
mbedtls_mpi_read_string(&rsa.N, 16, RRG_RSA_N);
mbedtls_mpi_read_string(&rsa.E, 16, RRG_RSA_E);
// Verify (public key)
int is_verified = mbedtls_rsa_pkcs1_verify(&rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, mem->signature);
mbedtls_rsa_free(&rsa);
if (is_verified == 0) {
return PM3_SUCCESS;
}
return PM3_EFAILED;
}
static int rdv4_sign_write(uint8_t *signature, uint8_t slen){
// save to mem
clearCommandBuffer();
PacketResponseNG resp;
SendCommandOLD(CMD_FLASHMEM_WRITE, FLASH_MEM_SIGNATURE_OFFSET, FLASH_MEM_SIGNATURE_LEN, 0, signature, slen);
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
} else {
if (!resp.oldarg[0]) {
PrintAndLogEx(FAILED, "Writing signature ( "_RED_("fail") ")");
} else {
PrintAndLogEx(SUCCESS, "Writing signature ( "_GREEN_("ok") " ) at offset %u", FLASH_MEM_SIGNATURE_OFFSET);
return PM3_SUCCESS;
}
}
return PM3_EFAILED;
}
static int CmdFlashmemSpiBaudrate(const char *Cmd) { static int CmdFlashmemSpiBaudrate(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
@ -344,23 +461,14 @@ static int CmdFlashMemInfo(const char *Cmd) {
// shall_write = arg_get_lit(ctx, 2); // shall_write = arg_get_lit(ctx, 2);
CLIParserFree(ctx); CLIParserFree(ctx);
clearCommandBuffer(); // validate signature data
SendCommandNG(CMD_FLASHMEM_INFO, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
uint8_t isok = resp.oldarg[0] & 0xFF;
if (isok == false) {
PrintAndLogEx(FAILED, "failed");
return PM3_EFLASH;
}
// validate signature here
rdv40_validation_t mem; rdv40_validation_t mem;
memcpy(&mem, (rdv40_validation_t *)resp.data.asBytes, sizeof(rdv40_validation_t)); int res = rdv4_get_signature(&mem);
if (res != PM3_SUCCESS) {
return res;
}
res = rdv4_validate(&mem);
// Flash ID hash (sha1) // Flash ID hash (sha1)
uint8_t sha_hash[20] = {0}; uint8_t sha_hash[20] = {0};
@ -369,7 +477,6 @@ static int CmdFlashMemInfo(const char *Cmd) {
// print header // print header
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Flash memory Information") " ---------"); PrintAndLogEx(INFO, "--- " _CYAN_("Flash memory Information") " ---------");
// PrintAndLogEx(INFO, "-----------------------------------------------------------------");
PrintAndLogEx(INFO, "ID................... %s", sprint_hex_inrow(mem.flashid, sizeof(mem.flashid))); PrintAndLogEx(INFO, "ID................... %s", sprint_hex_inrow(mem.flashid, sizeof(mem.flashid)));
PrintAndLogEx(INFO, "SHA1................. %s", sprint_hex_inrow(sha_hash, sizeof(sha_hash))); PrintAndLogEx(INFO, "SHA1................. %s", sprint_hex_inrow(sha_hash, sizeof(sha_hash)));
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
@ -378,69 +485,16 @@ static int CmdFlashMemInfo(const char *Cmd) {
PrintAndLogEx(INFO, " %s", sprint_hex_inrow(mem.signature + (i * 32), 32)); PrintAndLogEx(INFO, " %s", sprint_hex_inrow(mem.signature + (i * 32), 32));
} }
//-------------------------------------------------------------------------------
// RRG Public RSA Key
//
// public key Exponent E
#define RSA_E "010001"
// public key modulus N
#define RSA_N "E28D809BF323171D11D1ACA4C32A5B7E0A8974FD171E75AD120D60E9B76968FF" \
"4B0A6364AE50583F9555B8EE1A725F279E949246DF0EFCE4C02B9F3ACDCC623F" \
"9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33" \
"DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5"
//-------------------------------------------------------------------------------
// Example RSA-1024 keypair, for test purposes (from common/polarssl/rsa.c)
//
// private key Exponent D
#define RSA_D "24BF6185468786FDD303083D25E64EFC" \
"66CA472BC44D253102F8B4A9D3BFA750" \
"91386C0077937FE33FA3252D28855837" \
"AE1B484A8A9A45F7EE8C0C634F99E8CD" \
"DF79C5CE07EE72C7F123142198164234" \
"CABB724CF78B8173B9F880FC86322407" \
"AF1FEDFDDE2BEB674CA15F3E81A1521E" \
"071513A1E85B5DFA031F21ECAE91A34D"
// prime P
#define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \
"2C01CAD19EA484A87EA4377637E75500" \
"FCB2005C5C7DD6EC4AC023CDA285D796" \
"C3D9E75E1EFC42488BB4F1D13AC30A57"
// prime Q
#define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \
"E211C2B9E5DB1ED0BF61D0D9899620F4" \
"910E4168387E3C30AA1E00C339A79508" \
"8452DD96A9A5EA5D9DCA68DA636032AF"
#define RSA_DP "C1ACF567564274FB07A0BBAD5D26E298" \
"3C94D22288ACD763FD8E5600ED4A702D" \
"F84198A5F06C2E72236AE490C93F07F8" \
"3CC559CD27BC2D1CA488811730BB5725"
#define RSA_DQ "4959CBF6F8FEF750AEE6977C155579C7" \
"D8AAEA56749EA28623272E4F7D0592AF" \
"7C1F1313CAC9471B5C523BFE592F517B" \
"407A1BD76C164B93DA2D32A383E58357"
#define RSA_QP "9AE7FBC99546432DF71896FC239EADAE" \
"F38D18D2B2F0E2DD275AA977E2BF4411" \
"F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \
"A74206CEC169D74BF5A8C50D6F48EA08"
#define KEY_LEN 128
mbedtls_rsa_context rsa; mbedtls_rsa_context rsa;
mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0); mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0);
rsa.len = KEY_LEN; rsa.len = RRG_RSA_KEY_LEN;
mbedtls_mpi_read_string(&rsa.N, 16, RSA_N); // add public key
mbedtls_mpi_read_string(&rsa.E, 16, RSA_E); mbedtls_mpi_read_string(&rsa.N, 16, RRG_RSA_N);
mbedtls_mpi_read_string(&rsa.E, 16, RRG_RSA_E);
// add private key
mbedtls_mpi_read_string(&rsa.D, 16, RSA_D); mbedtls_mpi_read_string(&rsa.D, 16, RSA_D);
mbedtls_mpi_read_string(&rsa.P, 16, RSA_P); mbedtls_mpi_read_string(&rsa.P, 16, RSA_P);
mbedtls_mpi_read_string(&rsa.Q, 16, RSA_Q); mbedtls_mpi_read_string(&rsa.Q, 16, RSA_Q);
@ -448,8 +502,6 @@ static int CmdFlashMemInfo(const char *Cmd) {
mbedtls_mpi_read_string(&rsa.DQ, 16, RSA_DQ); mbedtls_mpi_read_string(&rsa.DQ, 16, RSA_DQ);
mbedtls_mpi_read_string(&rsa.QP, 16, RSA_QP); mbedtls_mpi_read_string(&rsa.QP, 16, RSA_QP);
bool is_keyok = (mbedtls_rsa_check_pubkey(&rsa) == 0 || mbedtls_rsa_check_privkey(&rsa) == 0);
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA Public key") " --------------"); PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA Public key") " --------------");
@ -466,48 +518,35 @@ static int CmdFlashMemInfo(const char *Cmd) {
PrintAndLogEx(INFO, " %.64s", str_pk + 64); PrintAndLogEx(INFO, " %.64s", str_pk + 64);
PrintAndLogEx(INFO, " %.64s", str_pk + 128); PrintAndLogEx(INFO, " %.64s", str_pk + 128);
PrintAndLogEx(INFO, " %.64s", str_pk + 192); PrintAndLogEx(INFO, " %.64s", str_pk + 192);
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
const char *msgkey = "RSA key validation... ";
if (is_keyok)
PrintAndLogEx(SUCCESS, "%s( " _GREEN_("ok") " )", msgkey);
else
PrintAndLogEx(FAILED, "%s( " _RED_("failed") " )", msgkey);
// bool is_keyok = (mbedtls_rsa_check_pubkey(&rsa) == 0 || mbedtls_rsa_check_privkey(&rsa) == 0);
uint8_t from_device[KEY_LEN]; PrintAndLogEx(
uint8_t sign[KEY_LEN]; (is_keyok) ? SUCCESS : FAILED,
"RSA key validation... ( %s )",
(is_keyok) ? _GREEN_("ok") : _RED_("fail")
);
// to be verified // to be verified
memcpy(from_device, mem.signature, KEY_LEN); uint8_t from_device[RRG_RSA_KEY_LEN];
memcpy(from_device, mem.signature, RRG_RSA_KEY_LEN);
// to be signed (all zeros // to be signed
memset(sign, 0, KEY_LEN); uint8_t sign[RRG_RSA_KEY_LEN];
memset(sign, 0, RRG_RSA_KEY_LEN);
// Signing (private key) // Signing (private key)
if (shall_sign) { if (shall_sign) {
int is_signed = mbedtls_rsa_pkcs1_sign(&rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA1, 20, sha_hash, sign); int is_signed = mbedtls_rsa_pkcs1_sign(&rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA1, 20, sha_hash, sign);
const char *msgsign = "RSA signing.......... "; PrintAndLogEx(
if (is_signed == 0) (is_signed == 0) ? SUCCESS : FAILED,
PrintAndLogEx(SUCCESS, "%s( " _GREEN_("ok") " )", msgsign); "RSA signing.......... ( %s )",
else (is_signed == 0) ? _GREEN_("ok") : _RED_("fail")
PrintAndLogEx(FAILED, "%s( " _RED_("failed") " )", msgsign); );
if (shall_write) { if (shall_write) {
// save to mem rdv4_sign_write(sign, RRG_RSA_KEY_LEN);
clearCommandBuffer();
SendCommandOLD(CMD_FLASHMEM_WRITE, FLASH_MEM_SIGNATURE_OFFSET, FLASH_MEM_SIGNATURE_LEN, 0, sign, sizeof(sign));
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
} else {
if (!resp.oldarg[0])
PrintAndLogEx(FAILED, "Writing signature failed");
else
PrintAndLogEx(SUCCESS, "Writing signature ok [offset: %u]", FLASH_MEM_SIGNATURE_OFFSET);
}
} }
PrintAndLogEx(INFO, "Signed"); PrintAndLogEx(INFO, "Signed");
for (int i = 0; i < (sizeof(sign) / 32); i++) { for (int i = 0; i < (sizeof(sign) / 32); i++) {
@ -517,14 +556,15 @@ static int CmdFlashMemInfo(const char *Cmd) {
// Verify (public key) // Verify (public key)
int is_verified = mbedtls_rsa_pkcs1_verify(&rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, from_device); int is_verified = mbedtls_rsa_pkcs1_verify(&rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, from_device);
const char *msgverify = "RSA verification..... "; mbedtls_rsa_free(&rsa);
if (is_verified == 0)
PrintAndLogEx(SUCCESS, "%s( " _GREEN_("ok") " )", msgverify); PrintAndLogEx(
else (is_verified == 0) ? SUCCESS : FAILED,
PrintAndLogEx(FAILED, "%s( " _RED_("failed") " )", msgverify); "RSA verification..... ( %s )",
(is_verified == 0) ? _GREEN_("ok") : _RED_("fail")
);
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
mbedtls_rsa_free(&rsa);
return PM3_SUCCESS; return PM3_SUCCESS;
} }

View file

@ -12,6 +12,7 @@
#define CMDFLASHMEM_H__ #define CMDFLASHMEM_H__
#include "common.h" #include "common.h"
#include "pmflash.h" // rdv40validation_t
typedef enum { typedef enum {
DICTIONARY_NONE = 0, DICTIONARY_NONE = 0,
@ -21,5 +22,6 @@ typedef enum {
} Dictionary_t; } Dictionary_t;
int CmdFlashMem(const char *Cmd); int CmdFlashMem(const char *Cmd);
int rdv4_get_signature(rdv40_validation_t *out);
int rdv4_validate(rdv40_validation_t *mem);
#endif #endif

View file

@ -2101,6 +2101,14 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
if (do_nack_test) if (do_nack_test)
detect_classic_nackbug(false); detect_classic_nackbug(false);
} }
uint8_t signature[32] = {0};
res = detect_mfc_ev1_signature(signature);
if (res == PM3_SUCCESS) {
mfc_ev1_print_signature(card.uid, card.uidlen, signature, sizeof(signature));
}
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mf`") " commands");
} }
if (isMifareUltralight) if (isMifareUltralight)

View file

@ -575,6 +575,10 @@ bool readHF15Uid(bool loop, bool verbose) {
* **cmd command line * **cmd command line
*/ */
static bool prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t *req, uint8_t iso15cmd) { // reqlen arg0 static bool prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t *req, uint8_t iso15cmd) { // reqlen arg0
if (*cmd == NULL || strlen(*cmd) == 0)
return false;
int temp; int temp;
uint8_t uid[8] = {0x00}; uint8_t uid[8] = {0x00};
uint32_t tmpreqlen = 0; uint32_t tmpreqlen = 0;
@ -598,13 +602,12 @@ static bool prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t
// strip // strip
while (**cmd == ' ' || **cmd == '\t')(*cmd)++; while (**cmd == ' ' || **cmd == '\t')(*cmd)++;
switch (**cmd) { char c = tolower(**cmd);
switch (c) {
case 0: case 0:
PrintAndLogEx(WARNING, "missing addr"); PrintAndLogEx(WARNING, "missing addr");
return false; return false;
break;
case 'u': case 'u':
case 'U':
// unaddressed mode may not be supported by all vendors // unaddressed mode may not be supported by all vendors
req[tmpreqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY; req[tmpreqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY;
req[tmpreqlen++] = iso15cmd; req[tmpreqlen++] = iso15cmd;
@ -637,9 +640,9 @@ static bool prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t
break; break;
} }
// skip to next space // skip to next space
while (**cmd != ' ' && **cmd != '\t')(*cmd)++; while (**cmd != '\0' && **cmd != ' ' && **cmd != '\t')(*cmd)++;
// skip over the space // skip over the space
while (**cmd == ' ' || **cmd == '\t')(*cmd)++; while (**cmd != '\0' && (**cmd == ' ' || **cmd == '\t'))(*cmd)++;
*reqlen = tmpreqlen; *reqlen = tmpreqlen;
return true; return true;
@ -1353,6 +1356,7 @@ static int CmdHF15Dump(const char *Cmd) {
// copy uid to read command // copy uid to read command
memcpy(req + 2, uid, sizeof(uid)); memcpy(req + 2, uid, sizeof(uid));
PrintAndLogEx(INFO, "." NOLF);
for (int retry = 0; retry < 5; retry++) { for (int retry = 0; retry < 5; retry++) {
req[10] = blocknum; req[10] = blocknum;
@ -1368,6 +1372,7 @@ static int CmdHF15Dump(const char *Cmd) {
continue; continue;
} }
if (len < 2) { if (len < 2) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(FAILED, "iso15693 command failed"); PrintAndLogEx(FAILED, "iso15693 command failed");
continue; continue;
} }
@ -1375,11 +1380,13 @@ static int CmdHF15Dump(const char *Cmd) {
recv = resp.data.asBytes; recv = resp.data.asBytes;
if (CheckCrc15(recv, len) == false) { if (CheckCrc15(recv, len) == false) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(FAILED, "crc (" _RED_("fail") ")"); PrintAndLogEx(FAILED, "crc (" _RED_("fail") ")");
continue; continue;
} }
if ((recv[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { if ((recv[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(FAILED, "Tag returned Error %i: %s", recv[1], TagErrorStr(recv[1])); PrintAndLogEx(FAILED, "Tag returned Error %i: %s", recv[1], TagErrorStr(recv[1]));
break; break;
} }
@ -1399,12 +1406,12 @@ static int CmdHF15Dump(const char *Cmd) {
DropField(); DropField();
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "block# | data |lck| ascii"); PrintAndLogEx(INFO, "block# | data |lck| ascii");
PrintAndLogEx(NORMAL, "---------+--------------+---+----------"); PrintAndLogEx(INFO, "---------+--------------+---+----------");
for (int i = 0; i < blocknum; i++) { for (int i = 0; i < blocknum; i++) {
PrintAndLogEx(NORMAL, "%3d/0x%02X | %s | %d | %s", i, i, sprint_hex(mem[i].block, 4), mem[i].lock, sprint_ascii(mem[i].block, 4)); PrintAndLogEx(INFO, "%3d/0x%02X | %s | %d | %s", i, i, sprint_hex(mem[i].block, 4), mem[i].lock, sprint_ascii(mem[i].block, 4));
} }
PrintAndLogEx(NORMAL, "\n"); PrintAndLogEx(NORMAL, "");
size_t datalen = blocknum * 4; size_t datalen = blocknum * 4;
saveFile(filename, ".bin", data, datalen); saveFile(filename, ".bin", data, datalen);
@ -1595,14 +1602,15 @@ static int CmdHF15Readmulti(const char *Cmd) {
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "block# | data |lck| ascii"); PrintAndLogEx(INFO, "block# | data |lck| ascii");
PrintAndLogEx(NORMAL, "---------+--------------+---+----------"); PrintAndLogEx(INFO, "---------+--------------+---+----------");
for (int i = start; i < stop; i += 5) { for (int i = start; i < stop; i += 5) {
PrintAndLogEx(NORMAL, "%3d/0x%02X | %s | %d | %s", currblock, currblock, sprint_hex(data + i + 1, 4), data[i], sprint_ascii(data + i + 1, 4)); PrintAndLogEx(INFO, "%3d/0x%02X | %s | %d | %s", currblock, currblock, sprint_hex(data + i + 1, 4), data[i], sprint_ascii(data + i + 1, 4));
currblock++; currblock++;
} }
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS; return PM3_SUCCESS;
} }
@ -1676,9 +1684,9 @@ static int CmdHF15Read(const char *Cmd) {
// print response // print response
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "block #%3d |lck| ascii", blocknum); PrintAndLogEx(INFO, "block #%3d |lck| ascii", blocknum);
PrintAndLogEx(NORMAL, "------------+---+------"); PrintAndLogEx(INFO, "------------+---+------");
PrintAndLogEx(NORMAL, "%s| %d | %s", sprint_hex(data + 2, status - 4), data[1], sprint_ascii(data + 2, status - 4)); PrintAndLogEx(INFO, "%s| %d | %s", sprint_hex(data + 2, status - 4), data[1], sprint_ascii(data + 2, status - 4));
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS; return PM3_SUCCESS;
} }

View file

@ -58,6 +58,7 @@ 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_dg1_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_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_dg12_info(uint8_t *data, size_t datalen);
static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen);
typedef enum { // list must match dg_table typedef enum { // list must match dg_table
EF_COM = 0, EF_COM = 0,
@ -102,7 +103,7 @@ static emrtd_dg_t dg_table[] = {
{0x6f, 15, "010F", "EF_DG15", "Active Authentication Public Key Info", false, false, false, true, NULL, NULL}, {0x6f, 15, "010F", "EF_DG15", "Active Authentication Public Key Info", false, false, false, true, NULL, NULL},
{0x70, 16, "0110", "EF_DG16", "Person(s) to Notify", false, false, false, true, NULL, NULL}, {0x70, 16, "0110", "EF_DG16", "Person(s) to Notify", false, false, false, true, NULL, NULL},
{0x77, 0, "011D", "EF_SOD", "Document Security Object", false, false, false, false, NULL, emrtd_dump_ef_sod}, {0x77, 0, "011D", "EF_SOD", "Document Security Object", false, false, false, false, NULL, emrtd_dump_ef_sod},
{0xff, 0, "011C", "EF_CardAccess", "PACE SecurityInfos", true, false, true, true, NULL, NULL}, {0xff, 0, "011C", "EF_CardAccess", "PACE SecurityInfos", true, false, true, true, emrtd_print_ef_cardaccess_info, NULL},
{0xff, 0, "011D", "EF_CardSecurity", "PACE SecurityInfos for Chip Authentication Mapping", true, false, false, true, NULL, NULL}, {0xff, 0, "011D", "EF_CardSecurity", "PACE SecurityInfos for Chip Authentication Mapping", true, false, false, true, NULL, NULL},
{0x00, 0, NULL, NULL, NULL, false, false, false, false, NULL, NULL} {0x00, 0, NULL, NULL, NULL, false, false, false, false, NULL, NULL}
}; };
@ -117,6 +118,46 @@ static emrtd_hashalg_t hashalg_table[] = {
{NULL, NULL, 0, 0, {}} {NULL, NULL, 0, 0, {}}
}; };
static emrtd_pacealg_t pacealg_table[] = {
// name keygen descriptor
{"DH, Generic Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x01}},
{"DH, Generic Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x02}},
{"DH, Generic Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x03}},
{"DH, Generic Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x04}},
{"ECDH, Generic Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x01}},
{"ECDH, Generic Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x02}},
{"ECDH, Generic Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x03}},
{"ECDH, Generic Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x04}},
{"DH, Integrated Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x01}},
{"DH, Integrated Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x02}},
{"DH, Integrated Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x03}},
{"DH, Integrated Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x04}},
{"ECDH, Integrated Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x01}},
{"ECDH, Integrated Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x02}},
{"ECDH, Integrated Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x03}},
{"ECDH, Integrated Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x04}},
{NULL, NULL, {}}
};
static emrtd_pacesdp_t pacesdp_table[] = {
// id name size
{0, "1024-bit MODP Group with 160-bit Prime Order Subgroup", 1024},
{1, "2048-bit MODP Group with 224-bit Prime Order Subgroup", 2048},
{2, "2048-bit MODP Group with 256-bit Prime Order Subgroup", 2048},
{8, "NIST P-192 (secp192r1)", 192},
{10, "NIST P-224 (secp224r1)", 224},
{12, "NIST P-256 (secp256r1)", 256},
{15, "NIST P-384 (secp384r1)", 384},
{18, "NIST P-521 (secp521r1)", 521},
{9, "BrainpoolP192r1", 192},
{11, "BrainpoolP224r1", 224},
{13, "BrainpoolP256r1", 256},
{14, "BrainpoolP320r1", 320},
{16, "BrainpoolP384r1", 384},
{17, "BrainpoolP521r1", 521},
{32, NULL, 0}
};
static emrtd_dg_t *emrtd_tag_to_dg(uint8_t tag) { static emrtd_dg_t *emrtd_tag_to_dg(uint8_t tag) {
for (int dgi = 0; dg_table[dgi].filename != NULL; dgi++) { for (int dgi = 0; dg_table[dgi].filename != NULL; dgi++) {
if (dg_table[dgi].tag == tag) { if (dg_table[dgi].tag == tag) {
@ -992,7 +1033,7 @@ static bool emrtd_do_auth(char *documentnumber, char *dob, char *expiry, bool BA
// Select EF_COM // Select EF_COM
if (emrtd_select_file(EMRTD_P1_SELECT_BY_EF, dg_table[EF_COM].fileid, *use_14b) == false) { if (emrtd_select_file(EMRTD_P1_SELECT_BY_EF, dg_table[EF_COM].fileid, *use_14b) == false) {
*BAC = true; *BAC = true;
PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication."); PrintAndLogEx(INFO, "Authentication is enforced. Will attempt external authentication.");
} else { } else {
*BAC = false; *BAC = false;
// Select EF_DG1 // Select EF_DG1
@ -1000,7 +1041,7 @@ static bool emrtd_do_auth(char *documentnumber, char *dob, char *expiry, bool BA
if (emrtd_read_file(response, &resplen, NULL, NULL, NULL, false, *use_14b) == false) { if (emrtd_read_file(response, &resplen, NULL, NULL, NULL, false, *use_14b) == false) {
*BAC = true; *BAC = true;
PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication."); PrintAndLogEx(INFO, "Authentication is enforced. Will attempt external authentication.");
} else { } else {
*BAC = false; *BAC = false;
} }
@ -1010,7 +1051,7 @@ static bool emrtd_do_auth(char *documentnumber, char *dob, char *expiry, bool BA
if (*BAC) { if (*BAC) {
// If BAC isn't available, exit out and warn user. // If BAC isn't available, exit out and warn user.
if (!BAC_available) { if (!BAC_available) {
PrintAndLogEx(ERR, "This eMRTD enforces Basic Access Control, but you didn't supply MRZ data. Cannot proceed."); PrintAndLogEx(ERR, "This eMRTD enforces authentication, but you didn't supply MRZ data. Cannot proceed.");
PrintAndLogEx(HINT, "Check out hf emrtd info/dump --help, supply data with -n -d and -e."); PrintAndLogEx(HINT, "Check out hf emrtd info/dump --help, supply data with -n -d and -e.");
return false; return false;
} }
@ -1726,6 +1767,64 @@ static int emrtd_print_ef_sod_info(uint8_t *dg_hashes_calc, uint8_t *dg_hashes_s
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen) {
uint8_t dataset[100] = { 0x00 };
size_t datasetlen = 0;
uint8_t datafromtag[100] = { 0x00 };
size_t datafromtaglen = 0;
uint8_t parsednum = 0;
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "----------------- " _CYAN_("EF_CardAccess") " ----------------");
if (!emrtd_lds_get_data_by_tag(data, datalen, dataset, &datasetlen, 0x30, 0x00, false, true, 0)) {
PrintAndLogEx(ERR, "Failed to read set from EF_CardAccess.");
return PM3_ESOFT;
}
// Get PACE version
if (!emrtd_lds_get_data_by_tag(dataset, datasetlen, datafromtag, &datafromtaglen, 0x02, 0x00, false, false, 0)) {
PrintAndLogEx(ERR, "Failed to read PACE version from EF_CardAccess.");
return PM3_ESOFT;
}
// TODO: hack!!!
memcpy(&parsednum, datafromtag, datafromtaglen);
PrintAndLogEx(SUCCESS, "PACE version..........: " _YELLOW_("%i"), parsednum);
// Get PACE algorithm
if (!emrtd_lds_get_data_by_tag(dataset, datasetlen, datafromtag, &datafromtaglen, 0x06, 0x00, false, false, 0)) {
PrintAndLogEx(ERR, "Failed to read PACE algorithm from EF_CardAccess.");
return PM3_ESOFT;
}
for (int pacei = 0; pacealg_table[pacei].name != NULL; pacei++) {
PrintAndLogEx(DEBUG, "Trying: %s", hashalg_table[pacei].name);
if (memcmp(pacealg_table[pacei].descriptor, datafromtag, datafromtaglen) == 0) {
PrintAndLogEx(SUCCESS, "PACE algorithm........: " _YELLOW_("%s"), pacealg_table[pacei].name);
}
}
// Get PACE parameter ID
if (!emrtd_lds_get_data_by_tag(dataset, datasetlen, datafromtag, &datafromtaglen, 0x02, 0x00, false, false, 1)) {
PrintAndLogEx(ERR, "Failed to read PACE parameter ID from EF_CardAccess.");
return PM3_ESOFT;
}
// TODO: hack!!!
memcpy(&parsednum, datafromtag, datafromtaglen);
for (int pacepari = 0; pacesdp_table[pacepari].id != 32; pacepari++) {
PrintAndLogEx(DEBUG, "Trying: %s", hashalg_table[pacepari].name);
if (pacesdp_table[pacepari].id == parsednum) {
PrintAndLogEx(SUCCESS, "PACE parameter........: " _YELLOW_("%s"), pacesdp_table[pacepari].name);
}
// TODO: account for RFU
}
return PM3_SUCCESS;
}
int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) {
uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 };
int resplen = 0; int resplen = 0;
@ -1733,6 +1832,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
uint8_t ks_enc[16] = { 0x00 }; uint8_t ks_enc[16] = { 0x00 };
uint8_t ks_mac[16] = { 0x00 }; uint8_t ks_mac[16] = { 0x00 };
bool BAC = false; bool BAC = false;
bool PACE_available = true;
bool use_14b = false; bool use_14b = false;
// Select the eMRTD // Select the eMRTD
@ -1741,20 +1841,32 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
return PM3_ESOFT; return PM3_ESOFT;
} }
// Read EF_CardAccess
if (!emrtd_select_and_read(response, &resplen, dg_table[EF_CardAccess].fileid, ks_enc, ks_mac, ssc, BAC, use_14b)) {
PACE_available = false;
PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE.");
}
// Select and authenticate with the eMRTD // Select and authenticate with the eMRTD
bool auth_result = emrtd_do_auth(documentnumber, dob, expiry, BAC_available, &BAC, ssc, ks_enc, ks_mac, &use_14b); bool auth_result = emrtd_do_auth(documentnumber, dob, expiry, BAC_available, &BAC, ssc, ks_enc, ks_mac, &use_14b);
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "------------------ " _CYAN_("Basic Info") " ------------------"); PrintAndLogEx(INFO, "------------------ " _CYAN_("Basic Info") " ------------------");
PrintAndLogEx(SUCCESS, "Communication standard: %s", use_14b ? _YELLOW_("ISO/IEC 14443(B)") : _YELLOW_("ISO/IEC 14443(A)")); PrintAndLogEx(SUCCESS, "Communication standard: %s", use_14b ? _YELLOW_("ISO/IEC 14443(B)") : _YELLOW_("ISO/IEC 14443(A)"));
PrintAndLogEx(SUCCESS, "BAC...................: %s", BAC ? _GREEN_("Enforced") : _RED_("Not enforced")); PrintAndLogEx(SUCCESS, "Authentication........: %s", BAC ? _GREEN_("Enforced") : _RED_("Not enforced"));
PrintAndLogEx(SUCCESS, "PACE..................: %s", PACE_available ? _GREEN_("Available") : _YELLOW_("Not available"));
PrintAndLogEx(SUCCESS, "Authentication result.: %s", auth_result ? _GREEN_("Successful") : _RED_("Failed")); PrintAndLogEx(SUCCESS, "Authentication result.: %s", auth_result ? _GREEN_("Successful") : _RED_("Failed"));
if (PACE_available) {
emrtd_print_ef_cardaccess_info(response, resplen);
}
if (!auth_result) { if (!auth_result) {
DropField(); DropField();
return PM3_ESOFT; return PM3_ESOFT;
} }
// Read EF_COM to get file list
if (!emrtd_select_and_read(response, &resplen, dg_table[EF_COM].fileid, ks_enc, ks_mac, ssc, BAC, use_14b)) { if (!emrtd_select_and_read(response, &resplen, dg_table[EF_COM].fileid, ks_enc, ks_mac, ssc, BAC, use_14b)) {
PrintAndLogEx(ERR, "Failed to read EF_COM."); PrintAndLogEx(ERR, "Failed to read EF_COM.");
DropField(); DropField();
@ -1776,7 +1888,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
return PM3_ESOFT; return PM3_ESOFT;
} }
// Grab the hash list // Grab the hash list from EF_SOD
uint8_t dg_hashes_sod[17][64] = { { 0 } }; uint8_t dg_hashes_sod[17][64] = { { 0 } };
uint8_t dg_hashes_calc[17][64] = { { 0 } }; uint8_t dg_hashes_calc[17][64] = { { 0 } };
int hash_algo = 0; int hash_algo = 0;
@ -1860,6 +1972,16 @@ int infoHF_EMRTD_offline(const char *path) {
uint8_t dg_hashes_calc[17][64] = { { 0 } }; uint8_t dg_hashes_calc[17][64] = { { 0 } };
int hash_algo = 0; int hash_algo = 0;
strcpy(filepath, path);
strncat(filepath, PATHSEP, 2);
strcat(filepath, dg_table[EF_CardAccess].filename);
if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) {
emrtd_print_ef_cardaccess_info(data, datalen);
} else {
PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE.");
}
strcpy(filepath, path); strcpy(filepath, path);
strncat(filepath, PATHSEP, 2); strncat(filepath, PATHSEP, 2);
strcat(filepath, dg_table[EF_SOD].filename); strcat(filepath, dg_table[EF_SOD].filename);

View file

@ -35,6 +35,19 @@ typedef struct emrtd_hashalg_s {
const uint8_t descriptor[15]; const uint8_t descriptor[15];
} emrtd_hashalg_t; } emrtd_hashalg_t;
typedef struct emrtd_pacealg_s {
const char *name;
int (*keygenerator)(uint8_t *datain, int datainlen, uint8_t *dataout);
const uint8_t descriptor[10];
} emrtd_pacealg_t;
// Standardized Domain Parameters
typedef struct emrtd_pacesdp_s {
uint8_t id;
const char *name;
size_t size;
} emrtd_pacesdp_t;
int CmdHFeMRTD(const char *Cmd); int CmdHFeMRTD(const char *Cmd);
int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, const char *path); int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, const char *path);

View file

@ -940,23 +940,29 @@ static int CmdHFiClassDecrypt(const char *Cmd) {
} }
bool verbose = arg_get_lit(clictx, 4); bool verbose = arg_get_lit(clictx, 4);
CLIParserFree(clictx); CLIParserFree(clictx);
size_t keylen = 0;
uint8_t dec_data[8] = {0}; uint8_t dec_data[8] = {0};
bool use_sc = false;
bool use_sc = IsCryptoHelperPresent(verbose); if (have_key == false) {
use_sc = IsCryptoHelperPresent(verbose);
if (have_key == false && use_sc == false) { if (use_sc == false) {
size_t keylen = 0;
int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen); int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen);
if (res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
PrintAndLogEx(INFO, "Couldn't find any decryption methods"); PrintAndLogEx(INFO, "Couldn't find any decryption methods");
return PM3_EINVARG; return PM3_EINVARG;
} }
if (keylen != 16) {
PrintAndLogEx(ERR, "Failed to load transport key from file");
free(keyptr);
return PM3_EINVARG;
}
memcpy(key, keyptr, sizeof(key)); memcpy(key, keyptr, sizeof(key));
free(keyptr); free(keyptr);
} }
}
// tripledes // tripledes
mbedtls_des3_context ctx; mbedtls_des3_context ctx;
@ -1157,19 +1163,26 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) {
CLIParserFree(clictx); CLIParserFree(clictx);
bool use_sc = IsCryptoHelperPresent(verbose); bool use_sc = false;
if (have_key == false) {
if (have_key == false && use_sc == false) { use_sc = IsCryptoHelperPresent(verbose);
if (use_sc == false) {
size_t keylen = 0; size_t keylen = 0;
int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen); int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen);
if (res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Failed to find the transport key"); PrintAndLogEx(ERR, "Failed to find any encryption methods");
return PM3_EINVARG; return PM3_EINVARG;
} }
if (keylen != 16) {
PrintAndLogEx(ERR, "Failed to load transport key from file");
free(keyptr);
return PM3_EINVARG;
}
memcpy(key, keyptr, sizeof(key)); memcpy(key, keyptr, sizeof(key));
free(keyptr); free(keyptr);
} }
}
if (use_sc) { if (use_sc) {
Encrypt(blk_data, blk_data); Encrypt(blk_data, blk_data);

View file

@ -40,7 +40,7 @@ typedef struct {
// sample set for selftest. // sample set for selftest.
jooki_test_t jooks[] = { jooki_test_t jooks[] = {
{ {0x04, 0xDA, 0xB7, 0x6A, 0xE7, 0x4C, 0x80}, "ruxow8lnn88uyeX+", 0x01, 0x00}, { {0x04, 0xDA, 0xB7, 0x6A, 0xE7, 0x4C, 0x80}, "ruxow8lnn88uyeX+", 0x01, 0x00},
{ {0x04, 0xf0, 0x22, 0xc2, 0x33, 0x5e, 0x80}, "\0", 0x01 , 0x00}, { {0x04, 0xf0, 0x22, 0xc2, 0x33, 0x5e, 0x80}, "\0", 0x01, 0x00},
{ {0x04, 0x8C, 0xEC, 0xDA, 0xF0, 0x4A, 0x80}, "ONrsVf7jX6IaSNV6", 0x01, 0x01}, { {0x04, 0x8C, 0xEC, 0xDA, 0xF0, 0x4A, 0x80}, "ONrsVf7jX6IaSNV6", 0x01, 0x01},
{ {0x04, 0x92, 0xA7, 0x6A, 0xE7, 0x4C, 0x81}, "Hjjpcx/mZwuveTF+", 0x01, 0x02}, { {0x04, 0x92, 0xA7, 0x6A, 0xE7, 0x4C, 0x81}, "Hjjpcx/mZwuveTF+", 0x01, 0x02},
{ {0x04, 0xD0, 0xB0, 0x3A, 0xD3, 0x63, 0x80}, "\0", 0x01, 0x02}, { {0x04, 0xD0, 0xB0, 0x3A, 0xD3, 0x63, 0x80}, "\0", 0x01, 0x02},
@ -67,7 +67,7 @@ jooki_figure_t jooks_figures[] = {
{0x01, 0x0C, "White Knight", "Figurine"}, {0x01, 0x0C, "White Knight", "Figurine"},
{0x01, 0x0D, "White Whale", "Figurine"}, {0x01, 0x0D, "White Whale", "Figurine"},
{0x02, 0x01, "Generic Flat", "Stone"}, {0x02, 0x00, "Generic Flat", "Stone"},
{0x03, 0x00, "record", "Sys"}, {0x03, 0x00, "record", "Sys"},
{0x03, 0x01, "factory_mode_on", "Sys"}, {0x03, 0x01, "factory_mode_on", "Sys"},
@ -77,9 +77,10 @@ jooki_figure_t jooks_figures[] = {
{0x03, 0x05, "toy_safe_on", "Sys"}, {0x03, 0x05, "toy_safe_on", "Sys"},
{0x03, 0x06, "toy_safe_off", "Sys"}, {0x03, 0x06, "toy_safe_off", "Sys"},
{0x03, 0x07, "wifi_on", "Sys"}, {0x03, 0x07, "wifi_on", "Sys"},
{0x03, 0x08, "bt_on", "Sys"}, {0x03, 0x08, "wifi_off", "Sys"},
{0x03, 0x0a, "bt_off", "Sys"}, {0x03, 0x09, "bt_on", "Sys"},
{0x03, 0x0b, "production_finished", "Sys"}, {0x03, 0x0A, "bt_off", "Sys"},
{0x03, 0x0B, "production_finished", "Sys"},
{0x04, 0x00, "test.0", "Test"}, {0x04, 0x00, "test.0", "Test"},
{0x04, 0x01, "test.1", "Test"}, {0x04, 0x01, "test.1", "Test"},
@ -105,7 +106,7 @@ jooki_figure_t jooks_figures[] = {
}; };
static int jooki_lookup(uint8_t tid, uint8_t fid) { static int jooki_lookup(uint8_t tid, uint8_t fid) {
for (int i=0; i < ARRAYLEN(jooks_figures); i++) { for (int i = 0; i < ARRAYLEN(jooks_figures); i++) {
jooki_figure_t tmp = jooks_figures[i]; jooki_figure_t tmp = jooks_figures[i];
if (tmp.typeid == tid && tmp.figureid == fid) { if (tmp.typeid == tid && tmp.figureid == fid) {
return i; return i;
@ -134,7 +135,7 @@ static int jooki_encode(uint8_t *iv, uint8_t tid, uint8_t fid, uint8_t *uid, uin
return PM3_EINVARG; return PM3_EINVARG;
} }
uint8_t d[JOOKI_PLAIN_LEN] = {iv[0], iv[1],iv[2], tid, fid, uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6]}; uint8_t d[JOOKI_PLAIN_LEN] = {iv[0], iv[1], iv[2], tid, fid, uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6]};
uint8_t enc[JOOKI_PLAIN_LEN] = {0}; uint8_t enc[JOOKI_PLAIN_LEN] = {0};
for (uint8_t i = 0; i < JOOKI_PLAIN_LEN; i++) { for (uint8_t i = 0; i < JOOKI_PLAIN_LEN; i++) {
@ -149,7 +150,7 @@ static int jooki_encode(uint8_t *iv, uint8_t tid, uint8_t fid, uint8_t *uid, uin
size_t b64len = 0; size_t b64len = 0;
uint8_t b64[20]; uint8_t b64[20];
memset(b64, 0, 20); memset(b64, 0, 20);
mbedtls_base64_encode(b64, sizeof(b64), &b64len, (const unsigned char*)enc, sizeof(enc)); mbedtls_base64_encode(b64, sizeof(b64), &b64len, (const unsigned char *)enc, sizeof(enc));
memcpy(out, b64, b64len); memcpy(out, b64, b64len);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
@ -157,7 +158,7 @@ static int jooki_encode(uint8_t *iv, uint8_t tid, uint8_t fid, uint8_t *uid, uin
static int jooki_decode(uint8_t *b64, uint8_t *result) { static int jooki_decode(uint8_t *b64, uint8_t *result) {
uint8_t ndef[JOOKI_PLAIN_LEN] = {0}; uint8_t ndef[JOOKI_PLAIN_LEN] = {0};
size_t outputlen = 0; size_t outputlen = 0;
mbedtls_base64_decode(ndef, sizeof(ndef), &outputlen, (const unsigned char*)b64, 16); mbedtls_base64_decode(ndef, sizeof(ndef), &outputlen, (const unsigned char *)b64, 16);
PrintAndLogEx(DEBUG, "(decode_jooki) raw encoded... " _GREEN_("%s"), sprint_hex(ndef, sizeof(ndef))); PrintAndLogEx(DEBUG, "(decode_jooki) raw encoded... " _GREEN_("%s"), sprint_hex(ndef, sizeof(ndef)));
@ -236,7 +237,7 @@ static int jooki_selftest(void) {
uint8_t iv[JOOKI_IV_LEN] = {0}; uint8_t iv[JOOKI_IV_LEN] = {0};
uint8_t uid[JOOKI_UID_LEN] = {0}; uint8_t uid[JOOKI_UID_LEN] = {0};
uint8_t result[JOOKI_PLAIN_LEN] = {0}; uint8_t result[JOOKI_PLAIN_LEN] = {0};
jooki_decode((uint8_t*)jooks[i].b64, result); jooki_decode((uint8_t *)jooks[i].b64, result);
memcpy(iv, result, JOOKI_IV_LEN); memcpy(iv, result, JOOKI_IV_LEN);
uint8_t tid = result[3]; uint8_t tid = result[3];
@ -249,7 +250,7 @@ static int jooki_selftest(void) {
int idx = jooki_lookup(tid, fid); int idx = jooki_lookup(tid, fid);
PrintAndLogEx(INFO, "Encoded URL.. %s ( %s )", sprint_hex((const uint8_t*)jooks[i].b64, 12), jooks[i].b64); PrintAndLogEx(INFO, "Encoded URL.. %s ( %s )", sprint_hex((const uint8_t *)jooks[i].b64, 12), jooks[i].b64);
PrintAndLogEx(INFO, "Type......... %02x - " _GREEN_("%s") " ( %s )", tid, (idx != -1) ? jooks_figures[idx].typedesc : "n/a", tid_ok ? _GREEN_("ok") : _RED_("fail")); PrintAndLogEx(INFO, "Type......... %02x - " _GREEN_("%s") " ( %s )", tid, (idx != -1) ? jooks_figures[idx].typedesc : "n/a", tid_ok ? _GREEN_("ok") : _RED_("fail"));
PrintAndLogEx(INFO, "Figurine..... %02x - " _GREEN_("%s") " ( %s )", fid, (idx != -1) ? jooks_figures[idx].figdesc : "n/a", fid_ok ? _GREEN_("ok") : _RED_("fail")); PrintAndLogEx(INFO, "Figurine..... %02x - " _GREEN_("%s") " ( %s )", fid, (idx != -1) ? jooks_figures[idx].figdesc : "n/a", fid_ok ? _GREEN_("ok") : _RED_("fail"));
PrintAndLogEx(INFO, "iv........... %s", sprint_hex(iv, sizeof(iv))); PrintAndLogEx(INFO, "iv........... %s", sprint_hex(iv, sizeof(iv)));
@ -264,7 +265,7 @@ static int jooki_selftest(void) {
PrintAndLogEx(INFO, "NDEF raw .... %s", sprint_hex(ndefmsg, sizeof(ndefmsg))); PrintAndLogEx(INFO, "NDEF raw .... %s", sprint_hex(ndefmsg, sizeof(ndefmsg)));
int status = NDEFRecordsDecodeAndPrint(ndefmsg, sizeof(ndefmsg)); int status = NDEFRecordsDecodeAndPrint(ndefmsg, sizeof(ndefmsg));
if ( status != PM3_SUCCESS) { if (status != PM3_SUCCESS) {
status = NDEFDecodeAndPrint(ndefmsg, sizeof(ndefmsg), true); status = NDEFDecodeAndPrint(ndefmsg, sizeof(ndefmsg), true);
} }
PrintAndLogEx(INFO, "=================================================================="); PrintAndLogEx(INFO, "==================================================================");
@ -278,7 +279,8 @@ static int CmdHF14AJookiEncode(const char *Cmd) {
"Encode a Jooki token to base64 NDEF URI format", "Encode a Jooki token to base64 NDEF URI format",
"hf jooki encode -t --> selftest\n" "hf jooki encode -t --> selftest\n"
"hf jooki encode -r --dragon --> read uid from tag and use for encoding\n" "hf jooki encode -r --dragon --> read uid from tag and use for encoding\n"
"hf jooki encode --uid 04010203040506 --dragon" "hf jooki encode --uid 04010203040506 --dragon\n"
"hf jooki encode --uid 04010203040506 --tid 1 --fid 1"
); );
void *argtable[] = { void *argtable[] = {
@ -300,6 +302,8 @@ static int CmdHF14AJookiEncode(const char *Cmd) {
arg_lit0(NULL, "whitefox", "figurine type"), arg_lit0(NULL, "whitefox", "figurine type"),
arg_lit0(NULL, "whiteknight", "figurine type"), arg_lit0(NULL, "whiteknight", "figurine type"),
arg_lit0(NULL, "whitewhale", "figurine type"), arg_lit0(NULL, "whitewhale", "figurine type"),
arg_u64_0(NULL, "tid", "<dec>", "figurine type id"),
arg_u64_0(NULL, "fid", "<dec>", "figurine id"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -328,18 +332,48 @@ static int CmdHF14AJookiEncode(const char *Cmd) {
bool tb = arg_get_lit(ctx, 15); bool tb = arg_get_lit(ctx, 15);
bool tc = arg_get_lit(ctx, 16); bool tc = arg_get_lit(ctx, 16);
bool td = arg_get_lit(ctx, 17); bool td = arg_get_lit(ctx, 17);
uint8_t ftid = arg_get_u32_def(ctx, 18, 0);
uint8_t ffid = arg_get_u32_def(ctx, 19, 0);
bool figure_abbr = true;
CLIParserFree(ctx); CLIParserFree(ctx);
if (selftest) { if (selftest) {
return jooki_selftest(); return jooki_selftest();
} }
if ((t0 + t1 + t2 + t3 + t5 + t6 + t7 + t8 + t9 + ta + tb + tc + td) > 1) { uint8_t tid, fid;
PrintAndLogEx(ERR, "Select one tag type");
if (ftid || ffid) {
figure_abbr = false;
}
if (ftid > 0x04 || ffid > 0x20) {
PrintAndLogEx(ERR, "Use a valid Figure Type ID and Figure ID");
return PM3_EINVARG; return PM3_EINVARG;
} }
uint8_t tid = 0x01;
uint8_t fid = 0x00; uint8_t figure_abbr_val = t0 + t1 + t2 + t3 + t5 + t6 + t7 + t8 + t9 + ta + tb + tc + td;
if (figure_abbr_val > 1) {
PrintAndLogEx(ERR, "Select one tag type or use figurine type id and figurine id");
return PM3_EINVARG;
}
if (figure_abbr_val == 1 && !figure_abbr) {
PrintAndLogEx(ERR, "Use either --tid and --fid or one of the figurine types");
return PM3_EINVARG;
}
if (figure_abbr) {
tid = 0x01;
} else {
tid = ftid;
}
fid = ffid;
if (t1) if (t1)
fid = 0x01; fid = 0x01;
if (t2) if (t2)
@ -450,7 +484,7 @@ static int CmdHF14AJookiSim(const char *Cmd) {
uint8_t *data = calloc(144, sizeof(uint8_t)); uint8_t *data = calloc(144, sizeof(uint8_t));
memcpy(data, uid, 3); memcpy(data, uid, 3);
memcpy(data + (1*4), uid + 3, 4); memcpy(data + (1 * 4), uid + 3, 4);
// bbc0 // bbc0
data[3] = 0x88 ^ data[0] ^ data[1] ^ data[2]; data[3] = 0x88 ^ data[0] ^ data[1] ^ data[2];
@ -459,7 +493,7 @@ static int CmdHF14AJookiSim(const char *Cmd) {
data[8] = data[4] ^ data[5] ^ data[6] ^ data[7]; data[8] = data[4] ^ data[5] ^ data[6] ^ data[7];
// copy NDEF magic firs, skip BBC1 // copy NDEF magic firs, skip BBC1
memcpy(data + (2*4) + 1, "\x48\x00\x00\xE1\x10\x12\x00", 7); memcpy(data + (2 * 4) + 1, "\x48\x00\x00\xE1\x10\x12\x00", 7);
// copy raw NDEF // copy raw NDEF
jooki_create_ndef(b64, data + (4 * 4)); jooki_create_ndef(b64, data + (4 * 4));
@ -539,7 +573,7 @@ static int CmdHF14AJookiSim(const char *Cmd) {
} }
free(data); free(data);
PrintAndLogEx(INFO, "Done"); PrintAndLogEx(INFO, "Done");
PrintAndLogEx(HINT, "Try `" _YELLOW_("hf 14a list") "` to view trace log" ); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf 14a list") "` to view trace log");
return PM3_SUCCESS; return PM3_SUCCESS;
} }
@ -547,7 +581,7 @@ static int CmdHF14AJookiClone(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "hf jooki clone", CLIParserInit(&ctx, "hf jooki clone",
"Write a Jooki token to a Ultralight or NTAG tag", "Write a Jooki token to a Ultralight or NTAG tag",
"hf jooki clone -d <hex bytes> --> where hex is raw NDEF" "hf jooki clone -d <hex bytes> --> where hex is raw NDEF\n"
"hf jooki clone --b64 7WzlgEzqLgwTnWNy --> using base64 url parameter" "hf jooki clone --b64 7WzlgEzqLgwTnWNy --> using base64 url parameter"
); );
@ -617,7 +651,7 @@ static int CmdHF14AJookiClone(const char *Cmd) {
} }
PrintAndLogEx(INFO, "Done"); PrintAndLogEx(INFO, "Done");
PrintAndLogEx(HINT, "Try `" _YELLOW_("hf mfu ndef") "` to view" ); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf mfu ndef") "` to view");
return PM3_SUCCESS; return PM3_SUCCESS;
} }

View file

@ -27,6 +27,7 @@
#include "des.h" // des ecb #include "des.h" // des ecb
#include "crapto1/crapto1.h" // prng_successor #include "crapto1/crapto1.h" // prng_successor
#include "cmdhf14a.h" // exchange APDU #include "cmdhf14a.h" // exchange APDU
#include "crypto/libpcrypto.h"
#define MFBLOCK_SIZE 16 #define MFBLOCK_SIZE 16
@ -425,6 +426,46 @@ static int usage_hf14_gen3freeze(void) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) {
// ref: MIFARE Classic EV1 Originality Signature Validation
#define PUBLIC_MFCEV1_ECDA_KEYLEN 33
const ecdsa_publickey_t nxp_mfc_public_keys[] = {
{"NXP Mifare Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"},
};
uint8_t i;
bool is_valid = false;
for (i = 0; i < ARRAYLEN(nxp_mfc_public_keys); i++) {
int dl = 0;
uint8_t key[PUBLIC_MFCEV1_ECDA_KEYLEN];
param_gethex_to_eol(nxp_mfc_public_keys[i].value, 0, key, PUBLIC_MFCEV1_ECDA_KEYLEN, &dl);
int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, uidlen, signature, signature_len, false);
is_valid = (res == 0);
if (is_valid)
break;
}
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature"));
if (is_valid == false || i == ARRAYLEN(nxp_mfc_public_keys)) {
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32));
PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed"));
return PM3_ESOFT;
}
PrintAndLogEx(INFO, " IC signature public key name: %s", nxp_mfc_public_keys[i].desc);
PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfc_public_keys[i].value);
PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1");
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32));
PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful"));
return PM3_SUCCESS;
}
static int GetHFMF14AUID(uint8_t *uid, int *uidlen) { static int GetHFMF14AUID(uint8_t *uid, int *uidlen) {
clearCommandBuffer(); clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
@ -634,14 +675,14 @@ static int CmdHF14AMfWrBl(const char *Cmd) {
PrintAndLogEx(NORMAL, "Usage: hf mf wrbl <block number> <key A/B> <key (12 hex symbols)> <block data (32 hex symbols)>"); PrintAndLogEx(NORMAL, "Usage: hf mf wrbl <block number> <key A/B> <key (12 hex symbols)> <block data (32 hex symbols)>");
PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, " hf mf wrbl 1 A FFFFFFFFFFFF 000102030405060708090A0B0C0D0E0F"); PrintAndLogEx(NORMAL, " hf mf wrbl 1 A FFFFFFFFFFFF 000102030405060708090A0B0C0D0E0F");
return 0; return PM3_SUCCESS;
} }
blockNo = param_get8(Cmd, 0); blockNo = param_get8(Cmd, 0);
cmdp = tolower(param_getchar(Cmd, 1)); cmdp = tolower(param_getchar(Cmd, 1));
if (cmdp == 0x00) { if (cmdp == 0x00) {
PrintAndLogEx(NORMAL, "Key type must be A or B"); PrintAndLogEx(NORMAL, "Key type must be A or B");
return 1; return PM3_EINVARG;
} }
if (cmdp != 'a') if (cmdp != 'a')
@ -649,12 +690,12 @@ static int CmdHF14AMfWrBl(const char *Cmd) {
if (param_gethex(Cmd, 2, key, 12)) { if (param_gethex(Cmd, 2, key, 12)) {
PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols"); PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols");
return 1; return PM3_EINVARG;
} }
if (param_gethex(Cmd, 3, bldata, 32)) { if (param_gethex(Cmd, 3, bldata, 32)) {
PrintAndLogEx(NORMAL, "Block data must include 32 HEX symbols"); PrintAndLogEx(NORMAL, "Block data must include 32 HEX symbols");
return 1; return PM3_EINVARG;
} }
PrintAndLogEx(NORMAL, "--block no %d, key %c - %s", blockNo, keyType ? 'B' : 'A', sprint_hex(key, 6)); PrintAndLogEx(NORMAL, "--block no %d, key %c - %s", blockNo, keyType ? 'B' : 'A', sprint_hex(key, 6));
@ -674,7 +715,7 @@ static int CmdHF14AMfWrBl(const char *Cmd) {
PrintAndLogEx(NORMAL, "Command execute timeout"); PrintAndLogEx(NORMAL, "Command execute timeout");
} }
return 0; return PM3_SUCCESS;
} }
static int CmdHF14AMfRdBl(const char *Cmd) { static int CmdHF14AMfRdBl(const char *Cmd) {
@ -706,34 +747,15 @@ static int CmdHF14AMfRdBl(const char *Cmd) {
} }
PrintAndLogEx(NORMAL, "--block no %d, key %c - %s", blockNo, keyType ? 'B' : 'A', sprint_hex(key, 6)); PrintAndLogEx(NORMAL, "--block no %d, key %c - %s", blockNo, keyType ? 'B' : 'A', sprint_hex(key, 6));
mf_readblock_t payload; uint8_t data[16] = {0};
payload.blockno = blockNo; int res = mfReadBlock(blockNo, keyType, key, data);
payload.keytype = keyType; if (res == PM3_SUCCESS) {
memcpy(payload.key, key, sizeof(payload.key)); PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data, 16));
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) {
uint8_t *data = resp.data.asBytes;
if (resp.status == PM3_SUCCESS) {
PrintAndLogEx(NORMAL, "data: %s", sprint_hex(data, 16));
} else {
PrintAndLogEx(FAILED, "failed reading block");
return PM3_ESOFT;
}
if ((data[6] || data[7] || data[8])) { if ((data[6] || data[7] || data[8])) {
decode_print_st(blockNo, data); decode_print_st(blockNo, data);
} }
} else {
PrintAndLogEx(WARNING, "Command execute timeout");
return PM3_ETIMEOUT;
} }
return PM3_SUCCESS;
return 0;
} }
static int CmdHF14AMfRdSc(const char *Cmd) { static int CmdHF14AMfRdSc(const char *Cmd) {
@ -769,17 +791,15 @@ static int CmdHF14AMfRdSc(const char *Cmd) {
} }
PrintAndLogEx(NORMAL, "--sector no %d, key %c - %s ", sectorNo, keyType ? 'B' : 'A', sprint_hex(key, 6)); PrintAndLogEx(NORMAL, "--sector no %d, key %c - %s ", sectorNo, keyType ? 'B' : 'A', sprint_hex(key, 6));
clearCommandBuffer(); uint8_t sc_size = mfNumBlocksPerSector(sectorNo) * 16;
SendCommandMIX(CMD_HF_MIFARE_READSC, sectorNo, keyType, 0, key, 6); uint8_t *data = calloc(sc_size, sizeof(uint8_t));
PrintAndLogEx(NORMAL, ""); if (data == NULL) {
PrintAndLogEx(ERR, "failed to allocate memory");
return PM3_EMALLOC;
}
PacketResponseNG resp; int res = mfReadSector(sectorNo, keyType, key, data);
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { if (res == PM3_SUCCESS) {
uint8_t isOK = resp.oldarg[0] & 0xff;
uint8_t *data = resp.data.asBytes;
PrintAndLogEx(NORMAL, "isOk:%02x", isOK);
if (isOK) {
uint8_t blocks = NumBlocksPerSector(sectorNo); uint8_t blocks = NumBlocksPerSector(sectorNo);
uint8_t start = FirstBlockOfSector(sectorNo); uint8_t start = FirstBlockOfSector(sectorNo);
@ -788,11 +808,9 @@ static int CmdHF14AMfRdSc(const char *Cmd) {
PrintAndLogEx(NORMAL, "%3d | %s", start + i, sprint_hex(data + (i * 16), 16)); PrintAndLogEx(NORMAL, "%3d | %s", start + i, sprint_hex(data + (i * 16), 16));
} }
decode_print_st(start + blocks - 1, data + ((blocks - 1) * 16)); decode_print_st(start + blocks - 1, data + ((blocks - 1) * 16));
}
} else {
PrintAndLogEx(WARNING, "Command execute timeout");
}
}
free(data);
return PM3_SUCCESS; return PM3_SUCCESS;
} }

View file

@ -24,4 +24,6 @@ void readerAttack(sector_t *k_sector, uint8_t k_sectorsCount, nonces_t data, boo
void printKeyTable(uint8_t sectorscnt, sector_t *e_sector); void printKeyTable(uint8_t sectorscnt, sector_t *e_sector);
void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_sector); void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_sector);
void printKeyTable_fast(uint8_t sectorscnt, icesector_t *e_sector, uint64_t bar, uint64_t foo); void printKeyTable_fast(uint8_t sectorscnt, icesector_t *e_sector, uint64_t bar, uint64_t foo);
int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len);
#endif #endif

View file

@ -1310,7 +1310,8 @@ static int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signat
{"DESFire EV3", "041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743"}, {"DESFire EV3", "041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743"},
{"NTAG424DNA, NTAG424DNATT, DESFire Light EV2", "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3B"}, {"NTAG424DNA, NTAG424DNATT, DESFire Light EV2", "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3B"},
{"DESFire Light", "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"}, {"DESFire Light", "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"},
{"MIFARE Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"} {"MIFARE Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"},
{"MIFARE Pluc Evx", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"},
}; };

View file

@ -163,7 +163,8 @@ static int plus_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature
// ref: MIFARE Plus EV1 Originality Signature Validation // ref: MIFARE Plus EV1 Originality Signature Validation
#define PUBLIC_PLUS_ECDA_KEYLEN 57 #define PUBLIC_PLUS_ECDA_KEYLEN 57
const ecdsa_publickey_t nxp_plus_public_keys[] = { const ecdsa_publickey_t nxp_plus_public_keys[] = {
{"Mifare Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"} {"MIFARE Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"},
{"MIFARE Pluc Ev_x", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"}
}; };
uint8_t i; uint8_t i;

View file

@ -745,8 +745,8 @@ static int ulev1_print_signature(TagTypeUL_t tagtype, uint8_t *uid, uint8_t *sig
// ref: AN11350 NTAG 21x Originality Signature Validation // ref: AN11350 NTAG 21x Originality Signature Validation
// ref: AN11341 MIFARE Ultralight EV1 Originality Signature Validation // ref: AN11341 MIFARE Ultralight EV1 Originality Signature Validation
const ecdsa_publickey_t nxp_mfu_public_keys[] = { const ecdsa_publickey_t nxp_mfu_public_keys[] = {
{"NXP Mifare Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, {"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"},
{"Manufacturer Mifare Classic MFC1C14_x", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"}, {"Manufacturer MIFARE Classic MFC1C14_x", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"},
{"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"}, {"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"},
{"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"}, {"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"},
{"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"}, {"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"},
@ -805,7 +805,7 @@ static int ulev1_print_signature(TagTypeUL_t tagtype, uint8_t *uid, uint8_t *sig
for (i = 0; i < ARRAYLEN(nxp_mfu_public_keys); i++) { for (i = 0; i < ARRAYLEN(nxp_mfu_public_keys); i++) {
int dl = 0; int dl = 0;
uint8_t key[PUBLIC_ECDA_KEYLEN]; uint8_t key[PUBLIC_ECDA_KEYLEN] = {0};
param_gethex_to_eol(nxp_mfu_public_keys[i].value, 0, key, PUBLIC_ECDA_KEYLEN, &dl); param_gethex_to_eol(nxp_mfu_public_keys[i].value, 0, key, PUBLIC_ECDA_KEYLEN, &dl);
int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 7, signature, signature_len, false); int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 7, signature, signature_len, false);
@ -941,6 +941,267 @@ static char *GenerateFilename(const char *prefix, const char *suffix) {
return fptr; return fptr;
} }
//------------------------------------
/*
static int mfu_decrypt_amiibo(uint8_t *encrypted, uint16_t elen, uint8_t *decrypted, uint16_t *dlen) {
if (elen < NFC3D_AMIIBO_SIZE / 4) {
PrintAndLogEx(ERR, "ERR, data wrong length, got %zu , expected %zu", elen, (NFC3D_AMIIBO_SIZE / 4));
return PM3_ESOFT;
}
nfc3d_amiibo_keys amiibo_keys = {0};
if (nfc3d_amiibo_load_keys(&amiibo_keys) == false) {
return PM3_ESOFT;
}
if (nfc3d_amiibo_unpack(&amiibo_keys, encrypted, decrypted) == false) {
PrintAndLogEx(ERR, "WARNING, Tag signature was NOT valid");
}
*dlen = NFC3D_AMIIBO_SIZE;
return PM3_SUCCESS;
}
static int mfu_dump_tag(uint16_t pages, void **pdata, uint16_t *len) {
int res = PM3_SUCCESS;
uint16_t maxbytes = (pages * 4);
*pdata = calloc(maxbytes, sizeof(uint8_t));
if (*pdata == NULL) {
PrintAndLogEx(FAILED, "error, cannot allocate memory");
res = PM3_EMALLOC;
goto out;
}
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFAREU_READCARD, 0, pages, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "Command execute time-out");
free(*pdata);
res = PM3_ETIMEOUT;
goto out;
}
if (resp.oldarg[0] != 1) {
PrintAndLogEx(WARNING, "Failed reading card");
free(*pdata);
res = PM3_ESOFT;
goto out;
}
// read all memory
uint32_t startindex = resp.oldarg[2];
uint32_t buffer_size = resp.oldarg[1];
if (buffer_size > maxbytes) {
PrintAndLogEx(FAILED, "Data exceeded buffer size!");
buffer_size = maxbytes;
}
if (!GetFromDevice(BIG_BUF, *pdata, buffer_size, startindex, NULL, 0, NULL, 2500, false)) {
PrintAndLogEx(WARNING, "command execution time out");
free(*pdata);
res = PM3_ETIMEOUT;
goto out;
}
if (len)
*len = buffer_size;
out:
return res;
}
*/
/*
Lego Dimensions,
Version: 00 04 04 02 01 00 0F 03
matching bytes:
index 12 ( 3 * 4 )
E1 10 12 00 01 03 A0 0C 34 03 13 D1 01 0F 54 02 65 6E
*/
typedef struct {
const char *desc;
const char *version;
uint8_t mpos;
uint8_t mlen;
const char *match;
uint32_t (*Pwd)(uint8_t *uid);
uint16_t (*Pack)(uint8_t *uid);
const char *hint;
} PACKED mfu_identify_t;
static mfu_identify_t mfu_ident_table[] = {
{
"Jooki", "0004040201000F03",
12, 32, "E11012000103A00C340329D101255504732E6A6F6F6B692E726F636B732F732F",
ul_ev1_pwdgen_def, ul_ev1_packgen_def,
"hf jooki decode -r"
},
{
"Lego Dimensions", "0004040201000F03",
12, 18, "E11012000103A00C340313D1010F5402656E",
ul_ev1_pwdgenC, ul_ev1_packgenC,
"hf mfu dump -k %08x"
},
{
"Hotwheels", "0004040201000F03",
9, 9, "E110120F",
ul_ev1_pwdgen_def, ul_ev1_packgen_def,
"hf mfu dump -k %08x"
},
{
"Minecraft Earth", "0004040201000F03",
9, 26, "48F6FFE1101200037C91012C55027069642E6D617474656C2F4167",
ul_ev1_pwdgen_def, ul_ev1_packgen_def,
"hf mfu dump -k %08x"
},
{
"Snackworld", "0004040101000B03",
9, 7, "483000E1100600",
NULL, NULL,
"hf mfu dump -k %08x"
},
{
"Amiibo", "0004040201001103",
9, 9, "480FE0F110FFEEA500",
ul_ev1_pwdgenB, ul_ev1_packgenB,
"hf mfu dump -k %08x"
},
{NULL, NULL, 0, 0, NULL, NULL, NULL, NULL}
};
static mfu_identify_t* mfu_match_fingerprint(uint8_t *version, uint8_t *data) {
uint8_t i = 0;
do {
int vl = 0;
uint8_t vtmp[10] = {0};
param_gethex_to_eol(mfu_ident_table[i].version, 0, vtmp, sizeof(vtmp), &vl);
bool m1 = (memcmp(vtmp, version, vl) == 0);
if (m1 == false) {
PrintAndLogEx(DEBUG, "(fingerprint) wrong version");
continue;
}
int ml = 0;
uint8_t mtmp[40] = {0};
param_gethex_to_eol(mfu_ident_table[i].match, 0, mtmp, sizeof(mtmp), &ml);
bool m2 = (memcmp(mtmp, data + mfu_ident_table[i].mpos, mfu_ident_table[i].mlen) == 0);
if (m2) {
PrintAndLogEx(DEBUG, "(fingerprint) found %s", mfu_ident_table[i].desc);
return &mfu_ident_table[i];
}
} while (mfu_ident_table[++i].desc);
return NULL;
}
static uint8_t mfu_max_len(void) {
uint8_t n = 0, i = 0;
do {
uint8_t tmp = mfu_ident_table[i].mpos + mfu_ident_table[i].mlen;
if (tmp > n) {
n = tmp;
}
} while (mfu_ident_table[++i].desc);
return n;
}
static int mfu_get_version_uid(uint8_t *version, uint8_t *uid) {
iso14a_card_select_t card;
if (ul_select(&card) == false)
return PM3_ESOFT;
uint8_t v[10] = {0x00};
int len = ulev1_getVersion(v, sizeof(v));
DropField();
if (len != sizeof(v))
return PM3_ESOFT;
memcpy(version, v, 8);
memcpy(uid, card.uid, 7);
return PM3_SUCCESS;
}
static int mfu_fingerprint(void) {
uint8_t *data = NULL;
int res = PM3_SUCCESS;
PrintAndLogEx(INFO, "------------------------ " _CYAN_("Fingerprint") " -----------------------");
uint8_t maxbytes = mfu_max_len();
if (maxbytes == 0) {
PrintAndLogEx(ERR, "fingerprint table wrong");
res = PM3_ESOFT;
goto out;
}
maxbytes = ((maxbytes / 4) + 1) * 4;
data = calloc(maxbytes, sizeof(uint8_t));
if (data == NULL) {
PrintAndLogEx(ERR, "failed to allocate memory");
res = PM3_EMALLOC;
goto out;
}
uint8_t pages = (maxbytes / 4);
PrintAndLogEx(INFO, "Reading tag memory...");
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFAREU_READCARD, 0, pages, 0, NULL, 0);
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) {
PrintAndLogEx(WARNING, "Command execute time-out");
res = PM3_ETIMEOUT;
goto out;
}
if (resp.oldarg[0] != 1) {
PrintAndLogEx(WARNING, "Failed reading card");
res = PM3_ESOFT;
goto out;
}
// read all memory
uint32_t startindex = resp.oldarg[2];
uint32_t buffer_size = resp.oldarg[1];
if (buffer_size > maxbytes) {
PrintAndLogEx(FAILED, "Data exceeded buffer size!");
buffer_size = maxbytes;
}
if (!GetFromDevice(BIG_BUF, data, buffer_size, startindex, NULL, 0, NULL, 2500, false)) {
PrintAndLogEx(WARNING, "command execution time out");
res = PM3_ETIMEOUT;
goto out;
}
uint8_t version[8] = {0};
uint8_t uid[7] = {0};
if (mfu_get_version_uid(version, uid) == PM3_SUCCESS) {
mfu_identify_t* item = mfu_match_fingerprint(version, data);
if (item) {
PrintAndLogEx(SUCCESS, "Found " _GREEN_("%s"), item->desc);
if (item->Pwd) {
char s[40] = {0};
sprintf(s, item->hint, item->Pwd(uid));
PrintAndLogEx(HINT, "Use `" _YELLOW_("%s") "`", s);
} else {
PrintAndLogEx(HINT, "Use `" _YELLOW_("%s") "`", item->hint);
}
}
}
out:
free(data);
PrintAndLogEx(INFO, "------------------------------------------------------------");
return res;
}
uint32_t GetHF14AMfU_Type(void) { uint32_t GetHF14AMfU_Type(void) {
TagTypeUL_t tagtype = UNKNOWN; TagTypeUL_t tagtype = UNKNOWN;
@ -1385,17 +1646,20 @@ static int CmdHF14AMfUInfo(const char *Cmd) {
} }
if (len < 1) { if (len < 1) {
PrintAndLogEx(WARNING, _YELLOW_("password not known")); PrintAndLogEx(WARNING, _YELLOW_("password not known"));
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen r`") " to get see known pwd gen algo suggestions"); PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen -r`") " to get see known pwd gen algo suggestions");
} }
} else { } else {
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen r`") " to get see known pwd gen algo suggestions"); PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen -r`") " to get see known pwd gen algo suggestions");
} }
} }
mfu_fingerprint();
out: out:
DropField(); DropField();
if (locked) { if (locked) {
PrintAndLogEx(INFO, "\nTag appears to be locked, try using a key to get more info"); PrintAndLogEx(INFO, "\nTag appears to be locked, try using a key to get more info");
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen r`") " to get see known pwd gen algo suggestions"); PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu pwdgen -r`") " to get see known pwd gen algo suggestions");
} }
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS; return PM3_SUCCESS;
@ -2223,6 +2487,7 @@ static int CmdHF14AMfURestore(const char *Cmd) {
} }
PrintAndLogEx(INFO, "Restoring data blocks."); PrintAndLogEx(INFO, "Restoring data blocks.");
PrintAndLogEx(INFO, "." NOLF);
// write all other data // write all other data
// Skip block 0,1,2,3 (only magic tags can write to them) // Skip block 0,1,2,3 (only magic tags can write to them)
// Skip last 5 blocks usually is configuration // Skip last 5 blocks usually is configuration
@ -2234,6 +2499,7 @@ static int CmdHF14AMfURestore(const char *Cmd) {
SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, b, keytype, 0, data, sizeof(data)); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, b, keytype, 0, data, sizeof(data));
wait4response(b); wait4response(b);
PrintAndLogEx(NORMAL, "." NOLF); PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
} }
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
@ -2811,14 +3077,14 @@ static int CmdHF14AMfUPwdGen(const char *Cmd) {
PrintAndLogEx(INFO, " Using UID : %s", sprint_hex(uid, 7)); PrintAndLogEx(INFO, " Using UID : %s", sprint_hex(uid, 7));
PrintAndLogEx(INFO, "---------------------------------"); PrintAndLogEx(INFO, "---------------------------------");
PrintAndLogEx(INFO, " algo | pwd | pack"); PrintAndLogEx(INFO, " algo | pwd | pack");
PrintAndLogEx(INFO, "------+----------+-----"); PrintAndLogEx(INFO, "----------------+----------+-----");
PrintAndLogEx(INFO, " EV1 | %08X | %04X", ul_ev1_pwdgenA(uid), ul_ev1_packgenA(uid)); PrintAndLogEx(INFO, " EV1 | %08X | %04X", ul_ev1_pwdgenA(uid), ul_ev1_packgenA(uid));
PrintAndLogEx(INFO, " Ami | %08X | %04X", ul_ev1_pwdgenB(uid), ul_ev1_packgenB(uid)); PrintAndLogEx(INFO, " Amiibo | %08X | %04X", ul_ev1_pwdgenB(uid), ul_ev1_packgenB(uid));
PrintAndLogEx(INFO, " LD | %08X | %04X", ul_ev1_pwdgenC(uid), ul_ev1_packgenC(uid)); PrintAndLogEx(INFO, " Lego Dimension | %08X | %04X", ul_ev1_pwdgenC(uid), ul_ev1_packgenC(uid));
PrintAndLogEx(INFO, " XYZ | %08X | %04X", ul_ev1_pwdgenD(uid), ul_ev1_packgenD(uid)); PrintAndLogEx(INFO, " XYZ 3D printer | %08X | %04X", ul_ev1_pwdgenD(uid), ul_ev1_packgenD(uid));
PrintAndLogEx(INFO, "------+----------+-----"); PrintAndLogEx(INFO, "----------------+----------+-----");
PrintAndLogEx(INFO, " Vingcard algo"); PrintAndLogEx(INFO, " Vingcard algo");
PrintAndLogEx(INFO, "--------------------"); PrintAndLogEx(INFO, "---------------------------------");
return PM3_SUCCESS; return PM3_SUCCESS;
} }
@ -3560,14 +3826,14 @@ static int CmdHF14MfuNDEF(const char *Cmd) {
DropField(); DropField();
status = NDEFRecordsDecodeAndPrint(records, (size_t)maxsize); status = NDEFRecordsDecodeAndPrint(records, (size_t)maxsize);
if ( status != PM3_SUCCESS) { if (status != PM3_SUCCESS) {
status = NDEFDecodeAndPrint(records, (size_t)maxsize, true); status = NDEFDecodeAndPrint(records, (size_t)maxsize, true);
} }
char *jooki = strstr((char*)records, "s.jooki.rocks/s/?s="); char *jooki = strstr((char *)records, "s.jooki.rocks/s/?s=");
if (jooki) { if (jooki) {
jooki += 17; jooki += 17;
while(jooki) { while (jooki) {
if ((*jooki) != '=') if ((*jooki) != '=')
jooki++; jooki++;
else { else {

View file

@ -23,6 +23,8 @@
#include "cmddata.h" #include "cmddata.h"
#include "commonutil.h" #include "commonutil.h"
#include "pm3_cmd.h" #include "pm3_cmd.h"
#include "pmflash.h" // rdv40validation_t
#include "cmdflashmem.h" // get_signature..
static int CmdHelp(const char *Cmd); static int CmdHelp(const char *Cmd);
@ -224,10 +226,10 @@ static void lookupChipID(uint32_t iChipID, uint32_t mem_used) {
sprintf(asBuff, "ROMless or on-chip Flash"); sprintf(asBuff, "ROMless or on-chip Flash");
break; break;
case 2: case 2:
sprintf(asBuff, "Embedded Flash Memory"); sprintf(asBuff, "Embedded flash memory");
break; break;
case 3: case 3:
sprintf(asBuff, "ROM and Embedded Flash Memory\nNVPSIZ is ROM size\nNVPSIZ2 is Flash size"); sprintf(asBuff, "ROM and Embedded flash memory\nNVPSIZ is ROM size\nNVPSIZ2 is Flash size");
break; break;
case 4: case 4:
sprintf(asBuff, "SRAM emulating ROM"); sprintf(asBuff, "SRAM emulating ROM");
@ -266,12 +268,13 @@ static void lookupChipID(uint32_t iChipID, uint32_t mem_used) {
break; break;
} }
PrintAndLogEx(NORMAL, " --= Nonvolatile program memory: " _YELLOW_("%uK") " bytes %s ( " _YELLOW_("%2.0f%%") " used )" PrintAndLogEx(NORMAL, " --= %s " _YELLOW_("%uK") " bytes ( " _YELLOW_("%2.0f%%") " used )"
, mem_avail
, asBuff , asBuff
, mem_avail
, mem_avail == 0 ? 0.0f : (float)mem_used / (mem_avail * 1024) * 100 , mem_avail == 0 ? 0.0f : (float)mem_used / (mem_avail * 1024) * 100
); );
/*
switch ((iChipID & 0xF000) >> 12) { switch ((iChipID & 0xF000) >> 12) {
case 0: case 0:
sprintf(asBuff, "None"); sprintf(asBuff, "None");
@ -305,6 +308,7 @@ static void lookupChipID(uint32_t iChipID, uint32_t mem_used) {
break; break;
} }
PrintAndLogEx(NORMAL, " --= Second nonvolatile program memory size: %s", asBuff); PrintAndLogEx(NORMAL, " --= Second nonvolatile program memory size: %s", asBuff);
*/
} }
static int CmdDbg(const char *Cmd) { static int CmdDbg(const char *Cmd) {
@ -959,17 +963,29 @@ void pm3_version(bool verbose, bool oneliner) {
PrintAndLogEx(NORMAL, " compiled with " PM3CLIENTCOMPILER __VERSION__ PM3HOSTOS PM3HOSTARCH); PrintAndLogEx(NORMAL, " compiled with " PM3CLIENTCOMPILER __VERSION__ PM3HOSTOS PM3HOSTARCH);
PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("PROXMARK3") " ]"); PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("PROXMARK3") " ]");
if (IfPm3Rdv4Fw() == false) { if (IfPm3Rdv4Fw()) {
PrintAndLogEx(NORMAL, " firmware.................. %s", _YELLOW_("PM3GENERIC"));
if (IfPm3FpcUsartHost()) { bool is_genuine_rdv4 = false;
PrintAndLogEx(NORMAL, " FPC USART for BT add-on... %s", _GREEN_("present")); // validate signature data
rdv40_validation_t mem;
if (rdv4_get_signature(&mem) == PM3_SUCCESS) {
if (rdv4_validate(&mem) == PM3_SUCCESS) {
is_genuine_rdv4 = true;
} }
} else { }
PrintAndLogEx(NORMAL, " firmware.................. %s", _YELLOW_("PM3RDV4"));
PrintAndLogEx(NORMAL, " device.................... %s", (is_genuine_rdv4) ? _GREEN_("RDV4") : _RED_("device / fw mismatch"));
PrintAndLogEx(NORMAL, " firmware.................. %s", _YELLOW_("RDV4"));
PrintAndLogEx(NORMAL, " external flash............ %s", IfPm3Flash() ? _GREEN_("present") : _YELLOW_("absent")); PrintAndLogEx(NORMAL, " external flash............ %s", IfPm3Flash() ? _GREEN_("present") : _YELLOW_("absent"));
PrintAndLogEx(NORMAL, " smartcard reader.......... %s", IfPm3Smartcard() ? _GREEN_("present") : _YELLOW_("absent")); PrintAndLogEx(NORMAL, " smartcard reader.......... %s", IfPm3Smartcard() ? _GREEN_("present") : _YELLOW_("absent"));
PrintAndLogEx(NORMAL, " FPC USART for BT add-on... %s", IfPm3FpcUsartHost() ? _GREEN_("present") : _YELLOW_("absent")); PrintAndLogEx(NORMAL, " FPC USART for BT add-on... %s", IfPm3FpcUsartHost() ? _GREEN_("present") : _YELLOW_("absent"));
} else {
PrintAndLogEx(NORMAL, " firmware.................. %s", _YELLOW_("PM3 GENERIC"));
if (IfPm3FpcUsartHost()) {
PrintAndLogEx(NORMAL, " FPC USART for BT add-on... %s", _GREEN_("present"));
} }
}
if (IfPm3FpcUsartDevFromUsb()) { if (IfPm3FpcUsartDevFromUsb()) {
PrintAndLogEx(NORMAL, " FPC USART for developer... %s", _GREEN_("present")); PrintAndLogEx(NORMAL, " FPC USART for developer... %s", _GREEN_("present"));
} }
@ -989,5 +1005,5 @@ void pm3_version(bool verbose, bool oneliner) {
lookupChipID(payload->id, payload->section_size); lookupChipID(payload->id, payload->section_size);
} }
PrintAndLogEx(NORMAL, "\n"); PrintAndLogEx(NORMAL, "");
} }

View file

@ -356,11 +356,9 @@ static int CmdHIDClone(const char *Cmd) {
bool q5 = arg_get_lit(ctx, 7); bool q5 = arg_get_lit(ctx, 7);
bool em = arg_get_lit(ctx, 8); bool em = arg_get_lit(ctx, 8);
int bin_len = 63; int bin_len = 63;
uint8_t bin[70] = {0}; uint8_t bin[70] = {0};
CLIGetStrWithReturn(ctx, 9, bin, &bin_len); CLIGetStrWithReturn(ctx, 9, bin, &bin_len);
CLIParserFree(ctx); CLIParserFree(ctx);
if (q5 && em) { if (q5 && em) {
@ -383,12 +381,21 @@ static int CmdHIDClone(const char *Cmd) {
return PM3_EINVARG; return PM3_EINVARG;
} }
if (raw_len) {
uint32_t top = 0, mid = 0, bot = 0; uint32_t top = 0, mid = 0, bot = 0;
if (raw_len) {
hexstring_to_u96(&top, &mid, &bot, raw); hexstring_to_u96(&top, &mid, &bot, raw);
packed.Top = top; packed.Top = top;
packed.Mid = mid; packed.Mid = mid;
packed.Bot = bot; packed.Bot = bot;
} else if (bin_len) {
int res = binstring_to_u96(&top, &mid, &bot, (const char*)bin);
if (res != bin_len) {
PrintAndLogEx(ERR, "Binary string contains none <0|1> chars");
return PM3_EINVARG;
}
packed.Top = top;
packed.Mid = mid;
packed.Bot = bot;
} else { } else {
if (HIDPack(format_idx, &card, &packed, true) == false) { if (HIDPack(format_idx, &card, &packed, true) == false) {
PrintAndLogEx(WARNING, "The card data could not be encoded in the selected format."); PrintAndLogEx(WARNING, "The card data could not be encoded in the selected format.");

View file

@ -3869,7 +3869,7 @@ static int CmdT55xxSniff(const char *Cmd) {
"Sniff LF t55xx based trafic and decode possible cmd / blocks.\n" "Sniff LF t55xx based trafic and decode possible cmd / blocks.\n"
"Lower tolerance means tighter pulses. ", "Lower tolerance means tighter pulses. ",
"lf t55xx sniff\n" "lf t55xx sniff\n"
"lf t55xx sniff -1 -t 2 -> use buffer with tolernace of 2\n" "lf t55xx sniff -1 -t 2 -> use buffer with tolerance of 2\n"
"lf t55xx sniff -1 --zero 7 --one 14 -> use buffer, zero pulse width 7, one pulse width 15" "lf t55xx sniff -1 --zero 7 --one 14 -> use buffer, zero pulse width 7, one pulse width 15"
); );

View file

@ -60,7 +60,7 @@ int CmdWiegandEncode(const char *Cmd) {
arg_u64_0(NULL, "issue", "<dec>", "issue level"), arg_u64_0(NULL, "issue", "<dec>", "issue level"),
arg_u64_0(NULL, "oem", "<dec>", "OEM code"), arg_u64_0(NULL, "oem", "<dec>", "OEM code"),
arg_str0("w", "wiegand", "<format>", "see `wiegand list` for available formats"), arg_str0("w", "wiegand", "<format>", "see `wiegand list` for available formats"),
arg_lit0(NULL, "pre", "add HID preamble to wiegand"), arg_lit0(NULL, "pre", "add HID ProxII preamble to wiegand output"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);

View file

@ -384,9 +384,8 @@ int ecdsa_signature_verify(mbedtls_ecp_group_id curveid, uint8_t *key_xy, uint8_
// take signature bytes, converts to ASN1 signature and tries to verify // take signature bytes, converts to ASN1 signature and tries to verify
int ecdsa_signature_r_s_verify(mbedtls_ecp_group_id curveid, uint8_t *key_xy, uint8_t *input, int length, uint8_t *r_s, size_t r_s_len, bool hash) { int ecdsa_signature_r_s_verify(mbedtls_ecp_group_id curveid, uint8_t *key_xy, uint8_t *input, int length, uint8_t *r_s, size_t r_s_len, bool hash) {
int res; uint8_t signature[MBEDTLS_ECDSA_MAX_LEN] = {0};
uint8_t signature[MBEDTLS_ECDSA_MAX_LEN]; size_t signature_len = 0;
size_t signature_len;
// convert r & s to ASN.1 signature // convert r & s to ASN.1 signature
mbedtls_mpi r, s; mbedtls_mpi r, s;
@ -395,7 +394,7 @@ int ecdsa_signature_r_s_verify(mbedtls_ecp_group_id curveid, uint8_t *key_xy, ui
mbedtls_mpi_read_binary(&r, r_s, r_s_len / 2); mbedtls_mpi_read_binary(&r, r_s, r_s_len / 2);
mbedtls_mpi_read_binary(&s, r_s + r_s_len / 2, r_s_len / 2); mbedtls_mpi_read_binary(&s, r_s + r_s_len / 2, r_s_len / 2);
res = ecdsa_signature_to_asn1(&r, &s, signature, &signature_len); int res = ecdsa_signature_to_asn1(&r, &s, signature, &signature_len);
if (res < 0) { if (res < 0) {
return res; return res;
} }

View file

@ -403,9 +403,19 @@ static int wait_for_ack(PacketResponseNG *ack) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static bool g_printed_msg = false;
static void flash_suggest_update_bootloader(void) { static void flash_suggest_update_bootloader(void) {
if (g_printed_msg)
return;
PrintAndLogEx(ERR, _RED_("It is recommended that you first" _YELLOW_(" update your bootloader") _RED_(" alone,"))); PrintAndLogEx(ERR, _RED_("It is recommended that you first" _YELLOW_(" update your bootloader") _RED_(" alone,")));
PrintAndLogEx(ERR, _RED_("reboot the Proxmark3 then only update the main firmware") "\n"); PrintAndLogEx(ERR, _RED_("reboot the Proxmark3 then only update the main firmware") "\n");
PrintAndLogEx(ERR, "Follow these steps :");
PrintAndLogEx(ERR, " 1) ./pm3-flash-bootrom");
PrintAndLogEx(ERR, " 2) ./pm3-flash-flash-all");
PrintAndLogEx(ERR, " 3) ./pm3");
PrintAndLogEx(INFO, "--------------------------------------------------------");
g_printed_msg = true;
} }
static void flash_suggest_update_flasher(void) { static void flash_suggest_update_flasher(void) {
@ -532,6 +542,7 @@ const char ice[] =
" !!: :!! !!: !!: !!: !!: !!! !!: !!!\n : :: :: : : :: ::: : : : : : :: : \n" " !!: :!! !!: !!: !!: !!: !!! !!: !!!\n : :: :: : : :: ::: : : : : : :: : \n"
_RED_(" . .. .. . . .. ... . . . . . .. . ") _RED_(" . .. .. . . .. ... . . . . . .. . ")
"\n...................................................................\n" "\n...................................................................\n"
"...................................................................\n"
; ;
// Write a file's segments to Flash // Write a file's segments to Flash

View file

@ -748,7 +748,6 @@ int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data)
clearCommandBuffer(); clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_READSC, sectorNo, keyType, 0, key, 6); SendCommandMIX(CMD_HF_MIFARE_READSC, sectorNo, keyType, 0, key, 6);
PacketResponseNG resp; PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
uint8_t isOK = resp.oldarg[0] & 0xff; uint8_t isOK = resp.oldarg[0] & 0xff;
@ -760,10 +759,33 @@ int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data)
return PM3_EUNDEF; return PM3_EUNDEF;
} }
} else { } else {
PrintAndLogEx(ERR, "Command execute timeout"); PrintAndLogEx(DEBUG, "Command execute timeout");
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
return PM3_SUCCESS;
}
int mfReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t *data) {
mf_readblock_t payload = {
.blockno = blockNo,
.keytype = keyType
};
memcpy(payload.key, key, sizeof(payload.key));
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) {
memcpy(data, resp.data.asBytes, 16);
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(DEBUG, "failed reading block");
return PM3_ESOFT;
}
} else {
PrintAndLogEx(DEBUG, "Command execute timeout");
return PM3_ETIMEOUT;
}
return PM3_SUCCESS; return PM3_SUCCESS;
} }
@ -1196,3 +1218,19 @@ int detect_mf_magic(bool is_mfc) {
} }
return isGeneration; return isGeneration;
} }
int detect_mfc_ev1_signature(uint8_t *signature) {
if (signature == NULL) {
return PM3_EINVARG;
}
uint8_t sign[32] = {0};
uint8_t key[] = {0x4b, 0x79, 0x1b, 0xea, 0x7b, 0xcc};
int res = mfReadBlock(69, 1, key, sign);
if ( res == PM3_SUCCESS) {
res = mfReadBlock(70, 1, key, sign + 16);
if (res == PM3_SUCCESS) {
memcpy(signature, sign, sizeof(sign));
}
}
return res;
}

View file

@ -70,6 +70,7 @@ int mfCheckKeys_file(uint8_t *destfn, uint64_t *key);
int mfKeyBrute(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint64_t *resultkey); int mfKeyBrute(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint64_t *resultkey);
int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data); int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data);
int mfReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t *data);
int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount);
int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount); int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount);
@ -90,5 +91,7 @@ int detect_classic_prng(void);
int detect_classic_nackbug(bool verbose); int detect_classic_nackbug(bool verbose);
int detect_mf_magic(bool is_mfc); int detect_mf_magic(bool is_mfc);
int detect_classic_static_nonce(void); int detect_classic_static_nonce(void);
int detect_mfc_ev1_signature(uint8_t *signature);
void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool isEncrypted); void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool isEncrypted);
#endif #endif

View file

@ -673,6 +673,8 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, char *filenames[
} }
finish: finish:
if (ret != PM3_SUCCESS)
PrintAndLogEx(INFO, "The flashing procedure failed, follow the suggested steps!");
ret = flash_stop_flashing(); ret = flash_stop_flashing();
CloseProxmark(session.current_device); CloseProxmark(session.current_device);
finish2: finish2:
@ -684,7 +686,7 @@ finish2:
PrintAndLogEx(SUCCESS, _CYAN_("All done")); PrintAndLogEx(SUCCESS, _CYAN_("All done"));
else else
PrintAndLogEx(ERR, "Aborted on error"); PrintAndLogEx(ERR, "Aborted on error");
PrintAndLogEx(NORMAL, "\nHave a nice day!"); PrintAndLogEx(INFO, "\nHave a nice day!");
return ret; return ret;
} }
#endif //LIBPM3 #endif //LIBPM3

View file

@ -951,13 +951,13 @@ char *str_ndup(const char *src, size_t len) {
} }
/** /**
* Converts a hex string to component "hi2", "hi" and "lo" 32-bit integers, one nibble * Converts a hex string to component "hi2", "hi" and "lo" 32-bit integers
* at a time. * one nibble at a time.
* *
* Returns the number of nibbles (4 bits) entered. * Returns the number of nibbles (4 bits) entered.
*/ */
int hexstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str) { int hexstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str) {
unsigned int n = 0, i = 0; uint32_t n = 0, i = 0;
while (sscanf(&str[i++], "%1x", &n) == 1) { while (sscanf(&str[i++], "%1x", &n) == 1) {
*hi2 = (*hi2 << 4) | (*hi >> 28); *hi2 = (*hi2 << 4) | (*hi >> 28);
@ -967,6 +967,30 @@ int hexstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str)
return i - 1; return i - 1;
} }
/**
* Converts a binary string to component "hi2", "hi" and "lo" 32-bit integers,
* one bit at a time.
*
* Returns the number of bits entered.
*/
int binstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str) {
uint32_t n = 0, i = 0;
for(;;) {
int res = sscanf(&str[i], "%1u", &n);
if ((res != 1) || (n > 1))
break;
*hi2 = (*hi2 << 1) | (*hi >> 31);
*hi = (*hi << 1) | (*lo >> 31);
*lo = (*lo << 1) | (n & 0x1);
i++;
}
return i;
}
inline uint32_t bitcount32(uint32_t a) { inline uint32_t bitcount32(uint32_t a) {
#if defined __GNUC__ #if defined __GNUC__
return __builtin_popcountl(a); return __builtin_popcountl(a);

View file

@ -105,6 +105,7 @@ void strcreplace(char *buf, size_t len, char from, char to);
char *str_dup(const char *src); char *str_dup(const char *src);
char *str_ndup(const char *src, size_t len); char *str_ndup(const char *src, size_t len);
int hexstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str); int hexstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str);
int binstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str);
uint32_t bitcount32(uint32_t a); uint32_t bitcount32(uint32_t a);
uint64_t bitcount64(uint64_t a); uint64_t bitcount64(uint64_t a);

View file

@ -182,6 +182,13 @@ uint16_t ul_ev1_packgenD(uint8_t *uid) {
return BSWAP_16(p & 0xFFFF); return BSWAP_16(p & 0xFFFF);
} }
uint32_t ul_ev1_pwdgen_def(uint8_t *uid) {
return 0xFFFFFFFF;
}
uint16_t ul_ev1_packgen_def(uint8_t *uid) {
return 0x0000;
}
//------------------------------------ //------------------------------------
// MFC key generation stuff // MFC key generation stuff
// Each algo implementation should offer two key generation functions. // Each algo implementation should offer two key generation functions.

View file

@ -13,11 +13,13 @@
#include "common.h" #include "common.h"
uint32_t ul_ev1_pwdgen_def(uint8_t *uid);
uint32_t ul_ev1_pwdgenA(uint8_t *uid); uint32_t ul_ev1_pwdgenA(uint8_t *uid);
uint32_t ul_ev1_pwdgenB(uint8_t *uid); uint32_t ul_ev1_pwdgenB(uint8_t *uid);
uint32_t ul_ev1_pwdgenC(uint8_t *uid); uint32_t ul_ev1_pwdgenC(uint8_t *uid);
uint32_t ul_ev1_pwdgenD(uint8_t *uid); uint32_t ul_ev1_pwdgenD(uint8_t *uid);
uint16_t ul_ev1_packgen_def(uint8_t *uid);
uint16_t ul_ev1_packgenA(uint8_t *uid); uint16_t ul_ev1_packgenA(uint8_t *uid);
uint16_t ul_ev1_packgenB(uint8_t *uid); uint16_t ul_ev1_packgenB(uint8_t *uid);
uint16_t ul_ev1_packgenC(uint8_t *uid); uint16_t ul_ev1_packgenC(uint8_t *uid);

View file

@ -289,20 +289,18 @@ cleanup:
int ecdsa_signature_to_asn1(const mbedtls_mpi *r, const mbedtls_mpi *s, int ecdsa_signature_to_asn1(const mbedtls_mpi *r, const mbedtls_mpi *s,
unsigned char *sig, size_t *slen) { unsigned char *sig, size_t *slen) {
int ret; int ret;
unsigned char buf[MBEDTLS_ECDSA_MAX_LEN]; unsigned char buf[MBEDTLS_ECDSA_MAX_LEN] = {0};
unsigned char *p = buf + sizeof(buf); unsigned char *p = buf + sizeof(buf) - 1;
size_t len = 0; size_t len = 0;
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_mpi(&p, buf, s)); MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_mpi(&p, buf, s));
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_mpi(&p, buf, r)); MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_mpi(&p, buf, r));
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, buf, len)); MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, buf, len));
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE));
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE));
memcpy(sig, p, len); memcpy(sig, p, len);
*slen = len; *slen = len;
return (0); return (0);
} }

View file

@ -143,7 +143,7 @@ Check column "offline" for their availability.
|`hf 14a raw `|N |`Send raw hex data to tag` |`hf 14a raw `|N |`Send raw hex data to tag`
|`hf 14a antifuzz `|N |`Fuzzing the anticollision phase. Warning! Readers may react strange` |`hf 14a antifuzz `|N |`Fuzzing the anticollision phase. Warning! Readers may react strange`
|`hf 14a config `|N |`Configure 14a settings (use with caution)` |`hf 14a config `|N |`Configure 14a settings (use with caution)`
|`hf 14a apdufind `|N |`Enuerate APDUs - CLA/INS/P1P2` |`hf 14a apdufind `|N |`Enumerate APDUs - CLA/INS/P1P2`
### hf 14b ### hf 14b
@ -255,6 +255,19 @@ Check column "offline" for their availability.
|`hf fido assert `|N |`FIDO2 GetAssertion command.` |`hf fido assert `|N |`FIDO2 GetAssertion command.`
### hf jooki
{ Jooki RFIDs... }
|command |offline |description
|------- |------- |-----------
|`hf jooki help `|Y |`This help`
|`hf jooki clone `|N |`Write a Jooki token`
|`hf jooki decode `|Y |`Decode Jooki token`
|`hf jooki encode `|Y |`Encode Jooki token`
|`hf jooki sim `|N |`Simulate Jooki token`
### hf iclass ### hf iclass
{ ICLASS RFIDs... } { ICLASS RFIDs... }

148
doc/jooki_notes.md Normal file
View file

@ -0,0 +1,148 @@
# Jooki Figurine Notes
- NTAG213 (Should be tested if other NTAG2xx work)
- A single NDEF record of type URL
- Physical figurines are Fox, Dragon, Knight, Ghost, Whale, Generic Flat. Than there are variations of those figures with different colors.
## Jooki proxmark commands
You can `encode`, `decode` a NDEF record, write with `clone` a record to a card or simulate with`sim`.
### Decoding NDEF URL parameter
`hf jooki decode -d g+t07s57aX1bB6tk`
### Encoding NDEF record
You can either use figurine abbreviation arguments:
```
--dragon
--fox
--ghost
--knight
--whale
--blackdragon
--blackfox
--blackknight
--blackwhale
--whitedragon
--whitefox
--whiteknight
--whitewhale
```
Or pass directly the figurine type id `--tid` and figurine id `--fid`
Example encoding NDEF record for UID `04010203040506`
`hf jooki encode --uid 04010203040506 --tid 1 --fid 1`
or use `--dragon` parameter to achieve the same:
`hf jooki encode --uid 04010203040506 --dragon`
Output:
```
[=] Encoded URL.. 67 2B 74 30 37 73 35 37 61 58 31 62 ( g+t07s57aX1bB6tk )
[=] Figurine..... 01 00 - Figurine, Dragon
[=] iv........... 80 77 51
[=] uid.......... 04 01 02 03 04 05 06
[=] NDEF raw..... 0103A00C340329D101255504732E6A6F6F6B692E726F636B732F732F3F733D672B743037733537615831624236746B0AFE000000
```
Use `-r` parameter to read UID directly from tag.
### Simulation
To simulate the above figurine use the encoded URL parameter given in `encode` output and type following command into your proxmark:
`hf jooki sim -b g+t07s57aX1bB6tk`
If no parameter is given to the simulation command, last loaded dump is used.
### Cloning to a NTAG213 tag
```
hf jooki clone [-h] [-b <base64>] [-d <hex>] [-p <hex>]
options:
-h, --help This help
-b, --b64 <base64> base64 url parameter
-d, --data <hex> raw NDEF bytes
-p, --pwd <hex> password for authentication (EV1/NTAG 4 bytes)
examples/notes:
hf jooki clone -d <hex bytes> -> where hex is raw NDEFhf jooki clone --b64 7WzlgEzqLgwTnWNy --> using base64 url parameter
```
Use either the above NDEF raw output from `encode` to write a new record to a tag:
`hf jooki clone -d 0103A00C340329D101255504732E6A6F6F6B692E726F636B732F732F3F733D672B743037733537615831624236746B0AFE000000`
or use the base64 encoded parameter to clone:
`hf jooki clone -b A5wlbrkq6QoKh9w1
Note: Jooki doesn't like more than one NDEF record, so make sure you just have one. Check with `hf mfu ndef`
### List of known figurine types
`Value`|`Figurine Type`|
|------|---------------|
**01** | Stones |
**02** | Generic Flat |
**03** | System Commands |
**04** | Tests |
| `Figurine Type` | `Figurine ID` | `Figurine` |
|---------------|-------------|--------------------------|
| 01 | 00 | 狐狸 Fox |
| 01 | 01 | 龙 Dragon |
| 01 | 02 | 骑士 Knight |
| 01 | 03 | 鬼 Ghost |
| 01 | 04 | 鲸 Whale |
| 01 | 05 | ThankYou |
| 01 | 06 | Black.Fox |
| 01 | 07 | Black.Dragon |
| 01 | 08 | Black.Whale |
| 01 | 09 | Black.Knight |
| 01 | 0a | White.Fox |
| 01 | 0b | White.Dragon |
| 01 | 0c | White.Whale |
| 01 | 0d | White.Knight |
| | | |
| `02` | | `Generic Flat` |
| 02 | 00 | 圆盘 Generic Flat |
| 02 | 01 | unknown_0201 |
| | | |
| `03` | | `System Commands` |
| 03 | 00 | sys.record |
| 03 | 01 | sys.factory_mode_on |
| 03 | 02 | sys.factory_mode_off |
| 03 | 03 | sys.airplane_mode_on |
| 03 | 04 | sys.airplane_mode_off |
| 03 | 05 | sys.toy_safe_on |
| 03 | 06 | sys.toy_safe_off |
| 03 | 07 | sys.wifi_on |
| 03 | 08 | sys.wifi_off |
| 03 | 09 | sys.bt_on |
| 03 | 0a | sys.bt_off |
| 03 | 0b | sys.production_finished |
| | | |
| `04` | | `Tests` |
| 04 | 00 | Hello test.0 |
| 04 | 01 | Hello test.1 |
| 04 | 02 | Hello test.2 |
| 04 | 03 | Hello test.3 |
| 04 | 04 | Hello test.4 |
| 04 | 05 | Hello test.5 |
| 04 | 06 | Hello test.6 |
| 04 | 07 | Hello test.7 |
| 04 | 08 | Hello test.8 |
| 04 | 09 | Hello test.9 |
| 04 | 0a | Hello unknown_040A |
| 04 | 10 | Hello test.10 |
| 04 | 11 | Hello test.11 |
| 04 | 12 | Hello test.12 |
| 04 | 13 | Hello test.13 |
| 04 | 14 | Hello test.14 |
| 04 | 15 | Hello test.15 |
| 04 | 16 | Hello test.16 |
| 04 | 17 | Hello test.17 |
| 04 | 18 | Hello test.18 |
| 04 | 19 | Hello test.19 |

View file

@ -4,7 +4,6 @@ If all went well you should get some information about the firmware and memory u
``` ```
[=] Session log /home/iceman/.proxmark3/log_20200521.txt [=] Session log /home/iceman/.proxmark3/log_20200521.txt
[=] Loading Preferences...
[+] loaded from JSON file /home/iceman/.proxmark3/preferences.json [+] loaded from JSON file /home/iceman/.proxmark3/preferences.json
[=] Using UART port /dev/ttyS7 [=] Using UART port /dev/ttyS7
[=] Communicating with PM3 over USB-CDC [=] Communicating with PM3 over USB-CDC
@ -13,10 +12,11 @@ If all went well you should get some information about the firmware and memory u
██████╗ ███╗ ███╗ ████╗ ██████╗ ███╗ ███╗ ████╗
██╔══██╗████╗ ████║ ══█║ ██╔══██╗████╗ ████║ ══█║
██████╔╝██╔████╔██║ ████╔╝ ██████╔╝██╔████╔██║ ████╔╝
██╔═══╝ ██║╚██╔╝██║ ══█║ iceman@icesql.net ██╔═══╝ ██║╚██╔╝██║ ══█║
██║ ██║ ╚═╝ ██║ ████╔╝ https://github.com/rfidresearchgroup/proxmark3/ ██║ ██║ ╚═╝ ██║ ████╔╝ Iceman ☕
╚═╝ ╚═╝ ╚═╝ ╚═══╝ Release v4.9237 - Ice Coffee ╚═╝ ╚═╝ ╚═╝ ╚═══╝ ❄️ bleeding edge
https://github.com/rfidresearchgroup/proxmark3/
[ Proxmark3 RFID instrument ] [ Proxmark3 RFID instrument ]
@ -25,30 +25,28 @@ If all went well you should get some information about the firmware and memory u
compiled with GCC 9.3.0 OS:Linux ARCH:x86_64 compiled with GCC 9.3.0 OS:Linux ARCH:x86_64
[ PROXMARK RDV4 ] [ PROXMARK RDV4 ]
external flash: present device.................... RDV4
smartcard reader: present firmware.................. RDV4
external flash............ present
[ PROXMARK RDV4 Extras ] smartcard reader.......... present
FPC USART for BT add-on support: absent FPC USART for BT add-on... absent
[ ARM ] [ ARM ]
bootrom: RRG/Iceman/master/v4.9237-2-g2cb19874 2020-05-21 22:00:10 bootrom: RRG/Iceman/master/v4.9237-2-g2cb19874 2020-05-21 22:00:10
os: RRG/Iceman/master/v4.9237-2-g2cb19874 2019-05-21 22:00:26 os: RRG/Iceman/master/v4.9237-2-g2cb19874 2019-05-21 22:00:26
compiled with GCC 8.3.1 20190703 (release) [gcc-8-branch revision 273027] compiled with GCC 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599]
[ FPGA ] [ FPGA ]
LF image built for 2s30vq100 on 2020/02/22 at 12:51:14 LF image built for 2s30vq100 on 2020-07-08 at 23: 8: 7
HF image built for 2s30vq100 on 2020/01/12 at 15:31:16 HF image built for 2s30vq100 on 2020-07-08 at 23: 8:19
HF FeliCa image built for 2s30vq100 on 2020-07-08 at 23: 8:30
[ Hardware ] [ Hardware ]
--= uC: AT91SAM7S512 Rev B --= uC: AT91SAM7S512 Rev A
--= Embedded Processor: ARM7TDMI --= Embedded Processor: ARM7TDMI
--= Nonvolatile Program Memory Size: 512K bytes, Used: 291382 bytes (56%) Free: 232906 bytes (44%) --= Internal SRAM size: 64K bytes
--= Second Nonvolatile Program Memory Size: None --= Architecture identifier: AT91SAM7Sxx Series
--= Internal SRAM Size: 64K bytes --= Embedded flash memory 512K bytes ( 59% used )
--= Architecture Identifier: AT91SAM7Sxx Series
--= Nonvolatile Program Memory Type: Embedded Flash Memory
[usb] pm3 --> [usb] pm3 -->
``` ```

View file

@ -14,8 +14,8 @@
my $UIDLOC = -540; # UID is 540 bytes from the end my $UIDLOC = -540; # UID is 540 bytes from the end
my $BLOCKSIZE = 4; # in bytes my $BLOCKSIZE = 4; # in bytes
my $AMIITOOL = '../client/amiitool/amiitool'; # path to amiitool (unless in $PATH) my $AMIITOOL = '../client/deps/amiitool/amiitool'; # path to amiitool (unless in $PATH)
my $KEYFILE = '../client/amiitool/key_retail.bin'; # path to retail key file my $KEYFILE = '../client/resources/key_retail.bin'; # path to retail key file
my $ADDHDR = 1; # add 56 byte header? my $ADDHDR = 1; # add 56 byte header?
my $FIXPWD = 1; # recalculate PWD if dump value is 0 my $FIXPWD = 1; # recalculate PWD if dump value is 0
my $FIXACK = 1; # set ACK if dump value is 0 my $FIXACK = 1; # set ACK if dump value is 0

View file

@ -2,420 +2,39 @@
# MIT License # MIT License
# Copyright (c) 2020 @doegox # Copyright (c) 2020 @doegox
# Requirements:
# python3 -m pip install ansicolors sslcrypto
import binascii import binascii
import sys import sys
import sslcrypto
from colors import color
debug = False debug = False
#######################################################################
# Using external sslcrypto library:
# import sslcrypto
# ... sslcrypto.ecc.get_curve()
# But to get this script autonomous, i.e. for CI, we embedded the
# code snippets we needed:
#######################################################################
# code snippets from JacobianCurve:
# This code is public domain. Everyone has the right to do whatever they want with it for any purpose.
# Copyright (c) 2013 Vitalik Buterin
class JacobianCurve:
def __init__(self, p, n, a, b, g):
self.p = p
self.n = n
self.a = a
self.b = b
self.g = g
self.n_length = len(bin(self.n).replace("0b", ""))
def to_jacobian(self, p):
return p[0], p[1], 1
def jacobian_double(self, p):
if not p[1]:
return 0, 0, 0
ysq = (p[1] ** 2) % self.p
s = (4 * p[0] * ysq) % self.p
m = (3 * p[0] ** 2 + self.a * p[2] ** 4) % self.p
nx = (m ** 2 - 2 * s) % self.p
ny = (m * (s - nx) - 8 * ysq ** 2) % self.p
nz = (2 * p[1] * p[2]) % self.p
return nx, ny, nz
def jacobian_add(self, p, q):
if not p[1]:
return q
if not q[1]:
return p
u1 = (p[0] * q[2] ** 2) % self.p
u2 = (q[0] * p[2] ** 2) % self.p
s1 = (p[1] * q[2] ** 3) % self.p
s2 = (q[1] * p[2] ** 3) % self.p
if u1 == u2:
if s1 != s2:
return (0, 0, 1)
return self.jacobian_double(p)
h = u2 - u1
r = s2 - s1
h2 = (h * h) % self.p
h3 = (h * h2) % self.p
u1h2 = (u1 * h2) % self.p
nx = (r ** 2 - h3 - 2 * u1h2) % self.p
ny = (r * (u1h2 - nx) - s1 * h3) % self.p
nz = (h * p[2] * q[2]) % self.p
return (nx, ny, nz)
def from_jacobian(self, p):
z = inverse(p[2], self.p)
return (p[0] * z ** 2) % self.p, (p[1] * z ** 3) % self.p
def jacobian_shamir(self, a, n, b, m):
ab = self.jacobian_add(a, b)
if n < 0 or n >= self.n:
n %= self.n
if m < 0 or m >= self.n:
m %= self.n
res = 0, 0, 1 # point on infinity
for i in range(self.n_length - 1, -1, -1):
res = self.jacobian_double(res)
has_n = n & (1 << i)
has_m = m & (1 << i)
if has_n:
if has_m == 0:
res = self.jacobian_add(res, a)
if has_m != 0:
res = self.jacobian_add(res, ab)
else:
if has_m == 0:
res = self.jacobian_add(res, (0, 0, 1)) # Try not to leak
if has_m != 0:
res = self.jacobian_add(res, b)
return res
def fast_shamir(self, a, n, b, m):
return self.from_jacobian(self.jacobian_shamir(self.to_jacobian(a), n, self.to_jacobian(b), m))
#######################################################################
# code snippets from sslcrypto
# MIT License
# Copyright (c) 2019 Ivan Machugovskiy
import hmac
import os
import hashlib
import struct
def int_to_bytes(raw, length):
data = []
for _ in range(length):
data.append(raw % 256)
raw //= 256
return bytes(data[::-1])
def bytes_to_int(data):
raw = 0
for byte in data:
raw = raw * 256 + byte
return raw
def legendre(a, p):
res = pow(a, (p - 1) // 2, p)
if res == p - 1:
return -1
else:
return res
def inverse(a, n):
if a == 0:
return 0
lm, hm = 1, 0
low, high = a % n, n
while low > 1:
r = high // low
nm, new = hm - lm * r, high - low * r
lm, low, hm, high = nm, new, lm, low
return lm % n
def square_root_mod_prime(n, p):
if n == 0:
return 0
if p == 2:
return n # We should never get here but it might be useful
if legendre(n, p) != 1:
raise ValueError("No square root")
# Optimizations
if p % 4 == 3:
return pow(n, (p + 1) // 4, p)
# 1. By factoring out powers of 2, find Q and S such that p - 1 =
# Q * 2 ** S with Q odd
q = p - 1
s = 0
while q % 2 == 0:
q //= 2
s += 1
# 2. Search for z in Z/pZ which is a quadratic non-residue
z = 1
while legendre(z, p) != -1:
z += 1
m, c, t, r = s, pow(z, q, p), pow(n, q, p), pow(n, (q + 1) // 2, p)
while True:
if t == 0:
return 0
elif t == 1:
return r
# Use repeated squaring to find the least i, 0 < i < M, such
# that t ** (2 ** i) = 1
t_sq = t
i = 0
for i in range(1, m):
t_sq = t_sq * t_sq % p
if t_sq == 1:
break
else:
raise ValueError("Should never get here")
# Let b = c ** (2 ** (m - i - 1))
b = pow(c, 2 ** (m - i - 1), p)
m = i
c = b * b % p
t = t * b * b % p
r = r * b % p
return r
# name: (nid, p, n, a, b, (Gx, Gy)),
CURVES = {
"secp128r1": (
706,
0xFFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF,
0xFFFFFFFE0000000075A30D1B9038A115,
0xFFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC,
0xE87579C11079F43DD824993C2CEE5ED3,
(
0x161FF7528B899B2D0C28607CA52C5B86,
0xCF5AC8395BAFEB13C02DA292DDED7A83
)
),
# ! h=4, how to handle that?
"secp128r2": (
707,
0xFFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF,
0x3FFFFFFF7FFFFFFFBE0024720613B5A3,
0xD6031998D1B3BBFEBF59CC9BBFF9AEE1,
0x5EEEFCA380D02919DC2C6558BB6D8A5D,
(
0x7B6AA5D85E572983E6FB32A7CDEBC140,
0x27B6916A894D3AEE7106FE805FC34B44
)
),
"secp192k1": (
711,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37,
0xFFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D,
0x000000000000000000000000000000000000000000000000,
0x000000000000000000000000000000000000000000000003,
(
0xDB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D,
0x9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D
)
),
# p192
"secp192r1": (
409,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC,
0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1,
(
0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012,
0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811
)
),
"secp224k1": (
712,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D,
0x10000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7,
0x00000000000000000000000000000000000000000000000000000000,
0x00000000000000000000000000000000000000000000000000000005,
(
0xA1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C,
0x7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5
)
),
# p224
"secp224r1": (
713,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE,
0xB4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4,
(
0xB70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21,
0xBD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34
)
),
"secp256k1": (
714,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000007,
(
0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
)
),
# p256, openssl uses the name: prime256v1.
"secp256r1": (
415,
0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF,
0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551,
0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC,
0x5AC635D8AA3A93E7B3EbBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B,
(
0x6B17D1F2E12c4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296,
0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5
)
),
# p384
"secp384r1": (
715,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC,
0xB3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF,
(
0xAA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7,
0x3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F
)
),
"secp521r1": (
716,
0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,
0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409,
0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC,
0x0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00,
(
0x00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66,
0x011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650
)
)
}
def get_curve(name):
if name not in CURVES:
raise ValueError("Unknown curve {}".format(name))
nid, p, n, a, b, g = CURVES[name]
params = {"p": p, "n": n, "a": a, "b": b, "g": g}
return EllipticCurve(nid, p, n, a, b, g)
class EllipticCurve:
def __init__(self, nid, p, n, a, b, g):
self.p, self.n, self.a, self.b, self.g = p, n, a, b, g
self.jacobian = JacobianCurve(self.p, self.n, self.a, self.b, self.g)
self.public_key_length = (len(bin(p).replace("0b", "")) + 7) // 8
self.order_bitlength = len(bin(n).replace("0b", ""))
def _int_to_bytes(self, raw, len=None):
return int_to_bytes(raw, len or self.public_key_length)
def _subject_to_int(self, subject):
return bytes_to_int(subject[:(self.order_bitlength + 7) // 8])
def recover(self, signature, data, hash="sha256"):
# Sanity check: is this signature recoverable?
if len(signature) != 1 + 2 * self.public_key_length:
raise ValueError("Cannot recover an unrecoverable signature")
subject = self._digest(data, hash)
z = self._subject_to_int(subject)
recid = signature[0] - 27 if signature[0] < 31 else signature[0] - 31
r = bytes_to_int(signature[1:self.public_key_length + 1])
s = bytes_to_int(signature[self.public_key_length + 1:])
# Verify bounds
if not 0 <= recid < 2 * (self.p // self.n + 1):
raise ValueError("Invalid recovery ID")
if r >= self.n:
raise ValueError("r is out of bounds")
if s >= self.n:
raise ValueError("s is out of bounds")
rinv = inverse(r, self.n)
u1 = (-z * rinv) % self.n
u2 = (s * rinv) % self.n
# Recover R
rx = r + (recid // 2) * self.n
if rx >= self.p:
raise ValueError("Rx is out of bounds")
# Almost copied from decompress_point
ry_square = (pow(rx, 3, self.p) + self.a * rx + self.b) % self.p
try:
ry = square_root_mod_prime(ry_square, self.p)
except Exception:
raise ValueError("Invalid recovered public key") from None
# Ensure the point is correct
if ry % 2 != recid % 2:
# Fix Ry sign
ry = self.p - ry
x, y = self.jacobian.fast_shamir(self.g, u1, (rx, ry), u2)
x, y = self._int_to_bytes(x), self._int_to_bytes(y)
is_compressed = signature[0] >= 31
if is_compressed:
return bytes([0x02 + (y[-1] % 2)]) + x
else:
return bytes([0x04]) + x + y
def _digest(self, data, hash):
if hash is None:
return data
elif callable(hash):
return hash(data)
elif hash == "md5":
return hashlib.md5(data).digest()
elif hash == "sha1":
return hashlib.sha1(data).digest()
elif hash == "sha256":
return hashlib.sha256(data).digest()
elif hash == "sha512":
return hashlib.sha512(data).digest()
else:
raise ValueError("Unknown hash/derivation method")
#######################################################################
def guess_curvename(signature): def guess_curvename(signature):
l = (len(signature) // 2) & 0xfe siglen = (len(signature) // 2) & 0xfe
if l == 32 : if siglen == 32:
curves = [ "secp128r1", "secp128r2" ] curves = ["secp128r1", "secp128r2"]
elif l == 48: elif siglen == 48:
curves = [ "secp192k1", "secp192r1" ] curves = ["secp192k1", "secp192r1"]
elif l == 56: elif siglen == 56:
curves = [ "secp224k1", "secp224r1" ] curves = ["secp224k1", "secp224r1"]
elif l == 64: elif siglen == 64:
curves = [ "secp256k1", "secp256r1" ] curves = ["secp256k1", "secp256r1"]
elif l == 96: elif siglen == 96:
curves = [ "secp384r1" ] curves = ["secp384r1"]
elif l == 132: elif siglen == 132:
curves = [ "secp521r1" ] curves = ["secp521r1"]
else: else:
raise ValueError("Unsupported signature size %i" % len(signature)) raise ValueError("Unsupported signature size %i" % len(signature))
return curves return curves
def recover(data, signature, curvename, alghash=None): def recover(data, signature, curvename, alghash=None):
recovered = set() recovered = set()
curve = get_curve(curvename) curve = sslcrypto.ecc.get_curve(curvename)
recoverable = len(signature) % 1 == 1 recoverable = len(signature) % 1 == 1
if (recoverable): if (recoverable):
try: try:
@ -423,7 +42,7 @@ def recover(data, signature, curvename, alghash=None):
recovered.add(pk) recovered.add(pk)
if debug: if debug:
print("Possible Pk: ", binascii.hexlify(pk)) print("Possible Pk: ", binascii.hexlify(pk))
except: except ValueError:
pass pass
else: else:
for i in range(2): for i in range(2):
@ -434,20 +53,23 @@ def recover(data, signature, curvename, alghash=None):
recovered.add(pk) recovered.add(pk)
if debug: if debug:
print("Possible Pk: ", binascii.hexlify(pk)) print("Possible Pk: ", binascii.hexlify(pk))
except: except ValueError:
pass pass
return recovered return recovered
def recover_multiple(uids, sigs, curvename, alghash=None): def recover_multiple(uids, sigs, curvename, alghash=None):
recovered = set() recovered = set()
assert len(uids) == len(sigs) assert len(uids) == len(sigs)
for i in range(len(uids)): for i in range(len(uids)):
data = binascii.unhexlify(uids[i]) data = binascii.unhexlify(uids[i])
if debug: if debug:
print("UID (%2i): " % len(data), binascii.hexlify(data)) print("UID (%2i): " %
len(data), binascii.hexlify(data))
signature = binascii.unhexlify(sigs[i]) signature = binascii.unhexlify(sigs[i])
if debug: if debug:
print("Signature (%2i): " % len(signature), binascii.hexlify(signature)) print("Signature (%2i): " %
len(signature), binascii.hexlify(signature))
recovered_tmp = recover(data, signature, curvename, alghash) recovered_tmp = recover(data, signature, curvename, alghash)
if i == 0: if i == 0:
if recovered_tmp == set(): if recovered_tmp == set():
@ -463,54 +85,57 @@ def selftests():
{'name': "Mifare Ultralight EV1", {'name': "Mifare Ultralight EV1",
'samples': ["04C1285A373080", "CEA2EB0B3C95D0844A95B824A7553703B3702378033BF0987899DB70151A19E7", 'samples': ["04C1285A373080", "CEA2EB0B3C95D0844A95B824A7553703B3702378033BF0987899DB70151A19E7",
"04C2285A373080", "A561506723D422D29ED9F93E60D20B9ED1E05CC1BF81DA19FE500CA0B81CC0ED"], "04C2285A373080", "A561506723D422D29ED9F93E60D20B9ED1E05CC1BF81DA19FE500CA0B81CC0ED"],
'pk': "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8" }, 'pk': "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"},
{'name': "NTAG21x", {'name': "NTAG21x",
'samples': ["04E10CDA993C80", "8B76052EE42F5567BEB53238B3E3F9950707C0DCC956B5C5EFCFDB709B2D82B3", 'samples': ["04E10CDA993C80", "8B76052EE42F5567BEB53238B3E3F9950707C0DCC956B5C5EFCFDB709B2D82B3",
"04DB0BDA993C80", "6048EFD9417CD10F6B7F1818D471A7FE5B46868D2EABDC6307A1E0AAE139D8D0"], "04DB0BDA993C80", "6048EFD9417CD10F6B7F1818D471A7FE5B46868D2EABDC6307A1E0AAE139D8D0"],
'pk': "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61" }, 'pk': "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"},
{'name': "Mifare Classic EV1", {'name': "Mifare Classic EV1",
'samples': ["0433619AB35780", "B9FAE369EC21C980650D87ED9AE9B1610E859131B4B8699C647548AB68D249BB", 'samples': ["0433619AB35780", "B9FAE369EC21C980650D87ED9AE9B1610E859131B4B8699C647548AB68D249BB",
"524374E2", "F8758CE30A58553A9985C458FB9C7D340FCFB04847B928A0667939272BC58B5E", "524374E2", "F8758CE30A58553A9985C458FB9C7D340FCFB04847B928A0667939272BC58B5E",
"53424B8A", "B4F533E8C06C021E242EFE8558C1672ED7022E5AE4E7AA2D46113B0AB6928AFC"], "53424B8A", "B4F533E8C06C021E242EFE8558C1672ED7022E5AE4E7AA2D46113B0AB6928AFC",
'pk': "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF" }, "BD2A4146", "19505576ED327D8F8870C86B1ED00898BFEDFFF27CC82FC515BA2EEC26050873"],
'pk': "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"},
{'name': "DESFire Light", {'name': "DESFire Light",
'samples': ["0439556ACB6480", "D5BD0978106E1E38B513642335966AB21E9F950DCFCFAB45FF13D0DC3CA4C2AE7E0D671DF1240937D040DAC4601C5F66ED62C546EE03ED08", 'samples': ["0439556ACB6480", "D5BD0978106E1E38B513642335966AB21E9F950DCFCFAB45FF13D0DC3CA4C2AE7E0D671DF1240937D040DAC4601C5F66ED62C546EE03ED08",
"043B156ACB6480", "76B46932BF2FCF4931A24C755F5CB1686B914F1856177686B864BDAD58EFA6A7493E5C2232F3ADDAA434EA4647BFD1D385BDA6115E77D74C"], "043B156ACB6480", "76B46932BF2FCF4931A24C755F5CB1686B914F1856177686B864BDAD58EFA6A7493E5C2232F3ADDAA434EA4647BFD1D385BDA6115E77D74C"],
'pk': "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D" }, 'pk': "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"},
{'name': "DESFire EV2", {'name': "DESFire EV2",
'samples': ["042A41CAE45380", "B2769F8DDB575AEA2A680ADCA8FFED4FAB81A1E9908E2B82FE0FABB697BBD9B23835C416970E75768F12902ACA491349E94E6589EAF4F508", 'samples': ["042A41CAE45380", "B2769F8DDB575AEA2A680ADCA8FFED4FAB81A1E9908E2B82FE0FABB697BBD9B23835C416970E75768F12902ACA491349E94E6589EAF4F508",
"045640CAE45380", "D34B53A8C2C100D700DEA1C4C0D0DE4409F3A418CD8D57C4F41F146E42AD9A55F014199ABBF5CA259C7799DB0AE20D5E77D4950AC7E95D33"], "045640CAE45380", "D34B53A8C2C100D700DEA1C4C0D0DE4409F3A418CD8D57C4F41F146E42AD9A55F014199ABBF5CA259C7799DB0AE20D5E77D4950AC7E95D33"],
'pk': "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A" }, 'pk': "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A"},
{'name': "DESFire EV3", {'name': "DESFire EV3",
'samples': ["04448BD2DB6B80", "5CBB5632795C8F15263FEFB095B51C7B541AFD914A1AE44EF6FB8AF605EDF13DBFEE6C3A2DB372245E671DFE0D42CB1F0D0B8FE67A89D2F6", 'samples': ["04448BD2DB6B80", "5CBB5632795C8F15263FEFB095B51C7B541AFD914A1AE44EF6FB8AF605EDF13DBFEE6C3A2DB372245E671DFE0D42CB1F0D0B8FE67A89D2F6",
"04445DD2DB6B80", "166BFD9F9BFAA451172566101580DF9894F582C4A4E258C15037AD2F35A475CF1D7FB817618623A6569F991931AFB2766984E21A18512A6D"], "04445DD2DB6B80", "166BFD9F9BFAA451172566101580DF9894F582C4A4E258C15037AD2F35A475CF1D7FB817618623A6569F991931AFB2766984E21A18512A6D"],
'pk': "041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743" }, 'pk': "041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743"},
# TODO one more Mifare Plus EV1...
{'name': "Mifare Plus EV1", {'name': "Mifare Plus EV1",
# TODO one more Mifare Plus EV1...
'samples': ["042A2B221C5080", "BAC40CD88E9193C58ADA5055350C4F648EB5A7AEC4FCF9BD4CDD7B1C558DE5F59C6636F26286ED48622AAA2331D4DF1CEE23B57B94BDA631"], 'samples': ["042A2B221C5080", "BAC40CD88E9193C58ADA5055350C4F648EB5A7AEC4FCF9BD4CDD7B1C558DE5F59C6636F26286ED48622AAA2331D4DF1CEE23B57B94BDA631"],
'pk': "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E" }, 'pk': "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"},
{'name': "NTAG413DNA", {'name': "NTAG413DNA",
'samples': ["042468222F5C80", "B9211E320F321BD1D0E158E10FF15109B389638BAE15D9909D7725BF1250ED236D66F1AF75C94D60330E4E92535F5E6997675281A5687173", 'samples': ["042468222F5C80", "B9211E320F321BD1D0E158E10FF15109B389638BAE15D9909D7725BF1250ED236D66F1AF75C94D60330E4E92535F5E6997675281A5687173",
"042938222F5C80", "18B642797D1FD71806146A7A6EC778D3FDD04F39C4A3B36A592BD1A114DC44E5528380FA766C0B7EA32B284AFBE84300B620369F0686D8CC"], "042938222F5C80", "18B642797D1FD71806146A7A6EC778D3FDD04F39C4A3B36A592BD1A114DC44E5528380FA766C0B7EA32B284AFBE84300B620369F0686D8CC"],
'pk': "04bb5d514f7050025c7d0f397310360eec91eaf792e96fc7e0f496cb4e669d414f877b7b27901fe67c2e3b33cd39d1c797715189ac951c2add" }, 'pk': "04bb5d514f7050025c7d0f397310360eec91eaf792e96fc7e0f496cb4e669d414f877b7b27901fe67c2e3b33cd39d1c797715189ac951c2add"},
{'name': "NTAG424DNA", {'name': "NTAG424DNA",
'samples': ["0463474AA26A80", "27E9A50E6CA4BA9037C02F7D20A80D0284D0C1D83C67F5A5AC1D8A4EF86C9508417E4E9C6F85AA7920F0ABDED984CAF20467D66EA54BBF08", 'samples': ["0463474AA26A80", "27E9A50E6CA4BA9037C02F7D20A80D0284D0C1D83C67F5A5AC1D8A4EF86C9508417E4E9C6F85AA7920F0ABDED984CAF20467D66EA54BBF08",
"04C46C222A6380", "344A806EBF704C05C19215D2F840529CE365AAD2D08A469A95896D75D477D9FAB02A0C827E9F215BD8EB0E56A3A9A008FB75D706AABBD4DA"], "04C46C222A6380", "344A806EBF704C05C19215D2F840529CE365AAD2D08A469A95896D75D477D9FAB02A0C827E9F215BD8EB0E56A3A9A008FB75D706AABBD4DA"],
'pk': "048A9B380AF2EE1B98DC417FECC263F8449C7625CECE82D9B916C992DA209D68422B81EC20B65A66B5102A61596AF3379200599316A00A1410" }, 'pk': "048A9B380AF2EE1B98DC417FECC263F8449C7625CECE82D9B916C992DA209D68422B81EC20B65A66B5102A61596AF3379200599316A00A1410"},
{'name': "Vivokey Spark1", {'name': "Vivokey Spark1",
# ! tag signature bytes output by pm3 must be read right to left: echo $sig |sed 's/\(..\)/\1\n/g'|tac|tr -d '\n' (and it uses a SHA256) # ! tag signature bytes output by pm3 must be read right to left:
# echo $sig |sed 's/\(..\)/\1\n/g'|tac|tr -d '\n'
# (and it uses a SHA256)
'samples': ["E0040118009C870C", "4B4E03E1211952EF6A5F9D84AB218CD4D7549D0CDF8CA8779F9AD16C9A9CBF3B", 'samples': ["E0040118009C870C", "4B4E03E1211952EF6A5F9D84AB218CD4D7549D0CDF8CA8779F9AD16C9A9CBF3B",
"E0040118009B4D62", "25CF13747C3389EC7889DE916E3747584978511CC78B51CFB1883B494CBED7AB"], "E0040118009B4D62", "25CF13747C3389EC7889DE916E3747584978511CC78B51CFB1883B494CBED7AB"],
'pk': "04d64bb732c0d214e7ec580736acf847284b502c25c0f7f2fa86aace1dada4387a" }, 'pk': "04d64bb732c0d214e7ec580736acf847284b502c25c0f7f2fa86aace1dada4387a"},
# ! tag UID is considered inversed: E0040118009B5FEE => EE5F9B00180104E0
# TODO one more ICODE-DNA...
{'name': "ICODE DNA, ICODE SLIX2", {'name': "ICODE DNA, ICODE SLIX2",
# ! tag UID is considered inverted: E0040118009B5FEE => EE5F9B00180104E0
# TODO one more ICODE-DNA...
'samples': ["EE5F9B00180104E0", "32D9E7579CD77E6F1FA11419231E874826984C5F189FDE1421684563A9663377"], 'samples': ["EE5F9B00180104E0", "32D9E7579CD77E6F1FA11419231E874826984C5F189FDE1421684563A9663377"],
'pk': "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0" }, 'pk': "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"},
# uses secp256r1?, SHA-256, # {'name': "Minecraft Earth",
# {'name': "Minecraft Earth", # # uses secp256r1?, SHA-256,
# 'samples': ["aa", "DF0E506DFF8FCFC4B7B979D917644445F1230D2C7CDC342AFA842CA240C210BE7275F62073A9670F2DCEFC602CBEE771C2B4CD4A04F3D1EA11F49ABDF7E8B721"], # 'samples': ["aa", "DF0E506DFF8FCFC4B7B979D917644445F1230D2C7CDC342AFA842CA240C210BE7275F62073A9670F2DCEFC602CBEE771C2B4CD4A04F3D1EA11F49ABDF7E8B721"],
# 'pk': "" }, # 'pk': ""},
] ]
succeeded = True succeeded = True
for t in tests: for t in tests:
@ -540,6 +165,7 @@ def selftests():
print("[FAIL]") print("[FAIL]")
print("Tests: [%s]" % ["FAIL", "OK"][succeeded]) print("Tests: [%s]" % ["FAIL", "OK"][succeeded])
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) == 2 and sys.argv[1] == "selftests": if len(sys.argv) == 2 and sys.argv[1] == "selftests":
selftests() selftests()
@ -549,14 +175,17 @@ if __name__ == "__main__":
print("Example: \n%s 04ee45daa34084 ebb6102bff74b087d18a57a54bc375159a04ea9bc61080b7f4a85afe1587d73b" % sys.argv[0]) print("Example: \n%s 04ee45daa34084 ebb6102bff74b087d18a57a54bc375159a04ea9bc61080b7f4a85afe1587d73b" % sys.argv[0])
exit(1) exit(1)
uids, sigs = sys.argv[1:][::2], sys.argv[1:][1::2] uids, sigs = sys.argv[1:][::2], sys.argv[1:][1::2]
once = True
curvenames = guess_curvename(sigs[0]) curvenames = guess_curvename(sigs[0])
for c in curvenames: for c in curvenames:
print("\nAssuming curve=%s" % c)
print("========================")
for h in [None, "md5", "sha1", "sha256", "sha512"]: for h in [None, "md5", "sha1", "sha256", "sha512"]:
print("Assuming hash=%s" % h)
recovered = recover_multiple(uids, sigs, c, alghash=h) recovered = recover_multiple(uids, sigs, c, alghash=h)
if recovered: if recovered:
if once:
print(color('curve=%s', fg='yellow') % c)
once = False
print(color('hash=%s', fg='yellow') % h)
print("Possible uncompressed Pk(s):") print("Possible uncompressed Pk(s):")
for pk in list(recovered): for pk in list(recovered):
print(binascii.hexlify(pk).decode('utf8')) print(binascii.hexlify(pk).decode('utf8'))
once = True