mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-20 21:33:47 -07:00
Merge branch 'master' into 4x50_fix
update 210223
This commit is contained in:
commit
b37c32677c
47 changed files with 1515 additions and 828 deletions
18
.github/workflows/macos.yml
vendored
18
.github/workflows/macos.yml
vendored
|
@ -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
|
||||
|
||||
|
|
21
.github/workflows/ubuntu.yml
vendored
21
.github/workflows/ubuntu.yml
vendored
|
@ -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
|
||||
|
||||
|
|
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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 = \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@ A5B4C3D2
|
|||
00434343
|
||||
44B44CAE
|
||||
88661858
|
||||
# MKF fobs
|
||||
E9920427
|
||||
# paxton bullit?
|
||||
575F4F4B
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
|
212
client/luascripts/lf_em4100_bulk.lua
Normal file
212
client/luascripts/lf_em4100_bulk.lua
Normal 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)
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"},
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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, "");
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
148
doc/jooki_notes.md
Normal 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 |
|
||||
|
|
@ -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 -->
|
||||
```
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue