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
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
run: make clean
@ -60,6 +66,12 @@ jobs:
- name: Install dependencies
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
run: make clean
@ -96,6 +108,12 @@ jobs:
- name: Install dependencies
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
run: mkdir -p client/build

View file

@ -15,6 +15,13 @@ jobs:
- 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
- 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
run: make clean
@ -40,6 +47,13 @@ jobs:
- 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
- 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
run: make clean
@ -66,6 +80,13 @@ jobs:
- 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
- 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
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...
## [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 `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)
- 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)
- Change `PLATFORM=PM3OTHER` to `PLATFORM=PM3GENERIC` (@iceman1001)
- 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 Zlib library if available (@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]
- 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
- 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
- Ubuntu 16.04 -> 20.04
- 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)
include(mbedtls.cmake)
endif()
target_link_libraries(pm3rrg_rdv4_amiibo PRIVATE
m
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)
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 =
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 =
MYDEFS =
MYSRCS = \

View file

@ -9,6 +9,7 @@
#include "md.h"
#include "aes.h"
#include "commonutil.h"
#include "../src/fileutils.h"
#define HMAC_POS_DATA 0x008
#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);
}
bool nfc3d_amiibo_load_keys(nfc3d_amiibo_keys *amiiboKeys, const char *path) {
FILE *f = fopen(path, "rb");
if (!f) {
bool nfc3d_amiibo_load_keys(nfc3d_amiibo_keys *amiiboKeys) {
#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;
}
size_t len = fread(amiiboKeys, sizeof(*amiiboKeys), 1, f);
fclose(f);
if (len != sizeof(*amiiboKeys)) {
if (bytes_read != sizeof(*amiiboKeys)) {
free(dump);
return false;
}
if ((amiiboKeys->data.magicBytesSize > 16) ||
(amiiboKeys->tag.magicBytesSize > 16)) {
if ((amiiboKeys->data.magicBytesSize > 16) || (amiiboKeys->tag.magicBytesSize > 16)) {
free(dump);
return false;
}
memcpy(amiiboKeys, dump, bytes_read);
free(dump);
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);
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);
#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) {
self = argv[0];

View file

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

View file

@ -115,8 +115,7 @@ local function main(args)
local dumpdata = readdump(infile)
-- 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
local uid = string.sub(dumpdata, 1, 8)
@ -124,8 +123,7 @@ local function main(args)
-- Format some linebreaks
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')
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 "cmdparser.h" // command_t
#include "cliparser.h"
#include "pmflash.h"
#include "pmflash.h" // rdv40validation_t
#include "fileutils.h" // saveFile
#include "comms.h" // getfromdevice
#include "cmdflashmemspiffs.h" // spiffs commands
@ -26,6 +26,123 @@
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) {
CLIParserContext *ctx;
@ -344,23 +461,14 @@ static int CmdFlashMemInfo(const char *Cmd) {
// shall_write = arg_get_lit(ctx, 2);
CLIParserFree(ctx);
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, "failed");
return PM3_EFLASH;
}
// validate signature here
// validate signature data
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)
uint8_t sha_hash[20] = {0};
@ -369,7 +477,6 @@ static int CmdFlashMemInfo(const char *Cmd) {
// print header
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Flash memory Information") " ---------");
// PrintAndLogEx(INFO, "-----------------------------------------------------------------");
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(NORMAL, "");
@ -378,69 +485,16 @@ static int CmdFlashMemInfo(const char *Cmd) {
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_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);
mbedtls_mpi_read_string(&rsa.E, 16, RSA_E);
// add public key
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.P, 16, RSA_P);
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.QP, 16, RSA_QP);
bool is_keyok = (mbedtls_rsa_check_pubkey(&rsa) == 0 || mbedtls_rsa_check_privkey(&rsa) == 0);
PrintAndLogEx(NORMAL, "");
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 + 128);
PrintAndLogEx(INFO, " %.64s", str_pk + 192);
PrintAndLogEx(NORMAL, "");
const char *msgkey = "RSA key validation... ";
if (is_keyok)
PrintAndLogEx(SUCCESS, "%s( " _GREEN_("ok") " )", msgkey);
else
PrintAndLogEx(FAILED, "%s( " _RED_("failed") " )", msgkey);
//
uint8_t from_device[KEY_LEN];
uint8_t sign[KEY_LEN];
bool is_keyok = (mbedtls_rsa_check_pubkey(&rsa) == 0 || mbedtls_rsa_check_privkey(&rsa) == 0);
PrintAndLogEx(
(is_keyok) ? SUCCESS : FAILED,
"RSA key validation... ( %s )",
(is_keyok) ? _GREEN_("ok") : _RED_("fail")
);
// 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
memset(sign, 0, KEY_LEN);
// to be signed
uint8_t sign[RRG_RSA_KEY_LEN];
memset(sign, 0, RRG_RSA_KEY_LEN);
// Signing (private key)
if (shall_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.......... ";
if (is_signed == 0)
PrintAndLogEx(SUCCESS, "%s( " _GREEN_("ok") " )", msgsign);
else
PrintAndLogEx(FAILED, "%s( " _RED_("failed") " )", msgsign);
PrintAndLogEx(
(is_signed == 0) ? SUCCESS : FAILED,
"RSA signing.......... ( %s )",
(is_signed == 0) ? _GREEN_("ok") : _RED_("fail")
);
if (shall_write) {
// save to mem
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);
}
rdv4_sign_write(sign, RRG_RSA_KEY_LEN);
}
PrintAndLogEx(INFO, "Signed");
for (int i = 0; i < (sizeof(sign) / 32); i++) {
@ -517,14 +556,15 @@ static int CmdFlashMemInfo(const char *Cmd) {
// Verify (public key)
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..... ";
if (is_verified == 0)
PrintAndLogEx(SUCCESS, "%s( " _GREEN_("ok") " )", msgverify);
else
PrintAndLogEx(FAILED, "%s( " _RED_("failed") " )", msgverify);
mbedtls_rsa_free(&rsa);
PrintAndLogEx(
(is_verified == 0) ? SUCCESS : FAILED,
"RSA verification..... ( %s )",
(is_verified == 0) ? _GREEN_("ok") : _RED_("fail")
);
PrintAndLogEx(NORMAL, "");
mbedtls_rsa_free(&rsa);
return PM3_SUCCESS;
}

View file

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

View file

@ -2101,6 +2101,14 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
if (do_nack_test)
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)

View file

@ -575,6 +575,10 @@ bool readHF15Uid(bool loop, bool verbose) {
* **cmd command line
*/
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;
uint8_t uid[8] = {0x00};
uint32_t tmpreqlen = 0;
@ -598,13 +602,12 @@ static bool prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t
// strip
while (**cmd == ' ' || **cmd == '\t')(*cmd)++;
switch (**cmd) {
char c = tolower(**cmd);
switch (c) {
case 0:
PrintAndLogEx(WARNING, "missing addr");
return false;
break;
case 'u':
case 'U':
// unaddressed mode may not be supported by all vendors
req[tmpreqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY;
req[tmpreqlen++] = iso15cmd;
@ -637,9 +640,9 @@ static bool prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t
break;
}
// skip to next space
while (**cmd != ' ' && **cmd != '\t')(*cmd)++;
while (**cmd != '\0' && **cmd != ' ' && **cmd != '\t')(*cmd)++;
// skip over the space
while (**cmd == ' ' || **cmd == '\t')(*cmd)++;
while (**cmd != '\0' && (**cmd == ' ' || **cmd == '\t'))(*cmd)++;
*reqlen = tmpreqlen;
return true;
@ -1353,6 +1356,7 @@ static int CmdHF15Dump(const char *Cmd) {
// copy uid to read command
memcpy(req + 2, uid, sizeof(uid));
PrintAndLogEx(INFO, "." NOLF);
for (int retry = 0; retry < 5; retry++) {
req[10] = blocknum;
@ -1368,6 +1372,7 @@ static int CmdHF15Dump(const char *Cmd) {
continue;
}
if (len < 2) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(FAILED, "iso15693 command failed");
continue;
}
@ -1375,11 +1380,13 @@ static int CmdHF15Dump(const char *Cmd) {
recv = resp.data.asBytes;
if (CheckCrc15(recv, len) == false) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(FAILED, "crc (" _RED_("fail") ")");
continue;
}
if ((recv[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(FAILED, "Tag returned Error %i: %s", recv[1], TagErrorStr(recv[1]));
break;
}
@ -1399,12 +1406,12 @@ static int CmdHF15Dump(const char *Cmd) {
DropField();
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "block# | data |lck| ascii");
PrintAndLogEx(NORMAL, "---------+--------------+---+----------");
PrintAndLogEx(INFO, "block# | data |lck| ascii");
PrintAndLogEx(INFO, "---------+--------------+---+----------");
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;
saveFile(filename, ".bin", data, datalen);
@ -1595,14 +1602,15 @@ static int CmdHF15Readmulti(const char *Cmd) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "block# | data |lck| ascii");
PrintAndLogEx(NORMAL, "---------+--------------+---+----------");
PrintAndLogEx(INFO, "block# | data |lck| ascii");
PrintAndLogEx(INFO, "---------+--------------+---+----------");
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++;
}
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
@ -1676,9 +1684,9 @@ static int CmdHF15Read(const char *Cmd) {
// print response
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "block #%3d |lck| ascii", blocknum);
PrintAndLogEx(NORMAL, "------------+---+------");
PrintAndLogEx(NORMAL, "%s| %d | %s", sprint_hex(data + 2, status - 4), data[1], sprint_ascii(data + 2, status - 4));
PrintAndLogEx(INFO, "block #%3d |lck| ascii", blocknum);
PrintAndLogEx(INFO, "------------+---+------");
PrintAndLogEx(INFO, "%s| %d | %s", sprint_hex(data + 2, status - 4), data[1], sprint_ascii(data + 2, status - 4));
PrintAndLogEx(NORMAL, "");
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_dg11_info(uint8_t *data, size_t datalen);
static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen);
static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen);
typedef enum { // list must match dg_table
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},
{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},
{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},
{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, {}}
};
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) {
for (int dgi = 0; dg_table[dgi].filename != NULL; dgi++) {
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
if (emrtd_select_file(EMRTD_P1_SELECT_BY_EF, dg_table[EF_COM].fileid, *use_14b) == false) {
*BAC = true;
PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication.");
PrintAndLogEx(INFO, "Authentication is enforced. Will attempt external authentication.");
} else {
*BAC = false;
// 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) {
*BAC = true;
PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication.");
PrintAndLogEx(INFO, "Authentication is enforced. Will attempt external authentication.");
} else {
*BAC = false;
}
@ -1010,7 +1051,7 @@ static bool emrtd_do_auth(char *documentnumber, char *dob, char *expiry, bool BA
if (*BAC) {
// If BAC isn't available, exit out and warn user.
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.");
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;
}
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) {
uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 };
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_mac[16] = { 0x00 };
bool BAC = false;
bool PACE_available = true;
bool use_14b = false;
// Select the eMRTD
@ -1741,20 +1841,32 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
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
bool auth_result = emrtd_do_auth(documentnumber, dob, expiry, BAC_available, &BAC, ssc, ks_enc, ks_mac, &use_14b);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "------------------ " _CYAN_("Basic Info") " ------------------");
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"));
if (PACE_available) {
emrtd_print_ef_cardaccess_info(response, resplen);
}
if (!auth_result) {
DropField();
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)) {
PrintAndLogEx(ERR, "Failed to read EF_COM.");
DropField();
@ -1776,7 +1888,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
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_calc[17][64] = { { 0 } };
int hash_algo = 0;
@ -1860,6 +1972,16 @@ int infoHF_EMRTD_offline(const char *path) {
uint8_t dg_hashes_calc[17][64] = { { 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);
strncat(filepath, PATHSEP, 2);
strcat(filepath, dg_table[EF_SOD].filename);

View file

@ -35,6 +35,19 @@ typedef struct emrtd_hashalg_s {
const uint8_t descriptor[15];
} 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 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);
CLIParserFree(clictx);
size_t keylen = 0;
uint8_t dec_data[8] = {0};
bool use_sc = IsCryptoHelperPresent(verbose);
if (have_key == false && use_sc == false) {
bool use_sc = false;
if (have_key == false) {
use_sc = IsCryptoHelperPresent(verbose);
if (use_sc == false) {
size_t keylen = 0;
int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen);
if (res != PM3_SUCCESS) {
PrintAndLogEx(INFO, "Couldn't find any decryption methods");
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));
free(keyptr);
}
}
// tripledes
mbedtls_des3_context ctx;
@ -1157,19 +1163,26 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) {
CLIParserFree(clictx);
bool use_sc = IsCryptoHelperPresent(verbose);
if (have_key == false && use_sc == false) {
bool use_sc = false;
if (have_key == false) {
use_sc = IsCryptoHelperPresent(verbose);
if (use_sc == false) {
size_t keylen = 0;
int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Failed to find the transport key");
PrintAndLogEx(ERR, "Failed to find any encryption methods");
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));
free(keyptr);
}
}
if (use_sc) {
Encrypt(blk_data, blk_data);

View file

@ -67,7 +67,7 @@ jooki_figure_t jooks_figures[] = {
{0x01, 0x0C, "White Knight", "Figurine"},
{0x01, 0x0D, "White Whale", "Figurine"},
{0x02, 0x01, "Generic Flat", "Stone"},
{0x02, 0x00, "Generic Flat", "Stone"},
{0x03, 0x00, "record", "Sys"},
{0x03, 0x01, "factory_mode_on", "Sys"},
@ -77,9 +77,10 @@ jooki_figure_t jooks_figures[] = {
{0x03, 0x05, "toy_safe_on", "Sys"},
{0x03, 0x06, "toy_safe_off", "Sys"},
{0x03, 0x07, "wifi_on", "Sys"},
{0x03, 0x08, "bt_on", "Sys"},
{0x03, 0x0a, "bt_off", "Sys"},
{0x03, 0x0b, "production_finished", "Sys"},
{0x03, 0x08, "wifi_off", "Sys"},
{0x03, 0x09, "bt_on", "Sys"},
{0x03, 0x0A, "bt_off", "Sys"},
{0x03, 0x0B, "production_finished", "Sys"},
{0x04, 0x00, "test.0", "Test"},
{0x04, 0x01, "test.1", "Test"},
@ -278,7 +279,8 @@ static int CmdHF14AJookiEncode(const char *Cmd) {
"Encode a Jooki token to base64 NDEF URI format",
"hf jooki encode -t --> selftest\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[] = {
@ -300,6 +302,8 @@ static int CmdHF14AJookiEncode(const char *Cmd) {
arg_lit0(NULL, "whitefox", "figurine type"),
arg_lit0(NULL, "whiteknight", "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
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -328,18 +332,48 @@ static int CmdHF14AJookiEncode(const char *Cmd) {
bool tb = arg_get_lit(ctx, 15);
bool tc = arg_get_lit(ctx, 16);
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);
if (selftest) {
return jooki_selftest();
}
if ((t0 + t1 + t2 + t3 + t5 + t6 + t7 + t8 + t9 + ta + tb + tc + td) > 1) {
PrintAndLogEx(ERR, "Select one tag type");
uint8_t tid, fid;
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;
}
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)
fid = 0x01;
if (t2)
@ -547,7 +581,7 @@ static int CmdHF14AJookiClone(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf jooki clone",
"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"
);

View file

@ -27,6 +27,7 @@
#include "des.h" // des ecb
#include "crapto1/crapto1.h" // prng_successor
#include "cmdhf14a.h" // exchange APDU
#include "crypto/libpcrypto.h"
#define MFBLOCK_SIZE 16
@ -425,6 +426,46 @@ static int usage_hf14_gen3freeze(void) {
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) {
clearCommandBuffer();
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, "Examples:");
PrintAndLogEx(NORMAL, " hf mf wrbl 1 A FFFFFFFFFFFF 000102030405060708090A0B0C0D0E0F");
return 0;
return PM3_SUCCESS;
}
blockNo = param_get8(Cmd, 0);
cmdp = tolower(param_getchar(Cmd, 1));
if (cmdp == 0x00) {
PrintAndLogEx(NORMAL, "Key type must be A or B");
return 1;
return PM3_EINVARG;
}
if (cmdp != 'a')
@ -649,12 +690,12 @@ static int CmdHF14AMfWrBl(const char *Cmd) {
if (param_gethex(Cmd, 2, key, 12)) {
PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols");
return 1;
return PM3_EINVARG;
}
if (param_gethex(Cmd, 3, bldata, 32)) {
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));
@ -674,7 +715,7 @@ static int CmdHF14AMfWrBl(const char *Cmd) {
PrintAndLogEx(NORMAL, "Command execute timeout");
}
return 0;
return PM3_SUCCESS;
}
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));
mf_readblock_t payload;
payload.blockno = blockNo;
payload.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)) {
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;
}
uint8_t data[16] = {0};
int res = mfReadBlock(blockNo, keyType, key, data);
if (res == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data, 16));
if ((data[6] || data[7] || data[8])) {
decode_print_st(blockNo, data);
}
} else {
PrintAndLogEx(WARNING, "Command execute timeout");
return PM3_ETIMEOUT;
}
return 0;
return PM3_SUCCESS;
}
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));
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_READSC, sectorNo, keyType, 0, key, 6);
PrintAndLogEx(NORMAL, "");
uint8_t sc_size = mfNumBlocksPerSector(sectorNo) * 16;
uint8_t *data = calloc(sc_size, sizeof(uint8_t));
if (data == NULL) {
PrintAndLogEx(ERR, "failed to allocate memory");
return PM3_EMALLOC;
}
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
uint8_t isOK = resp.oldarg[0] & 0xff;
uint8_t *data = resp.data.asBytes;
PrintAndLogEx(NORMAL, "isOk:%02x", isOK);
if (isOK) {
int res = mfReadSector(sectorNo, keyType, key, data);
if (res == PM3_SUCCESS) {
uint8_t blocks = NumBlocksPerSector(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));
}
decode_print_st(start + blocks - 1, data + ((blocks - 1) * 16));
}
} else {
PrintAndLogEx(WARNING, "Command execute timeout");
}
}
free(data);
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 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);
int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len);
#endif

View file

@ -1310,7 +1310,8 @@ static int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signat
{"DESFire EV3", "041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743"},
{"NTAG424DNA, NTAG424DNATT, DESFire Light EV2", "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3B"},
{"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
#define PUBLIC_PLUS_ECDA_KEYLEN 57
const ecdsa_publickey_t nxp_plus_public_keys[] = {
{"Mifare Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"}
{"MIFARE Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"},
{"MIFARE Pluc Ev_x", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"}
};
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: AN11341 MIFARE Ultralight EV1 Originality Signature Validation
const ecdsa_publickey_t nxp_mfu_public_keys[] = {
{"NXP Mifare Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"},
{"Manufacturer Mifare Classic MFC1C14_x", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"},
{"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"},
{"Manufacturer MIFARE Classic MFC1C14_x", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"},
{"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"},
{"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"},
{"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++) {
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);
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;
}
//------------------------------------
/*
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) {
TagTypeUL_t tagtype = UNKNOWN;
@ -1385,17 +1646,20 @@ static int CmdHF14AMfUInfo(const char *Cmd) {
}
if (len < 1) {
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 {
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:
DropField();
if (locked) {
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, "");
return PM3_SUCCESS;
@ -2223,6 +2487,7 @@ static int CmdHF14AMfURestore(const char *Cmd) {
}
PrintAndLogEx(INFO, "Restoring data blocks.");
PrintAndLogEx(INFO, "." NOLF);
// write all other data
// Skip block 0,1,2,3 (only magic tags can write to them)
// 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));
wait4response(b);
PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
}
PrintAndLogEx(NORMAL, "");
@ -2811,14 +3077,14 @@ static int CmdHF14AMfUPwdGen(const char *Cmd) {
PrintAndLogEx(INFO, " Using UID : %s", sprint_hex(uid, 7));
PrintAndLogEx(INFO, "---------------------------------");
PrintAndLogEx(INFO, " algo | pwd | pack");
PrintAndLogEx(INFO, "------+----------+-----");
PrintAndLogEx(INFO, "----------------+----------+-----");
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, " LD | %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, "------+----------+-----");
PrintAndLogEx(INFO, " Amiibo | %08X | %04X", ul_ev1_pwdgenB(uid), ul_ev1_packgenB(uid));
PrintAndLogEx(INFO, " Lego Dimension | %08X | %04X", ul_ev1_pwdgenC(uid), ul_ev1_packgenC(uid));
PrintAndLogEx(INFO, " XYZ 3D printer | %08X | %04X", ul_ev1_pwdgenD(uid), ul_ev1_packgenD(uid));
PrintAndLogEx(INFO, "----------------+----------+-----");
PrintAndLogEx(INFO, " Vingcard algo");
PrintAndLogEx(INFO, "--------------------");
PrintAndLogEx(INFO, "---------------------------------");
return PM3_SUCCESS;
}

View file

@ -23,6 +23,8 @@
#include "cmddata.h"
#include "commonutil.h"
#include "pm3_cmd.h"
#include "pmflash.h" // rdv40validation_t
#include "cmdflashmem.h" // get_signature..
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");
break;
case 2:
sprintf(asBuff, "Embedded Flash Memory");
sprintf(asBuff, "Embedded flash memory");
break;
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;
case 4:
sprintf(asBuff, "SRAM emulating ROM");
@ -266,12 +268,13 @@ static void lookupChipID(uint32_t iChipID, uint32_t mem_used) {
break;
}
PrintAndLogEx(NORMAL, " --= Nonvolatile program memory: " _YELLOW_("%uK") " bytes %s ( " _YELLOW_("%2.0f%%") " used )"
, mem_avail
PrintAndLogEx(NORMAL, " --= %s " _YELLOW_("%uK") " bytes ( " _YELLOW_("%2.0f%%") " used )"
, asBuff
, mem_avail
, mem_avail == 0 ? 0.0f : (float)mem_used / (mem_avail * 1024) * 100
);
/*
switch ((iChipID & 0xF000) >> 12) {
case 0:
sprintf(asBuff, "None");
@ -305,6 +308,7 @@ static void lookupChipID(uint32_t iChipID, uint32_t mem_used) {
break;
}
PrintAndLogEx(NORMAL, " --= Second nonvolatile program memory size: %s", asBuff);
*/
}
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, "\n [ " _YELLOW_("PROXMARK3") " ]");
if (IfPm3Rdv4Fw() == false) {
if (IfPm3Rdv4Fw()) {
bool is_genuine_rdv4 = false;
// validate signature data
rdv40_validation_t mem;
if (rdv4_get_signature(&mem) == PM3_SUCCESS) {
if (rdv4_validate(&mem) == PM3_SUCCESS) {
is_genuine_rdv4 = true;
}
}
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, " smartcard reader.......... %s", IfPm3Smartcard() ? _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"));
}
} else {
PrintAndLogEx(NORMAL, " firmware.................. %s", _YELLOW_("PM3RDV4"));
PrintAndLogEx(NORMAL, " external flash............ %s", IfPm3Flash() ? _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"));
}
if (IfPm3FpcUsartDevFromUsb()) {
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);
}
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 em = arg_get_lit(ctx, 8);
int bin_len = 63;
uint8_t bin[70] = {0};
CLIGetStrWithReturn(ctx, 9, bin, &bin_len);
CLIParserFree(ctx);
if (q5 && em) {
@ -383,12 +381,21 @@ static int CmdHIDClone(const char *Cmd) {
return PM3_EINVARG;
}
if (raw_len) {
uint32_t top = 0, mid = 0, bot = 0;
if (raw_len) {
hexstring_to_u96(&top, &mid, &bot, raw);
packed.Top = top;
packed.Mid = mid;
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 {
if (HIDPack(format_idx, &card, &packed, true) == false) {
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"
"Lower tolerance means tighter pulses. ",
"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"
);

View file

@ -60,7 +60,7 @@ int CmdWiegandEncode(const char *Cmd) {
arg_u64_0(NULL, "issue", "<dec>", "issue level"),
arg_u64_0(NULL, "oem", "<dec>", "OEM code"),
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
};
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
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];
size_t signature_len;
uint8_t signature[MBEDTLS_ECDSA_MAX_LEN] = {0};
size_t signature_len = 0;
// convert r & s to ASN.1 signature
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(&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) {
return res;
}

View file

@ -403,9 +403,19 @@ static int wait_for_ack(PacketResponseNG *ack) {
return PM3_SUCCESS;
}
static bool g_printed_msg = false;
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_("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) {
@ -532,6 +542,7 @@ const char ice[] =
" !!: :!! !!: !!: !!: !!: !!! !!: !!!\n : :: :: : : :: ::: : : : : : :: : \n"
_RED_(" . .. .. . . .. ... . . . . . .. . ")
"\n...................................................................\n"
"...................................................................\n"
;
// 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();
SendCommandMIX(CMD_HF_MIFARE_READSC, sectorNo, keyType, 0, key, 6);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
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;
}
} else {
PrintAndLogEx(ERR, "Command execute timeout");
PrintAndLogEx(DEBUG, "Command execute timeout");
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;
}
@ -1196,3 +1218,19 @@ int detect_mf_magic(bool is_mfc) {
}
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 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 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_mf_magic(bool is_mfc);
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);
#endif

View file

@ -673,6 +673,8 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, char *filenames[
}
finish:
if (ret != PM3_SUCCESS)
PrintAndLogEx(INFO, "The flashing procedure failed, follow the suggested steps!");
ret = flash_stop_flashing();
CloseProxmark(session.current_device);
finish2:
@ -684,7 +686,7 @@ finish2:
PrintAndLogEx(SUCCESS, _CYAN_("All done"));
else
PrintAndLogEx(ERR, "Aborted on error");
PrintAndLogEx(NORMAL, "\nHave a nice day!");
PrintAndLogEx(INFO, "\nHave a nice day!");
return ret;
}
#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
* at a time.
* Converts a hex string to component "hi2", "hi" and "lo" 32-bit integers
* one nibble at a time.
*
* Returns the number of nibbles (4 bits) entered.
*/
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) {
*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;
}
/**
* 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) {
#if defined __GNUC__
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_ndup(const char *src, size_t len);
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);
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);
}
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
// Each algo implementation should offer two key generation functions.

View file

@ -13,11 +13,13 @@
#include "common.h"
uint32_t ul_ev1_pwdgen_def(uint8_t *uid);
uint32_t ul_ev1_pwdgenA(uint8_t *uid);
uint32_t ul_ev1_pwdgenB(uint8_t *uid);
uint32_t ul_ev1_pwdgenC(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_packgenB(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,
unsigned char *sig, size_t *slen) {
int ret;
unsigned char buf[MBEDTLS_ECDSA_MAX_LEN];
unsigned char *p = buf + sizeof(buf);
unsigned char buf[MBEDTLS_ECDSA_MAX_LEN] = {0};
unsigned char *p = buf + sizeof(buf) - 1;
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, r));
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_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE));
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE));
memcpy(sig, p, len);
*slen = len;
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 antifuzz `|N |`Fuzzing the anticollision phase. Warning! Readers may react strange`
|`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
@ -255,6 +255,19 @@ Check column "offline" for their availability.
|`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
{ 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
[=] Loading Preferences...
[+] loaded from JSON file /home/iceman/.proxmark3/preferences.json
[=] Using UART port /dev/ttyS7
[=] 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/
╚═╝ ╚═╝ ╚═╝ ╚═══╝ Release v4.9237 - Ice Coffee
██╔═══╝ ██║╚██╔╝██║ ══█║
██║ ██║ ╚═╝ ██║ ████╔╝ Iceman ☕
╚═╝ ╚═╝ ╚═╝ ╚═══╝ ❄️ bleeding edge
https://github.com/rfidresearchgroup/proxmark3/
[ 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
[ PROXMARK RDV4 ]
external flash: present
smartcard reader: present
[ PROXMARK RDV4 Extras ]
FPC USART for BT add-on support: absent
device.................... RDV4
firmware.................. RDV4
external flash............ present
smartcard reader.......... present
FPC USART for BT add-on... absent
[ ARM ]
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
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 ]
LF image built for 2s30vq100 on 2020/02/22 at 12:51:14
HF image built for 2s30vq100 on 2020/01/12 at 15:31:16
LF image built for 2s30vq100 on 2020-07-08 at 23: 8: 7
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 ]
--= uC: AT91SAM7S512 Rev B
--= uC: AT91SAM7S512 Rev A
--= Embedded Processor: ARM7TDMI
--= Nonvolatile Program Memory Size: 512K bytes, Used: 291382 bytes (56%) Free: 232906 bytes (44%)
--= Second Nonvolatile Program Memory Size: None
--= Internal SRAM Size: 64K bytes
--= Architecture Identifier: AT91SAM7Sxx Series
--= Nonvolatile Program Memory Type: Embedded Flash Memory
--= Internal SRAM size: 64K bytes
--= Architecture identifier: AT91SAM7Sxx Series
--= Embedded flash memory 512K bytes ( 59% used )
[usb] pm3 -->
```

View file

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

View file

@ -2,420 +2,39 @@
# MIT License
# Copyright (c) 2020 @doegox
# Requirements:
# python3 -m pip install ansicolors sslcrypto
import binascii
import sys
import sslcrypto
from colors import color
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):
l = (len(signature) // 2) & 0xfe
if l == 32 :
siglen = (len(signature) // 2) & 0xfe
if siglen == 32:
curves = ["secp128r1", "secp128r2"]
elif l == 48:
elif siglen == 48:
curves = ["secp192k1", "secp192r1"]
elif l == 56:
elif siglen == 56:
curves = ["secp224k1", "secp224r1"]
elif l == 64:
elif siglen == 64:
curves = ["secp256k1", "secp256r1"]
elif l == 96:
elif siglen == 96:
curves = ["secp384r1"]
elif l == 132:
elif siglen == 132:
curves = ["secp521r1"]
else:
raise ValueError("Unsupported signature size %i" % len(signature))
return curves
def recover(data, signature, curvename, alghash=None):
recovered = set()
curve = get_curve(curvename)
curve = sslcrypto.ecc.get_curve(curvename)
recoverable = len(signature) % 1 == 1
if (recoverable):
try:
@ -423,7 +42,7 @@ def recover(data, signature, curvename, alghash=None):
recovered.add(pk)
if debug:
print("Possible Pk: ", binascii.hexlify(pk))
except:
except ValueError:
pass
else:
for i in range(2):
@ -434,20 +53,23 @@ def recover(data, signature, curvename, alghash=None):
recovered.add(pk)
if debug:
print("Possible Pk: ", binascii.hexlify(pk))
except:
except ValueError:
pass
return recovered
def recover_multiple(uids, sigs, curvename, alghash=None):
recovered = set()
assert len(uids) == len(sigs)
for i in range(len(uids)):
data = binascii.unhexlify(uids[i])
if debug:
print("UID (%2i): " % len(data), binascii.hexlify(data))
print("UID (%2i): " %
len(data), binascii.hexlify(data))
signature = binascii.unhexlify(sigs[i])
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)
if i == 0:
if recovered_tmp == set():
@ -471,7 +93,8 @@ def selftests():
{'name': "Mifare Classic EV1",
'samples': ["0433619AB35780", "B9FAE369EC21C980650D87ED9AE9B1610E859131B4B8699C647548AB68D249BB",
"524374E2", "F8758CE30A58553A9985C458FB9C7D340FCFB04847B928A0667939272BC58B5E",
"53424B8A", "B4F533E8C06C021E242EFE8558C1672ED7022E5AE4E7AA2D46113B0AB6928AFC"],
"53424B8A", "B4F533E8C06C021E242EFE8558C1672ED7022E5AE4E7AA2D46113B0AB6928AFC",
"BD2A4146", "19505576ED327D8F8870C86B1ED00898BFEDFFF27CC82FC515BA2EEC26050873"],
'pk': "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"},
{'name': "DESFire Light",
'samples': ["0439556ACB6480", "D5BD0978106E1E38B513642335966AB21E9F950DCFCFAB45FF13D0DC3CA4C2AE7E0D671DF1240937D040DAC4601C5F66ED62C546EE03ED08",
@ -485,8 +108,8 @@ def selftests():
'samples': ["04448BD2DB6B80", "5CBB5632795C8F15263FEFB095B51C7B541AFD914A1AE44EF6FB8AF605EDF13DBFEE6C3A2DB372245E671DFE0D42CB1F0D0B8FE67A89D2F6",
"04445DD2DB6B80", "166BFD9F9BFAA451172566101580DF9894F582C4A4E258C15037AD2F35A475CF1D7FB817618623A6569F991931AFB2766984E21A18512A6D"],
'pk': "041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743"},
# TODO one more Mifare Plus EV1...
{'name': "Mifare Plus EV1",
# TODO one more Mifare Plus EV1...
'samples': ["042A2B221C5080", "BAC40CD88E9193C58ADA5055350C4F648EB5A7AEC4FCF9BD4CDD7B1C558DE5F59C6636F26286ED48622AAA2331D4DF1CEE23B57B94BDA631"],
'pk': "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"},
{'name': "NTAG413DNA",
@ -498,17 +121,19 @@ def selftests():
"04C46C222A6380", "344A806EBF704C05C19215D2F840529CE365AAD2D08A469A95896D75D477D9FAB02A0C827E9F215BD8EB0E56A3A9A008FB75D706AABBD4DA"],
'pk': "048A9B380AF2EE1B98DC417FECC263F8449C7625CECE82D9B916C992DA209D68422B81EC20B65A66B5102A61596AF3379200599316A00A1410"},
{'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",
"E0040118009B4D62", "25CF13747C3389EC7889DE916E3747584978511CC78B51CFB1883B494CBED7AB"],
'pk': "04d64bb732c0d214e7ec580736acf847284b502c25c0f7f2fa86aace1dada4387a"},
# ! tag UID is considered inversed: E0040118009B5FEE => EE5F9B00180104E0
# TODO one more ICODE-DNA...
{'name': "ICODE DNA, ICODE SLIX2",
# ! tag UID is considered inverted: E0040118009B5FEE => EE5F9B00180104E0
# TODO one more ICODE-DNA...
'samples': ["EE5F9B00180104E0", "32D9E7579CD77E6F1FA11419231E874826984C5F189FDE1421684563A9663377"],
'pk': "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"},
# uses secp256r1?, SHA-256,
# {'name': "Minecraft Earth",
# # uses secp256r1?, SHA-256,
# 'samples': ["aa", "DF0E506DFF8FCFC4B7B979D917644445F1230D2C7CDC342AFA842CA240C210BE7275F62073A9670F2DCEFC602CBEE771C2B4CD4A04F3D1EA11F49ABDF7E8B721"],
# 'pk': ""},
]
@ -540,6 +165,7 @@ def selftests():
print("[FAIL]")
print("Tests: [%s]" % ["FAIL", "OK"][succeeded])
if __name__ == "__main__":
if len(sys.argv) == 2 and sys.argv[1] == "selftests":
selftests()
@ -549,14 +175,17 @@ if __name__ == "__main__":
print("Example: \n%s 04ee45daa34084 ebb6102bff74b087d18a57a54bc375159a04ea9bc61080b7f4a85afe1587d73b" % sys.argv[0])
exit(1)
uids, sigs = sys.argv[1:][::2], sys.argv[1:][1::2]
once = True
curvenames = guess_curvename(sigs[0])
for c in curvenames:
print("\nAssuming curve=%s" % c)
print("========================")
for h in [None, "md5", "sha1", "sha256", "sha512"]:
print("Assuming hash=%s" % h)
recovered = recover_multiple(uids, sigs, c, alghash=h)
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):")
for pk in list(recovered):
print(binascii.hexlify(pk).decode('utf8'))
once = True