From 2eb9d7f6612865782e2ebf3984b35f5becd43069 Mon Sep 17 00:00:00 2001 From: Philipp Schuler Date: Tue, 29 Apr 2025 12:53:04 +0200 Subject: [PATCH 001/149] Add keys for Aral Gas Station Car-Wash cards --- client/dictionaries/mfc_default_keys.dic | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index e5af9c766..5c8e1853f 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -3108,3 +3108,29 @@ AB921CF0752C 265A5F32DE73 567D734C403C 2426217B3B3B + +# German Aral Gas Station Car-Wash cards +080507020706 +0100815D8D00 +2459514AED5B +5D493F6B0352 +1CEC0F0ACC0E +922B5D1BF2BC +2D7E76C7B8EC +5E59896806FF +097EEA4FE51B +688FC86BAB79 +C01D1DBEEE79 +2529BF8544C2 +C6052FBAA150 +A1D7B3A95605 +00D0BF748E77 +C082C0F35CE6 +3C86C78541A7 +5632DCC517E1 +9310191C338F +2761858C02D7 +8C64B49C7638 +B1BA3E778930 +2037627D9260 +28C4D7170FCD From 1f718683b3edd159f40e84882df2510147eddb5f Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 16 Jun 2025 16:18:01 +0200 Subject: [PATCH 002/149] Revert "Release v4.20469 - Daddy Iceman" This reverts commit 9fa173c727e91d296aeb0279c26b0fee9ef9ed36. --- Makefile.defs | 4 ++-- armsrc/Makefile | 2 +- bootrom/Makefile | 2 +- client/CMakeLists.txt | 4 ++-- client/Makefile | 4 ++-- client/deps/amiibo.cmake | 2 +- client/deps/cliparser.cmake | 2 +- client/deps/hardnested.cmake | 18 ++++++++-------- client/deps/id48lib.cmake | 2 +- client/deps/jansson.cmake | 2 +- client/deps/lua.cmake | 2 +- client/deps/mbedtls.cmake | 2 +- client/deps/reveng.cmake | 2 +- client/deps/tinycbor.cmake | 2 +- client/deps/whereami.cmake | 2 +- client/experimental_lib/CMakeLists.txt | 2 +- client/src/proxmark3.c | 2 +- common/default_version_pm3.c | 29 +++++++++++++++++++------- common_arm/Makefile.common | 2 +- 19 files changed, 51 insertions(+), 36 deletions(-) diff --git a/Makefile.defs b/Makefile.defs index 0d6066489..2496057fa 100644 --- a/Makefile.defs +++ b/Makefile.defs @@ -112,8 +112,8 @@ ifeq ($(DEBUG),1) DEFCFLAGS = -g -O0 -fstrict-aliasing -pipe DEFLDFLAGS = else - DEFCXXFLAGS = -Wall -O3 -pipe - DEFCFLAGS = -Wall -O3 -fstrict-aliasing -pipe + DEFCXXFLAGS = -Wall -Werror -O3 -pipe + DEFCFLAGS = -Wall -Werror -O3 -fstrict-aliasing -pipe DEFLDFLAGS = endif diff --git a/armsrc/Makefile b/armsrc/Makefile index 0f83596be..9929b3ae3 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -186,7 +186,7 @@ showinfo: # version_pm3.c should be checked on every time fullimage.stage1.elf should be remade version_pm3.c: default_version_pm3.c $(OBJDIR)/fpga_version_info.o $(OBJDIR)/fpga_all.o $(THUMBOBJ) $(ARMOBJ) .FORCE $(info [-] CHECK $@) - $(Q)$(CP) $< $@ + $(Q)$(SH) ../tools/mkversion.sh $@ || $(CP) $< $@ fpga_version_info.c: $(FPGA_BITSTREAMS) $(FPGA_COMPRESSOR) $(info [-] GEN $@) diff --git a/bootrom/Makefile b/bootrom/Makefile index 86c785cd1..b6825530d 100644 --- a/bootrom/Makefile +++ b/bootrom/Makefile @@ -56,7 +56,7 @@ OBJS = $(OBJDIR)/bootrom.s19 # version_pm3.c should be checked on every compilation version_pm3.c: default_version_pm3.c .FORCE $(info [=] CHECK $@) - $(Q)$(CP) $< $@ + $(Q)$(SH) ../tools/mkversion.sh $@ || $(CP) $< $@ all: showinfo $(OBJS) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 615c581cf..854828df9 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -434,7 +434,7 @@ set (TARGET_SOURCES add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/version_pm3.c - COMMAND ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c + COMMAND sh ${PM3_ROOT}/tools/mkversion.sh ${CMAKE_BINARY_DIR}/version_pm3.c || ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c DEPENDS ${PM3_ROOT}/common/default_version_pm3.c ) @@ -692,7 +692,7 @@ add_executable(proxmark3 ${ADDITIONAL_SRC} ) -target_compile_options(proxmark3 PUBLIC -Wall -O3) +target_compile_options(proxmark3 PUBLIC -Wall -Werror -O3) if (EMBED_READLINE) if (NOT SKIPREADLINE EQUAL 1) add_dependencies(proxmark3 ncurses readline) diff --git a/client/Makefile b/client/Makefile index 89f7a17a4..014211325 100644 --- a/client/Makefile +++ b/client/Makefile @@ -446,7 +446,7 @@ endif PM3CFLAGS += -DHAVE_SNPRINTF -CXXFLAGS ?= -Wall +CXXFLAGS ?= -Wall -Werror CXXFLAGS += $(MYDEFS) $(MYCXXFLAGS) $(MYINCLUDES) PM3CXXFLAGS = $(CXXFLAGS) @@ -995,7 +995,7 @@ src/pm3_pywrap.c: pm3.i # version_pm3.c should be checked on every compilation src/version_pm3.c: default_version_pm3.c .FORCE $(info [=] CHECK $@) - $(Q)$(CP) $< $@ + $(Q)$(SH) ../tools/mkversion.sh $@ || $(CP) $< $@ # easy printing of MAKE VARIABLES print-%: ; @echo $* = $($*) diff --git a/client/deps/amiibo.cmake b/client/deps/amiibo.cmake index 8c524c170..c946c0682 100644 --- a/client/deps/amiibo.cmake +++ b/client/deps/amiibo.cmake @@ -19,7 +19,7 @@ target_link_libraries(pm3rrg_rdv4_amiibo PRIVATE m pm3rrg_rdv4_mbedtls) -target_compile_options(pm3rrg_rdv4_amiibo PRIVATE -Wall -O3) +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 diff --git a/client/deps/cliparser.cmake b/client/deps/cliparser.cmake index a85cc2374..fccae33b7 100644 --- a/client/deps/cliparser.cmake +++ b/client/deps/cliparser.cmake @@ -9,5 +9,5 @@ target_include_directories(pm3rrg_rdv4_cliparser PRIVATE ../../include ../src) target_include_directories(pm3rrg_rdv4_cliparser INTERFACE cliparser) -target_compile_options(pm3rrg_rdv4_cliparser PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_cliparser PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_cliparser PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/hardnested.cmake b/client/deps/hardnested.cmake index 468ee4ef2..ec545e2a8 100644 --- a/client/deps/hardnested.cmake +++ b/client/deps/hardnested.cmake @@ -2,7 +2,7 @@ add_library(pm3rrg_rdv4_hardnested_nosimd OBJECT hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) -target_compile_options(pm3rrg_rdv4_hardnested_nosimd PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_hardnested_nosimd PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_hardnested_nosimd PROPERTY POSITION_INDEPENDENT_CODE ON) target_include_directories(pm3rrg_rdv4_hardnested_nosimd PRIVATE @@ -32,7 +32,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_mmx PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_mmx PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_mmx BEFORE PRIVATE -mmmx -mno-sse2 -mno-avx -mno-avx2 -mno-avx512f) set_property(TARGET pm3rrg_rdv4_hardnested_mmx PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -47,7 +47,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_sse2 PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_sse2 PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_sse2 BEFORE PRIVATE -mmmx -msse2 -mno-avx -mno-avx2 -mno-avx512f) set_property(TARGET pm3rrg_rdv4_hardnested_sse2 PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -62,7 +62,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_avx PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_avx PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_avx BEFORE PRIVATE -mmmx -msse2 -mavx -mno-avx2 -mno-avx512f) set_property(TARGET pm3rrg_rdv4_hardnested_avx PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -77,7 +77,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_avx2 PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_avx2 PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_avx2 BEFORE PRIVATE -mmmx -msse2 -mavx -mavx2 -mno-avx512f) set_property(TARGET pm3rrg_rdv4_hardnested_avx2 PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -92,7 +92,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_avx512 PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_avx512 PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_avx512 BEFORE PRIVATE -mmmx -msse2 -mavx -mavx2 -mavx512f) set_property(TARGET pm3rrg_rdv4_hardnested_avx512 PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -116,7 +116,7 @@ elseif ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST ARM64_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_neon PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_neon PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_hardnested_neon PROPERTY POSITION_INDEPENDENT_CODE ON) target_include_directories(pm3rrg_rdv4_hardnested_neon PRIVATE @@ -134,7 +134,7 @@ elseif ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST ARM32_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_neon PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_neon PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_neon BEFORE PRIVATE -mfpu=neon) set_property(TARGET pm3rrg_rdv4_hardnested_neon PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -155,7 +155,7 @@ add_library(pm3rrg_rdv4_hardnested STATIC hardnested/hardnested_bruteforce.c $ ${SIMD_TARGETS}) -target_compile_options(pm3rrg_rdv4_hardnested PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_hardnested PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_hardnested PROPERTY POSITION_INDEPENDENT_CODE ON) target_include_directories(pm3rrg_rdv4_hardnested PRIVATE ../../common diff --git a/client/deps/id48lib.cmake b/client/deps/id48lib.cmake index fa57d7855..47205d494 100644 --- a/client/deps/id48lib.cmake +++ b/client/deps/id48lib.cmake @@ -3,7 +3,7 @@ add_library(pm3rrg_rdv4_id48 STATIC id48/id48_generator.c id48/id48_recover.c ) -target_compile_options( pm3rrg_rdv4_id48 PRIVATE -Wpedantic -Wall -O3 -Wno-unknown-pragmas -Wno-inline -Wno-unused-function -DID48_NO_STDIO) +target_compile_options( pm3rrg_rdv4_id48 PRIVATE -Wpedantic -Wall -Werror -O3 -Wno-unknown-pragmas -Wno-inline -Wno-unused-function -DID48_NO_STDIO) target_include_directories(pm3rrg_rdv4_id48 PRIVATE id48) target_include_directories(pm3rrg_rdv4_id48 INTERFACE id48) set_property(TARGET pm3rrg_rdv4_id48 PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/jansson.cmake b/client/deps/jansson.cmake index 42c701d5e..c91a47047 100644 --- a/client/deps/jansson.cmake +++ b/client/deps/jansson.cmake @@ -14,5 +14,5 @@ add_library(pm3rrg_rdv4_jansson STATIC target_compile_definitions(pm3rrg_rdv4_jansson PRIVATE HAVE_STDINT_H) target_include_directories(pm3rrg_rdv4_jansson INTERFACE jansson) -target_compile_options(pm3rrg_rdv4_jansson PRIVATE -Wall -Wno-unused-function -O3) +target_compile_options(pm3rrg_rdv4_jansson PRIVATE -Wall -Werror -Wno-unused-function -O3) set_property(TARGET pm3rrg_rdv4_jansson PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/lua.cmake b/client/deps/lua.cmake index 3bf85e1ce..d89275be6 100644 --- a/client/deps/lua.cmake +++ b/client/deps/lua.cmake @@ -52,5 +52,5 @@ if (NOT MINGW) endif (NOT MINGW) target_include_directories(pm3rrg_rdv4_lua INTERFACE liblua) -target_compile_options(pm3rrg_rdv4_lua PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_lua PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_lua PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/mbedtls.cmake b/client/deps/mbedtls.cmake index 7c72925ab..49c141b68 100644 --- a/client/deps/mbedtls.cmake +++ b/client/deps/mbedtls.cmake @@ -49,5 +49,5 @@ add_library(pm3rrg_rdv4_mbedtls STATIC target_include_directories(pm3rrg_rdv4_mbedtls PRIVATE ../../common) target_include_directories(pm3rrg_rdv4_mbedtls INTERFACE ../../common/mbedtls) -target_compile_options(pm3rrg_rdv4_mbedtls PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_mbedtls PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_mbedtls PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/reveng.cmake b/client/deps/reveng.cmake index 1040730f1..d7e3cfd8a 100644 --- a/client/deps/reveng.cmake +++ b/client/deps/reveng.cmake @@ -13,5 +13,5 @@ target_include_directories(pm3rrg_rdv4_reveng PRIVATE ../src ../../include) target_include_directories(pm3rrg_rdv4_reveng INTERFACE reveng) -target_compile_options(pm3rrg_rdv4_reveng PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_reveng PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_reveng PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/tinycbor.cmake b/client/deps/tinycbor.cmake index c74618149..5a6abda25 100644 --- a/client/deps/tinycbor.cmake +++ b/client/deps/tinycbor.cmake @@ -11,5 +11,5 @@ add_library(pm3rrg_rdv4_tinycbor STATIC target_include_directories(pm3rrg_rdv4_tinycbor INTERFACE tinycbor) # Strange errors on Mingw when compiling with -O3 -target_compile_options(pm3rrg_rdv4_tinycbor PRIVATE -Wall -O2) +target_compile_options(pm3rrg_rdv4_tinycbor PRIVATE -Wall -Werror -O2) set_property(TARGET pm3rrg_rdv4_tinycbor PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/whereami.cmake b/client/deps/whereami.cmake index 721873066..d2d6a5b2a 100644 --- a/client/deps/whereami.cmake +++ b/client/deps/whereami.cmake @@ -2,5 +2,5 @@ add_library(pm3rrg_rdv4_whereami STATIC whereami/whereami.c) target_compile_definitions(pm3rrg_rdv4_whereami PRIVATE WAI_PM3_TUNED) target_include_directories(pm3rrg_rdv4_whereami INTERFACE whereami) -target_compile_options(pm3rrg_rdv4_whereami PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_whereami PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_whereami PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/experimental_lib/CMakeLists.txt b/client/experimental_lib/CMakeLists.txt index 6d4a9d9c8..7d0c952b1 100644 --- a/client/experimental_lib/CMakeLists.txt +++ b/client/experimental_lib/CMakeLists.txt @@ -434,7 +434,7 @@ set (TARGET_SOURCES add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/version_pm3.c - COMMAND ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c + COMMAND sh ${PM3_ROOT}/tools/mkversion.sh ${CMAKE_BINARY_DIR}/version_pm3.c || ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c DEPENDS ${PM3_ROOT}/common/default_version_pm3.c ) diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index f19adb71b..bf3842406 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -49,7 +49,7 @@ static int mainret = PM3_SUCCESS; #ifndef LIBPM3 #define BANNERMSG1 "" #define BANNERMSG2 "" -#define BANNERMSG3 "Release v4.20469 - Daddy Iceman" +#define BANNERMSG3 "" typedef enum LogoMode { UTF8, ANSI, ASCII } LogoMode; diff --git a/common/default_version_pm3.c b/common/default_version_pm3.c index 419ee95ff..d93a7ef15 100644 --- a/common/default_version_pm3.c +++ b/common/default_version_pm3.c @@ -1,5 +1,20 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- #include "common.h" -/* Generated file, do not edit */ +/* This is the default version_pm3.c file that Makefile.common falls back to if sh is not available */ #ifndef ON_DEVICE #define SECTVERSINFO #else @@ -8,10 +23,10 @@ const struct version_information_t SECTVERSINFO g_version_information = { VERSION_INFORMATION_MAGIC, - 1, - 1, - 2, - "Iceman/master/v4.20469", - "2025-06-16 16:18:01", - "72b1b17a3" + 1, /* version 1 */ + 0, /* version information not present */ + 2, /* cleanliness couldn't be determined */ + "Iceman/master/unknown", + "1970-01-01 00:00:00", + "no sha256" }; diff --git a/common_arm/Makefile.common b/common_arm/Makefile.common index a845963b2..e8e574112 100644 --- a/common_arm/Makefile.common +++ b/common_arm/Makefile.common @@ -49,7 +49,7 @@ VPATH = . ../common_arm ../common ../common/crapto1 ../common/mbedtls ../common/ INCLUDES = ../include/proxmark3_arm.h ../include/at91sam7s512.h ../include/config_gpio.h ../include/pm3_cmd.h ARMCFLAGS = -mthumb-interwork -fno-builtin -DEFCFLAGS = -Wall -Os -pedantic -fstrict-aliasing -pipe +DEFCFLAGS = -Wall -Werror -Os -pedantic -fstrict-aliasing -pipe # Some more warnings we want as errors: DEFCFLAGS += -Wbad-function-cast -Wchar-subscripts -Wundef -Wunused -Wuninitialized -Wpointer-arith -Wformat -Wformat-security -Winit-self -Wmissing-include-dirs -Wnested-externs -Wempty-body -Wignored-qualifiers -Wmissing-field-initializers -Wtype-limits From 508c8943e7cd33a22de688ffe38e1b2225e27cf8 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 16 Jun 2025 16:20:56 +0200 Subject: [PATCH 003/149] text --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c7014d3e..58cf9bd10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ 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... -## [Daddy Iceman][2025-06-16] +## [Daddy Iceman.4.20469][2025-06-16] - Fixed edge case in fm11rf08s key recovery tools (@doegox) - Removed `--par` from `lf em 4x70` commands. - Changed `hf 14a info` - refactored code to be able to detect card technology across the client easier (@iceman1001) From 52b00fa6537647f99f9cfd58f0f6576d13c65eac Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Mon, 16 Jun 2025 18:14:02 +0200 Subject: [PATCH 004/149] Release script: removing some Werror left --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8518bbd61..f70a16b67 100644 --- a/Makefile +++ b/Makefile @@ -369,8 +369,8 @@ release: @echo "# - Release Tag: $(VERSION)" @echo "# - Release Name: $(RELEASE_NAME)" # - Removing -Werror... - @find . \( -path "./Makefile.defs" -or -path "./client/Makefile" -or -path "./common_arm/Makefile.common" -or -path "./tools/hitag2crack/*/Makefile" \) -exec sed -i 's/ -Werror//' {} \; - @find . \( -path "./client/deps/*.cmake" -or -path "./client/CMakeLists.txt" \) -exec sed -i 's/ -Werror//' {} \; + @find . \( -path "./Makefile.defs" -or -path "./client/Makefile" -or -path "./common_arm/Makefile.common" -or -path "./tools/hitag2crack/*/Makefile" -or -path "./client/deps/*/Makefile" \) -exec sed -i 's/ -Werror//' {} \; + @find . \( -path "./client/deps/*.cmake" -or -path "./client/CMakeLists.txt" -or -path "./client/experimental_lib/CMakeLists.txt" \) -exec sed -i 's/ -Werror//' {} \; # - Changing banner... @sed -i "s/^#define BANNERMSG3 .*/#define BANNERMSG3 \"Release $(VERSION) - $(RELEASE_NAME)\"/" client/src/proxmark3.c @echo -n "# ";grep "^#define BANNERMSG3" client/src/proxmark3.c From cc8ea65679ab7fb3ef9e60e50348c590bfe237fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20M=C3=B8ller?= <37707273+LupusE@users.noreply.github.com> Date: Mon, 16 Jun 2025 19:03:58 +0200 Subject: [PATCH 005/149] Update 4_Advanced-compilation-parameters.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Little typo in new PM3 Ultimate option. Signed-off-by: Benjamin Møller <37707273+LupusE@users.noreply.github.com> --- doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md b/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md index cb1fc583e..55ca51687 100644 --- a/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md +++ b/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md @@ -66,7 +66,7 @@ Here are the supported values you can assign to `PLATFORM` in `Makefile.platform | PM3RDV4 (def) | Proxmark3 RDV4 | | PM3GENERIC | Proxmark3 generic target | | PM3ICOPYX | iCopy-X with XC3S100E | -| PM3ULTIMATE | Proxmar3 Ultimate with XC2S50 | +| PM3ULTIMATE | Proxmark3 Ultimate with XC2S50 | By default `PLATFORM=PM3RDV4`. From e97d521f8c468d0188a4272e446b9b5b98066c95 Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 16 Jun 2025 19:52:08 +0200 Subject: [PATCH 006/149] Update README.md Signed-off-by: Iceman --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 64ae86137..9ea8a3c82 100644 --- a/README.md +++ b/README.md @@ -96,12 +96,14 @@ We define generic Proxmark3 platforms as following devices. - **Note**: currently incompatible with iCopy-X GUI as Proxmark client commands using different syntax - **Note**: see also [icopyx-community repos](https://github.com/iCopy-X-Community/) for upstream sources, reversed hw etc. - **Note**: Uses DRM to lock down tags, ignores the open source licences. Use on your own risk. +- ⚠ Proxmark3 Ultimate + - **Note**: unknown device hw + - **Note**: FPGA images is building for it. Use on your own risk. **Unknown support status** - ⚠ VX - **Note**: unknown device hw -- ⚠ Proxmark3 Ultimate - - **Note**: unknown device hw + When it comes to these new unknown models we are depending on the community to report in if this repo works and what they did to make it work. From 7a9b3383d48cfe3a0187ae2f6a18f3572a33038f Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 17 Jun 2025 18:20:04 +0200 Subject: [PATCH 007/149] fix missing flushing bits also in thinfilm fct --- CHANGELOG.md | 2 ++ armsrc/iso14443a.c | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58cf9bd10..101242adf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ 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] + ## [Daddy Iceman.4.20469][2025-06-16] - Fixed edge case in fm11rf08s key recovery tools (@doegox) - Removed `--par` from `lf em 4x70` commands. diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 5a1e66b74..069aa87c8 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -697,6 +697,9 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t static RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) { if (Demod.len == Demod.output_len) { + // Flush last parity bits + Demod.parityBits <<= (8 - (Demod.len & 0x0007)); // left align remaining parity bits + Demod.parity[Demod.parityLen++] = Demod.parityBits; // and store them return true; } @@ -2903,8 +2906,9 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint if (anticollision) { // clear uid - if (uid_ptr) + if (uid_ptr) { memset(uid_ptr, 0, 10); + } } if (hf14aconfig.forceanticol == 0) { From 35493f5b2c61fed38efef837fc85cebf10a09b05 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 17 Jun 2025 18:21:32 +0200 Subject: [PATCH 008/149] at least run hitag2crack clean, for us who do compile it... --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f70a16b67..b5a0e430d 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ ifneq (,$(DESTDIR)) endif endif -all clean install uninstall check: %: client/% bootrom/% armsrc/% recovery/% mfc_card_only/% mfc_card_reader/% mfd_aes_brute/% fpga_compress/% cryptorf/% +all clean install uninstall check: %: client/% bootrom/% armsrc/% recovery/% mfc_card_only/% mfc_card_reader/% mfd_aes_brute/% fpga_compress/% cryptorf/% hitag2crack/% # hitag2crack toolsuite is not yet integrated in "all", it must be called explicitly: "make hitag2crack" #all clean install uninstall check: %: hitag2crack/% From 79400d077957380541f7a4ee0cc50850f7a99a00 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 17 Jun 2025 18:24:40 +0200 Subject: [PATCH 009/149] text and style --- armsrc/mifarecmd.c | 4 ++-- armsrc/mifaresim.c | 19 ------------------- client/src/cmdhf14a.c | 9 ++++++--- client/src/cmdhficlass.c | 3 ++- client/src/cmdhfmf.c | 6 ++++-- client/src/cmdhfmfp.c | 2 +- client/src/cmdparser.c | 3 ++- client/src/crypto/libpcrypto.c | 6 ++++-- client/src/fileutils.c | 8 ++++---- client/src/mifare/mad.c | 1 - 10 files changed, 25 insertions(+), 36 deletions(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index d0fe1e524..a84ad0096 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -274,7 +274,7 @@ void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes) { return; }; - if (!mifare_ultra_auth(keybytes)) { + if (mifare_ultra_auth(keybytes) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Authentication failed"); OnError(1); return; @@ -304,7 +304,7 @@ void MifareUL_AES_Auth(bool turn_off_field, uint8_t keyno, uint8_t *keybytes) { return; }; - if (!mifare_ultra_aes_auth(keyno, keybytes)) { + if (mifare_ultra_aes_auth(keyno, keybytes) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Authentication failed"); OnErrorNG(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT); return; diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index 7b7a48550..21014be12 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -579,21 +579,6 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t counter++; } - /* - // find reader field - if (cardSTATE == MFEMUL_NOFIELD) { - - vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; - - if (vHf > MF_MINFIELDV) { - cardSTATE_TO_IDLE(); - LED_A_ON(); - } - button_pushed = BUTTON_PRESS(); - continue; - } - */ - FpgaEnableTracing(); //Now, get data int res = EmGetCmd(receivedCmd, sizeof(receivedCmd), &receivedCmd_len, receivedCmd_par); @@ -760,10 +745,6 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t // WORK case MFEMUL_WORK: { - if (g_dbglevel >= DBG_EXTENDED) { - // Dbprintf("[MFEMUL_WORK] Enter in case"); - } - if (receivedCmd_len == 0) { if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] NO CMD received"); break; diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index e9319ec21..9d929b6b7 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -957,14 +957,17 @@ int CmdHF14ASim(const char *Cmd) { bool keypress = kbd_enter_pressed(); while (keypress == false) { - if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == false) + if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == false) { continue; + } - if (resp.status != PM3_SUCCESS) + if (resp.status != PM3_SUCCESS) { break; + } - if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) + if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) { break; + } const nonces_t *data = (nonces_t *)resp.data.asBytes; readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose); diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 4c945153e..0cf89a6d1 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -2130,8 +2130,9 @@ static int CmdHFiClassDump(const char *Cmd) { return PM3_EOPABORTED; } - if (WaitForResponseTimeout(CMD_HF_ICLASS_DUMP, &resp, 2000)) + if (WaitForResponseTimeout(CMD_HF_ICLASS_DUMP, &resp, 2000)) { break; + } } PrintAndLogEx(NORMAL, ""); diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 68a9eb044..21ccd12ae 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -3939,7 +3939,9 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { PrintAndLogEx(NORMAL, ""); firstChunk = true; lastChunk = false; - if (blockn != -1) break; + if (blockn != -1) { + break; + } } // end strategy } out: @@ -5247,7 +5249,7 @@ int CmdHF14AMfELoad(const char *Cmd) { // update expected blocks to match converted data. block_cnt = bytes_read / MFU_BLOCK_SIZE; - PrintAndLogEx(INFO, "MIFARE Ultralight override, will use %d blocks ( %u bytes )", block_cnt, block_cnt * block_width); + PrintAndLogEx(INFO, "MIFARE Ultralight override, will use " _YELLOW_("%d") " blocks ( " _YELLOW_("%u") " bytes )", block_cnt, block_cnt * block_width); } PrintAndLogEx(INFO, "Uploading to emulator memory"); diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 6007acf53..dc5684e43 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -315,7 +315,7 @@ static int mfp_load_keys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *userke int len = hex_to_bytes(g_mifare_plus_default_keys[cnt], (uint8_t *)(*pkeyBlock + (*pkeycnt + cnt) * AES_KEY_LEN), AES_KEY_LEN); - PrintAndLogEx(DEBUG, _YELLOW_("%2d") " - %s", *pkeycnt + cnt, sprint_hex_inrow(*pkeyBlock + (*pkeycnt + cnt) * AES_KEY_LEN, AES_KEY_LEN)); + PrintAndLogEx(DEBUG, _YELLOW_("%2u") " - %s", *pkeycnt + cnt, sprint_hex_inrow(*pkeyBlock + (*pkeycnt + cnt) * AES_KEY_LEN, AES_KEY_LEN)); if (len != AES_KEY_LEN) { break; } diff --git a/client/src/cmdparser.c b/client/src/cmdparser.c index 7421b3f58..cf6bb1941 100644 --- a/client/src/cmdparser.c +++ b/client/src/cmdparser.c @@ -287,8 +287,9 @@ int CmdsParse(const command_t Commands[], const char *Cmd) { } // Comment - if (cmd_name[0] == '#') + if (cmd_name[0] == '#') { return PM3_SUCCESS; + } // find args, check for -h / --help int tmplen = len; diff --git a/client/src/crypto/libpcrypto.c b/client/src/crypto/libpcrypto.c index 031005ce0..41f7574c5 100644 --- a/client/src/crypto/libpcrypto.c +++ b/client/src/crypto/libpcrypto.c @@ -55,13 +55,15 @@ void des_decrypt(void *out, const void *in, const void *key) { } void des_encrypt_ecb(void *out, const void *in, const int length, const void *key) { - for (int i = 0; i < length; i += 8) + for (int i = 0; i < length; i += 8) { des_encrypt((uint8_t *)out + i, (uint8_t *)in + i, key); + } } void des_decrypt_ecb(void *out, const void *in, const int length, const void *key) { - for (int i = 0; i < length; i += 8) + for (int i = 0; i < length; i += 8) { des_decrypt((uint8_t *)out + i, (uint8_t *)in + i, key); + } } void des_encrypt_cbc(void *out, const void *in, const int length, const void *key, uint8_t *iv) { diff --git a/client/src/fileutils.c b/client/src/fileutils.c index a9d7cd3af..aee5e827c 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -72,9 +72,7 @@ DumpFileType_t get_filetype(const char *filename) { size_t len = strlen(filename); if (len > 4) { // check if valid file extension and attempt to load data - char s[FILE_PATH_SIZE]; - memset(s, 0, sizeof(s)); - memcpy(s, filename, len); + char *s = str_dup(filename); str_lower(s); if (str_endswith(s, "bin")) { @@ -97,6 +95,8 @@ DumpFileType_t get_filetype(const char *filename) { // log is text // .pm3 is text values of signal data } + + free(s); } return o; } @@ -2577,7 +2577,7 @@ int detect_nfc_dump_format(const char *preferredName, nfc_df_e *dump_type, bool } FILE *f = fopen(path, "r"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); free(path); return PM3_EFILE; diff --git a/client/src/mifare/mad.c b/client/src/mifare/mad.c index d15b4f4b6..c72f33c1b 100644 --- a/client/src/mifare/mad.c +++ b/client/src/mifare/mad.c @@ -317,7 +317,6 @@ static int MADInfoByteDecode(const uint8_t *sector, bool swapmad, int mad_ver, b void MADPrintHeader(void) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("MIFARE App Directory Information") " ----------------"); - PrintAndLogEx(INFO, "-----------------------------------------------------"); } int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMAD2) { From fc9f70c43672ad5c41a54a4a625ada7b81931657 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 17 Jun 2025 18:25:15 +0200 Subject: [PATCH 010/149] fix release name style --- Makefile | 2 ++ client/src/proxmark3.c | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index b5a0e430d..713c21dc7 100644 --- a/Makefile +++ b/Makefile @@ -372,7 +372,9 @@ release: @find . \( -path "./Makefile.defs" -or -path "./client/Makefile" -or -path "./common_arm/Makefile.common" -or -path "./tools/hitag2crack/*/Makefile" -or -path "./client/deps/*/Makefile" \) -exec sed -i 's/ -Werror//' {} \; @find . \( -path "./client/deps/*.cmake" -or -path "./client/CMakeLists.txt" -or -path "./client/experimental_lib/CMakeLists.txt" \) -exec sed -i 's/ -Werror//' {} \; # - Changing banner... + @sed -i "s/^#define BANNERMSG2 .*/#define BANNERMSG2 \" -----------------------------------\"/" client/src/proxmark3.c @sed -i "s/^#define BANNERMSG3 .*/#define BANNERMSG3 \"Release $(VERSION) - $(RELEASE_NAME)\"/" client/src/proxmark3.c + @echo -n "# ";grep "^#define BANNERMSG2" client/src/proxmark3.c @echo -n "# ";grep "^#define BANNERMSG3" client/src/proxmark3.c # - Committing temporarily... @git commit -a -m "Release $(VERSION) - $(RELEASE_NAME)" diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index bf3842406..67bda5628 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -75,8 +75,9 @@ static void showBanner_logo(LogoMode mode) { sq, sq, tl, hl, hl, hl, br, __, sq, sq, vl, bl, sq, sq, tl, br, sq, sq, vl, __, bl, hl, hl, sq, sq, tr); PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s")" " BANNERMSG1, sq, sq, vl, __, __, __, __, __, sq, sq, vl, __, bl, hl, br, __, sq, sq, vl, sq, sq, sq, sq, sq, tl, br); - PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s")" " BANNERMSG2, + PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"), bl, hl, br, __, __, __, __, __, bl, hl, br, __, __, __, __, __, bl, hl, br, bl, hl, hl, hl, hl, br, __); + PrintAndLogEx(NORMAL, " " BANNERMSG2); break; } case ANSI: { @@ -87,7 +88,8 @@ static void showBanner_logo(LogoMode mode) { PrintAndLogEx(NORMAL, " " _CYAN_("8888888P\" 888 Y888P 888 \"Y8b. ")); PrintAndLogEx(NORMAL, " " _CYAN_("888 888 Y8P 888 888 888 ")); PrintAndLogEx(NORMAL, " " _CYAN_("888 888 \" 888 Y88b d88P") " " BANNERMSG1); - PrintAndLogEx(NORMAL, " " _CYAN_("888 888 888 \"Y8888P\"") " " BANNERMSG2); + PrintAndLogEx(NORMAL, " " _CYAN_("888 888 888 \"Y8888P\"")); + PrintAndLogEx(NORMAL, " " BANNERMSG2); break; } case ASCII: { @@ -98,11 +100,11 @@ static void showBanner_logo(LogoMode mode) { PrintAndLogEx(NORMAL, " 8888888P\" 888 Y888P 888 \"Y8b. "); PrintAndLogEx(NORMAL, " 888 888 Y8P 888 888 888 "); PrintAndLogEx(NORMAL, " 888 888 \" 888 Y88b d88P " BANNERMSG1); - PrintAndLogEx(NORMAL, " 888 888 888 \"Y8888P\" " BANNERMSG2); + PrintAndLogEx(NORMAL, " 888 888 888 \"Y8888P\""); + PrintAndLogEx(NORMAL, " " BANNERMSG2); break; } } - PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, BANNERMSG3); } From 7fa9f7bdfe9fad984e8d9caf87019dd527d47859 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 17 Jun 2025 18:26:23 +0200 Subject: [PATCH 011/149] change parameter, we like shorter parameter names remember... 'hf mfu aesauth --idx' --- client/src/cmdhfmfu.c | 4 ++-- doc/commands.json | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 6ee8e2fd2..c4990b6d9 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -3930,13 +3930,13 @@ static int CmdHF14AMfUAESAuth(const char *Cmd) { " Key index 1... UIDRetrKey\n" " Key index 2... OriginalityKey\n", "hf mfu aesauth\n" - "hf mfu aesauth --key <16 hex bytes> --index <0..2>" + "hf mfu aesauth --key <16 hex bytes> --idx <0..2>" ); void *argtable[] = { arg_param_begin, arg_str0(NULL, "key", "", "AES key (16 hex bytes)"), - arg_int0("i", "index", "<0..2>", "Key index, default: 0"), + arg_int0("i", "idx", "<0..2>", "Key index (def: 0)"), arg_lit0("k", NULL, "Keep field on (only if a key is provided)"), arg_param_end }; diff --git a/doc/commands.json b/doc/commands.json index b92990219..362333ef8 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -7088,13 +7088,13 @@ "description": "Tests AES key on Mifare Ultralight AES tags. If no key is specified, null key will be tried. Key index 0... DataProtKey (default) Key index 1... UIDRetrKey Key index 2... OriginalityKey", "notes": [ "hf mfu aesauth", - "hf mfu aesauth --key <16 hex bytes> --index <0..2>" + "hf mfu aesauth --key <16 hex bytes> --idx <0..2>" ], "offline": false, "options": [ "-h, --help This help", "--key AES key (16 hex bytes)", - "-i, --index <0..2> Key index, default: 0", + "-i, --idx <0..2> Key index (def: 0)", "-k Keep field on (only if a key is provided)" ], "usage": "hf mfu aesauth [-hk] [--key ] [-i <0..2>]" @@ -13365,6 +13365,6 @@ "metadata": { "commands_extracted": 768, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-06-15T10:52:29" + "extracted_on": "2025-06-17T16:11:53" } } From cc17db26bf98b51c394066ca2b3c9ac57e240b4a Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 17 Jun 2025 18:26:49 +0200 Subject: [PATCH 012/149] ndef key --- client/dictionaries/mfdes_default_keys.dic | 1 + 1 file changed, 1 insertion(+) diff --git a/client/dictionaries/mfdes_default_keys.dic b/client/dictionaries/mfdes_default_keys.dic index 782fc65bb..3b4e1dc28 100644 --- a/client/dictionaries/mfdes_default_keys.dic +++ b/client/dictionaries/mfdes_default_keys.dic @@ -9,6 +9,7 @@ d3f7d3f7d3f7d3f7 000000000000000000000000000000000000000000000000 #NXP Default 3K3DES 00112233445566778899AABBCCDDEEFF0102030405060708 ffffffffffffffffffffffffffffffffffffffffffffffff +d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7 425245414B4D454946594F5543414E21 # default UL-C key 00112233445566778899AABBCCDDEEFF #TI TRF7970A sloa213 79702553797025537970255379702553 #TI TRF7970A sloa213 From fe92f55653f740a8ea6a8bacc5ac8a0eb5a457cc Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 17 Jun 2025 19:54:58 +0200 Subject: [PATCH 013/149] ok, not build hitag2crack, only clean --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 713c21dc7..39b823b89 100644 --- a/Makefile +++ b/Makefile @@ -29,9 +29,10 @@ ifneq (,$(DESTDIR)) endif endif -all clean install uninstall check: %: client/% bootrom/% armsrc/% recovery/% mfc_card_only/% mfc_card_reader/% mfd_aes_brute/% fpga_compress/% cryptorf/% hitag2crack/% +all clean install uninstall check: %: client/% bootrom/% armsrc/% recovery/% mfc_card_only/% mfc_card_reader/% mfd_aes_brute/% fpga_compress/% cryptorf/% # hitag2crack toolsuite is not yet integrated in "all", it must be called explicitly: "make hitag2crack" #all clean install uninstall check: %: hitag2crack/% +clean: %: hitag2crack/% INSTALLTOOLS=mfc/pm3_eml2lower.sh mfc/pm3_eml2upper.sh mfc/pm3_mfdread.py mfc/pm3_mfd2eml.py mfc/pm3_eml2mfd.py pm3_amii_bin2eml.pl pm3_reblay-emulating.py pm3_reblay-reading.py INSTALLSIMFW=sim011.bin sim011.sha512.txt sim013.bin sim013.sha512.txt sim014.bin sim014.sha512.txt From 545e49201f7b98b0d07493d5d01eeea816b84a6e Mon Sep 17 00:00:00 2001 From: Lucifer Voeltner Date: Thu, 19 Jun 2025 12:37:24 +0700 Subject: [PATCH 014/149] Fix `hf mfu incr` due to modified functionality of identification functions --- client/src/cmdhfmfu.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index c4990b6d9..ccf0de708 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -5919,12 +5919,6 @@ static int CmdHF14AMfUIncr(const char *Cmd) { increment_cmd[i + 2] = (value >> (8 * i)) & 0xff; } - iso14a_card_select_t card; - if (ul_select(&card) == false) { - PrintAndLogEx(FAILED, "failed to select card, exiting..."); - return PM3_ESOFT; - } - uint64_t tagtype = GetHF14AMfU_Type(); uint64_t tags_with_counter_ul = MFU_TT_UL_EV1_48 | MFU_TT_UL_EV1_128 | MFU_TT_UL_EV1; uint64_t tags_with_counter_ntag = MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C | MFU_TT_NTAG_213_TT | MFU_TT_NTAG_215 | MFU_TT_NTAG_216; @@ -5950,6 +5944,12 @@ static int CmdHF14AMfUIncr(const char *Cmd) { } } + iso14a_card_select_t card; + if (ul_select(&card) == false) { + PrintAndLogEx(FAILED, "failed to select card, exiting..."); + return PM3_ESOFT; + } + uint8_t current_counter[3] = { 0, 0, 0 }; int len = ulev1_readCounter(counter, current_counter, sizeof(current_counter)); if (len != sizeof(current_counter)) { From 65607fc727ac3b1905df995ed5f864d273eda1df Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 19 Jun 2025 17:26:20 +0200 Subject: [PATCH 015/149] added Ultralight-C simulation. hf mfu sim -t 13. Use eload first. Also added support to upload UL-C dictionaries and UL-AES to spiffs memory. A lot of textual reworked across client. Unifiy texts and a bit more color ;) --- CHANGELOG.md | 3 + armsrc/Standalone/hf_aveful.c | 4 +- armsrc/Standalone/hf_cardhopper.c | 2 +- armsrc/Standalone/hf_colin.c | 9 +- armsrc/Standalone/hf_craftbyte.c | 12 +- armsrc/Standalone/hf_msdsal.c | 2 +- armsrc/Standalone/hf_reblay.c | 2 +- armsrc/Standalone/hf_tcprst.c | 4 +- armsrc/Standalone/hf_young.c | 16 +- armsrc/appmain.c | 31 +- armsrc/dbprint.c | 4 +- armsrc/iso14443a.c | 241 +++++++++--- armsrc/iso14443a.h | 16 +- armsrc/mifarecmd.c | 6 +- armsrc/mifaredesfire.c | 4 +- armsrc/sam_picopass.c | 28 +- .../deps/hardnested/hardnested_bruteforce.c | 14 +- client/pyscripts/fm11rf08s_recovery.py | 2 - client/src/cmdflashmem.c | 82 ++++- client/src/cmdflashmem.h | 4 +- client/src/cmdhf.c | 2 +- client/src/cmdhf14a.c | 15 +- client/src/cmdhffelica.c | 6 +- client/src/cmdhficlass.c | 4 +- client/src/cmdhfjooki.c | 2 +- client/src/cmdhflegic.c | 2 +- client/src/cmdhfmf.c | 344 ++++++++++-------- client/src/cmdhfmfhard.c | 42 ++- client/src/cmdhfmfp.c | 3 - client/src/cmdhfmfu.c | 26 +- client/src/cmdlf.c | 2 +- client/src/cmdlfem4x70.c | 2 +- client/src/cmdlfhitag.c | 2 +- client/src/cmdparser.c | 31 +- client/src/emv/emvcore.c | 2 +- client/src/fileutils.c | 76 ++++ client/src/fileutils.h | 11 + client/src/loclass/cipherutils.c | 47 ++- client/src/loclass/elite_crack.c | 90 +++-- client/src/loclass/ikeys.c | 33 +- client/src/mifare/mad.c | 3 +- client/src/mifare/mifarehost.c | 75 ++-- client/src/proxmark3.c | 87 +++-- client/src/util.c | 34 ++ client/src/util.h | 3 + common/crapto1/crypto1.c | 11 +- common_arm/ticks.c | 3 +- doc/commands.json | 33 +- include/ansi.h | 10 +- include/pm3_cmd.h | 6 + include/pmflash.h | 7 + tools/pm3_tests.sh | 4 +- 52 files changed, 1074 insertions(+), 430 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 101242adf..aef0c3883 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ 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] +- Changed `mem load` - now handles UL-C and UL-AES dictionary files (@iceman1001) +- Changed `hf mfu sim` - now support UL-C simulation (@iceman1001) +- Added `!` - run system commands from inside the client. Potentially dangerous if running client as SUDO, SU, ROOT (@iceman1001) ## [Daddy Iceman.4.20469][2025-06-16] - Fixed edge case in fm11rf08s key recovery tools (@doegox) diff --git a/armsrc/Standalone/hf_aveful.c b/armsrc/Standalone/hf_aveful.c index 626aa9d17..d634f819c 100644 --- a/armsrc/Standalone/hf_aveful.c +++ b/armsrc/Standalone/hf_aveful.c @@ -157,7 +157,7 @@ void RunMod(void) { if (button_pressed != BUTTON_NO_CLICK || data_available()) break; else if (state == STATE_SEARCH) { - if (!iso14443a_select_card(NULL, &card, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, &card, NULL, true, 0, true) == 0) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelay(500); @@ -246,7 +246,7 @@ void RunMod(void) { FLAG_SET_UID_IN_DATA(flags, 7); Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state."); - SimulateIso14443aTag(7, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(7, flags, card.uid, 0, NULL, 0, false, false); // Go back to search state if user presses pm3-button state = STATE_SEARCH; diff --git a/armsrc/Standalone/hf_cardhopper.c b/armsrc/Standalone/hf_cardhopper.c index 167e5b52f..d199dcdab 100644 --- a/armsrc/Standalone/hf_cardhopper.c +++ b/armsrc/Standalone/hf_cardhopper.c @@ -234,7 +234,7 @@ static void become_card(void) { tag_response_info_t *canned; uint32_t cuid; uint8_t pages; - if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &canned, &cuid, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &canned, &cuid, &pages, NULL) == false) { DbpString(_RED_("Error initializing the emulation process!")); return; } diff --git a/armsrc/Standalone/hf_colin.c b/armsrc/Standalone/hf_colin.c index 423e093f6..0aa49c092 100644 --- a/armsrc/Standalone/hf_colin.c +++ b/armsrc/Standalone/hf_colin.c @@ -498,7 +498,7 @@ failtag: SpinOff(50); LED_A_ON(); - while (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + while (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { WDT_HIT(); if (BUTTON_HELD(10) == BUTTON_HOLD) { WDT_HIT(); @@ -785,7 +785,7 @@ static int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype) { bool isOK = true; - if (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + if (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { isOK = false; } @@ -844,8 +844,7 @@ static int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTr for (uint8_t i = 0; i < keyCount; i++) { /* no need for anticollision. just verify tag is still here */ - // if (!iso14443a_fast_select_card(colin_cjuid, 0)) { - if (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + if (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { cjSetCursLeft(); DbprintfEx(FLAG_NEWLINE, "%sFATAL%s : E_MF_LOSTTAG", _XRED_, _XWHITE_); break; @@ -963,7 +962,7 @@ static int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, const // get UID from chip if (workFlags & 0x01) { - if (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + if (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { DbprintfEx(FLAG_NEWLINE, "Can't select card"); break; }; diff --git a/armsrc/Standalone/hf_craftbyte.c b/armsrc/Standalone/hf_craftbyte.c index 6eb2ae2a2..736e89aca 100644 --- a/armsrc/Standalone/hf_craftbyte.c +++ b/armsrc/Standalone/hf_craftbyte.c @@ -89,22 +89,22 @@ void RunMod(void) { Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state."); if (card.sak == 0x08 && card.atqa[0] == 0x04 && card.atqa[1] == 0) { DbpString("Mifare Classic 1k"); - SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x08 && card.atqa[0] == 0x44 && card.atqa[1] == 0) { DbpString("Mifare Classic 4k "); - SimulateIso14443aTag(8, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(8, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x00 && card.atqa[0] == 0x44 && card.atqa[1] == 0) { DbpString("Mifare Ultralight"); - SimulateIso14443aTag(2, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(2, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x20 && card.atqa[0] == 0x04 && card.atqa[1] == 0x03) { DbpString("Mifare DESFire"); - SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x20 && card.atqa[0] == 0x44 && card.atqa[1] == 0x03) { DbpString("Mifare DESFire Ev1/Plus/JCOP"); - SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0, false, false); } else { Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); - SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0, false, false); } // Go back to search state if user presses pm3-button diff --git a/armsrc/Standalone/hf_msdsal.c b/armsrc/Standalone/hf_msdsal.c index 5e36a92c5..6eb5b46b2 100644 --- a/armsrc/Standalone/hf_msdsal.c +++ b/armsrc/Standalone/hf_msdsal.c @@ -379,7 +379,7 @@ void RunMod(void) { BigBuf_free_keep_EM(); // tag type: 11 = ISO/IEC 14443-4 - javacard (JCOP) - if (SimulateIso14443aInit(11, flags, data, NULL, 0, &responses, &cuid, NULL) == false) { + if (SimulateIso14443aInit(11, flags, data, NULL, 0, &responses, &cuid, NULL, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_RED_("Error initializing the emulation process!")); diff --git a/armsrc/Standalone/hf_reblay.c b/armsrc/Standalone/hf_reblay.c index 8ecba8cf4..30db64f41 100644 --- a/armsrc/Standalone/hf_reblay.c +++ b/armsrc/Standalone/hf_reblay.c @@ -268,7 +268,7 @@ void RunMod() { BigBuf_free_keep_EM(); // 4 = ISO/IEC 14443-4 - javacard (JCOP) - if (SimulateIso14443aInit(4, flags, data, NULL, 0, &responses, &cuid, NULL) == false) { + if (SimulateIso14443aInit(4, flags, data, NULL, 0, &responses, &cuid, NULL, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_RED_("Error initializing the emulation process!")); diff --git a/armsrc/Standalone/hf_tcprst.c b/armsrc/Standalone/hf_tcprst.c index c2b3ff51d..d8ced29d4 100644 --- a/armsrc/Standalone/hf_tcprst.c +++ b/armsrc/Standalone/hf_tcprst.c @@ -191,7 +191,7 @@ void RunMod(void) { memcpy(data, stuid, sizeof(stuid)); - if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, &pages, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_YELLOW_("!!") "Error initializing the simulation process!"); @@ -369,7 +369,7 @@ void RunMod(void) { memcpy(data, stuid, sizeof(stuid)); - if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, &pages, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_YELLOW_("!!") "Error initializing the simulation process!"); diff --git a/armsrc/Standalone/hf_young.c b/armsrc/Standalone/hf_young.c index 62d215dba..83ad1999a 100644 --- a/armsrc/Standalone/hf_young.c +++ b/armsrc/Standalone/hf_young.c @@ -96,7 +96,7 @@ void RunMod(void) { } } - if (!iso14443a_select_card(NULL, &card[selected], NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, &card[selected], NULL, true, 0, true) == 0) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelay(500); @@ -253,25 +253,25 @@ void RunMod(void) { if (uids[selected].sak == 0x08 && uids[selected].atqa[0] == 0x04 && uids[selected].atqa[1] == 0) { DbpString("Mifare Classic 1k"); - SimulateIso14443aTag(1, flags, data, 0, NULL, 0); + SimulateIso14443aTag(1, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x18 && uids[selected].atqa[0] == 0x02 && uids[selected].atqa[1] == 0) { DbpString("Mifare Classic 4k (4b uid)"); - SimulateIso14443aTag(8, flags, data, 0, NULL, 0); + SimulateIso14443aTag(8, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x08 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0) { DbpString("Mifare Classic 4k (7b uid)"); - SimulateIso14443aTag(8, flags, data, 0, NULL, 0); + SimulateIso14443aTag(8, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x00 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0) { DbpString("Mifare Ultralight"); - SimulateIso14443aTag(2, flags, data, 0, NULL, 0); + SimulateIso14443aTag(2, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x20 && uids[selected].atqa[0] == 0x04 && uids[selected].atqa[1] == 0x03) { DbpString("Mifare DESFire"); - SimulateIso14443aTag(3, flags, data, 0, NULL, 0); + SimulateIso14443aTag(3, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x20 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0x03) { DbpString("Mifare DESFire Ev1/Plus/JCOP"); - SimulateIso14443aTag(3, flags, data, 0, NULL, 0); + SimulateIso14443aTag(3, flags, data, 0, NULL, 0, false, false); } else { Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); - SimulateIso14443aTag(1, flags, data, 0, NULL, 0); + SimulateIso14443aTag(1, flags, data, 0, NULL, 0, false, false); } } else if (button_pressed == BUTTON_SINGLE_CLICK) { diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 9399592c9..db16394ab 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -480,6 +480,32 @@ static void SendStatus(uint32_t wait) { } else { Dbprintf(" iClass... "_RED_("%u")" keys - "_RED_("%s"), num, ICLASS_KEYS_FILE); } + + if (exists_in_spiffs(MFULC_KEYS_FILE)) { + num = size_in_spiffs(MFULC_KEYS_FILE) / MFULC_KEY_LENGTH; + } else { + num = 0; + } + + if (num > 0) { + Dbprintf(" UL-C..... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, MFULC_KEYS_FILE); + } else { + Dbprintf(" UL-C..... "_RED_("%u")" keys - "_RED_("%s"), num, MFULC_KEYS_FILE); + } + + if (exists_in_spiffs(MFULAES_KEYS_FILE)) { + num = size_in_spiffs(MFULAES_KEYS_FILE) / MFULAES_KEY_LENGTH; + } else { + num = 0; + } + + if (num > 0) { + Dbprintf(" UL-AES... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, MFULAES_KEYS_FILE); + } else { + Dbprintf(" UL-AES... "_RED_("%u")" keys - "_RED_("%s"), num, MFULAES_KEYS_FILE); + } + + #endif DbpString(""); reply_ng(CMD_STATUS, PM3_SUCCESS, NULL, 0); @@ -1723,10 +1749,13 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t uid[10]; uint8_t exitAfter; uint8_t rats[20]; + bool ulc_p1; + bool ulc_p2; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; SimulateIso14443aTag(payload->tagtype, payload->flags, payload->uid, - payload->exitAfter, payload->rats, sizeof(payload->rats)); // ## Simulate iso14443a tag - pass tag type & UID + payload->exitAfter, payload->rats, sizeof(payload->rats), + payload->ulc_p1, payload->ulc_p2); // ## Simulate iso14443a tag - pass tag type & UID break; } case CMD_HF_ISO14443A_SIM_AID: { diff --git a/armsrc/dbprint.c b/armsrc/dbprint.c index 42d96fc3e..687bdfb90 100644 --- a/armsrc/dbprint.c +++ b/armsrc/dbprint.c @@ -102,9 +102,7 @@ void Dbhexdump(int len, const uint8_t *d, bool bAsci) { } #endif } -void print_result(const char *name, const uint8_t *d, size_t - - n) { +void print_result(const char *name, const uint8_t *d, size_t n) { const uint8_t *p = d; uint16_t tmp = n & 0xFFF0; diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 069aa87c8..4bab0a0b4 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -370,16 +370,16 @@ tUart14a *GetUart14a(void) { void Uart14aReset(void) { Uart.state = STATE_14A_UNSYNCD; + Uart.shiftReg = 0; // shiftreg to hold decoded data bits Uart.bitCount = 0; Uart.len = 0; // number of decoded data bytes - Uart.parityLen = 0; // number of decoded parity bytes - Uart.shiftReg = 0; // shiftreg to hold decoded data bits - Uart.parityBits = 0; // holds 8 parity bits - Uart.startTime = 0; - Uart.endTime = 0; - Uart.fourBits = 0x00000000; // clear the buffer for 4 Bits Uart.posCnt = 0; Uart.syncBit = 9999; + Uart.parityBits = 0; // holds 8 parity bits + Uart.parityLen = 0; // number of decoded parity bytes + Uart.fourBits = 0x00000000; // clear the buffer for 4 Bits + Uart.startTime = 0; + Uart.endTime = 0; } void Uart14aInit(uint8_t *d, uint16_t n, uint8_t *par) { @@ -1188,9 +1188,24 @@ bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_ } } +static void Simulate_reread_ulc_key(uint8_t *ulc_key) { + // copy UL-C key from emulator memory + + mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr(); + + memcpy(ulc_key, mfu_header->data + (0x2D * 4), 4); + memcpy(ulc_key + 4, mfu_header->data + (0x2C * 4), 4); + memcpy(ulc_key + 8, mfu_header->data + (0x2F * 4), 4); + memcpy(ulc_key + 12, mfu_header->data + (0x2E * 4), 4); + + reverse_array(ulc_key, 4); + reverse_array(ulc_key + 4, 4); + reverse_array(ulc_key + 8, 4); + reverse_array(ulc_key + 12, 4); +} bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t *ats, size_t ats_len, tag_response_info_t **responses, - uint32_t *cuid, uint8_t *pages) { + uint32_t *cuid, uint8_t *pages, uint8_t *ulc_key) { uint8_t sak = 0; // The first response contains the ATQA (note: bytes are transmitted in reverse order). static uint8_t rATQA[2] = { 0x00 }; @@ -1340,6 +1355,38 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, sak = 0x20; break; } + case 13: { // MIFARE Ultralight-C + + rATQA[0] = 0x44; + sak = 0x00; + + // some first pages of UL/NTAG dump is special data + mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr(); + *pages = MAX(mfu_header->pages, 47); + + // copy UL-C key from emulator memory + memcpy(ulc_key, mfu_header->data + (0x2D * 4), 4); + memcpy(ulc_key + 4, mfu_header->data + (0x2C * 4), 4); + memcpy(ulc_key + 8, mfu_header->data + (0x2F * 4), 4); + memcpy(ulc_key + 12, mfu_header->data + (0x2E * 4), 4); + + reverse_array(ulc_key, 4); + reverse_array(ulc_key + 4, 4); + reverse_array(ulc_key + 8, 4); + reverse_array(ulc_key + 12, 4); + + /* + Dbprintf("UL-C Pages....... %u ( 47 )", *pages); + DbpString("UL-C 3des key... "); + Dbhexdump(16, ulc_key, false); + */ + + if (IS_FLAG_UID_IN_DATA(flags, 7)) { + DbpString("UL-C UID........ "); + Dbhexdump(7, data, false); + } + break; + } default: { if (g_dbglevel >= DBG_ERROR) Dbprintf("Error: unknown tagtype (%d)", tagType); return false; @@ -1365,7 +1412,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, // if uid not supplied then get from emulator memory if ((memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 10) == 0) || IS_FLAG_UID_IN_EMUL(flags)) { - if (tagType == 2 || tagType == 7) { + if (tagType == 2 || tagType == 7 || tagType == 13) { uint16_t start = MFU_DUMP_PREFIX_LENGTH; uint8_t emdata[8]; emlGet(emdata, start, sizeof(emdata)); @@ -1532,13 +1579,18 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, // 'hf 14a sim' //----------------------------------------------------------------------------- void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads, - uint8_t *ats, size_t ats_len) { + uint8_t *ats, size_t ats_len, bool ulc_part1, bool ulc_part2) { #define ATTACK_KEY_COUNT 16 +#define ULC_TAG_NONCE "\x01\x02\x03\x04\x05\x06\x07\x08" tag_response_info_t *responses; uint32_t cuid = 0; uint32_t nonce = 0; + /// Ultralight-C 3des2k + uint8_t ulc_key[16] = { 0x00 }; + uint8_t ulc_iv[8] = { 0x00 }; + bool ulc_reread_key = false; uint8_t pages = 0; // Here, we collect CUID, block1, keytype1, NT1, NR1, AR1, CUID, block2, keytyp2, NT2, NR2, AR2 @@ -1582,7 +1634,9 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin .modulation_n = 0 }; - if (SimulateIso14443aInit(tagType, flags, useruid, ats, ats_len, &responses, &cuid, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, useruid, ats, ats_len + , &responses, &cuid, &pages + , ulc_key) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); return; @@ -1670,7 +1724,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin order = ORDER_NONE; // back to work state p_response = NULL; - } else if (order == ORDER_AUTH && len == 8) { + } else if (order == ORDER_AUTH && len == 8 && tagType != 2 && tagType != 7 && tagType != 13) { // Received {nr] and {ar} (part of authentication) LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); uint32_t nr = bytes_to_num(receivedCmd, 4); @@ -1760,7 +1814,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin } else if (receivedCmd[0] == ISO14443A_CMD_READBLOCK && len == 4) { // Received a (plain) READ uint8_t block = receivedCmd[1]; // if Ultralight or NTAG (4 byte blocks) - if (tagType == 7 || tagType == 2) { + if (tagType == 7 || tagType == 2 || tagType == 13) { if (block > pages) { // send NACK 0x0 == invalid argument EmSend4bit(CARD_NACK_IV); @@ -1809,7 +1863,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin EmSendCmd(emdata, len + 2); } p_response = NULL; - } else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7)) { // Received a WRITE + } else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7 || tagType == 13)) { // Received a WRITE p_response = NULL; @@ -1847,12 +1901,15 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin // send ACK EmSend4bit(CARD_ACK); + if (tagType == 13 && block >= 0x2c && block <= 0x2F) { + ulc_reread_key = true; + } } else { // send NACK 0x1 == crc/parity error EmSend4bit(CARD_NACK_PA); } goto jump; - } else if (receivedCmd[0] == MIFARE_ULC_COMP_WRITE && len == 4 && (tagType == 2 || tagType == 7)) { + } else if (receivedCmd[0] == MIFARE_ULC_COMP_WRITE && len == 4 && (tagType == 2 || tagType == 7 || tagType == 13)) { // cmd + block + 2 bytes crc if (CheckCrc14A(receivedCmd, len)) { wrblock = receivedCmd[1]; @@ -1926,7 +1983,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin p_response = &responses[RESP_INDEX_VERSION]; } else if (receivedCmd[0] == MFDES_GET_VERSION && len == 4 && (tagType == 3)) { p_response = &responses[RESP_INDEX_VERSION]; - } else if ((receivedCmd[0] == MIFARE_AUTH_KEYA || receivedCmd[0] == MIFARE_AUTH_KEYB) && len == 4 && tagType != 2 && tagType != 7) { // Received an authentication request + } else if ((receivedCmd[0] == MIFARE_AUTH_KEYA || receivedCmd[0] == MIFARE_AUTH_KEYB) && len == 4 && tagType != 2 && tagType != 7 && tagType != 13) { // Received an authentication request cardAUTHKEY = receivedCmd[0] - 0x60; cardAUTHSC = receivedCmd[1] / 4; // received block num @@ -1945,9 +2002,84 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin } else { p_response = &responses[RESP_INDEX_ATS]; } - } else if (receivedCmd[0] == MIFARE_ULC_AUTH_1) { // ULC authentication, or Desfire Authentication - LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - p_response = NULL; + } else if (receivedCmd[0] == MIFARE_ULC_AUTH_1 && len == 4 && tagType == 13) { // ULC authentication, or Desfire Authentication + + // reset IV to all zeros + memset(ulc_iv, 0x00, 8); + + if (ulc_reread_key) { + Simulate_reread_ulc_key(ulc_key); + ulc_reread_key = false; + } + + dynamic_response_info.response[0] = MIFARE_ULC_AUTH_2; + + // our very random TAG NONCE + memcpy(dynamic_response_info.response + 1, ULC_TAG_NONCE, 8); + + if (ulc_part1) { + memset(dynamic_response_info.response + 1, 0, 8); + } else { + // encrypt TAG NONCE + tdes_nxp_send(dynamic_response_info.response + 1, dynamic_response_info.response + 1, 8, ulc_key, ulc_iv, 2); + } + + // Add CRC + AddCrc14A(dynamic_response_info.response, 9); + + // prepare to send + dynamic_response_info.response_n = 1 + 8 + 2; + prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); + p_response = &dynamic_response_info; + order = ORDER_AUTH; + + } else if (receivedCmd[0] == MIFARE_ULC_AUTH_2 && len == 19 && tagType == 13) { // ULC authentication, or Desfire Authentication + + uint8_t enc_rnd_ab[16] = { 0x00 }; + uint8_t rnd_ab[16] = { 0x00 }; + + // copy reader response + memcpy(enc_rnd_ab, receivedCmd + 1, 16); + + // decrypt + tdes_nxp_receive(enc_rnd_ab, rnd_ab, 16, ulc_key, ulc_iv, 2); + + ror(rnd_ab + 8, 8); + + if (memcmp(rnd_ab + 8, ULC_TAG_NONCE, 8) != 0) { + Dbprintf("failed authentication"); + } + + // OK response + dynamic_response_info.response[0] = 0x00; + + if (ulc_part2) { + // try empty auth but with correct CRC and 0x00 command + memset(dynamic_response_info.response + 1, 0, 8); + } else { + // rol RndA + rol(rnd_ab, 8); + + // encrypt RndA + tdes_nxp_send(rnd_ab, dynamic_response_info.response + 1, 8, ulc_key, ulc_iv, 2); + } + + // Add CRC + AddCrc14A(dynamic_response_info.response, 9); + + dynamic_response_info.response_n = 1 + 8 + 2; + + prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); + p_response = &dynamic_response_info; + order = ORDER_NONE; + // Add CRC + AddCrc14A(dynamic_response_info.response, 17); + + dynamic_response_info.response_n = 1 + 16 + 2; + + prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); + p_response = &dynamic_response_info; + order = ORDER_NONE; } else if (receivedCmd[0] == MIFARE_ULEV1_AUTH && len == 7 && tagType == 7) { // NTAG / EV-1 uint8_t pwd[4] = {0, 0, 0, 0}; emlGet(pwd, (pages - 1) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(pwd)); @@ -2128,13 +2260,16 @@ jump: // of bits specified in the delay parameter. static void PrepareDelayedTransfer(uint16_t delay) { delay &= 0x07; - if (!delay) return; + if (delay == 0) { + return; + } uint8_t bitmask = 0; uint8_t bits_shifted = 0; - for (uint16_t i = 0; i < delay; i++) + for (uint16_t i = 0; i < delay; i++) { bitmask |= (0x01 << i); + } tosend_t *ts = get_tosend(); @@ -2163,6 +2298,7 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing Dbprintf("Warning: HF field is off"); return; } + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); if (timing) { @@ -2337,7 +2473,7 @@ int EmGetCmd(uint8_t *received, uint16_t received_max_len, uint16_t *len, uint8_ // button press, takes a bit time, might mess with simualtion if (checker-- == 0) { if (BUTTON_PRESS()) { - Dbprintf("----------- " _GREEN_("Breaking / User aborted") " ----------"); + Dbprintf("----------- " _GREEN_("Button pressed, user aborted") " ----------"); return false; } @@ -2515,9 +2651,9 @@ int EmSendPrecompiledCmd(tag_response_info_t *p_response) { return ret; } -bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, - uint32_t reader_EndTime, uint8_t *reader_Parity, uint8_t *tag_data, - uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, uint8_t *tag_Parity) { +bool EmLogTrace(const uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, + uint32_t reader_EndTime, const uint8_t *reader_Parity, const uint8_t *tag_data, + uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, const uint8_t *tag_Parity) { // we cannot exactly measure the end and start of a received command from reader. However we know that the delay from // end of the received command to start of the tag's (simulated by us) answer is n*128+20 or n*128+84 resp. @@ -2851,7 +2987,7 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 // requests ATS unless no_rats is true int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats, - iso14a_polling_parameters_t *polling_parameters) { + const iso14a_polling_parameters_t *polling_parameters) { uint8_t resp[MAX_FRAME_SIZE] = {0}; // theoretically. A usual RATS will be much smaller @@ -3106,7 +3242,7 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint return 1; } -int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades) { +int iso14443a_fast_select_card(const uint8_t *uid_ptr, uint8_t num_cascades) { uint8_t resp[3] = { 0 }; // theoretically. max 1 Byte SAK, 2 Byte CRC, 3 bytes is enough uint8_t resp_par[1] = {0}; @@ -3524,17 +3660,23 @@ OUT: // Therefore try in alternating directions. static int32_t dist_nt(uint32_t nt1, uint32_t nt2) { - if (nt1 == nt2) return 0; + if (nt1 == nt2) { + return 0; + } uint32_t nttmp1 = nt1; uint32_t nttmp2 = nt2; for (uint16_t i = 1; i < 32768; i++) { nttmp1 = prng_successor(nttmp1, 1); - if (nttmp1 == nt2) return i; + if (nttmp1 == nt2) { + return i; + } nttmp2 = prng_successor(nttmp2, 1); - if (nttmp2 == nt1) return -i; + if (nttmp2 == nt1) { + return -i; + } } return (-99999); // either nt1 or nt2 are invalid nonces @@ -3542,8 +3684,8 @@ static int32_t dist_nt(uint32_t nt1, uint32_t nt2) { #define PRNG_SEQUENCE_LENGTH (1 << 16) -#define MAX_UNEXPECTED_RANDOM 4 // maximum number of unexpected (i.e. real) random numbers when trying to sync. Then give up. -#define MAX_SYNC_TRIES 32 +#define MAX_UNEXPECTED_RANDOM (4) // maximum number of unexpected (i.e. real) random numbers when trying to sync. Then give up. +#define MAX_SYNC_TRIES (32) //----------------------------------------------------------------------------- // Recover several bits of the cypher stream. This implements (first stages of) @@ -3697,9 +3839,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { // we didn't calibrate our clock yet, // iceman: has to be calibrated every time. - if (previous_nt && !nt_attacked) { + if (previous_nt && (nt_attacked == 0)) { - int nt_distance = dist_nt(previous_nt, nt); + int32_t nt_distance = dist_nt(previous_nt, nt); // if no distance between, then we are in sync. if (nt_distance == 0) { @@ -3725,7 +3867,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { sync_cycles = (sync_cycles - nt_distance) / elapsed_prng_sequences; // no negative sync_cycles, and too small sync_cycles will result in continuous misses - if (sync_cycles <= 10) sync_cycles += PRNG_SEQUENCE_LENGTH; + if (sync_cycles <= 10) { + sync_cycles += PRNG_SEQUENCE_LENGTH; + } // reset sync_cycles if (sync_cycles > PRNG_SEQUENCE_LENGTH * 2) { @@ -3733,8 +3877,14 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { sync_time = GetCountSspClk() & 0xfffffff8; } - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("calibrating in cycle %d. nt_distance=%d, elapsed_prng_sequences=%d, new sync_cycles: %d\n", i, nt_distance, elapsed_prng_sequences, sync_cycles); + if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("calibrating in cycle %d. nt_distance=%d, elapsed_prng_sequences=%d, new sync_cycles: %d\n" + , i + , nt_distance + , elapsed_prng_sequences + , sync_cycles + ); + } continue; } @@ -3764,8 +3914,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { } else { sync_cycles += catch_up_cycles; - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("Lost sync in cycle %d for the fourth time consecutively (nt_distance = %d). Adjusting sync_cycles to %d.\n", i, catch_up_cycles, sync_cycles); + } last_catch_up = 0; catch_up_cycles = 0; @@ -3778,8 +3929,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { if (received_nack) { catch_up_cycles = 8; // the PRNG is delayed by 8 cycles due to the NAC (4Bits = 0x05 encrypted) transfer - if (nt_diff == 0) + if (nt_diff == 0) { par_low = par[0] & 0xE0; // there is no need to check all parities for other nt_diff. Parity Bits for mf_nr_ar[0..2] won't change + } par_list[nt_diff] = reflect8(par[0]); ks_list[nt_diff] = receivedAnswer[0] ^ 0x05; // xor with NACK value to get keystream @@ -3798,12 +3950,15 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { } else { // No NACK. if (nt_diff == 0 && first_try) { + par[0]++; + if (par[0] == 0) { // tried all 256 possible parities without success. Card doesn't send NACK. isOK = 2; return_status = PM3_ESOFT; break; } + } else { // Why this? par[0] = ((par[0] & 0x1F) + 1) | par_low; @@ -3854,7 +4009,7 @@ void DetectNACKbug(void) { uint8_t uid[10] = { 0x00 }; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = { 0x00 }; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = { 0x00 }; - uint8_t par[1] = {0x00 }; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough + uint8_t par[2] = {0x00 }; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough uint32_t nt = 0, previous_nt = 0, nt_attacked = 0, cuid = 0; int32_t catch_up_cycles = 0, last_catch_up = 0; @@ -3905,9 +4060,9 @@ void DetectNACKbug(void) { ++checkbtn_cnt; // this part is from Piwi's faster nonce collecting part in Hardnested. - if (!have_uid) { // need a full select cycle to get the uid first + if (have_uid == false) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; - if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { + if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) { if (g_dbglevel >= DBG_INFO) Dbprintf("Mifare: Can't select card (ALL)"); i = 0; continue; @@ -3929,7 +4084,7 @@ void DetectNACKbug(void) { } have_uid = true; } else { // no need for anticollision. We can directly select the card - if (!iso14443a_fast_select_card(uid, cascade_levels)) { + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { if (g_dbglevel >= DBG_INFO) Dbprintf("Mifare: Can't select card (UID)"); i = 0; have_uid = false; @@ -4143,7 +4298,7 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, .modulation_n = 0 }; - if (SimulateIso14443aInit(tagType, flags, uid, ats, ats_len, &responses, &cuid, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, uid, ats, ats_len, &responses, &cuid, &pages, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); return; diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 420d6e0c0..d3a73bd6a 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -143,7 +143,7 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t void RAMFUNC SniffIso14443a(uint8_t param); void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads, - uint8_t *ats, size_t ats_len); + uint8_t *ats, size_t ats_len, bool ulc_part1, bool ulc_part2); void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, uint8_t *ats, size_t ats_len, uint8_t *aid, size_t aid_len, @@ -152,7 +152,8 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t *ats, size_t ats_len, tag_response_info_t **responses, - uint32_t *cuid, uint8_t *pages); + uint32_t *cuid, uint8_t *pages, + uint8_t *ulc_key); bool GetIso14443aCommandFromReader(uint8_t *received, uint16_t received_maxlen, uint8_t *par, int *len); void iso14443a_antifuzz(uint32_t flags); @@ -165,8 +166,10 @@ uint16_t ReaderReceive(uint8_t *receivedAnswer, uint16_t answer_maxlen, uint8_t void iso14443a_setup(uint8_t fpga_minor_mode); int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, uint16_t data_len, uint8_t *res); int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); -int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats, iso14a_polling_parameters_t *polling_parameters); -int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades); +int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, + bool anticollision, uint8_t num_cascades, bool no_rats, + const iso14a_polling_parameters_t *polling_parameters); +int iso14443a_fast_select_card(const uint8_t *uid_ptr, uint8_t num_cascades); void iso14a_set_trigger(bool enable); int EmSendCmd14443aRaw(const uint8_t *resp, uint16_t respLen); @@ -181,8 +184,9 @@ int EmSendPrecompiledCmd(tag_response_info_t *p_response); bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_t **buffer, size_t *max_buffer_size); bool prepare_tag_modulation(tag_response_info_t *response_info, size_t max_buffer_size); -bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, uint32_t reader_EndTime, uint8_t *reader_Parity, - uint8_t *tag_data, uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, uint8_t *tag_Parity); +bool EmLogTrace(const uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, + uint32_t reader_EndTime, const uint8_t *reader_Parity, const uint8_t *tag_data, + uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, const uint8_t *tag_Parity); void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype); void DetectNACKbug(void); diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index a84ad0096..4386f5003 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -344,7 +344,7 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { uint8_t key[16] = {0x00}; memcpy(key, datain, sizeof(key)); - if (!mifare_ultra_auth(key)) { + if (mifare_ultra_auth(key) == 0) { OnError(1); return; } @@ -1947,7 +1947,7 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da // Now append the SPI flash dictionnary if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, dictkeys + (keyCount * MF_KEY_LENGTH), (key_mem_available - keyCount) * MF_KEY_LENGTH, RDV40_SPIFFS_SAFETY_SAFE)) { if (g_dbglevel >= DBG_ERROR) { - Dbprintf("loaded " _GREEN_("%u") " keys from spiffs file `" _YELLOW_("%s") "`", key_mem_available, MF_KEYS_FILE); + Dbprintf("loaded " _GREEN_("%u") " keys from spiffs file `" _YELLOW_("%s") "`", key_mem_available - keyCount, MF_KEYS_FILE); } } else { Dbprintf("Spiffs file `" _RED_("%s") "` cannot be read", MF_KEYS_FILE); @@ -3561,7 +3561,7 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) { AddCrc14A(cmd, sizeof(block_cmd) + MIFARE_BLOCK_SIZE); if (doReselect) { - if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) { retval = PM3_ESOFT; goto OUT; } diff --git a/armsrc/mifaredesfire.c b/armsrc/mifaredesfire.c index 448f475dd..cc2996b23 100644 --- a/armsrc/mifaredesfire.c +++ b/armsrc/mifaredesfire.c @@ -60,7 +60,7 @@ bool InitDesfireCard(void) { iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); set_tracing(true); - if (!iso14443a_select_card(NULL, &card, NULL, true, 0, false)) { + if (iso14443a_select_card(NULL, &card, NULL, true, 0, false) == 0) { if (g_dbglevel >= DBG_ERROR) DbpString("Can't select card"); OnError(1); return false; @@ -157,7 +157,7 @@ void MifareDesfireGetInformation(void) { pcb_blocknum = 0; // card select - information - if (!iso14443a_select_card(NULL, &card, NULL, true, 0, false)) { + if (iso14443a_select_card(NULL, &card, NULL, true, 0, false) == 0) { if (g_dbglevel >= DBG_ERROR) { DbpString("Can't select card"); } diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c index fe9393283..fa040288f 100644 --- a/armsrc/sam_picopass.c +++ b/armsrc/sam_picopass.c @@ -103,10 +103,13 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf); - bool is_cmd_check = (nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_CHECK; + bool is_cmd_check = ((nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_CHECK); + if (is_cmd_check && break_on_nr_mac) { + memcpy(response, nfc_tx_buf, nfc_tx_len); *response_len = nfc_tx_len; + if (g_dbglevel >= DBG_INFO) { DbpString("NR-MAC: "); Dbhexdump((*response_len) - 1, response + 1, false); @@ -115,7 +118,8 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re goto out; } - bool is_cmd_update = (nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_UPDATE; + bool is_cmd_update = ((nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_UPDATE); + if (is_cmd_update && prevent_epurse_update && nfc_tx_buf[0] == 0x87 && nfc_tx_buf[1] == 0x02) { // block update(2) command and fake the response to prevent update of epurse @@ -223,13 +227,13 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re // 07 // 90 00 if (request_len == 0) { - if ( - !(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0x8a && sam_rx_buf[5 + 4] == 0x03) - && - !(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0xb3 && sam_rx_buf[5 + 4] == 0xa0) - ) { - if (g_dbglevel >= DBG_ERROR) + + if (!(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0x8a && sam_rx_buf[5 + 4] == 0x03) && + !(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0xb3 && sam_rx_buf[5 + 4] == 0xa0)) { + + if (g_dbglevel >= DBG_ERROR) { Dbprintf("No PACS data in SAM response"); + } res = PM3_ESOFT; } } @@ -361,14 +365,14 @@ int sam_picopass_get_pacs(PacketCommandNG *c) { goto out; } - if (!skipDetect) { + if (skipDetect == false) { // step 2: get card information picopass_hdr_t card_a_info; uint32_t eof_time = 0; // implicit StartSspClk() happens here Iso15693InitReader(); - if (!select_iclass_tag(&card_a_info, false, &eof_time, shallow_mod)) { + if (select_iclass_tag(&card_a_info, false, &eof_time, shallow_mod) == false) { goto err; } @@ -383,8 +387,10 @@ int sam_picopass_get_pacs(PacketCommandNG *c) { if (res != PM3_SUCCESS) { goto err; } - if (g_dbglevel >= DBG_INFO) + + if (g_dbglevel >= DBG_INFO) { print_result("Response data", sam_response, sam_response_len); + } goto out; diff --git a/client/deps/hardnested/hardnested_bruteforce.c b/client/deps/hardnested/hardnested_bruteforce.c index 655ef9dbb..c5d6aef43 100644 --- a/client/deps/hardnested/hardnested_bruteforce.c +++ b/client/deps/hardnested/hardnested_bruteforce.c @@ -177,14 +177,15 @@ crack_states_thread(void *x) { char progress_text[80]; char keystr[19]; - snprintf(keystr, sizeof(keystr), "%012" PRIX64 " ", key); + snprintf(keystr, sizeof(keystr), "%012" PRIX64, key); snprintf(progress_text, sizeof(progress_text), "Brute force phase completed. Key found: " _GREEN_("%s"), keystr); hardnested_print_progress(thread_arg->num_acquired_nonces, progress_text, 0.0, 0); + PrintAndLogEx(INFO, "---------+---------+---------------------------------------------------------+-----------------+-------"); break; } else if (keys_found) { break; } else { - if (!thread_arg->silent) { + if (thread_arg->silent == false) { char progress_text[80]; snprintf(progress_text, sizeof(progress_text), "Brute force phase: %6.02f%% ", 100.0 * (float)num_keys_tested / (float)(thread_arg->maximum_states)); float remaining_bruteforce = thread_arg->nonces[thread_arg->best_first_bytes[0]].expected_num_brute_force - (float)num_keys_tested / 2; @@ -337,7 +338,7 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint bucket_count = 0; for (statelist_t *p = candidates; p != NULL; p = p->next) { if (p->states[ODD_STATE] != NULL && p->states[EVEN_STATE] != NULL) { - if (!ensure_buckets_alloc(bucket_count + 1)) { + if (ensure_buckets_alloc(bucket_count + 1) == false) { PrintAndLogEx(ERR, "Can't allocate buckets, abort!"); return false; } @@ -375,6 +376,7 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint thread_args[i].best_first_bytes = best_first_bytes; pthread_create(&threads[i], NULL, crack_states_thread, (void *)&thread_args[i]); } + for (uint32_t i = 0; i < num_brute_force_threads; i++) { pthread_join(threads[i], 0); } @@ -385,11 +387,13 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint uint64_t elapsed_time = msclock() - start_time; - if (bf_rate != NULL) + if (bf_rate != NULL) { *bf_rate = (float)num_keys_tested / ((float)elapsed_time / 1000.0); + } - if (keys_found > 0) + if (keys_found > 0) { *found_key = found_bs_key; + } return (keys_found != 0); } diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index cbb7246d5..5e13b461c 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -584,8 +584,6 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, if "Found keys have been dumped to" in line: keyfile = line[line.index("`"):].strip("`") else: - show() - show(color("found keys:", fg="green"), prompt=plus) show(prompt=plus) show("-----+-----+--------------+---+--------------+----", prompt=plus) show(" Sec | Blk | key A |res| key B |res", prompt=plus) diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index fa88549f7..607a36b31 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -192,21 +192,24 @@ static int CmdFlashMemLoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "mem load", "Loads binary file into flash memory on device\n" - "Warning: mem area to be written must have been wiped first\n" - "( dictionaries are serviced as files in spiffs so no wipe is needed )", - "mem load -f myfile -> upload file myfile values at default offset 0\n" - "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n" - "mem load -f mfc_default_keys -m -> upload MFC keys\n" - "mem load -f t55xx_default_pwds -t -> upload T55XX passwords\n" - "mem load -f iclass_default_keys -i -> upload iCLASS keys\n" + "Warning! - mem area to be written must have been wiped first\n\n" + "OBS! - dictionaries are serviced as files in spiffs so no wipe is needed", + "mem load -f myfile -> upload file myfile values at default offset 0\n" + "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n" + "mem load -f mfc_default_keys -m -> upload MIFARE Classic keys\n" + "mem load -f t55xx_default_pwds -t -> upload T55XX passwords\n" + "mem load -f iclass_default_keys -i -> upload iCLASS keys\n" + "mem load -f mfulc_default_keys --ulc -> upload MIFARE UL-C keys\n" ); void *argtable[] = { arg_param_begin, arg_int0("o", "offset", "", "offset in memory"), - arg_lit0("m", "mifare,mfc", "upload 6 bytes keys (mifare key dictionary)"), - arg_lit0("i", "iclass", "upload 8 bytes keys (iClass key dictionary)"), - arg_lit0("t", "t55xx", "upload 4 bytes keys (password dictionary)"), + arg_lit0("m", "mfc", "upload 6 bytes keys (MIFARE Classic dictionary)"), + arg_lit0("i", "iclass", "upload 8 bytes keys (iClass dictionary)"), + arg_lit0("t", "t55xx", "upload 4 bytes keys (T55xx dictionary)"), + arg_lit0(NULL, "ulc", "upload 16 bytes keys (MIFARE UL-C dictionary)"), + arg_lit0(NULL, "aes", "upload 16 bytes keys (MIFARE UL-AES dictionary)"), arg_str1("f", "file", "", "file name"), arg_param_end }; @@ -216,28 +219,35 @@ static int CmdFlashMemLoad(const char *Cmd) { bool is_mfc = arg_get_lit(ctx, 2); bool is_iclass = arg_get_lit(ctx, 3); bool is_t55xx = arg_get_lit(ctx, 4); + bool is_ulc = arg_get_lit(ctx, 5); + bool is_ulaes = arg_get_lit(ctx, 6); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - char spiffsDest[32] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParserFree(ctx); Dictionary_t d = DICTIONARY_NONE; if (is_mfc) { d = DICTIONARY_MIFARE; - PrintAndLogEx(INFO, "treating file as MIFARE Classic keys"); + PrintAndLogEx(INFO, "Treating file as MIFARE Classic keys"); } else if (is_iclass) { d = DICTIONARY_ICLASS; - PrintAndLogEx(INFO, "treating file as iCLASS keys"); + PrintAndLogEx(INFO, "Treating file as iCLASS keys"); } else if (is_t55xx) { d = DICTIONARY_T55XX; - PrintAndLogEx(INFO, "treating file as T55xx passwords"); + PrintAndLogEx(INFO, "Treating file as T55xx passwords"); + } else if (is_ulc) { + d = DICTIONARY_MIFARE_ULC; + PrintAndLogEx(INFO, "Treating file as MIFARE Ultralight-C keys"); + } else if (is_ulaes) { + d = DICTIONARY_MIFARE_ULAES; + PrintAndLogEx(INFO, "Treating file as MIFARE Ultralight AES keys"); } uint8_t spi_flash_pages = 0; int res = rdv4_get_flash_pages64k(&spi_flash_pages); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "failed to get flash pages count (%x)", res); + PrintAndLogEx(ERR, "Failed to get flash pages count (%x)", res); return res; } @@ -246,6 +256,8 @@ static int CmdFlashMemLoad(const char *Cmd) { uint8_t keylen = 0; uint8_t *data = calloc(FLASH_MEM_MAX_SIZE_P(spi_flash_pages), sizeof(uint8_t)); + char spiffsDest[32] = {0}; + switch (d) { case DICTIONARY_MIFARE: { keylen = MF_KEY_LENGTH; @@ -292,6 +304,36 @@ static int CmdFlashMemLoad(const char *Cmd) { strcpy(spiffsDest, ICLASS_KEYS_FILE); break; } + case DICTIONARY_MIFARE_ULC: { + keylen = MFULC_KEY_LENGTH; + res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); + if (res || !keycount) { + free(data); + return PM3_EFILE; + } + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; + } + strcpy(spiffsDest, MFULC_KEYS_FILE); + break; + } + case DICTIONARY_MIFARE_ULAES: { + keylen = MFULAES_KEY_LENGTH; + res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); + if (res || !keycount) { + free(data); + return PM3_EFILE; + } + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; + } + strcpy(spiffsDest, MFULAES_KEYS_FILE); + break; + } case DICTIONARY_NONE: { res = loadFile_safe(filename, ".bin", (void **)&data, &datalen); if (res != PM3_SUCCESS) { @@ -330,7 +372,12 @@ static int CmdFlashMemLoad(const char *Cmd) { free(data); return res; } - PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" passwords to file "_GREEN_("%s"), keycount, spiffsDest); + + if (d == DICTIONARY_T55XX) { + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" passwords to file "_GREEN_("%s"), keycount, spiffsDest); + } else { + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" keys to file "_GREEN_("%s"), keycount, spiffsDest); + } SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0); } else { @@ -729,6 +776,7 @@ static int CmdFlashMemInfo(const char *Cmd) { static command_t CommandTable[] = { {"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "{ SPI File system }"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"-----------", CmdHelp, IfPm3Flash, "------------------- " _CYAN_("Operations") " -------------------"}, {"baudrate", CmdFlashmemSpiBaud, IfPm3Flash, "Set Flash memory Spi baudrate"}, {"dump", CmdFlashMemDump, IfPm3Flash, "Dump data from flash memory"}, {"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information"}, diff --git a/client/src/cmdflashmem.h b/client/src/cmdflashmem.h index ad8727204..f47547166 100644 --- a/client/src/cmdflashmem.h +++ b/client/src/cmdflashmem.h @@ -26,7 +26,9 @@ typedef enum { DICTIONARY_NONE = 0, DICTIONARY_MIFARE, DICTIONARY_T55XX, - DICTIONARY_ICLASS + DICTIONARY_ICLASS, + DICTIONARY_MIFARE_ULC, + DICTIONARY_MIFARE_ULAES, } Dictionary_t; int CmdFlashMem(const char *Cmd); diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index 0cdf49b81..fb9aae5b9 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -477,7 +477,7 @@ int CmdHFSniff(const char *Cmd) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(INFO, "User aborted"); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); break; } diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 9d929b6b7..8d7ec5d5b 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -880,6 +880,7 @@ int CmdHF14ASim(const char *Cmd) { "hf 14a sim -t 10 -> ST25TA IKEA Rothult\n" "hf 14a sim -t 11 -> Javacard (JCOP)\n" "hf 14a sim -t 12 -> 4K Seos card\n" + "hf 14a sim -t 13 -> MIFARE Ultralight C" ); void *argtable[] = { @@ -890,6 +891,8 @@ int CmdHF14ASim(const char *Cmd) { arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader"), arg_lit0(NULL, "sk", "Fill simulator keys from found keys"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "c1", "UL-C Auth - all zero handshake part 1"), + arg_lit0(NULL, "c2", "UL-C Auth - all zero handshake part 2"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -923,9 +926,12 @@ int CmdHF14ASim(const char *Cmd) { bool setEmulatorMem = arg_get_lit(ctx, 5); bool verbose = arg_get_lit(ctx, 6); + bool ulc_p1 = arg_get_lit(ctx, 7); + bool ulc_p2 = arg_get_lit(ctx, 8); + CLIParserFree(ctx); - if (tagtype > 12) { + if (tagtype > 13) { PrintAndLogEx(ERR, "Undefined tag %d", tagtype); return PM3_EINVARG; } @@ -939,11 +945,16 @@ int CmdHF14ASim(const char *Cmd) { uint16_t flags; uint8_t uid[10]; uint8_t exitAfter; + uint8_t rats[20]; + bool ulc_p1; + bool ulc_p2; } PACKED payload; payload.tagtype = tagtype; payload.flags = flags; payload.exitAfter = exitAfterNReads; + payload.ulc_p1 = ulc_p1; + payload.ulc_p2 = ulc_p2; memcpy(payload.uid, uid, uid_len); clearCommandBuffer(); @@ -1319,7 +1330,7 @@ static int CmdExchangeAPDU(bool chainingin, const uint8_t *datain, int datainlen // Button pressed / user cancelled if (iLen == -3) { - PrintAndLogEx(DEBUG, "ERR: APDU: User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); return PM3_EAPDU_FAIL; } return PM3_SUCCESS; diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index d52cb52da..278b301c4 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -1803,7 +1803,7 @@ static int CmdHFFelicaSniff(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); msleep(300); break; } @@ -1851,7 +1851,7 @@ static int CmdHFFelicaSimLite(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); msleep(300); break; } @@ -2054,7 +2054,7 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); return PM3_EOPABORTED; } diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 0cf89a6d1..035031cf2 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4609,7 +4609,7 @@ static int iclass_recover(uint8_t key[8], uint32_t index_start, uint32_t loop, u repeat = false; } else if (resp.status == PM3_EOPABORTED) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(WARNING, "iCLASS Key Bits Recovery: " _YELLOW_("user aborted")); + PrintAndLogEx(WARNING, "iCLASS Key Bits Recovery: " _YELLOW_("aborted via keyboard!")); repeat = false; } else if (resp.status == PM3_ESOFT) { PrintAndLogEx(NORMAL, ""); @@ -5210,7 +5210,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { item = (iclass_prekey_t *) bsearch(&lookup, prekey, keycount, sizeof(iclass_prekey_t), cmp_uint32); if (item != NULL) { - PrintAndLogEx(SUCCESS, "Found valid key " _GREEN_("%s"), sprint_hex(item->key, 8)); + PrintAndLogEx(SUCCESS, "Found valid key " _GREEN_("%s"), sprint_hex_inrow(item->key, 8)); add_key(item->key); } diff --git a/client/src/cmdhfjooki.c b/client/src/cmdhfjooki.c index 51f61e619..81e7b3546 100644 --- a/client/src/cmdhfjooki.c +++ b/client/src/cmdhfjooki.c @@ -584,7 +584,7 @@ static int CmdHF14AJookiSim(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); break; } diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index 0ff062532..be84822bf 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -560,7 +560,7 @@ static int CmdLegicSim(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "Aborted via keyboard!"); break; } diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 21ccd12ae..269a06fab 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -63,6 +63,8 @@ typedef struct { const char *level_name; } SaflokKeyLevel; +static int CmdHelp(const char *Cmd); + // Static array for Saflok key levels static const SaflokKeyLevel saflok_key_levels[] = { {1, "Guest Key"}, @@ -218,9 +220,8 @@ static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t uint8_t key_id = decodedBA[1]; // Byte 2 & 3: KeyRecord, including OpeningKey flag - uint8_t key_record_high = decodedBA[2] & 0x7F; uint8_t opening_key = (decodedBA[2] & 0x80) >> 7; - uint16_t key_record = (key_record_high << 8) | decodedBA[3]; + uint16_t key_record = ((decodedBA[2] & 0x3F) << 8) | decodedBA[3]; // Byte 5 & 6: EncryptSequence + Combination uint16_t sequence_combination_number = ((decodedBA[5] & 0x0F) << 8) | decodedBA[6]; @@ -343,10 +344,6 @@ static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t PrintAndLogEx(SUCCESS, "Checksum Valid........ ( %s )", checksum_valid ? _GREEN_("ok") : _RED_("fail")); } - - -static int CmdHelp(const char *Cmd); - /* static int usage_hf14_keybrute(void) { PrintAndLogEx(NORMAL, "J_Run's 2nd phase of multiple sector nested authentication key recovery"); @@ -1248,13 +1245,15 @@ static int CmdHF14AMfDarkside(const char *Cmd) { uint64_t key = 0; uint64_t t1 = msclock(); - int ret = mf_dark_side(blockno, key_type, &key); + int res = mf_dark_side(blockno, key_type, &key); t1 = msclock() - t1; - if (ret != PM3_SUCCESS) return ret; + if (res != PM3_SUCCESS) { + return res; + } - PrintAndLogEx(SUCCESS, "found valid key: " _GREEN_("%012" PRIx64), key); - PrintAndLogEx(SUCCESS, "time in darkside " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + PrintAndLogEx(SUCCESS, "Found valid key [ "_GREEN_("%012" PRIX64) " ]", key); + PrintAndLogEx(SUCCESS, "Time in darkside " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); return PM3_SUCCESS; } @@ -2008,7 +2007,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't CLIExecWithReturn(ctx, Cmd, argtable, false); int keylen = 0; - uint8_t key[6] = {0}; + uint8_t key[MIFARE_KEY_SIZE] = {0}; CLIGetHexWithReturn(ctx, 1, key, &keylen); bool m0 = arg_get_lit(ctx, 2); @@ -2027,6 +2026,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } else if (arg_get_lit(ctx, 8)) { keyType = MF_KEY_B; } + uint8_t prev_keytype = keyType; keyType = arg_get_int_def(ctx, 9, keyType); if ((arg_get_lit(ctx, 7) || arg_get_lit(ctx, 8)) && (keyType != prev_keytype)) { @@ -2046,13 +2046,16 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } else if (arg_get_lit(ctx, 12)) { trgKeyType = MF_KEY_B; } + uint8_t prev_trgkeytype = trgKeyType; trgKeyType = arg_get_int_def(ctx, 13, trgKeyType); + if ((arg_get_lit(ctx, 11) || arg_get_lit(ctx, 12)) && (trgKeyType != prev_trgkeytype)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Choose one single target key type"); return PM3_EINVARG; } + bool transferToEml = arg_get_lit(ctx, 14); bool createDumpFile = arg_get_lit(ctx, 15); bool singleSector = trgBlockNo > -1; @@ -2101,16 +2104,16 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't return PM3_EINVARG; } } + if (SectorsCnt == 1) { SectorsCnt = MIFARE_1K_MAXSECTOR; } - if (keylen != 6) { - PrintAndLogEx(WARNING, "Input key must include 12 HEX symbols"); + if (keylen != MIFARE_KEY_SIZE) { + PrintAndLogEx(WARNING, "Input key must include 6 HEX bytes, got %u", keylen); return PM3_EINVARG; } - sector_t *e_sector = NULL; uint8_t keyBlock[(ARRAYLEN(g_mifare_default_keys) + 1) * 6]; uint64_t key64 = 0; @@ -2124,15 +2127,17 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't // check if we can authenticate to sector if (mf_check_keys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) { if (keyType < 2) { - PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%c", blockNo, keyType ? 'B' : 'A'); + PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block %3d key type %c", blockNo, keyType ? 'B' : 'A'); } else { - PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%02x", blockNo, MIFARE_AUTH_KEYA + keyType); + PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block %3d key type %02x", blockNo, MIFARE_AUTH_KEYA + keyType); } return PM3_EOPABORTED; } if (singleSector) { - int16_t isOK = mf_nested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, !ignore_static_encrypted); + + uint8_t foundkey[MIFARE_KEY_SIZE] = {0}; + int16_t isOK = mf_nested(blockNo, keyType, key, trgBlockNo, trgKeyType, foundkey, !ignore_static_encrypted); switch (isOK) { case PM3_ETIMEOUT: PrintAndLogEx(ERR, "command execution time out\n"); @@ -2150,8 +2155,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't PrintAndLogEx(ERR, "Static encrypted nonce detected. Aborted\n"); PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); break; - case PM3_SUCCESS: - key64 = bytes_to_num(keyBlock, 6); + case PM3_SUCCESS: { // transfer key to the emulator if (transferToEml) { @@ -2162,32 +2166,43 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } else { // 16 block sector sectortrailer = trgBlockNo | 0x0f; } - mf_eml_get_mem(keyBlock, sectortrailer, 1); - if (trgKeyType == MF_KEY_A) - num_to_bytes(key64, 6, keyBlock); - else - num_to_bytes(key64, 6, &keyBlock[10]); + uint8_t block[MFBLOCK_SIZE] = {0}; + mf_eml_get_mem(block, sectortrailer, 1); - mf_elm_set_mem(keyBlock, sectortrailer, 1); - PrintAndLogEx(SUCCESS, "Key transferred to emulator memory."); + if (trgKeyType == MF_KEY_A) { + memcpy(block, foundkey, MIFARE_KEY_SIZE); + } else { + memcpy(block + 10, foundkey, MIFARE_KEY_SIZE); + } + + mf_elm_set_mem(block, sectortrailer, 1); + PrintAndLogEx(SUCCESS, "Key transferred to emulator memory"); } return PM3_SUCCESS; - default : + } + default : { PrintAndLogEx(ERR, "Unknown error\n"); + } } return PM3_SUCCESS; } else { // ------------------------------------ multiple sectors working - uint64_t t1 = msclock(); - e_sector = calloc(SectorsCnt, sizeof(sector_t)); - if (e_sector == NULL) return PM3_EMALLOC; + uint64_t t2; + + sector_t *e_sector = NULL; + if (initSectorTable(&e_sector, SectorsCnt) != PM3_SUCCESS) { + return PM3_EMALLOC; + } // add our known key e_sector[mfSectorNum(blockNo)].foundKey[keyType] = 1; e_sector[mfSectorNum(blockNo)].Key[keyType] = key64; + PrintAndLogEx(SUCCESS, "--- " _CYAN_("Enter dictionary recovery mode") " ---------------"); + PrintAndLogEx(SUCCESS, "Sector count "_YELLOW_("%d"), SectorsCnt); + //test current key and additional standard keys first // add parameter key memcpy(keyBlock + (ARRAYLEN(g_mifare_default_keys) * 6), key, 6); @@ -2196,6 +2211,8 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t *)(keyBlock + cnt * 6)); } + uint64_t t1 = msclock(); + PrintAndLogEx(SUCCESS, "Testing known keys. Sector count "_YELLOW_("%d"), SectorsCnt); int res = mf_check_keys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, use_flashmemory, false); if (res == PM3_SUCCESS) { @@ -2203,9 +2220,9 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't goto jumptoend; } - uint64_t t2 = msclock() - t1; - PrintAndLogEx(SUCCESS, "Time to check " _YELLOW_("%zu") " known keys: %.0f seconds\n", ARRAYLEN(g_mifare_default_keys), (float)t2 / 1000.0); - PrintAndLogEx(SUCCESS, "enter nested key recovery"); + t2 = msclock() - t1; + PrintAndLogEx(SUCCESS, "Time in check keys " _YELLOW_("%.0f") " seconds\n", (float)t2 / 1000.0); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("Enter nested key recovery mode") " ---------------"); // nested sectors bool calibrate = !ignore_static_encrypted; @@ -2214,7 +2231,14 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; ++sectorNo) { for (int i = 0; i < MIFARE_SECTOR_RETRY; i++) { - if (e_sector[sectorNo].foundKey[trgKeyType]) continue; + while (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard!"); + return PM3_EOPABORTED; + } + + if (e_sector[sectorNo].foundKey[trgKeyType]) { + continue; + } int16_t isOK = mf_nested(blockNo, keyType, key, mfFirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate); switch (isOK) { @@ -2238,7 +2262,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't case PM3_SUCCESS: calibrate = false; e_sector[sectorNo].foundKey[trgKeyType] = 1; - e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6); + e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, MIFARE_KEY_SIZE); mf_check_keys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false); continue; @@ -2252,24 +2276,24 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "time in nested " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + PrintAndLogEx(SUCCESS, "Time in nested " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? - PrintAndLogEx(INFO, "trying to read key B..."); + PrintAndLogEx(INFO, "Trying to read key B..."); for (int i = 0; i < SectorsCnt; i++) { // KEY A but not KEY B if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) { uint8_t sectrail = (mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1); - PrintAndLogEx(SUCCESS, "reading block %d", sectrail); + PrintAndLogEx(SUCCESS, "Reading block " _YELLOW_("%d"), sectrail); mf_readblock_t payload; payload.blockno = sectrail; payload.keytype = MF_KEY_A; - num_to_bytes(e_sector[i].Key[0], 6, payload.key); // KEY A + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, payload.key); // KEY A clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); @@ -2284,9 +2308,9 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } uint8_t *data = resp.data.asBytes; - key64 = bytes_to_num(data + 10, 6); + key64 = bytes_to_num(data + 10, MIFARE_KEY_SIZE); if (key64) { - PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data + 10, 6)); + PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data + 10, MIFARE_KEY_SIZE)); e_sector[i].foundKey[1] = true; e_sector[i].Key[1] = key64; } @@ -2295,10 +2319,6 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't jumptoend: - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - - //print them printKeyTable(SectorsCnt, e_sector); // transfer them to the emulator @@ -2309,10 +2329,10 @@ jumptoend: mf_eml_get_mem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); if (e_sector[i].foundKey[0]) - num_to_bytes(e_sector[i].Key[0], 6, keyBlock); + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, keyBlock); if (e_sector[i].foundKey[1]) - num_to_bytes(e_sector[i].Key[1], 6, &keyBlock[10]); + num_to_bytes(e_sector[i].Key[1], MIFARE_KEY_SIZE, &keyBlock[10]); if (i == SectorsCnt - 1) { // Disable fast mode on last packet @@ -2336,6 +2356,9 @@ jumptoend: } free(e_sector); } + + PrintAndLogEx(INFO, "Done!"); + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -2470,8 +2493,8 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { } uint64_t t2 = msclock() - t1; - PrintAndLogEx(SUCCESS, "Time to check "_YELLOW_("%zu") " known keys: %.0f seconds\n", ARRAYLEN(g_mifare_default_keys), (float)t2 / 1000.0); - PrintAndLogEx(SUCCESS, "enter static nested key recovery"); + PrintAndLogEx(SUCCESS, "Time in check keys " _YELLOW_("%.0f") " seconds\n", (float)t2 / 1000.0); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("Enter static nested key recovery") " --------------"); // nested sectors for (trgKeyType = MF_KEY_A; trgKeyType <= MF_KEY_B; ++trgKeyType) { @@ -2511,7 +2534,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? - PrintAndLogEx(INFO, "trying to read key B..."); + PrintAndLogEx(INFO, "Trying to read key B..."); for (int i = 0; i < SectorsCnt; i++) { // KEY A but not KEY B if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) { @@ -2548,9 +2571,6 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { jumptoend: - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - //print them printKeyTable(SectorsCnt, e_sector); @@ -2731,9 +2751,15 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { SetSIMDInstr(SIMD_NEON); #endif - if (in) + if (in) { SetSIMDInstr(SIMD_NONE); + } + // santiy checks + if ((g_session.pm3_present == false) && (tests == false)) { + PrintAndLogEx(INFO, "No device connected"); + return PM3_EFAILED; + } bool known_target_key = (trg_keylen); @@ -2789,13 +2815,13 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { } } - PrintAndLogEx(INFO, "Target block no " _YELLOW_("%3d") ", target key type: " _YELLOW_("%c") ", known target key: " _YELLOW_("%02x%02x%02x%02x%02x%02x%s"), + PrintAndLogEx(INFO, "Target block no " _YELLOW_("%3d") " target key type: " _YELLOW_("%c") " known target key: " _YELLOW_("%02x%02x%02x%02x%02x%02x%s"), trg_blockno, (trg_keytype == MF_KEY_B) ? 'B' : 'A', trg_key[0], trg_key[1], trg_key[2], trg_key[3], trg_key[4], trg_key[5], known_target_key ? "" : " (not set)" ); - PrintAndLogEx(INFO, "File action: " _YELLOW_("%s") ", Slow: " _YELLOW_("%s") ", Tests: " _YELLOW_("%d"), + PrintAndLogEx(INFO, "File action: " _YELLOW_("%s") " Slow: " _YELLOW_("%s") " Tests: " _YELLOW_("%d"), nonce_file_write ? "write" : nonce_file_read ? "read" : "none", slow ? "Yes" : "No", tests); @@ -3010,7 +3036,6 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { int prng_type = PM3_EUNDEF; int isOK = 0; - // ------------------------------ PrintAndLogEx(NORMAL, ""); uint64_t tagT = GetHF14AMfU_Type(); @@ -3139,6 +3164,19 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { block_cnt += 8; } + // check if we can authenticate to sector + uint8_t loopupblk = mfFirstBlockOfSector(sectorno); + if (mf_check_keys(loopupblk, keytype, true, (in_keys_len / MIFARE_KEY_SIZE), in_keys, &key64) != PM3_SUCCESS) { + if (keytype < 2) { + PrintAndLogEx(WARNING, "Known key failed. Can't authenticate to block %3d key type %c", loopupblk, keytype ? 'B' : 'A'); + } else { + PrintAndLogEx(WARNING, "Known key failed. Can't authenticate to block %3d key type %02x", loopupblk, MIFARE_AUTH_KEYA + keytype); + } + known_key = false; + } else { + num_to_bytes(key64, MIFARE_KEY_SIZE, key); + } + // create/initialize key storage structure sector_t *e_sector = NULL; size_t e_sector_cnt = (sector_cnt > sectorno) ? sector_cnt : sectorno + 1; @@ -3172,7 +3210,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // card prng type (weak=1 / hard=0 / select/card comm error = negative value) if (has_staticnonce == NONCE_NORMAL) { + prng_type = detect_classic_prng(); + if (prng_type < 0) { PrintAndLogEx(FAILED, "\nNo tag detected or other tag communication error (%i)", prng_type); free(e_sector); @@ -3180,47 +3220,50 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { return PM3_ESOFT; } - // - has_staticnonce = detect_classic_static_encrypted_nonce(0, MF_KEY_A, g_mifare_default_key); + if (known_key) { + has_staticnonce = detect_classic_static_encrypted_nonce(loopupblk, keytype, key); + } else { + has_staticnonce = detect_classic_static_encrypted_nonce(0, MF_KEY_A, g_mifare_default_key); + } } // print parameters if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("SETTINGS") " ======================="); - PrintAndLogEx(INFO, " card sectors .. " _YELLOW_("%d"), sector_cnt); - PrintAndLogEx(INFO, " key supplied .. " _YELLOW_("%s"), known_key ? "True" : "False"); - PrintAndLogEx(INFO, " known sector .. " _YELLOW_("%d"), sectorno); - PrintAndLogEx(INFO, " keytype ....... " _YELLOW_("%c"), (keytype == MF_KEY_B) ? 'B' : 'A'); - PrintAndLogEx(INFO, " known key ..... " _YELLOW_("%s"), sprint_hex_inrow(key, sizeof(key))); + PrintAndLogEx(INFO, "---- " _CYAN_("Command settings") " ------------------------------------------"); + PrintAndLogEx(INFO, "Card sectors... " _YELLOW_("%d"), sector_cnt); + PrintAndLogEx(INFO, "Key supplied... " _YELLOW_("%s"), known_key ? "yes" : "no"); + PrintAndLogEx(INFO, "Known sector... " _YELLOW_("%d"), sectorno); + PrintAndLogEx(INFO, "Key type....... " _YELLOW_("%c"), (keytype == MF_KEY_B) ? 'B' : 'A'); + PrintAndLogEx(INFO, "Known key...... " _YELLOW_("%s"), sprint_hex_inrow(key, sizeof(key))); switch (has_staticnonce) { case NONCE_STATIC: { - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("STATIC")); + PrintAndLogEx(INFO, "Card PRNG ..... " _YELLOW_("STATIC")); break; } case NONCE_STATIC_ENC: { - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("STATIC ENCRYPTED")); + PrintAndLogEx(INFO, "Card PRNG ..... " _RED_("STATIC ENCRYPTED")); break; } case NONCE_NORMAL: { - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("%s"), prng_type ? "WEAK" : "HARD"); + PrintAndLogEx(INFO, "Card PRNG ..... %s", (prng_type) ? "weak" : "hard"); break; } default: { - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("Could not determine PRNG,") " " _RED_("read failed.")); + PrintAndLogEx(INFO, "Card PRNG ..... " _YELLOW_("Could not determine PRNG") " ( " _RED_("read failed") " ) %i", has_staticnonce); break; } } - PrintAndLogEx(INFO, " dictionary .... " _YELLOW_("%s"), strlen(filename) ? filename : "NONE"); - PrintAndLogEx(INFO, " legacy mode ... " _YELLOW_("%s"), legacy_mfchk ? "True" : "False"); + PrintAndLogEx(INFO, "Dictionary .... " _YELLOW_("%s"), strlen(filename) ? filename : "n/a"); + PrintAndLogEx(INFO, "Legacy mode ... %s", (legacy_mfchk) ? _YELLOW_("yes") : "no"); - PrintAndLogEx(INFO, "========================================================================"); + PrintAndLogEx(INFO, "----------------------------------------------------------------"); } // check the user supplied key if (known_key == false) { - PrintAndLogEx(WARNING, "no known key was supplied, key recovery might fail"); + PrintAndLogEx(WARNING, "No known key was supplied, key recovery might fail"); } // Start the timer @@ -3231,6 +3274,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (use_flashmemory) { fnlen = 0; } + int ret = mf_load_keys(&keyBlock, &key_cnt, in_keys, in_keys_len, filename, fnlen, true); if (ret != PM3_SUCCESS) { free(e_sector); @@ -3241,7 +3285,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Use the dictionary to find sector keys on the card if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START DICTIONARY ATTACK") " ======================="); + PrintAndLogEx(INFO, "--- " _CYAN_("Enter dictionary recovery mode") " -----------------------------"); } if (legacy_mfchk) { @@ -3314,6 +3358,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { uint8_t num_found_keys = 0; for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { + if (e_sector[i].foundKey[j] != 1) { continue; } @@ -3329,13 +3374,13 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { known_key = true; sectorno = i; keytype = j; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } else { - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) @@ -3354,7 +3399,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Check if the darkside attack can be used if (prng_type && has_staticnonce != NONCE_STATIC) { if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START DARKSIDE ATTACK") " ======================="); + PrintAndLogEx(INFO, "--- " _CYAN_("Enter darkside key recovery mode") " ---------------------------------"); } PrintAndLogEx(NORMAL, ""); @@ -3365,13 +3410,13 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { goto noValidKeyFound; } - PrintAndLogEx(SUCCESS, "Found valid key [ " _GREEN_("%012" PRIx64) " ]\n", key64); + PrintAndLogEx(SUCCESS, "Found valid key [ " _GREEN_("%012" PRIX64) " ]\n", key64); // Store the keys num_to_bytes(key64, MIFARE_KEY_SIZE, key); e_sector[sectorno].Key[keytype] = key64; e_sector[sectorno].foundKey[keytype] = 'S'; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%012" PRIx64) " ] (used for nested / hardnested attack)", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type "_GREEN_("%c") " -- found valid key [ " _GREEN_("%012" PRIX64) " ] (used for nested / hardnested attack)", sectorno, (keytype == MF_KEY_B) ? 'B' : 'A', key64 @@ -3410,8 +3455,9 @@ noValidKeyFound: // If the key is already known, just skip it if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) { - if (has_staticnonce == NONCE_STATIC) + if (has_staticnonce == NONCE_STATIC) { goto tryStaticnested; + } // Try the found keys are reused if (bytes_to_num(tmp_key, MIFARE_KEY_SIZE) != 0) { @@ -3420,14 +3466,15 @@ noValidKeyFound: for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { // Check if the sector key is already broken - if (e_sector[i].foundKey[j]) + if (e_sector[i].foundKey[j]) { continue; + } // Check if the key works if (mf_check_keys(mfFirstBlockOfSector(i), j, true, 1, tmp_key, &key64) == PM3_SUCCESS) { e_sector[i].Key[j] = bytes_to_num(tmp_key, MIFARE_KEY_SIZE); e_sector[i].foundKey[j] = 'R'; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) @@ -3442,7 +3489,7 @@ noValidKeyFound: if (current_key_type_i == MF_KEY_B) { if (e_sector[current_sector_i].foundKey[0] && !e_sector[current_sector_i].foundKey[1]) { if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START READ B KEY ATTACK") " ======================="); + PrintAndLogEx(INFO, "--- " _CYAN_("Enter read B key recovery mode") " -----------------------"); PrintAndLogEx(INFO, "reading B key of sector %3d with key type %c", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); @@ -3458,24 +3505,29 @@ noValidKeyFound: clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); - if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) goto skipReadBKey; + if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) { + goto skipReadBKey; + } - if (resp.status != PM3_SUCCESS) goto skipReadBKey; + if (resp.status != PM3_SUCCESS) { + goto skipReadBKey; + } uint8_t *data = resp.data.asBytes; key64 = bytes_to_num(data + 10, MIFARE_KEY_SIZE); + if (key64) { e_sector[current_sector_i].foundKey[current_key_type_i] = 'A'; e_sector[current_sector_i].Key[current_key_type_i] = key64; num_to_bytes(key64, MIFARE_KEY_SIZE, tmp_key); - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } else { if (verbose) { - PrintAndLogEx(WARNING, "unknown B key: sector: %3d key type: %c", + PrintAndLogEx(WARNING, "Unknown B key: sector %3d key type %c", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A' ); @@ -3491,14 +3543,17 @@ noValidKeyFound: skipReadBKey: if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) { - if (has_staticnonce == NONCE_STATIC) + if (has_staticnonce == NONCE_STATIC) { goto tryStaticnested; + } if (prng_type && (nested_failed == false)) { uint8_t retries = 0; + + PrintAndLogEx(NORMAL, ""); if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START NESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no %3d, target key type %c", + PrintAndLogEx(INFO, "--- " _CYAN_("Enter nested key recovery mode") " -----------------------------"); + PrintAndLogEx(INFO, "Sector " _YELLOW_("%3d") " key type " _YELLOW_("%c"), current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } @@ -3544,8 +3599,6 @@ tryNested: e_sector[current_sector_i].Key[current_key_type_i] = 0xffffffffffff; e_sector[current_sector_i].foundKey[current_key_type_i] = false; // Show the results to the user - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); printKeyTable(sector_cnt, e_sector); PrintAndLogEx(NORMAL, ""); free(e_sector); @@ -3559,7 +3612,7 @@ tryNested: break; } default: { - PrintAndLogEx(ERR, "unknown Error.\n"); + PrintAndLogEx(ERR, "Unknown error\n"); free(e_sector); free(fptr); return isOK; @@ -3573,8 +3626,6 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack if (isMifarePlus) { // Show the results to the user - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); printKeyTable(sector_cnt, e_sector); PrintAndLogEx(NORMAL, ""); free(e_sector); @@ -3582,9 +3633,10 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack return PM3_ESOFT; } + PrintAndLogEx(NORMAL, ""); if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START HARDNESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no %3d, target key type %c, Slow %s", + PrintAndLogEx(INFO, "--- " _CYAN_("Enter hardnested key recovery mode") " -------------------------"); + PrintAndLogEx(INFO, "Sector " _YELLOW_("%3d") " key type " _YELLOW_("%c") ", slow " _YELLOW_("%s"), current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', slow ? "Yes" : "No"); @@ -3610,8 +3662,6 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack e_sector[current_sector_i].foundKey[current_key_type_i] = false; // Show the results to the user - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); printKeyTable(sector_cnt, e_sector); PrintAndLogEx(NORMAL, ""); break; @@ -3637,9 +3687,10 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack if (has_staticnonce == NONCE_STATIC) { tryStaticnested: + PrintAndLogEx(NORMAL, ""); if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START STATIC NESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no %3d, target key type %c", + PrintAndLogEx(INFO, "--- " _CYAN_("Enter static nested key recovery mode") " -----------------------"); + PrintAndLogEx(INFO, "Sector " _YELLOW_("%3d") ", key type " _YELLOW_("%c"), current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } @@ -3672,7 +3723,7 @@ tryStaticnested: // Check if the key was found if (e_sector[current_sector_i].foundKey[current_key_type_i]) { - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) @@ -3686,9 +3737,6 @@ tryStaticnested: all_found: // Show the results to the user - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - printKeyTable(sector_cnt, e_sector); if (no_save == false) { @@ -3704,21 +3752,26 @@ all_found: clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_MEMCLR, NULL, 0); - PrintAndLogEx(INFO, "transferring keys to simulator memory " NOLF); + PrintAndLogEx(INFO, "Transferring keys to simulator memory " NOLF); bool transfer_status = true; for (current_sector_i = 0; current_sector_i < sector_cnt; current_sector_i++) { + mf_eml_get_mem(block, current_sector_i, 1); - if (e_sector[current_sector_i].foundKey[0]) + + if (e_sector[current_sector_i].foundKey[0]) { num_to_bytes(e_sector[current_sector_i].Key[0], MIFARE_KEY_SIZE, block); - if (e_sector[current_sector_i].foundKey[1]) + } + + if (e_sector[current_sector_i].foundKey[1]) { num_to_bytes(e_sector[current_sector_i].Key[1], MIFARE_KEY_SIZE, block + 10); + } transfer_status |= mf_elm_set_mem(block, mfFirstBlockOfSector(current_sector_i) + mfNumBlocksPerSector(current_sector_i) - 1, 1); } PrintAndLogEx(NORMAL, "( %s )", (transfer_status) ? _GREEN_("ok") : _RED_("fail")); - PrintAndLogEx(INFO, "dumping card content to emulator memory (Cmd Error: 04 can occur)"); + PrintAndLogEx(INFO, "Dumping card content to emulator memory (Cmd Error: 04 can occur)"); // use ecfill trick FastDumpWithEcFill(sector_cnt); @@ -3754,6 +3807,7 @@ all_found: } else { snprintf(suffix, sizeof(suffix), "-dump"); } + fptr = GenerateFilename("hf-mf-", suffix); if (fptr == NULL) { free(dump); @@ -3771,7 +3825,7 @@ all_found: out: // Generate and show statistics t1 = msclock() - t1; - PrintAndLogEx(INFO, "autopwn execution time: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); + PrintAndLogEx(INFO, "Autopwn execution time: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); DropField(); free(e_sector); @@ -3882,7 +3936,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { return PM3_EMALLOC; } - uint32_t chunksize = keycnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : keycnt; + uint32_t chunksize = (keycnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE)) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : keycnt; bool firstChunk = true, lastChunk = false; int i = 0; @@ -3901,30 +3955,26 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { // strategies. 1= deep first on sector 0 AB, 2= width first on all sectors for (uint8_t strategy = 1; strategy < 3; strategy++) { - PrintAndLogEx(INFO, "Running strategy %u", strategy); + PrintAndLogEx(INFO, "Running strategy " _YELLOW_("%u"), strategy); // main keychunk loop for (i = 0; i < keycnt; i += chunksize) { if (kbd_enter_pressed()) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - // field is still ON if not on last chunk clearCommandBuffer(); - SendCommandNG(CMD_FPGA_MAJOR_MODE_OFF, NULL, 0); - // TODO: we're missing these cleanups on arm side, not sure if it's important... - // set_tracing(false); - // BigBuf_free(); - // BigBuf_Clear_ext(false); + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + SendCommandNG(CMD_FPGA_MAJOR_MODE_OFF, NULL, 0); // field is still ON if not on last chunk + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); goto out; } - PrintAndLogEx(INPLACE, "Testing %5i/%5i ( " _YELLOW_("%02.1f%%") " )", i, keycnt, (float)i * 100 / keycnt); + uint32_t size = ((keycnt - i) > chunksize) ? chunksize : keycnt - i; // last chunk? - if (size == keycnt - i) + if (size == keycnt - i) { lastChunk = true; - + } int res = mf_check_keys_fast_ex(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, false, true, singleSectorParams); if (firstChunk) firstChunk = false; @@ -3934,11 +3984,16 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { PrintAndLogEx(NORMAL, ""); goto out; } + PrintAndLogEx(INPLACE, "Testing %5i/%5i ( " _YELLOW_("%02.1f %%") " )", i, keycnt, (float)i * 100 / keycnt); } // end chunks of keys - PrintAndLogEx(INPLACE, "Testing %5i/%5i ( " _YELLOW_("100.00%%") " )", keycnt, keycnt); + + PrintAndLogEx(INPLACE, "Testing %5i/%5i ( " _YELLOW_("100 %%") " ) ", keycnt, keycnt); PrintAndLogEx(NORMAL, ""); + + // reset chunks when swapping strategies firstChunk = true; lastChunk = false; + if (blockn != -1) { break; } @@ -3967,9 +4022,6 @@ out: PrintAndLogEx(WARNING, "No keys found"); } else { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - printKeyTable(sectorsCnt, e_sector); if (use_flashmemory && found_keys == (sectorsCnt << 1)) { @@ -4188,9 +4240,6 @@ out: PrintAndLogEx(WARNING, "No keys found"); } else { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - printKeyTable(sectorsCnt, e_sector); if (transferToEml) { @@ -4355,9 +4404,9 @@ static int CmdHF14AMfChk(const char *Cmd) { uint8_t *keyBlock = NULL; uint32_t keycnt = 0; - int ret = mf_load_keys(&keyBlock, &keycnt, key, keylen, filename, fnlen, load_default); - if (ret != PM3_SUCCESS) { - return ret; + int res = mf_load_keys(&keyBlock, &keycnt, key, keylen, filename, fnlen, load_default); + if (res != PM3_SUCCESS) { + return res; } uint64_t key64 = 0; @@ -4406,7 +4455,8 @@ static int CmdHF14AMfChk(const char *Cmd) { uint32_t size = keycnt - c > max_keys ? max_keys : keycnt - c; - if (mf_check_keys(b, trgKeyType, clearLog, size, &keyBlock[MIFARE_KEY_SIZE * c], &key64) == PM3_SUCCESS) { + res = mf_check_keys(b, trgKeyType, clearLog, size, &keyBlock[MIFARE_KEY_SIZE * c], &key64); + if (res == PM3_SUCCESS) { e_sector[i].Key[trgKeyType] = key64; e_sector[i].foundKey[trgKeyType] = true; clearLog = false; @@ -4414,18 +4464,21 @@ static int CmdHF14AMfChk(const char *Cmd) { } clearLog = false; } - if (singleSector) + + if (singleSector) { break; + } b < 127 ? (b += 4) : (b += 16); } } + t1 = msclock() - t1; - PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + PrintAndLogEx(INFO, "\nTime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? if (keyType != MF_KEY_B) { - PrintAndLogEx(INFO, "testing to read key B..."); + PrintAndLogEx(INFO, "Testing to read key B..."); // loop sectors but block is used as to keep track of from which blocks to test int b = blockNo; @@ -4472,9 +4525,6 @@ static int CmdHF14AMfChk(const char *Cmd) { } out: - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - //print keys // if (singleSector) // printKeyTableEx(1, e_sector, mfSectorNum(blockNo)); @@ -4485,15 +4535,18 @@ out: // fast push mode g_conn.block_after_ACK = true; uint8_t block[MFBLOCK_SIZE] = {0x00}; + for (int i = 0; i < sectors_cnt; ++i) { uint8_t blockno = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; mf_eml_get_mem(block, blockno, 1); - if (e_sector[i].foundKey[0]) + if (e_sector[i].foundKey[0]) { num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, block); + } - if (e_sector[i].foundKey[1]) + if (e_sector[i].foundKey[1]) { num_to_bytes(e_sector[i].Key[1], MIFARE_KEY_SIZE, block + 10); + } if (i == sectors_cnt - 1) { // Disable fast mode on last packet @@ -4855,7 +4908,7 @@ static int CmdHF14AMfKeyBrute(const char *Cmd) { uint64_t t1 = msclock(); if (mfKeyBrute(blockNo, keytype, key, &foundkey)) - PrintAndLogEx(SUCCESS, "found valid key: %012" PRIx64 " \n", foundkey); + PrintAndLogEx(SUCCESS, "Found valid key [ %012" PRIX64 " ]\n", foundkey); else PrintAndLogEx(FAILED, "key not found"); @@ -6546,8 +6599,9 @@ static int CmdHf14AMfNack(const char *Cmd) { bool verbose = arg_get_lit(ctx, 1); CLIParserFree(ctx); - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "Started testing card for NACK bug. Press Enter to abort"); + } detect_classic_nackbug(verbose); return PM3_SUCCESS; @@ -10130,7 +10184,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { } if (keylen != 0 && keylen != MIFARE_KEY_SIZE) { - PrintAndLogEx(ERR, "Key length must be %u bytes", MIFARE_KEY_SIZE); + PrintAndLogEx(ERR, "Key length must be %u bytes, got %d", MIFARE_KEY_SIZE, keylen); return PM3_EINVARG; } diff --git a/client/src/cmdhfmfhard.c b/client/src/cmdhfmfhard.c index 8381a4c8d..a04db22ec 100644 --- a/client/src/cmdhfmfhard.c +++ b/client/src/cmdhfmfhard.c @@ -127,7 +127,6 @@ static void print_progress_header(void) { get_SIMD_instruction_set(instr_set); snprintf(progress_text, sizeof(progress_text), "Start using " _YELLOW_("%d") " threads and " _YELLOW_("%s") " SIMD core", num_CPUs(), instr_set); - PrintAndLogEx(INFO, "Hardnested attack starting..."); PrintAndLogEx(INFO, "---------+---------+---------------------------------------------------------+-----------------+-------"); PrintAndLogEx(INFO, " | | | Expected to brute force"); PrintAndLogEx(INFO, " Time | #nonces | Activity | #states | time "); @@ -136,12 +135,16 @@ static void print_progress_header(void) { } void hardnested_print_progress(uint32_t nonces, const char *activity, float brute_force, uint64_t min_diff_print_time) { + static uint64_t last_print_time = 0; + if (msclock() - last_print_time >= min_diff_print_time) { + last_print_time = msclock(); uint64_t total_time = msclock() - start_time; float brute_force_time = brute_force / brute_force_per_second; char brute_force_time_string[20]; + if (brute_force_time < 90) { snprintf(brute_force_time_string, sizeof(brute_force_time_string), "%2.0fs", brute_force_time); } else if (brute_force_time < 60 * 90) { @@ -151,7 +154,24 @@ void hardnested_print_progress(uint32_t nonces, const char *activity, float brut } else { snprintf(brute_force_time_string, sizeof(brute_force_time_string), "%2.0fd", brute_force_time / (60 * 60 * 24)); } - PrintAndLogEx(INFO, " %7.0f | %7u | %-55s | %15.0f | %5s", (float)total_time / 1000.0, nonces, activity, brute_force, brute_force_time_string); + + if (strlen(activity) > 67) { + PrintAndLogEx(INFO, " %7.0f | %7u | %-82s | %15.0f | %5s" + , (float)total_time / 1000.0 + , nonces + , activity + , brute_force + , brute_force_time_string + ); + } else { + PrintAndLogEx(INFO, " %7.0f | %7u | %-55s | %15.0f | %5s" + , (float)total_time / 1000.0 + , nonces + , activity + , brute_force + , brute_force_time_string + ); + } } } @@ -486,8 +506,14 @@ static void init_bitflip_bitarrays(void) { effective_bitflip[odd_even][num_effective_bitflips[odd_even]] = 0x400; // EndOfList marker } { - char progress_text[80]; - snprintf(progress_text, sizeof(progress_text), "Loaded %u RAW / %u LZ4 / %u BZ2 in %"PRIu64" ms", nraw, nlz4, nbz2, msclock() - init_bitflip_bitarrays_starttime); + char progress_text[100]; + memset(progress_text, 0, sizeof(progress_text)); + snprintf(progress_text, sizeof(progress_text), "Loaded " _YELLOW_("%u") " RAW / " _YELLOW_("%u") " LZ4 / " _YELLOW_("%u") " BZ2 in %"PRIu64" ms" + , nraw + , nlz4 + , nbz2 + , msclock() - init_bitflip_bitarrays_starttime + ); hardnested_print_progress(0, progress_text, (float)(1LL << 47), 0); } uint16_t i = 0; @@ -2481,8 +2507,10 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc free_candidates_memory(candidates); candidates = NULL; } else { + pre_XOR_nonces(); prepare_bf_test_nonces(nonces, best_first_bytes[0]); + for (uint8_t j = 0; j < NUM_SUMS && !key_found; j++) { float expected_brute_force = nonces[best_first_bytes[0]].expected_num_brute_force; snprintf(progress_text, sizeof(progress_text), "(%d. guess: Sum(a8) = %" PRIu16 ")", j + 1, sums[nonces[best_first_bytes[0]].sum_a8_guess[j].sum_a8_idx]); @@ -2544,7 +2572,9 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc int res; if (nonce_file_read) { // use pre-acquired data from file nonces.bin + res = read_nonce_file(filename); + if (res != PM3_SUCCESS) { free_bitflip_bitarrays(); free_nonces_memory(); @@ -2554,12 +2584,16 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc free_part_sum_bitarrays(); return res; } + hardnested_stage = CHECK_1ST_BYTES | CHECK_2ND_BYTES; update_nonce_data(false); float brute_force_depth; shrink_key_space(&brute_force_depth); + } else { // acquire nonces. + res = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow, filename); + if (res != PM3_SUCCESS) { free_bitflip_bitarrays(); free_nonces_memory(); diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index dc5684e43..6433bcf2e 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -1802,9 +1802,6 @@ static int CmdHFMFPChk(const char *Cmd) { t1 = msclock() - t1; PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - // print result char strA[46 + 1] = {0}; char strB[46 + 1] = {0}; diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index ccf0de708..1815705de 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -59,11 +59,13 @@ static int CmdHelp(const char *Cmd); +static const char *key_type[] = { "DataProtKey", "UIDRetrKey", "OriginalityKey" }; + static uint8_t default_aes_keys[][16] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // all zeroes { 0x42, 0x52, 0x45, 0x41, 0x4b, 0x4d, 0x45, 0x49, 0x46, 0x59, 0x4f, 0x55, 0x43, 0x41, 0x4e, 0x21 }, // 3des std key - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46 }, // NFC-key + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // all ones { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // all FF { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, // 11 22 33 @@ -74,8 +76,8 @@ static uint8_t default_aes_keys[][16] = { static uint8_t default_3des_keys[][16] = { { 0x42, 0x52, 0x45, 0x41, 0x4b, 0x4d, 0x45, 0x49, 0x46, 0x59, 0x4f, 0x55, 0x43, 0x41, 0x4e, 0x21 }, // 3des std key { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // all zeroes - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46 }, // NFC-key + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // all ones { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // all FF { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, // 11 22 33 @@ -3836,18 +3838,21 @@ static int CmdHF14AMfUSim(const char *Cmd) { "ISO/IEC 14443 type A tag with 4,7 or 10 byte UID\n" "from emulator memory. See `hf mfu eload` first. \n" "The UID from emulator memory will be used if not specified.\n" - "See `hf 14a sim -h` to see available types. You want 2 or 7 usually.", + "See `hf 14a sim -h` to see available types. You want 2, 7 or 13 usually.", "hf mfu sim -t 2 --uid 11223344556677 -> MIFARE Ultralight\n" "hf mfu sim -t 7 --uid 11223344556677 -n 5 -> MFU EV1 / NTAG 215 Amiibo\n" - "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo" + "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo\n" + "hf mfu sim -t 13 -> MIFARE Ultralight-C\n" ); void *argtable[] = { arg_param_begin, - arg_int1("t", "type", "<1..12> ", "Simulation type to use"), + arg_int1("t", "type", "<1..13> ", "Simulation type to use"), arg_str0("u", "uid", "", "<4|7|10> hex bytes UID"), arg_int0("n", "num", "", "Exit simulation after blocks. 0 = infinite"), arg_lit0("v", "verbose", "Verbose output"), + arg_lit0(NULL, "c1", "UL-C Auth - all zero handshake part 1"), + arg_lit0(NULL, "c2", "UL-C Auth - all zero handshake part 2"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -3966,14 +3971,13 @@ static int CmdHF14AMfUAESAuth(const char *Cmd) { } int result = ulaes_requestAuthentication(authKeyPtr, key_index, !keep_field_on); - - const char *key_type[] = { "DataProtKey", "UIDRetrKey", "OriginalityKey" }; if (result == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Authentication with " _YELLOW_("%s") " " _GREEN_("%s") " ( " _GREEN_("ok")" )", - key_type[key_index], sprint_hex_inrow(authKeyPtr, ak_len)); + PrintAndLogEx(SUCCESS, "Authentication with " _YELLOW_("%s") " " _GREEN_("%s") " ( " _GREEN_("ok")" )" + , key_type[key_index] + , sprint_hex_inrow(authKeyPtr, ak_len) + ); } else { - PrintAndLogEx(WARNING, "Authentication with " _YELLOW_("%s") " ( " _RED_("fail") " )", - key_type[key_index]); + PrintAndLogEx(WARNING, "Authentication with " _YELLOW_("%s") " ( " _RED_("fail") " )", key_type[key_index]); } return result; } diff --git a/client/src/cmdlf.c b/client/src/cmdlf.c index 0282f6c72..20940ae6d 100644 --- a/client/src/cmdlf.c +++ b/client/src/cmdlf.c @@ -83,7 +83,7 @@ int lfsim_wait_check(uint32_t cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); break; } diff --git a/client/src/cmdlfem4x70.c b/client/src/cmdlfem4x70.c index 75b5da1e7..8fd80e3d7 100644 --- a/client/src/cmdlfem4x70.c +++ b/client/src/cmdlfem4x70.c @@ -587,7 +587,7 @@ static int CmdEM4x70Brute(const char *Cmd) { em4x70_cmd_output_brute_t data; int result = brute_em4x70(&opts, &data); if (result == PM3_EOPABORTED) { - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); } else if (result == PM3_ETIMEOUT) { PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting..."); } else if (result == PM3_SUCCESS) { diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 425b0412c..63c25ff99 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -498,7 +498,7 @@ static int ht2_check_dictionary(uint32_t key_count, uint8_t *keys, uint8_t keyl if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(INFO, "User aborted"); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); break; } diff --git a/client/src/cmdparser.c b/client/src/cmdparser.c index cf6bb1941..6b3984ad4 100644 --- a/client/src/cmdparser.c +++ b/client/src/cmdparser.c @@ -20,7 +20,8 @@ #include #include - +#include // spinlock +#include // system #include "ui.h" #include "comms.h" #include "util_posix.h" // msleep @@ -219,6 +220,28 @@ void CmdsHelp(const command_t Commands[]) { PrintAndLogEx(NORMAL, ""); } +pthread_spinlock_t sycmd_spinlock; + +static int execute_system_command(const char *command) { + + int ret; + + pthread_spin_lock(&sycmd_spinlock); +#if defined(_WIN32) + char wrapped_command[255]; + strncat(wrapped_command, "cmd /C \"", 9); + strncat(wrapped_command, command, strlen(command)); + strncat(wrapped_command, "\"", 2); + + ret = system(wrapped_command); +#else + ret = system(command); +#endif + pthread_spin_unlock(&sycmd_spinlock); + return ret; +} + + int CmdsParse(const command_t Commands[], const char *Cmd) { if (g_session.client_exe_delay != 0) { @@ -267,6 +290,12 @@ int CmdsParse(const command_t Commands[], const char *Cmd) { return PM3_SUCCESS; } + if (Cmd[0] == '!') { + pthread_spin_init(&sycmd_spinlock, 0); + int res = execute_system_command(Cmd + 1); + pthread_spin_destroy(&sycmd_spinlock); + return res; + } char cmd_name[128] = {0}; memset(cmd_name, 0, sizeof(cmd_name)); diff --git a/client/src/emv/emvcore.c b/client/src/emv/emvcore.c index b2aa524ac..b663af706 100644 --- a/client/src/emv/emvcore.c +++ b/client/src/emv/emvcore.c @@ -484,7 +484,7 @@ int EMVSearch(Iso7816CommandChannel channel, bool ActivateField, bool LeaveField for (int i = 0; i < ARRAYLEN(AIDlist); i ++) { if (kbd_enter_pressed()) { - PrintAndLogEx(INFO, "user aborted..."); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); break; } diff --git a/client/src/fileutils.c b/client/src/fileutils.c index aee5e827c..3bea9f3c4 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -3075,6 +3075,82 @@ int searchFile(char **foundpath, const char *pm3dir, const char *searchname, con return res; } +/** + * Inserts a line into a text file only if it does not already exist. + * Returns PM3_SUCCES or, PM3_EFILE; + * + * @param filepath Path to the file. + * @param line Line to insert (should not contain a trailing newline). + */ +int insert_line_if_not_exists(const char *preferredName, const char *keystr) { + + char *path; + int res = searchFile(&path, DICTIONARIES_SUBDIR, preferredName, ".dic", false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + FILE *f = fopen(path, "r"); + if (f == NULL) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + + // Maximum line length we assume (adjust as necessary for your use case) + char line[255]; + bool key_exists = false; + + char *keystrdup = str_dup(keystr); + str_upper(keystrdup); + + // First pass: check if the line exists + while (fgets(line, sizeof(line), f)) { + + // The line start with # is comment, skip + if (line[0] == '#') { + continue; + } + + // Remove trailing newline for comparison + line[strcspn(line, "\n")] = '\0'; + + // UPPER CASE + str_upper(line); + + key_exists = str_startswith(line, keystrdup); + if (key_exists) { + fclose(f); + free(path); + PrintAndLogEx(INFO, "already in there..."); + return PM3_SUCCESS; + } + } + + fclose(f); + + + // Reopen for appending if line doesn't exist + f = fopen(path, "a"); + if (f == NULL) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + + free(path); + + // Append the line with a newline + if (fprintf(f, "%s\n", keystrdup) < 0) { + PrintAndLogEx(WARNING, "error writing to file"); + fclose(f); + return PM3_EFILE; + } + + fclose(f); + return PM3_SUCCESS; +} + int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumplen) { int res = PM3_SUCCESS; diff --git a/client/src/fileutils.h b/client/src/fileutils.h index 6fc450dd1..3e8b022c1 100644 --- a/client/src/fileutils.h +++ b/client/src/fileutils.h @@ -385,4 +385,15 @@ int pm3_save_mf_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft); * @return PM3_SUCCESS if OK */ int pm3_save_fm11rf08s_nonces(const char *fn, iso14a_fm11rf08s_nonces_with_data_t *d, bool with_data); + + +/** + * Inserts a line into a text file only if it does not already exist. + * Returns PM3_SUCCES or, PM3_EFILE; + * + * @param filepath Path to the file. + * @param line Line to insert (should not contain a trailing newline). + */ +int insert_line_if_not_exists(const char *preferredName, const char *line); + #endif // FILEUTILS_H diff --git a/client/src/loclass/cipherutils.c b/client/src/loclass/cipherutils.c index 83b23c698..2ff42274d 100644 --- a/client/src/loclass/cipherutils.c +++ b/client/src/loclass/cipherutils.c @@ -126,47 +126,67 @@ uint64_t x_bytes_to_num(uint8_t *src, size_t len) { } void printarr(const char *name, uint8_t *arr, int len) { - if (name == NULL || arr == NULL) return; + + if (name == NULL || arr == NULL) { + return; + } int cx, i; size_t outsize = 40 + strlen(name) + len * 5; + char *output = calloc(outsize, sizeof(char)); if (output == NULL) { PrintAndLogEx(WARNING, "Failed to allocate memory"); return; } + cx = snprintf(output, outsize, "uint8_t %s[] = {", name); for (i = 0; i < len; i++) { - if (cx < outsize) + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "0x%02x,", *(arr + i)); //5 bytes per byte + } } - if (cx < outsize) + + if (cx < outsize) { snprintf(output + cx, outsize - cx, "};"); + } + PrintAndLogEx(INFO, output); free(output); } void printarr_human_readable(const char *title, uint8_t *arr, int len) { - if (arr == NULL) return; + if (arr == NULL) { + return; + } int cx = 0, i; size_t outsize = 100 + strlen(title) + (len * 4); char *output = calloc(outsize, sizeof(char)); PrintAndLogEx(INFO, "%s", title); + for (i = 0; i < len; i++) { + if (i % 16 == 0) { if (i == 0) { - if (cx < outsize) + + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "%02x| ", i); + } + } else { - if (cx < outsize) + + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "\n%02x| ", i); + } } } - if (cx < outsize) + + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "%02x ", *(arr + i)); + } } PrintAndLogEx(INFO, output); free(output); @@ -233,11 +253,14 @@ static int testReversedBitstream(void) { } int testCipherUtils(void) { - PrintAndLogEx(INFO, "Testing some internals..."); - int retval = testBitStream(); - if (retval == PM3_SUCCESS) - retval = testReversedBitstream(); - return retval; + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "---------------- " _CYAN_("Loclass selftests") " ----------------"); + + int res = testBitStream(); + if (res == PM3_SUCCESS) { + res = testReversedBitstream(); + } + return res; } #endif diff --git a/client/src/loclass/elite_crack.c b/client/src/loclass/elite_crack.c index 01ffe7b29..ad7beb249 100644 --- a/client/src/loclass/elite_crack.c +++ b/client/src/loclass/elite_crack.c @@ -248,10 +248,12 @@ void hash2(uint8_t *key64, uint8_t *outp_keytable) { } if (outp_keytable != NULL) { + for (uint8_t i = 0 ; i < 8 ; i++) { memcpy(outp_keytable + i * 16, y[i], 8); memcpy(outp_keytable + 8 + i * 16, z[i], 8); } + } else { printarr_human_readable("hash2", outp_keytable, 128); } @@ -329,7 +331,9 @@ static void *bf_thread(void *thread_arg) { int found = __atomic_load_n(&loclass_found, __ATOMIC_SEQ_CST); - if (found != 0xFF) return NULL; + if (found != 0xFF) { + return NULL; + } //Update the keytable with the brute-values for (uint8_t i = 0; i < numbytes_to_recover; i++) { @@ -383,15 +387,22 @@ static void *bf_thread(void *thread_arg) { #define _CLR_ "\x1b[0K" if (numbytes_to_recover == 3) { + if ((brute > 0) && ((brute & 0xFFFF) == 0)) { PrintAndLogEx(INPLACE, "[ %02x %02x %02x ] %8u / %u", bytes_to_recover[0], bytes_to_recover[1], bytes_to_recover[2], brute, 0xFFFFFF); } + } else if (numbytes_to_recover == 2) { - if ((brute > 0) && ((brute & 0x3F) == 0)) + + if ((brute > 0) && ((brute & 0x3F) == 0)) { PrintAndLogEx(INPLACE, "[ %02x %02x ] %5u / %u" _CLR_, bytes_to_recover[0], bytes_to_recover[1], brute, 0xFFFF); + } + } else { - if ((brute > 0) && ((brute & 0x1F) == 0)) + + if ((brute > 0) && ((brute & 0x1F) == 0)) { PrintAndLogEx(INPLACE, "[ %02x ] %3u / %u" _CLR_, bytes_to_recover[0], brute, 0xFF); + } } } pthread_exit(NULL); @@ -424,15 +435,19 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { uint8_t bytes_to_recover[3] = {0}; uint8_t numbytes_to_recover = 0; for (uint8_t i = 0; i < 8; i++) { - if (keytable[key_index[i]] & (LOCLASS_CRACKED | LOCLASS_BEING_CRACKED)) continue; + + if (keytable[key_index[i]] & (LOCLASS_CRACKED | LOCLASS_BEING_CRACKED)) { + continue; + } bytes_to_recover[numbytes_to_recover++] = key_index[i]; + keytable[key_index[i]] |= LOCLASS_BEING_CRACKED; if (numbytes_to_recover > 3) { PrintAndLogEx(FAILED, "The CSN requires > 3 byte bruteforce, not supported"); - PrintAndLogEx(INFO, "CSN %s", sprint_hex(item.csn, 8)); - PrintAndLogEx(INFO, "HASH1 %s", sprint_hex(key_index, 8)); + PrintAndLogEx(INFO, "CSN..... %s", sprint_hex_inrow(item.csn, 8)); + PrintAndLogEx(INFO, "HASH1... %s", sprint_hex_inrow(key_index, 8)); PrintAndLogEx(NORMAL, ""); //Before we exit, reset the 'BEING_CRACKED' to zero keytable[bytes_to_recover[0]] &= ~LOCLASS_BEING_CRACKED; @@ -443,6 +458,7 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { } if (numbytes_to_recover == 0) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "No bytes to recover, exiting"); return PM3_ESOFT; } @@ -472,8 +488,9 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { } // wait for threads to terminate: void *ptrs[loclass_tc]; - for (size_t i = 0; i < loclass_tc; i++) + for (size_t i = 0; i < loclass_tc; i++) { pthread_join(threads[i], &ptrs[i]); + } // was it a success? int res = PM3_SUCCESS; @@ -661,7 +678,6 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { * @return 0 for ok, 1 for failz */ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { - mbedtls_des_context ctx_e; uint8_t z_0[8] = {0}; uint8_t y_0[8] = {0}; @@ -680,8 +696,9 @@ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { permutekey_rev(z_0, z_0_rev); // ~K_cus = DESenc(z[0], y[0]) - mbedtls_des_setkey_enc(&ctx_e, z_0_rev); - mbedtls_des_crypt_ecb(&ctx_e, y_0, key64_negated); + mbedtls_des_context ctx; + mbedtls_des_setkey_enc(&ctx, z_0_rev); + mbedtls_des_crypt_ecb(&ctx, y_0, key64_negated); key64[0] = ~key64_negated[0]; key64[1] = ~key64_negated[1]; @@ -697,20 +714,24 @@ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { uint8_t key64_stdformat[8] = {0}; permutekey_rev(key64, key64_stdformat); - mbedtls_des_setkey_enc(&ctx_e, key64_stdformat); - mbedtls_des_crypt_ecb(&ctx_e, key64_negated, result); + mbedtls_des_setkey_enc(&ctx, key64_stdformat); + mbedtls_des_crypt_ecb(&ctx, key64_negated, result); + mbedtls_des_free(&ctx); - if (kcus != NULL) + // copy key to out array + if (kcus != NULL) { memcpy(kcus, key64, 8); + } if (memcmp(z_0, result, 4) != 0) { - PrintAndLogEx(WARNING, _RED_("Failed to verify") " calculated master key (k_cus)! Something is wrong."); + PrintAndLogEx(WARNING, "Calculated master key, k_cus ( %s )", _RED_("fail")); + PrintAndLogEx(NORMAL, ""); return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "----- " _CYAN_("High security custom key (Kcus)") " -----"); - PrintAndLogEx(SUCCESS, "Standard format %s", sprint_hex(key64_stdformat, 8)); - PrintAndLogEx(SUCCESS, "iCLASS format " _GREEN_("%s"), sprint_hex(key64, 8)); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("High security custom key (Kcus)") " ---"); + PrintAndLogEx(SUCCESS, "Standard format... %s", sprint_hex_inrow(key64_stdformat, sizeof(key64_stdformat))); + PrintAndLogEx(SUCCESS, "iCLASS format..... " _GREEN_("%s"), sprint_hex_inrow(key64, sizeof(key64))); PrintAndLogEx(SUCCESS, "Key verified ( " _GREEN_("ok") " )"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; @@ -723,7 +744,7 @@ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { * @return */ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { - uint8_t i; + size_t itemsize = sizeof(loclass_dumpdata_t); loclass_dumpdata_t *attack = (loclass_dumpdata_t *) calloc(itemsize, sizeof(uint8_t)); if (attack == NULL) { @@ -737,19 +758,28 @@ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { int res = 0; uint64_t t1 = msclock(); - for (i = 0 ; i * itemsize < dumpsize ; i++) { + for (uint16_t i = 0 ; i * itemsize < dumpsize ; i++) { + memcpy(attack, dump + i * itemsize, itemsize); + res = bruteforceItem(*attack, keytable); - if (res != PM3_SUCCESS) + if (res != PM3_SUCCESS) { break; + } } + free(attack); + t1 = msclock() - t1; - PrintAndLogEx(NORMAL, ""); + if (res == PM3_SUCCESS) { + PrintAndLogEx(NORMAL, ""); + } PrintAndLogEx(SUCCESS, "time " _YELLOW_("%" PRIu64) " seconds", t1 / 1000); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "loclass exiting. Try run " _YELLOW_("`hf iclass sim -t 2`") " again and collect new data"); + PrintAndLogEx(ERR, "loclass key recovery( %s )", _RED_("fail")); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass sim -t 2") "` again and collect new data"); + PrintAndLogEx(NORMAL, ""); return PM3_ESOFT; } @@ -758,11 +788,12 @@ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { // indicate crack-status. Those must be discarded for the // master key calculation uint8_t first16bytes[16] = {0}; - for (i = 0 ; i < 16 ; i++) { + for (uint8_t i = 0 ; i < 16 ; i++) { first16bytes[i] = keytable[i] & 0xFF; if ((keytable[i] & LOCLASS_CRACKED) != LOCLASS_CRACKED) { PrintAndLogEx(WARNING, "Warning: we are missing byte " _RED_("%d") " , custom key calculation will fail...", i); + PrintAndLogEx(NORMAL, ""); return PM3_ESOFT; } } @@ -873,7 +904,7 @@ static int _testHash1(void) { } int testElite(bool slowtests) { - PrintAndLogEx(INFO, "Testing iClass Elite functionality"); + PrintAndLogEx(INFO, "Testing iClass Elite functionality..."); PrintAndLogEx(INFO, "Testing hash2..."); uint8_t k_cus[8] = {0x5B, 0x7C, 0x62, 0xC4, 0x91, 0xC1, 0x1B, 0x39}; @@ -894,22 +925,23 @@ int testElite(bool slowtests) { */ uint8_t keytable[128] = {0}; hash2(k_cus, keytable); - printarr_human_readable("---------------------- Hash2 ----------------------", keytable, sizeof(keytable)); + printarr_human_readable("--------------------- " _CYAN_("Hash2") " -----------------------", keytable, sizeof(keytable)); if (keytable[3] == 0xA1 && keytable[0x30] == 0xA3 && keytable[0x6F] == 0x95) { - PrintAndLogEx(SUCCESS, " hash2 ( %s )", _GREEN_("ok")); + PrintAndLogEx(SUCCESS, " Hash2 ( %s )", _GREEN_("ok")); } int res = PM3_SUCCESS; PrintAndLogEx(INFO, "Testing hash1..."); res += _testHash1(); - PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " hash1 ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " Hash1 ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); PrintAndLogEx(INFO, "Testing key diversification..."); res += _test_iclass_key_permutation(); - PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " key diversification ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " Key diversification ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); - if (slowtests) + if (slowtests) { res += _testBruteforce(); + } return res; } diff --git a/client/src/loclass/ikeys.c b/client/src/loclass/ikeys.c index 9382074fb..b50699e9e 100644 --- a/client/src/loclass/ikeys.c +++ b/client/src/loclass/ikeys.c @@ -803,17 +803,17 @@ static bool des_getParityBitFromKey(uint8_t key) { } static void des_checkParity(uint8_t *key) { - int i; int fails = 0; - for (i = 0; i < 8; i++) { + for (uint8_t i = 0; i < 8; i++) { bool parity = des_getParityBitFromKey(key[i]); if (parity != (key[i] & 0x1)) { fails++; PrintAndLogEx(FAILED, "parity1 fail, byte %d [%02x] was %d, should be %d", i, key[i], (key[i] & 0x1), parity); } } + if (fails) { - PrintAndLogEx(FAILED, "parity fails: %d", fails); + PrintAndLogEx(FAILED, "parity fails... " _RED_("%d"), fails); } else { PrintAndLogEx(SUCCESS, " Key syntax is with parity bits inside each byte (%s)", _GREEN_("ok")); } @@ -894,15 +894,17 @@ static int testKeyDiversificationWithMasterkeyTestcases(uint8_t *key) { int i, error = 0; uint8_t empty[8] = {0}; - PrintAndLogEx(INFO, "Testing encryption/decryption"); + PrintAndLogEx(INFO, "Testing encryption/decryption..."); - for (i = 0; memcmp(testcases + i, empty, 8); i++) + for (i = 0; memcmp(testcases + i, empty, 8); i++) { error += testDES(key, testcases[i]); + } - if (error) - PrintAndLogEx(FAILED, "%d errors occurred (%d testcases)", error, i); - else - PrintAndLogEx(SUCCESS, "Hashing seems to work (%d testcases)", i); + if (error) { + PrintAndLogEx(FAILED, "%d errors occurred, %d testcases ( %s )", error, i, _RED_("fail")); + } else { + PrintAndLogEx(SUCCESS, " Hashing seems to work, " _YELLOW_("%d") " testcases ( %s )", i, _GREEN_("ok")); + } return error; } @@ -942,8 +944,9 @@ static int testDES2(uint8_t *key, uint64_t csn, uint64_t expected) { PrintAndLogEx(DEBUG, " {csn} %"PRIx64, crypt_csn); PrintAndLogEx(DEBUG, " expected %"PRIx64 " (%s)", expected, (expected == crypt_csn) ? _GREEN_("ok") : _RED_("fail")); - if (expected != crypt_csn) + if (expected != crypt_csn) { return PM3_ESOFT; + } return PM3_SUCCESS; } @@ -954,12 +957,12 @@ static int testDES2(uint8_t *key, uint64_t csn, uint64_t expected) { */ static int doTestsWithKnownInputs(void) { // KSel from http://www.proxmark.org/forum/viewtopic.php?pid=10977#p10977 - PrintAndLogEx(INFO, "Testing DES encryption"); + PrintAndLogEx(INFO, "Testing DES encryption... "); uint8_t key[8] = {0x6c, 0x8d, 0x44, 0xf9, 0x2a, 0x2d, 0x01, 0xbf}; testDES2(key, 0xbbbbaaaabbbbeeee, 0xd6ad3ca619659e6b); - PrintAndLogEx(INFO, "Testing hashing algorithm"); + PrintAndLogEx(INFO, "Testing hashing algorithm... "); int res = PM3_SUCCESS; res += testCryptedCSN(0x0102030405060708, 0x0bdd6512073c460a); @@ -973,10 +976,10 @@ static int doTestsWithKnownInputs(void) { res += testCryptedCSN(0x14e2adfc5bb7e134, 0x6ac90c6508bd9ea3); if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "%d res occurred (9 testcases)", res); + PrintAndLogEx(FAILED, "%d res occurred " _YELLOW_("9") " testcases ( %s )", res, _RED_("fail")); res = PM3_ESOFT; } else { - PrintAndLogEx(SUCCESS, "Hashing seems to work (9 testcases)"); + PrintAndLogEx(SUCCESS, " Hashing seems to work " _YELLOW_("9") " testcases ( %s )", _GREEN_("ok")); res = PM3_SUCCESS; } return res; @@ -986,6 +989,7 @@ int doKeyTests(void) { uint8_t key[8] = { 0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78 }; uint8_t parity[8] = {0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01}; + for (int i = 0; i < 8; i++) { key[i] += parity[i]; } @@ -994,7 +998,6 @@ int doKeyTests(void) { des_checkParity(key); // Test hashing functions - PrintAndLogEx(SUCCESS, "The following tests require the correct 8-byte master key"); testKeyDiversificationWithMasterkeyTestcases(key); PrintAndLogEx(INFO, "Testing key diversification with non-sensitive keys..."); return doTestsWithKnownInputs(); diff --git a/client/src/mifare/mad.c b/client/src/mifare/mad.c index c72f33c1b..eae149a4c 100644 --- a/client/src/mifare/mad.c +++ b/client/src/mifare/mad.c @@ -449,8 +449,9 @@ int MADDFDecodeAndPrint(uint32_t short_aid, bool verbose) { } bool HasMADKey(uint8_t *d) { - if (d == NULL) + if (d == NULL) { return false; + } return (memcmp(d + (3 * MFBLOCK_SIZE), g_mifare_mad_key, sizeof(g_mifare_mad_key)) == 0); } diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index 6688a7842..f6fc0b07a 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -43,6 +43,8 @@ #include "cmdhf14a.h" #include "gen4.h" #include "parity.h" +#include "pmflash.h" +#include "preferences.h" // setDeviceDebugLevel int mf_dark_side(uint8_t blockno, uint8_t key_type, uint64_t *key) { uint32_t uid = 0; @@ -275,7 +277,7 @@ int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastCh while (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "aborted via keyboard!"); return PM3_EOPABORTED; } @@ -305,12 +307,11 @@ int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastCh uint8_t curr_keys = resp.oldarg[0]; if ((singleSectorParams >> 15) & 1) { - if (curr_keys) { - // uint64_t foo = bytes_to_num(resp.data.asBytes, 6); - PrintAndLogEx(NORMAL, ""); -// PrintAndLogEx(SUCCESS, "found Key %s for block %2i found: " _GREEN_("%012" PRIx64), (singleSectorParams >> 8) & 1 ? "B" : "A", singleSectorParams & 0xFF, foo); - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]", + if (curr_keys) { + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "\nTarget block " _GREEN_("%4u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", singleSectorParams & 0xFF, ((singleSectorParams >> 8) & 1) ? 'B' : 'A', sprint_hex_inrow(resp.data.asBytes, MIFARE_KEY_SIZE) @@ -561,8 +562,9 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo struct p *package = (struct p *)resp.data.asBytes; // error during nested on device side - if (package->isOK != PM3_SUCCESS) + if (package->isOK != PM3_SUCCESS) { return package->isOK; + } memcpy(&uid, package->cuid, sizeof(package->cuid)); @@ -582,12 +584,14 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo pthread_t thread_id[2]; // create and run worker threads - for (uint8_t i = 0; i < 2; i++) + for (uint8_t i = 0; i < 2; i++) { pthread_create(thread_id + i, NULL, nested_worker_thread, &statelists[i]); + } // wait for threads to terminate: - for (uint8_t i = 0; i < 2; i++) + for (uint8_t i = 0; i < 2; i++) { pthread_join(thread_id[i], (void *)&statelists[i].head.slhead); + } // the first 16 Bits of the cryptostate already contain part of our key. // Create the intersection of the two lists based on these 16 Bits and @@ -596,9 +600,11 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo p2 = p4 = statelists[1].head.slhead; while (p1 <= statelists[0].tail.sltail && p2 <= statelists[1].tail.sltail) { + if (Compare16Bits(p1, p2) == 0) { struct Crypto1State savestate; + savestate = *p1; while (Compare16Bits(p1, &savestate) == 0 && p1 <= statelists[0].tail.sltail) { *p3 = *p1; @@ -606,6 +612,7 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo p3++; p1++; } + savestate = *p2; while (Compare16Bits(p2, &savestate) == 0 && p2 <= statelists[1].tail.sltail) { *p4 = *p2; @@ -613,6 +620,7 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo p4++; p2++; } + } else { while (Compare16Bits(p1, p2) == -1) p1++; while (Compare16Bits(p1, p2) == 1) p2++; @@ -635,13 +643,15 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo // Create the intersection statelists[0].len = intersection(statelists[0].head.keyhead, statelists[1].head.keyhead); + bool looped = false; + //statelists[0].tail.keytail = --p7; uint32_t keycnt = statelists[0].len; if (keycnt == 0) { goto out; } - PrintAndLogEx(SUCCESS, "Found " _YELLOW_("%u") " key candidates", keycnt); + PrintAndLogEx(SUCCESS, "Found " _YELLOW_("%u") " key candidate%c", keycnt, (keycnt > 1) ? 's' : ' '); memset(resultKey, 0, MIFARE_KEY_SIZE); uint64_t key64 = -1; @@ -659,44 +669,53 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo register uint8_t j; for (j = 0; j < size; j++) { crypto1_get_lfsr(statelists[0].head.slhead + i, &key64); - num_to_bytes(key64, 6, keyBlock + j * MIFARE_KEY_SIZE); + num_to_bytes(key64, MIFARE_KEY_SIZE, keyBlock + j * MIFARE_KEY_SIZE); } if (mf_check_keys(statelists[0].blockNo, statelists[0].keyType, false, size, keyBlock, &key64) == PM3_SUCCESS) { + + if (looped) { + PrintAndLogEx(NORMAL, ""); + } + free(statelists[0].head.slhead); free(statelists[1].head.slhead); - num_to_bytes(key64, 6, resultKey); + num_to_bytes(key64, MIFARE_KEY_SIZE, resultKey); if (package->keytype < 2) { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target block " _GREEN_("%4u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", package->block, package->keytype ? 'B' : 'A', sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE) ); } else { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %02x -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target block " _GREEN_("%4u") " key type " _GREEN_("%02x") " -- found valid key [ " _GREEN_("%s") " ]", package->block, MIFARE_AUTH_KEYA + package->keytype, sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE) ); } - - return PM3_SUCCESS; } float bruteforce_per_second = (float)(i + max_keys) / ((msclock() - start_time) / 1000.0); PrintAndLogEx(INPLACE, "%6d/%u keys | %5.1f keys/sec | worst case %6.1f seconds remaining", i, keycnt, bruteforce_per_second, (keycnt - i) / bruteforce_per_second); + looped = true; } out: + + if (looped) { + PrintAndLogEx(NORMAL, ""); + } + if (package->keytype < 2) { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c", + PrintAndLogEx(SUCCESS, "Target block " _YELLOW_("%4u") " key type " _YELLOW_("%c"), package->block, package->keytype ? 'B' : 'A' ); } else { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %02x", + PrintAndLogEx(SUCCESS, "Target block " _YELLOW_("%4u") " key type " _YELLOW_("%02x"), package->block, MIFARE_AUTH_KEYA + package->keytype ); @@ -911,10 +930,11 @@ int mf_static_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trg num_to_bytes(key64, MIFARE_KEY_SIZE, resultKey); - if (IfPm3Flash() && keycnt > 70) + if (IfPm3Flash() && keycnt > 70) { PrintAndLogEx(NORMAL, ""); + } - PrintAndLogEx(SUCCESS, "target block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "target block " _GREEN_("%4u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", package->block, package->keytype ? 'B' : 'A', sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE) @@ -1535,10 +1555,21 @@ int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, cdata[21] = corruptnrar; cdata[22] = corruptnrarparity; + uint8_t dbg_curr = DBG_NONE; + if (getDeviceDebugLevel(&dbg_curr) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + if (setDeviceDebugLevel(verbose ? MAX(dbg_curr, DBG_INFO) : DBG_NONE, false) != PM3_SUCCESS) { + return PM3_EFAILED; + } + clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, cdata, sizeof(cdata)); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1000)) { + if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1500)) { + + setDeviceDebugLevel(dbg_curr, false); if (resp.status == PM3_ESOFT) { return NONCE_FAIL; @@ -1604,6 +1635,8 @@ int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, } return resp.data.asBytes[0]; } + + setDeviceDebugLevel(dbg_curr, false); return NONCE_FAIL; } diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index 67bda5628..b916f0b71 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -308,16 +308,18 @@ static bool DetectWindowsAnsiSupport(void) { #endif // disable colors if stdin or stdout are redirected - if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) + if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) { return false; + } HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); DWORD dwMode = 0; GetConsoleMode(hOut, &dwMode); //ENABLE_VIRTUAL_TERMINAL_PROCESSING is already set - if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) + if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) { return true; + } dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; @@ -337,11 +339,13 @@ int push_cmdscriptfile(char *path, bool stayafter) { } FILE *f = fopen(path, "r"); - if (f == NULL) + if (f == NULL) { return PM3_EFILE; + } - if (cmdscriptfile_idx == 0) + if (cmdscriptfile_idx == 0) { cmdscriptfile_stayafter = stayafter; + } cmdscriptfile[++cmdscriptfile_idx] = f; return PM3_SUCCESS; @@ -373,28 +377,32 @@ main_loop(const char *script_cmds_file, char *script_cmd, bool stayInCommandLoop bool execCommand = (script_cmd != NULL); bool fromInteractive = false; uint16_t script_cmd_len = 0; + if (execCommand) { script_cmd_len = strlen(script_cmd); str_creplace(script_cmd, script_cmd_len, ';', '\0'); } + bool stdinOnPipe = !isatty(STDIN_FILENO); char script_cmd_buf[256] = {0x00}; // iceman, needs lua script the same file_path_buffer as the rest // cache Version information now: - if (execCommand || script_cmds_file || stdinOnPipe) + if (execCommand || script_cmds_file || stdinOnPipe) { pm3_version(false, false); - else + } else { pm3_version_short(); + } if (script_cmds_file) { char *path; int res = searchFile(&path, CMD_SCRIPTS_SUBDIR, script_cmds_file, ".cmd", false); if (res == PM3_SUCCESS) { - if (push_cmdscriptfile(path, stayInCommandLoop) == PM3_SUCCESS) + if (push_cmdscriptfile(path, stayInCommandLoop) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "executing commands from file: %s\n", path); - else + } else { PrintAndLogEx(ERR, "could not open " _YELLOW_("%s") "...", path); + } free(path); } } @@ -451,20 +459,23 @@ check_script: prompt_ctx = stdinOnPipe ? PROXPROMPT_CTX_STDIN : PROXPROMPT_CTX_SCRIPTCMD; cmd = str_dup(script_cmd); - if ((cmd != NULL) && (! fromInteractive)) + if ((cmd != NULL) && (! fromInteractive)) { printprompt = true; + } uint16_t len = strlen(script_cmd) + 1; script_cmd += len; - if (script_cmd_len == len - 1) + if (script_cmd_len == len - 1) { execCommand = false; + } script_cmd_len -= len; } else { // exit after exec command - if (script_cmd && !stayInCommandLoop) + if (script_cmd && !stayInCommandLoop) { break; + } // if there is a pipe from stdin if (stdinOnPipe) { @@ -554,22 +565,27 @@ check_script: mainret = CommandReceived(cmd); // exit or quit - if (mainret == PM3_EFATAL) + if (mainret == PM3_EFATAL) { break; + } + if (mainret == PM3_SQUIT) { // Normal quit, map to 0 mainret = PM3_SUCCESS; break; } } + free(cmd); cmd = NULL; + } else { PrintAndLogEx(NORMAL, "\n"); - if (script_cmds_file && stayInCommandLoop) + if (script_cmds_file && stayInCommandLoop) { stayInCommandLoop = false; - else + } else { break; + } } } // end while @@ -618,8 +634,9 @@ const char *get_my_executable_directory(void) { static void set_my_executable_path(void) { int path_length = wai_getExecutablePath(NULL, 0, NULL); - if (path_length == -1) + if (path_length == -1) { return; + } my_executable_path = (char *)calloc(path_length + 1, sizeof(uint8_t)); int dirname_length = 0; @@ -844,12 +861,13 @@ finish2: CloseProxmark(g_session.current_device); finish: - if (ret == PM3_SUCCESS) + if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, _CYAN_("All done")); - else if (ret == PM3_EOPABORTED) + } else if (ret == PM3_EOPABORTED) { PrintAndLogEx(FAILED, "Aborted by user"); - else + } else { PrintAndLogEx(ERR, "Aborted on error %u", ret); + } return ret; } @@ -908,8 +926,9 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *file goto finish; } - if (num_files == 0) + if (num_files == 0) { goto finish; + } for (int i = 0 ; i < num_files; ++i) { ret = flash_prepare(&files[i], can_write_bl, max_allowed * ONE_KB); @@ -938,12 +957,15 @@ finish2: for (int i = 0 ; i < num_files; ++i) { flash_free(&files[i]); } - if (ret == PM3_SUCCESS) + + if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, _CYAN_("All done")); - else if (ret == PM3_EOPABORTED) + } else if (ret == PM3_EOPABORTED) { PrintAndLogEx(FAILED, "Aborted by user"); - else + } else { PrintAndLogEx(ERR, "Aborted on error"); + } + PrintAndLogEx(INFO, "\nHave a nice day!"); return ret; } @@ -1054,6 +1076,7 @@ int main(int argc, char *argv[]) { show_help(false, exec_name); return 1; } + if (port != NULL) { // We got already one PrintAndLogEx(ERR, _RED_("ERROR:") " cannot parse command line. We got " _YELLOW_("%s") " as port and now we got also: " _YELLOW_("%s") "\n", port, argv[i + 1]); @@ -1315,21 +1338,22 @@ int main(int argc, char *argv[]) { // This will allow the command line to override the settings.json values preferences_load(); // quick patch for debug level - if (! debug_mode_forced) { + if (debug_mode_forced == false) { g_debugMode = g_session.client_debug_level; } // settings_save (); // End Settings // even if prefs, we disable colors if stdin or stdout is not a TTY - if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) { + if ((g_session.stdinOnTTY == false) || (g_session.stdoutOnTTY == false)) { g_session.supports_colors = false; g_session.emoji_mode = EMO_ALTTEXT; } // Let's take a baudrate ok for real UART, USB-CDC & BT don't use that info anyway - if (speed == 0) + if (speed == 0) { speed = USART_BAUD_RATE; + } if (dumpmem_mode) { dumpmem_pm3(port, dumpmem_filename, dumpmem_addr, dumpmem_len, dumpmem_raw); @@ -1347,8 +1371,9 @@ int main(int argc, char *argv[]) { } if (script_cmd) { - while (script_cmd[strlen(script_cmd) - 1] == ' ') + while (script_cmd[strlen(script_cmd) - 1] == ' ') { script_cmd[strlen(script_cmd) - 1] = 0x00; + } if (strlen(script_cmd) == 0) { script_cmd = NULL; @@ -1381,23 +1406,23 @@ int main(int argc, char *argv[]) { CloseProxmark(g_session.current_device); } - if ((port != NULL) && (!g_session.pm3_present)) { + if ((port != NULL) && (g_session.pm3_present == false)) { exit(EXIT_FAILURE); } - if (!g_session.pm3_present) { + if (g_session.pm3_present == false) { PrintAndLogEx(INFO, _YELLOW_("OFFLINE") " mode. Check " _YELLOW_("\"%s -h\"") " if it's not what you want.\n", exec_name); } // ascii art only in interactive client - if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && !dumpmem_mode && !flash_mode && !reboot_bootloader_mode) { + if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && (dumpmem_mode == false) && (flash_mode == false) && (reboot_bootloader_mode == false)) { showBanner(); } // Save settings if not loaded from settings json file. // Doing this here will ensure other checks and updates are saved to over rule default // e.g. Linux color use check - if ((!g_session.preferences_loaded) && (!g_session.incognito)) { + if ((g_session.preferences_loaded == false) && (g_session.incognito == false)) { PrintAndLogEx(INFO, "Creating initial preferences file"); // json save reports file name, so just info msg here preferences_save(); // Save defaults g_session.preferences_loaded = true; @@ -1417,7 +1442,7 @@ int main(int argc, char *argv[]) { #ifdef HAVE_GUI -# if defined(_WIN32) +# if defined(_WIN32) || (defined(__MACH__) && defined(__APPLE__)) InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop); MainGraphics(); # else diff --git a/client/src/util.c b/client/src/util.c index f5c3735d9..541b67dd8 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -1606,3 +1606,37 @@ uint8_t get_highest_frequency(const uint8_t *d, uint8_t n) { PrintAndLogEx(DEBUG, "highest occurance... %u xor byte... 0x%02X", highest, v); return v; } + +size_t unduplicate(uint8_t *d, size_t n, const uint8_t item_n) { + if (n == 0) { + return 0; + } + + int write_index = 0; + + for (int read_index = 0; read_index < n; ++read_index) { + uint8_t *current = d + read_index * item_n; + + bool is_duplicate = false; + + // Check against all previous unique elements + for (int i = 0; i < write_index; ++i) { + uint8_t *unique = d + i * item_n; + if (memcmp(current, unique, item_n) == 0) { + is_duplicate = 1; + break; + } + } + + // If not duplicate, move to the write_index position + if (is_duplicate == false) { + uint8_t *dest = d + write_index * item_n; + if (dest != current) { + memcpy(dest, current, item_n); + } + write_index++; + } + } + + return write_index; +} diff --git a/client/src/util.h b/client/src/util.h index 0ca53591e..c80e602f4 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -194,4 +194,7 @@ struct smartbuf { void sb_append_char(smartbuf *sb, unsigned char c); uint8_t get_highest_frequency(const uint8_t *d, uint8_t n); + +size_t unduplicate(uint8_t *d, size_t n, const uint8_t item_n); + #endif diff --git a/common/crapto1/crypto1.c b/common/crapto1/crypto1.c index 78d42cec4..8ffe04fdb 100644 --- a/common/crapto1/crypto1.c +++ b/common/crapto1/crypto1.c @@ -35,8 +35,9 @@ int filter(uint32_t const x) { (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16) void crypto1_init(struct Crypto1State *state, uint64_t key) { - if (state == NULL) + if (state == NULL) { return; + } state->odd = 0; state->even = 0; for (int i = 47; i > 0; i -= 2) { @@ -53,7 +54,9 @@ void crypto1_deinit(struct Crypto1State *state) { #if !defined(__arm__) || defined(__linux__) || defined(_WIN32) || defined(__APPLE__) // bare metal ARM Proxmark lacks calloc()/free() struct Crypto1State *crypto1_create(uint64_t key) { struct Crypto1State *state = calloc(sizeof(*state), sizeof(uint8_t)); - if (!state) return NULL; + if (state == NULL) { + return NULL; + } crypto1_init(state, key); return state; } @@ -145,8 +148,8 @@ uint32_t crypto1_word(struct Crypto1State *s, uint32_t in, int is_encrypted) { */ uint32_t prng_successor(uint32_t x, uint32_t n) { SWAPENDIAN(x); - while (n--) + while (n--) { x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31; - + } return SWAPENDIAN(x); } diff --git a/common_arm/ticks.c b/common_arm/ticks.c index 4abda2689..73182a11c 100644 --- a/common_arm/ticks.c +++ b/common_arm/ticks.c @@ -116,8 +116,9 @@ uint32_t RAMFUNC GetTickCount(void) { uint32_t RAMFUNC GetTickCountDelta(uint32_t start_ticks) { uint32_t stop_ticks = AT91C_BASE_RTTC->RTTC_RTVR; - if (stop_ticks >= start_ticks) + if (stop_ticks >= start_ticks) { return stop_ticks - start_ticks; + } return (UINT32_MAX - start_ticks) + stop_ticks; } diff --git a/doc/commands.json b/doc/commands.json index 362333ef8..034e055b1 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -1382,7 +1382,8 @@ "hf 14a sim -t 9 -> FM11RF005SH Shanghai Metro", "hf 14a sim -t 10 -> ST25TA IKEA Rothult", "hf 14a sim -t 11 -> Javacard (JCOP)", - "hf 14a sim -t 12 -> 4K Seos card" + "hf 14a sim -t 12 -> 4K Seos card", + "hf 14a sim -t 13 -> MIFARE Ultralight C" ], "offline": false, "options": [ @@ -1392,9 +1393,11 @@ "-n, --num Exit simulation after blocks have been read by reader. 0 = infinite", "-x Performs the 'reader attack', nr/ar attack against a reader", "--sk Fill simulator keys from found keys", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--c1 UL-C Auth - all zero handshake part 1", + "--c2 UL-C Auth - all zero handshake part 2" ], - "usage": "hf 14a sim [-hxv] -t <1-12> [-u ] [-n ] [--sk]" + "usage": "hf 14a sim [-hxv] -t <1-12> [-u ] [-n ] [--sk] [--c1] [--c2]" }, "hf 14a simaid": { "command": "hf 14a simaid", @@ -7398,21 +7401,24 @@ }, "hf mfu sim": { "command": "hf mfu sim", - "description": "Simulate MIFARE Ultralight family type based upon ISO/IEC 14443 type A tag with 4,7 or 10 byte UID from emulator memory. See `hf mfu eload` first. The UID from emulator memory will be used if not specified. See `hf 14a sim -h` to see available types. You want 2 or 7 usually.", + "description": "Simulate MIFARE Ultralight family type based upon ISO/IEC 14443 type A tag with 4,7 or 10 byte UID from emulator memory. See `hf mfu eload` first. The UID from emulator memory will be used if not specified. See `hf 14a sim -h` to see available types. You want 2, 7 or 13 usually.", "notes": [ "hf mfu sim -t 2 --uid 11223344556677 -> MIFARE Ultralight", "hf mfu sim -t 7 --uid 11223344556677 -n 5 -> MFU EV1 / NTAG 215 Amiibo", - "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo" + "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo", + "hf mfu sim -t 13 -> MIFARE Ultralight-C" ], "offline": false, "options": [ "-h, --help This help", - "-t, --type <1..12> Simulation type to use", + "-t, --type <1..13> Simulation type to use", "-u, --uid <4|7|10> hex bytes UID", "-n, --num Exit simulation after blocks. 0 = infinite", - "-v, --verbose Verbose output" + "-v, --verbose Verbose output", + "--c1 UL-C Auth - all zero handshake part 1", + "--c2 UL-C Auth - all zero handshake part 2" ], - "usage": "hf mfu sim [-hv] -t <1..12> [-u ] [-n ]" + "usage": "hf mfu sim [-hv] -t <1..13> [-u ] [-n ] [--c1] [--c2]" }, "hf mfu tamper": { "command": "hf mfu tamper", @@ -12121,9 +12127,10 @@ "notes": [ "mem load -f myfile -> upload file myfile values at default offset 0", "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024", - "mem load -f mfc_default_keys -m -> upload MFC keys", + "mem load -f mfc_default_keys -m -> upload MIFARE Classic keys", "mem load -f t55xx_default_pwds -t -> upload T55XX passwords", - "mem load -f iclass_default_keys -i -> upload iCLASS keys" + "mem load -f iclass_default_keys -i -> upload iCLASS keys", + "mem load -f mfulc_default_keys --ulc -> upload MIFARE UL-C keys" ], "offline": false, "options": [ @@ -12132,9 +12139,11 @@ "-m, --mifare, --mfc upload 6 bytes keys (mifare key dictionary)", "-i, --iclass upload 8 bytes keys (iClass key dictionary)", "-t, --t55xx upload 4 bytes keys (password dictionary)", + "--ulc upload 16 bytes keys (mifare UL-C key dictionary)", + "--ulaes upload 16 bytes keys (mifare UL-AES key dictionary)", "-f, --file file name" ], - "usage": "mem load [-hmit] [-o ] -f " + "usage": "mem load [-hmit] [-o ] [--ulc] [--ulaes] -f " }, "mem spiffs check": { "command": "mem spiffs check", @@ -13365,6 +13374,6 @@ "metadata": { "commands_extracted": 768, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-06-17T16:11:53" + "extracted_on": "2025-06-19T15:01:51" } } diff --git a/include/ansi.h b/include/ansi.h index 20815bc03..b791741f5 100644 --- a/include/ansi.h +++ b/include/ansi.h @@ -21,6 +21,12 @@ #define AEND "\x1b[0m" +#define _CLEAR_ "\x1b[2J" +#define _CLEAR_SCROLLBACK_ "\x1b[3J" +#define _TOP_ "\x1b[1;1f" + +#define _CLR_ "\x1b[0K" + #define _BLACK_(s) "\x1b[30m" s AEND #define _RED_(s) "\x1b[31m" s AEND #define _GREEN_(s) "\x1b[32m" s AEND @@ -57,10 +63,6 @@ #define _BACK_BRIGHT_CYAN_(s) "\x1b[46;1m" s AEND #define _BACK_BRIGHT_WHITE_(s) "\x1b[47;1m" s AEND -#define _CLEAR_ "\x1b[2J" -#define _CLEAR_SCROLLBACK_ "\x1b[3J" -#define _TOP_ "\x1b[1;1f" - #if defined(HAVE_READLINE) // https://wiki.hackzine.org/development/misc/readline-color-prompt.html // Applications may indicate that the prompt contains diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 82623f2dd..bb9123d7d 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -349,6 +349,12 @@ typedef struct { uint8_t key[6]; } PACKED mfc_eload_t; +typedef struct { + bool use_flashmem; + uint16_t keycount; + uint8_t keys[]; +} PACKED mfulc_keys_t; + typedef struct { uint8_t status; uint8_t CSN[8]; diff --git a/include/pmflash.h b/include/pmflash.h index 7820ad4e2..7e601c2bc 100644 --- a/include/pmflash.h +++ b/include/pmflash.h @@ -79,6 +79,13 @@ #define MF_KEYS_FILE "dict_mf.bin" #define MF_KEY_LENGTH 6 +// MIFARE Ultralight-C keys stored in spiffs +#define MFULC_KEYS_FILE "dict_mfulc.bin" +#define MFULC_KEY_LENGTH (16) + +// MIFARE Ultralight-AES keys stored in spiffs +#define MFULAES_KEYS_FILE "dict_mfulaes.bin" +#define MFULAES_KEY_LENGTH (16) // RDV40, validation structure to help identifying that client/firmware is talking with RDV40 typedef struct { uint8_t magic[4]; diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index 65a2f9985..f8e2dba2b 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -573,8 +573,8 @@ while true; do if ! CheckExecute slow "hf iclass loclass long test" "$CLIENTBIN -c 'hf iclass loclass --long'" "verified \( ok \)"; then break; fi if ! CheckExecute slow "emv long test" "$CLIENTBIN -c 'emv test -l'" "Tests \( ok"; then break; fi if ! CheckExecute "hf iclass lookup test" "$CLIENTBIN -c 'hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f $DICPATH/iclass_default_keys.dic'" \ - "valid key AE A6 84 A6 DA B2 32 78"; then break; fi - if ! CheckExecute "hf iclass loclass test" "$CLIENTBIN -c 'hf iclass loclass --test'" "key diversification \( ok \)"; then break; fi + "valid key AEA684A6DAB23278"; then break; fi + if ! CheckExecute "hf iclass loclass test" "$CLIENTBIN -c 'hf iclass loclass --test'" "Key diversification \( ok \)"; then break; fi if ! CheckExecute "emv test" "$CLIENTBIN -c 'emv test'" "Tests \( ok"; then break; fi if ! CheckExecute "hf cipurse test" "$CLIENTBIN -c 'hf cipurse test'" "Tests \( ok"; then break; fi if ! CheckExecute "hf mfdes test" "$CLIENTBIN -c 'hf mfdes test'" "Tests \( ok"; then break; fi From b6d826410d6d98d8c8fa09d83c8c936e339b6fa1 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 19 Jun 2025 17:42:56 +0200 Subject: [PATCH 016/149] fix macos... --- client/src/cmdparser.c | 19 +++++----- client/src/pthread_spin_lock_shim.h | 55 +++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 client/src/pthread_spin_lock_shim.h diff --git a/client/src/cmdparser.c b/client/src/cmdparser.c index 6b3984ad4..0264105e3 100644 --- a/client/src/cmdparser.c +++ b/client/src/cmdparser.c @@ -26,6 +26,9 @@ #include "comms.h" #include "util_posix.h" // msleep +#if defined(__MACH__) && defined(__APPLE__) +# include "pthread_spin_lock_shim.h" +#endif #define MAX_PM3_INPUT_ARGS_LENGTH 4096 @@ -220,14 +223,16 @@ void CmdsHelp(const command_t Commands[]) { PrintAndLogEx(NORMAL, ""); } -pthread_spinlock_t sycmd_spinlock; - static int execute_system_command(const char *command) { + pthread_spinlock_t sycmd_spinlock; + pthread_spin_init(&sycmd_spinlock, 0); + pthread_spin_lock(&sycmd_spinlock); + + int ret; - pthread_spin_lock(&sycmd_spinlock); -#if defined(_WIN32) + #if defined(_WIN32) char wrapped_command[255]; strncat(wrapped_command, "cmd /C \"", 9); strncat(wrapped_command, command, strlen(command)); @@ -238,6 +243,7 @@ static int execute_system_command(const char *command) { ret = system(command); #endif pthread_spin_unlock(&sycmd_spinlock); + pthread_spin_destroy(&sycmd_spinlock); return ret; } @@ -291,10 +297,7 @@ int CmdsParse(const command_t Commands[], const char *Cmd) { } if (Cmd[0] == '!') { - pthread_spin_init(&sycmd_spinlock, 0); - int res = execute_system_command(Cmd + 1); - pthread_spin_destroy(&sycmd_spinlock); - return res; + return execute_system_command(Cmd + 1); } char cmd_name[128] = {0}; diff --git a/client/src/pthread_spin_lock_shim.h b/client/src/pthread_spin_lock_shim.h new file mode 100644 index 000000000..16d1dc09d --- /dev/null +++ b/client/src/pthread_spin_lock_shim.h @@ -0,0 +1,55 @@ +/* + +Required imports: +#include + +*/ + +#ifndef PTHREAD_SPIN_LOCK_SHIM +#define PTHREAD_SPIN_LOCK_SHIM + +typedef int pthread_spinlock_t; + +#ifndef PTHREAD_PROCESS_SHARED +# define PTHREAD_PROCESS_SHARED 1 +#endif +#ifndef PTHREAD_PROCESS_PRIVATE +# define PTHREAD_PROCESS_PRIVATE 2 +#endif + +static inline int pthread_spin_init(pthread_spinlock_t *lock, int pshared) { + __asm__ __volatile__ ("" ::: "memory"); + *lock = 0; + return 0; +} + +static inline int pthread_spin_destroy(pthread_spinlock_t *lock) { + return 0; +} + +static inline int pthread_spin_lock(pthread_spinlock_t *lock) { + while (1) { + int i; + for (i=0; i < 10000; i++) { + if (__sync_bool_compare_and_swap(lock, 0, 1)) { + return 0; + } + } + sched_yield(); + } +} + +static inline int pthread_spin_trylock(pthread_spinlock_t *lock) { + if (__sync_bool_compare_and_swap(lock, 0, 1)) { + return 0; + } + return EBUSY; +} + +static inline int pthread_spin_unlock(pthread_spinlock_t *lock) { + __asm__ __volatile__ ("" ::: "memory"); + *lock = 0; + return 0; +} + +#endif From 570b1fcc40d957ccc1066a03e485408436a51d5d Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 19 Jun 2025 17:50:19 +0200 Subject: [PATCH 017/149] EBUSY error code is 16 --- client/src/pthread_spin_lock_shim.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/pthread_spin_lock_shim.h b/client/src/pthread_spin_lock_shim.h index 16d1dc09d..131598b02 100644 --- a/client/src/pthread_spin_lock_shim.h +++ b/client/src/pthread_spin_lock_shim.h @@ -43,7 +43,7 @@ static inline int pthread_spin_trylock(pthread_spinlock_t *lock) { if (__sync_bool_compare_and_swap(lock, 0, 1)) { return 0; } - return EBUSY; + return 16; // EBUSY; } static inline int pthread_spin_unlock(pthread_spinlock_t *lock) { From 80a86e741cfcf2c764ca9a779f1828410d54f966 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 20 Jun 2025 01:31:47 +0800 Subject: [PATCH 018/149] Fixed length check for snmp responses Fixed length check for snmp responses from the sam --- armsrc/sam_picopass.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c index fa040288f..ad801b1f4 100644 --- a/armsrc/sam_picopass.c +++ b/armsrc/sam_picopass.c @@ -238,7 +238,12 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re } } - *response_len = sam_rx_buf[5 + 1] + 2; + if (sam_rx_buf[6] == 0x81 && sam_rx_buf[8] == 0x8a && sam_rx_buf[9] == 0x81 ){ //check if the response is an SNMP message + *response_len = sam_rx_buf[6 + 1] + 3; + }else{ //if not, use the old logic + *response_len = sam_rx_buf[5 + 1] + 2; + } + memcpy(response, sam_rx_buf + 5, *response_len); goto out; From 2ed0c9a3013cd11d71a480769c42e351599f0ec3 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 20 Jun 2025 01:35:13 +0800 Subject: [PATCH 019/149] Update sam_picopass.c Signed-off-by: Antiklesys --- armsrc/sam_picopass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c index ad801b1f4..1504f4104 100644 --- a/armsrc/sam_picopass.c +++ b/armsrc/sam_picopass.c @@ -239,7 +239,7 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re } if (sam_rx_buf[6] == 0x81 && sam_rx_buf[8] == 0x8a && sam_rx_buf[9] == 0x81 ){ //check if the response is an SNMP message - *response_len = sam_rx_buf[6 + 1] + 3; + *response_len = sam_rx_buf[5 + 2] + 3; }else{ //if not, use the old logic *response_len = sam_rx_buf[5 + 1] + 2; } From 47648c541c587b853c3f67788e9c020f8f3c49de Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 19 Jun 2025 20:26:07 +0200 Subject: [PATCH 020/149] text --- client/luascripts/hf_mf_keycheck.lua | 2 +- client/luascripts/lf_electra.lua | 2 +- client/luascripts/lf_em4100_bulk.lua | 2 +- client/src/cmdparser.c | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/client/luascripts/hf_mf_keycheck.lua b/client/luascripts/hf_mf_keycheck.lua index 29eb46072..53a003ebc 100644 --- a/client/luascripts/hf_mf_keycheck.lua +++ b/client/luascripts/hf_mf_keycheck.lua @@ -216,7 +216,7 @@ local function perform_check(uid, numsectors) for sector = 0, #keys do -- Check if user aborted if core.kbd_enter_pressed() then - print('Aborted by user') + print('Aborted via keyboard!') break end diff --git a/client/luascripts/lf_electra.lua b/client/luascripts/lf_electra.lua index 5b0886aa4..7502522e8 100644 --- a/client/luascripts/lf_electra.lua +++ b/client/luascripts/lf_electra.lua @@ -299,7 +299,7 @@ local function main(args) if answer == 'n' then core.console('clear') print( string.rep('--',39) ) - print(ac.red..' USER ABORTED'..ac.reset) + print(ac.red..' Aborted via keyboard!'..ac.reset) print( string.rep('--',39) ) break end diff --git a/client/luascripts/lf_em4100_bulk.lua b/client/luascripts/lf_em4100_bulk.lua index 87d8bc91b..8a2ff399e 100644 --- a/client/luascripts/lf_em4100_bulk.lua +++ b/client/luascripts/lf_em4100_bulk.lua @@ -198,7 +198,7 @@ local function main(args) core.console('lf em 410x reader') end else - print(ac.red..'User aborted'..ac.reset) + print(ac.red..'aborted via keyboard!'..ac.reset) low = i break end diff --git a/client/src/cmdparser.c b/client/src/cmdparser.c index 0264105e3..d170c6828 100644 --- a/client/src/cmdparser.c +++ b/client/src/cmdparser.c @@ -27,7 +27,7 @@ #include "util_posix.h" // msleep #if defined(__MACH__) && defined(__APPLE__) -# include "pthread_spin_lock_shim.h" +# include "pthread_spin_lock_shim.h" // spinlock shim for OSX .. #endif #define MAX_PM3_INPUT_ARGS_LENGTH 4096 @@ -229,7 +229,6 @@ static int execute_system_command(const char *command) { pthread_spin_init(&sycmd_spinlock, 0); pthread_spin_lock(&sycmd_spinlock); - int ret; #if defined(_WIN32) From 16a7e4fba5bfe89d2a02aa166a37723ec3d59288 Mon Sep 17 00:00:00 2001 From: James #FFFFFF Date: Thu, 19 Jun 2025 20:41:35 +0100 Subject: [PATCH 021/149] Update README.md Update Operating Systems list Signed-off-by: James #FFFFFF --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ea8a3c82..5efcfc5d7 100644 --- a/README.md +++ b/README.md @@ -182,10 +182,11 @@ We usually merge your contributions fast since we do like the idea of getting a The [public roadmap](https://github.com/RfidResearchGroup/proxmark3/wiki/Public-Roadmap) is an excellent start to read if you are interesting in contributing. -## Supported operative systems +## Supported operating systems This repo compiles nicely on - WSL1 on Windows 10 + - WSL2 on Windows 10/11 - Proxspace environment [release v3.xx](https://github.com/Gator96100/ProxSpace/releases) - Windows/MinGW environment - Ubuntu, ParrotOS, Gentoo, Pentoo, Kali, NetHunter, Arch Linux, Fedora, Debian, Raspbian From 37166d6c73aec1e6465105aabb59b73af758abbb Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 20 Jun 2025 11:58:32 +0800 Subject: [PATCH 022/149] Improved sam response processing Improved sam response processing on client side, it detects when the response contains an error and highlights the error number, detects when the response is an snmp messages and does the asn.1 decoding of the snmp message. --- client/src/cmdhficlass.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 035031cf2..b7e1fedc7 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -5886,6 +5886,15 @@ static int CmdHFiClassConfigCard(const char *Cmd) { return PM3_SUCCESS; } +static bool match_with_wildcard(const uint8_t *data, const uint8_t *pattern, const bool *mask, size_t length) { + for (size_t i = 0; i < length; ++i) { + if (mask[i] && data[i] != pattern[i]) { + return false; + } + } + return true; +} + static int CmdHFiClassSAM(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass sam", @@ -5964,6 +5973,8 @@ static int CmdHFiClassSAM(const char *Cmd) { PacketResponseNG resp; WaitForResponse(CMD_HF_SAM_PICOPASS, &resp); + bool is_snmp = false; + switch (resp.status) { case PM3_SUCCESS: break; @@ -6022,11 +6033,27 @@ static int CmdHFiClassSAM(const char *Cmd) { PrintAndLogEx(SUCCESS, " hf iclass dump --nr -k %s", sprint_hex_inrow(d + 1, 8)); } } else { + //if it is an error decode it + if (memcmp(d, "\xBE\x07\x80\x01", 4) == 0) { //if it the string is 0xbe 0x07 0x80 0x01 the next byte will indicate the error code + PrintAndLogEx(ERR,_RED_("Sam Error Code: %s"), d[4]); print_hex(d, resp.length); + + }else{ + uint8_t pattern[] = {0xBD, 0x81, 0xFF, 0x8A, 0x81, 0xFF}; // 0xFF is a placeholder for the length message + bool mask[] = {true, true, false, true, true, false}; // false means wildcard + if (match_with_wildcard(d, pattern, mask, 4)) { // Pattern matched with wildcard support + is_snmp = true; + PrintAndLogEx(SUCCESS, _YELLOW_("samSNMPMessageResponse: ")"%s", sprint_hex(d + 6, resp.length - 6)); + }else{ + print_hex(d, resp.length); + } + } } - if (decodeTLV) { + if (decodeTLV && is_snmp == false) { asn1_print(d, d[1] + 2, " "); + } else{ + asn1_print(d + 6, resp.length - 6, " "); } return PM3_SUCCESS; From 67fbd6abbab180c20aa3c3ce942a5fa352e46913 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 20 Jun 2025 12:00:41 +0800 Subject: [PATCH 023/149] Update cmdhficlass.c Signed-off-by: Antiklesys --- client/src/cmdhficlass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index b7e1fedc7..df13c5cea 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -6035,7 +6035,7 @@ static int CmdHFiClassSAM(const char *Cmd) { } else { //if it is an error decode it if (memcmp(d, "\xBE\x07\x80\x01", 4) == 0) { //if it the string is 0xbe 0x07 0x80 0x01 the next byte will indicate the error code - PrintAndLogEx(ERR,_RED_("Sam Error Code: %s"), d[4]); + PrintAndLogEx(ERR,_RED_("Sam Error Code: %02x"), d[4]); print_hex(d, resp.length); }else{ From e0b1b5b4f8aedfc8fe9c9b9cd76e2e364cf9345a Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 20 Jun 2025 12:02:25 +0800 Subject: [PATCH 024/149] Update cmdhficlass.c Fixed indent Signed-off-by: Antiklesys --- client/src/cmdhficlass.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index df13c5cea..dcdc4b805 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -6035,9 +6035,8 @@ static int CmdHFiClassSAM(const char *Cmd) { } else { //if it is an error decode it if (memcmp(d, "\xBE\x07\x80\x01", 4) == 0) { //if it the string is 0xbe 0x07 0x80 0x01 the next byte will indicate the error code - PrintAndLogEx(ERR,_RED_("Sam Error Code: %02x"), d[4]); - print_hex(d, resp.length); - + PrintAndLogEx(ERR,_RED_("Sam Error Code: %02x"), d[4]); + print_hex(d, resp.length); }else{ uint8_t pattern[] = {0xBD, 0x81, 0xFF, 0x8A, 0x81, 0xFF}; // 0xFF is a placeholder for the length message bool mask[] = {true, true, false, true, true, false}; // false means wildcard From c504d433986ae03568aec9a95cab59bb0c66a322 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 20 Jun 2025 12:32:14 +0800 Subject: [PATCH 025/149] Update cmdhficlass.c Signed-off-by: Antiklesys --- client/src/cmdhficlass.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index dcdc4b805..81c70ee70 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -5974,6 +5974,10 @@ static int CmdHFiClassSAM(const char *Cmd) { WaitForResponse(CMD_HF_SAM_PICOPASS, &resp); bool is_snmp = false; + uint8_t snmp_pattern[] = {0xBD, 0x81, 0xFF, 0x8A, 0x81, 0xFF}; // 0xFF is a placeholder for the length message + bool snmp_mask[] = {true, true, false, true, true, false}; // false means wildcard + uint8_t ack_pattern[] = {0xBD, 0xFF, 0x8A}; // 0xFF is a placeholder for the length message + bool ack_mask[] = {true, false, true}; // false means wildcard switch (resp.status) { case PM3_SUCCESS: @@ -6037,21 +6041,19 @@ static int CmdHFiClassSAM(const char *Cmd) { if (memcmp(d, "\xBE\x07\x80\x01", 4) == 0) { //if it the string is 0xbe 0x07 0x80 0x01 the next byte will indicate the error code PrintAndLogEx(ERR,_RED_("Sam Error Code: %02x"), d[4]); print_hex(d, resp.length); + }else if (match_with_wildcard(d, snmp_pattern, snmp_mask, 6)){ + is_snmp = true; + PrintAndLogEx(SUCCESS, _YELLOW_("[samSNMPMessageResponse] ")"%s", sprint_hex(d + 6, resp.length - 6)); + }else if (match_with_wildcard(d,ack_pattern, ack_mask, 3)){ + PrintAndLogEx(SUCCESS, _YELLOW_("[samResponseAcknowledge] ")"%s", sprint_hex(d + 4, resp.length - 4)); }else{ - uint8_t pattern[] = {0xBD, 0x81, 0xFF, 0x8A, 0x81, 0xFF}; // 0xFF is a placeholder for the length message - bool mask[] = {true, true, false, true, true, false}; // false means wildcard - if (match_with_wildcard(d, pattern, mask, 4)) { // Pattern matched with wildcard support - is_snmp = true; - PrintAndLogEx(SUCCESS, _YELLOW_("samSNMPMessageResponse: ")"%s", sprint_hex(d + 6, resp.length - 6)); - }else{ - print_hex(d, resp.length); - } + print_hex(d, resp.length); } } if (decodeTLV && is_snmp == false) { asn1_print(d, d[1] + 2, " "); - } else{ + } else if (decodeTLV && is_snmp){ asn1_print(d + 6, resp.length - 6, " "); } From f5254880b9e50088e716acea68878637f6ccf4c0 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 20 Jun 2025 12:34:16 +0800 Subject: [PATCH 026/149] Update cmdhficlass.c Improved code comments Signed-off-by: Antiklesys --- client/src/cmdhficlass.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 81c70ee70..796bdd668 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -5974,10 +5974,10 @@ static int CmdHFiClassSAM(const char *Cmd) { WaitForResponse(CMD_HF_SAM_PICOPASS, &resp); bool is_snmp = false; - uint8_t snmp_pattern[] = {0xBD, 0x81, 0xFF, 0x8A, 0x81, 0xFF}; // 0xFF is a placeholder for the length message - bool snmp_mask[] = {true, true, false, true, true, false}; // false means wildcard - uint8_t ack_pattern[] = {0xBD, 0xFF, 0x8A}; // 0xFF is a placeholder for the length message - bool ack_mask[] = {true, false, true}; // false means wildcard + uint8_t snmp_pattern[] = {0xBD, 0x81, 0xFF, 0x8A, 0x81, 0xFF}; // SNMP Response header pattern, 0xFF is a wildcard value for message length + bool snmp_mask[] = {true, true, false, true, true, false}; // false means wildcard value in that position + uint8_t ack_pattern[] = {0xBD, 0xFF, 0x8A}; // Acknowledge Response header pattern, 0xFF is a wildcard value for message length + bool ack_mask[] = {true, false, true}; // false means wildcard value in that position switch (resp.status) { case PM3_SUCCESS: From c729c88f1d332e3ad7903484994a61594fff8244 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 20 Jun 2025 17:20:40 +0800 Subject: [PATCH 027/149] Renaming and snmp data format option for sam comms Added ability to pass data to sam directly in snmp format and for the client to calculate the packet headers accordingly. Renamed ack_mask to ok_mask as that is a generic mask for successful commands, the same bytes are used for multiple types of successful responses. --- client/src/cmdhficlass.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 796bdd668..97bb7315d 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -5914,6 +5914,7 @@ static int CmdHFiClassSAM(const char *Cmd) { arg_lit0("p", "prevent", "fake epurse update"), arg_lit0(NULL, "shallow", "shallow mod"), arg_strx0("d", "data", "", "DER encoded command to send to SAM"), + arg_lit0("s", "snmp", "data is in snmp format without headers"), arg_lit0(NULL, "info", "get SAM infos (version, serial number)"), arg_param_end }; @@ -5926,7 +5927,8 @@ static int CmdHFiClassSAM(const char *Cmd) { bool break_nrmac = arg_get_lit(ctx, 5); bool prevent = arg_get_lit(ctx, 6); bool shallow_mod = arg_get_lit(ctx, 7); - bool info = arg_get_lit(ctx, 9); + bool snmp_data = arg_get_lit(ctx, 9); + bool info = arg_get_lit(ctx, 10); uint8_t flags = 0; if (disconnect_after) { @@ -5968,6 +5970,14 @@ static int CmdHFiClassSAM(const char *Cmd) { return PM3_ESOFT; } + if (snmp_data) { + uint8_t header[4] = {0xa0, cmdlen+2 , 0x94, cmdlen }; + memmove(data + 4, data, cmdlen+1); + data[0] = flags; + memcpy(data+1, header, 4); + cmdlen += 4; + } + clearCommandBuffer(); SendCommandNG(CMD_HF_SAM_PICOPASS, data, cmdlen + 1); PacketResponseNG resp; @@ -5976,8 +5986,8 @@ static int CmdHFiClassSAM(const char *Cmd) { bool is_snmp = false; uint8_t snmp_pattern[] = {0xBD, 0x81, 0xFF, 0x8A, 0x81, 0xFF}; // SNMP Response header pattern, 0xFF is a wildcard value for message length bool snmp_mask[] = {true, true, false, true, true, false}; // false means wildcard value in that position - uint8_t ack_pattern[] = {0xBD, 0xFF, 0x8A}; // Acknowledge Response header pattern, 0xFF is a wildcard value for message length - bool ack_mask[] = {true, false, true}; // false means wildcard value in that position + uint8_t ok_pattern[] = {0xBD, 0xFF, 0x8A}; // Ok response header pattern, 0xFF is a wildcard value for message length + bool ok_mask[] = {true, false, true}; // false means wildcard value in that position switch (resp.status) { case PM3_SUCCESS: @@ -6044,8 +6054,8 @@ static int CmdHFiClassSAM(const char *Cmd) { }else if (match_with_wildcard(d, snmp_pattern, snmp_mask, 6)){ is_snmp = true; PrintAndLogEx(SUCCESS, _YELLOW_("[samSNMPMessageResponse] ")"%s", sprint_hex(d + 6, resp.length - 6)); - }else if (match_with_wildcard(d,ack_pattern, ack_mask, 3)){ PrintAndLogEx(SUCCESS, _YELLOW_("[samResponseAcknowledge] ")"%s", sprint_hex(d + 4, resp.length - 4)); + }else if (match_with_wildcard(d,ok_pattern, ok_mask, 3)){ }else{ print_hex(d, resp.length); } From 43943ce9a59e91a3a6fafb008ecd6625ad2f9da1 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 20 Jun 2025 17:22:14 +0800 Subject: [PATCH 028/149] Update cmdhficlass.c Signed-off-by: Antiklesys --- client/src/cmdhficlass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 97bb7315d..2bb6b3f16 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -6054,8 +6054,8 @@ static int CmdHFiClassSAM(const char *Cmd) { }else if (match_with_wildcard(d, snmp_pattern, snmp_mask, 6)){ is_snmp = true; PrintAndLogEx(SUCCESS, _YELLOW_("[samSNMPMessageResponse] ")"%s", sprint_hex(d + 6, resp.length - 6)); - PrintAndLogEx(SUCCESS, _YELLOW_("[samResponseAcknowledge] ")"%s", sprint_hex(d + 4, resp.length - 4)); }else if (match_with_wildcard(d,ok_pattern, ok_mask, 3)){ + PrintAndLogEx(SUCCESS, _YELLOW_("[samResponseAcknowledge] ")"%s", sprint_hex(d + 4, resp.length - 4)); }else{ print_hex(d, resp.length); } From f9bce6e21bf6cf354ef892a8241ffdbf2de4fe05 Mon Sep 17 00:00:00 2001 From: TeCHiScy <741195+TeCHiScy@users.noreply.github.com> Date: Sun, 22 Jun 2025 18:23:42 +0800 Subject: [PATCH 029/149] fix card limits for S12906 format As the S12906 [pack function]( https://github.com/RfidResearchGroup/proxmark3/blob/61a993de826335db2d92d1f60c3ef4ae64439b73/client/src/wiegand_formats.c#L684) suggests, the issue level takes 2 bits, thus ranging from 0 to 3. The card number takes 24 bits, ranging from 0 to 0xffffff. That shows an inconsistent with the limits defined in the format table. Reference from https://acre.my.site.com/knowledgearticles/s/article/x107 also confirms the CN should be ranging from 0 to 0xffffff. So I propose to fix the limits of S12906 format. Signed-off-by: TeCHiScy <741195+TeCHiScy@users.noreply.github.com> --- client/src/wiegand_formats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/wiegand_formats.c b/client/src/wiegand_formats.c index 03b0f5501..709dbf69a 100644 --- a/client/src/wiegand_formats.c +++ b/client/src/wiegand_formats.c @@ -1477,7 +1477,7 @@ static const cardformat_t FormatTable[] = { {"BQT34", Pack_bqt34, Unpack_bqt34, "BQT 34-bit", 34, {1, 1, 0, 0, 1, 0xFF, 0xFFFFFF, 0, 0}}, // from cardinfo.barkweb.com.au {"C1k35s", Pack_C1k35s, Unpack_C1k35s, "HID Corporate 1000 35-bit std", 35, {1, 1, 0, 0, 1, 0xFFF, 0xFFFFF, 0, 0}}, // imported from old pack/unpack {"C15001", Pack_C15001, Unpack_C15001, "HID KeyScan 36-bit", 36, {1, 1, 0, 1, 1, 0xFF, 0xFFFF, 0, 0x3FF}}, // from Proxmark forums - {"S12906", Pack_S12906, Unpack_S12906, "HID Simplex 36-bit", 36, {1, 1, 1, 0, 1, 0xFF, 0x3, 0xFFFFFF, 0}}, // from cardinfo.barkweb.com.au + {"S12906", Pack_S12906, Unpack_S12906, "HID Simplex 36-bit", 36, {1, 1, 1, 0, 1, 0xFF, 0xFFFFFF, 0x3, 0}}, // from cardinfo.barkweb.com.au {"Sie36", Pack_Sie36, Unpack_Sie36, "HID 36-bit Siemens", 36, {1, 1, 0, 0, 1, 0x3FFFF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au {"H10320", Pack_H10320, Unpack_H10320, "HID H10320 37-bit BCD", 37, {1, 0, 0, 0, 1, 0, 99999999, 0, 0}}, // from Proxmark forums {"H10302", Pack_H10302, Unpack_H10302, "HID H10302 37-bit huge ID", 37, {1, 0, 0, 0, 1, 0, 0x7FFFFFFFF, 0, 0}}, // from Proxmark forums From 217edd1e74e5dc7f423c0c50d44f8f1a07cb804e Mon Sep 17 00:00:00 2001 From: TeCHiScy <741195+TeCHiScy@users.noreply.github.com> Date: Sun, 22 Jun 2025 21:41:07 +0800 Subject: [PATCH 030/149] fix parity bit in Sie36 unpack Signed-off-by: TeCHiScy <741195+TeCHiScy@users.noreply.github.com> --- client/src/wiegand_formats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/wiegand_formats.c b/client/src/wiegand_formats.c index 709dbf69a..fa1d68eb1 100644 --- a/client/src/wiegand_formats.c +++ b/client/src/wiegand_formats.c @@ -733,7 +733,7 @@ static bool Unpack_Sie36(wiegand_message_t *packed, wiegand_card_t *card) { card->CardNumber = get_linear_field(packed, 19, 16); card->ParityValid = (get_bit_by_position(packed, 0) == oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]) {1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30, 31, 33, 34}))) && - (get_bit_by_position(packed, 35) == oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]) {1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34}))); + (get_bit_by_position(packed, 35) == evenparity32(get_nonlinear_field(packed, 23, (uint8_t[]) {1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34}))); return true; } From 883415fc991ea8fc497f22964871a8939192f979 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 20 Jun 2025 02:55:39 +0200 Subject: [PATCH 031/149] style --- armsrc/iso14443a.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 4bab0a0b4..733b692a2 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2080,6 +2080,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); p_response = &dynamic_response_info; order = ORDER_NONE; + } else if (receivedCmd[0] == MIFARE_ULEV1_AUTH && len == 7 && tagType == 7) { // NTAG / EV-1 uint8_t pwd[4] = {0, 0, 0, 0}; emlGet(pwd, (pages - 1) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(pwd)); @@ -3815,8 +3816,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { ReaderTransmit(mf_auth, sizeof(mf_auth), &sync_time); // Receive the (4 Byte) "random" TAG nonce - if (ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) != 4) + if (ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) != 4) { continue; + } previous_nt = nt; nt = bytes_to_num(receivedAnswer, 4); From 488f7aa01e5f445e01e363094e3f09c0b6fb1a2f Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 22 Jun 2025 17:36:33 +0200 Subject: [PATCH 032/149] clear a warning for potential use of uninitialized variable --- client/pyscripts/fm11rf08s_recovery.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index 5e13b461c..9c7b0797e 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -226,6 +226,7 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, dict_dnwd = None def_nt = ["" for _ in range(NUM_SECTORS)] if supply_chain: + default_nonces = '' try: default_nonces = f'{save_path}hf-mf-{uid:04X}-default_nonces.json' with open(default_nonces, 'r') as file: From 5de4dd68e59b4a26d3339318e4b40d366492584e Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 22 Jun 2025 20:34:54 +0200 Subject: [PATCH 033/149] text --- armsrc/sam_picopass.c | 4 +-- client/src/cmdhficlass.c | 16 ++++++------ client/src/cmdhfmf.c | 15 +++++------ client/src/cmdhfmfdes.c | 6 ++--- client/src/cmdhfmfhard.c | 2 +- client/src/cmdparser.c | 2 +- client/src/cmdtrace.c | 2 +- client/src/fileutils.c | 19 +++++++------- client/src/fileutils.h | 2 +- client/src/mifare/mifarehost.c | 4 +-- client/src/pthread_spin_lock_shim.h | 40 ++++++++++++++--------------- common/commonutil.c | 37 ++++++++++++++++++++++++++ common/commonutil.h | 3 +++ doc/commands.json | 19 +++++++------- include/pm3_cmd.h | 2 ++ 15 files changed, 107 insertions(+), 66 deletions(-) diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c index 1504f4104..dc794a7da 100644 --- a/armsrc/sam_picopass.c +++ b/armsrc/sam_picopass.c @@ -238,9 +238,9 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re } } - if (sam_rx_buf[6] == 0x81 && sam_rx_buf[8] == 0x8a && sam_rx_buf[9] == 0x81 ){ //check if the response is an SNMP message + if (sam_rx_buf[6] == 0x81 && sam_rx_buf[8] == 0x8a && sam_rx_buf[9] == 0x81) { //check if the response is an SNMP message *response_len = sam_rx_buf[5 + 2] + 3; - }else{ //if not, use the old logic + } else { //if not, use the old logic *response_len = sam_rx_buf[5 + 1] + 2; } diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 2bb6b3f16..ffa5d8f7d 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -5971,10 +5971,10 @@ static int CmdHFiClassSAM(const char *Cmd) { } if (snmp_data) { - uint8_t header[4] = {0xa0, cmdlen+2 , 0x94, cmdlen }; - memmove(data + 4, data, cmdlen+1); + uint8_t header[4] = {0xa0, cmdlen + 2, 0x94, cmdlen }; + memmove(data + 4, data, cmdlen + 1); data[0] = flags; - memcpy(data+1, header, 4); + memcpy(data + 1, header, 4); cmdlen += 4; } @@ -6049,21 +6049,21 @@ static int CmdHFiClassSAM(const char *Cmd) { } else { //if it is an error decode it if (memcmp(d, "\xBE\x07\x80\x01", 4) == 0) { //if it the string is 0xbe 0x07 0x80 0x01 the next byte will indicate the error code - PrintAndLogEx(ERR,_RED_("Sam Error Code: %02x"), d[4]); + PrintAndLogEx(ERR, _RED_("Sam Error Code: %02x"), d[4]); print_hex(d, resp.length); - }else if (match_with_wildcard(d, snmp_pattern, snmp_mask, 6)){ + } else if (match_with_wildcard(d, snmp_pattern, snmp_mask, 6)) { is_snmp = true; PrintAndLogEx(SUCCESS, _YELLOW_("[samSNMPMessageResponse] ")"%s", sprint_hex(d + 6, resp.length - 6)); - }else if (match_with_wildcard(d,ok_pattern, ok_mask, 3)){ + } else if (match_with_wildcard(d, ok_pattern, ok_mask, 3)) { PrintAndLogEx(SUCCESS, _YELLOW_("[samResponseAcknowledge] ")"%s", sprint_hex(d + 4, resp.length - 4)); - }else{ + } else { print_hex(d, resp.length); } } if (decodeTLV && is_snmp == false) { asn1_print(d, d[1] + 2, " "); - } else if (decodeTLV && is_snmp){ + } else if (decodeTLV && is_snmp) { asn1_print(d + 6, resp.length - 6, " "); } diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 269a06fab..038180ff1 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -433,9 +433,10 @@ static int mf_read_uid(uint8_t *uid, int *uidlen, int *nxptype) { } static char *GenerateFilename(const char *prefix, const char *suffix) { - if (! IfPm3Iso14443a()) { + if (IfPm3Iso14443a() == false) { return NULL; } + uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int uidlen = 0; char *fptr = calloc(sizeof(char) * (strlen(prefix) + strlen(suffix)) + sizeof(uid) * 2 + 1, sizeof(uint8_t)); @@ -924,7 +925,7 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n size_t alen = 0, blen = 0; uint8_t *keyA = NULL, *keyB = NULL; - if (loadFileBinaryKey(keyfn, "", (void **)&keyA, (void **)&keyB, &alen, &blen) != PM3_SUCCESS) { + if (loadFileBinaryKey(keyfn, "", (void **)&keyA, (void **)&keyB, &alen, &blen, true) != PM3_SUCCESS) { free(fptr); return PM3_ESOFT; } @@ -1565,7 +1566,7 @@ static int FastDumpWithEcFill(uint8_t numsectors) { } if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "fast dump reported back failure w KEY A, swapping to KEY B"); + PrintAndLogEx(FAILED, "fast dump reported back failure w KEY A. Swapping to KEY B"); // ecfill key B payload.keytype = MF_KEY_B; @@ -1823,12 +1824,10 @@ static int CmdHF14AMfRestore(const char *Cmd) { // size_t alen = 0, blen = 0; uint8_t *keyA, *keyB; - if (loadFileBinaryKey(keyfilename, "", (void **)&keyA, (void **)&keyB, &alen, &blen) != PM3_SUCCESS) { + if (loadFileBinaryKey(keyfilename, "", (void **)&keyA, (void **)&keyB, &alen, &blen, true) != PM3_SUCCESS) { return PM3_ESOFT; } - PrintAndLogEx(INFO, "Using key file `" _YELLOW_("%s") "`", keyfilename); - // try reading card uid and create filename if (datafnlen == 0) { char *fptr = GenerateFilename("hf-mf-", "-dump.bin"); @@ -7311,12 +7310,10 @@ int CmdHFMFNDEFFormat(const char *Cmd) { // size_t alen = 0, blen = 0; uint8_t *tmpA, *tmpB; - if (loadFileBinaryKey(keyFilename, "", (void **)&tmpA, (void **)&tmpB, &alen, &blen) != PM3_SUCCESS) { + if (loadFileBinaryKey(keyFilename, "", (void **)&tmpA, (void **)&tmpB, &alen, &blen, true) != PM3_SUCCESS) { goto skipfile; } - PrintAndLogEx(INFO, "Using `" _YELLOW_("%s") "`", keyFilename); - for (int i = 0; i < numSectors; i++) { memcpy(keyA[i], tmpA + (i * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); memcpy(keyB[i], tmpB + (i * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index a6f4f934c..341ced612 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -1059,7 +1059,7 @@ static int AuthCheckDesfire(DesfireContext_t *dctx, DesfireSetKeyNoClear(dctx, keyno, T_3DES, aeskeyList[curkey]); res = DesfireAuthenticate(dctx, secureChannel, false); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 2TDEA Key %02u : " _GREEN_("%s"), curaid, keyno, sprint_hex(aeskeyList[curkey], 16)); + PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 2TDEA Key %02u... " _GREEN_("%s"), curaid, keyno, sprint_hex_inrow(aeskeyList[curkey], 16)); foundKeys[1][keyno][0] = 0x01; *result = true; memcpy(&foundKeys[1][keyno][1], aeskeyList[curkey], 16); @@ -1091,7 +1091,7 @@ static int AuthCheckDesfire(DesfireContext_t *dctx, DesfireSetKeyNoClear(dctx, keyno, T_AES, aeskeyList[curkey]); res = DesfireAuthenticate(dctx, secureChannel, false); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "AID 0x%06X, Found AES Key %02u : " _GREEN_("%s"), curaid, keyno, sprint_hex(aeskeyList[curkey], 16)); + PrintAndLogEx(SUCCESS, "AID 0x%06X, Found AES Key %02u... " _GREEN_("%s"), curaid, keyno, sprint_hex_inrow(aeskeyList[curkey], 16)); foundKeys[2][keyno][0] = 0x01; *result = true; memcpy(&foundKeys[2][keyno][1], aeskeyList[curkey], 16); @@ -1123,7 +1123,7 @@ static int AuthCheckDesfire(DesfireContext_t *dctx, DesfireSetKeyNoClear(dctx, keyno, T_3K3DES, k3kkeyList[curkey]); res = DesfireAuthenticate(dctx, secureChannel, false); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 3TDEA Key %02u : " _GREEN_("%s"), curaid, keyno, sprint_hex(k3kkeyList[curkey], 24)); + PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 3TDEA Key %02u... " _GREEN_("%s"), curaid, keyno, sprint_hex_inrow(k3kkeyList[curkey], 24)); foundKeys[3][keyno][0] = 0x01; *result = true; memcpy(&foundKeys[3][keyno][1], k3kkeyList[curkey], 16); diff --git a/client/src/cmdhfmfhard.c b/client/src/cmdhfmfhard.c index a04db22ec..885162437 100644 --- a/client/src/cmdhfmfhard.c +++ b/client/src/cmdhfmfhard.c @@ -508,7 +508,7 @@ static void init_bitflip_bitarrays(void) { { char progress_text[100]; memset(progress_text, 0, sizeof(progress_text)); - snprintf(progress_text, sizeof(progress_text), "Loaded " _YELLOW_("%u") " RAW / " _YELLOW_("%u") " LZ4 / " _YELLOW_("%u") " BZ2 in %"PRIu64" ms" + snprintf(progress_text, sizeof(progress_text), "Loaded " _YELLOW_("%u") " RAW / " _YELLOW_("%u") " LZ4 / " _YELLOW_("%u") " BZ2 in %4"PRIu64" ms" , nraw , nlz4 , nbz2 diff --git a/client/src/cmdparser.c b/client/src/cmdparser.c index d170c6828..fc628b947 100644 --- a/client/src/cmdparser.c +++ b/client/src/cmdparser.c @@ -231,7 +231,7 @@ static int execute_system_command(const char *command) { int ret; - #if defined(_WIN32) +#if defined(_WIN32) char wrapped_command[255]; strncat(wrapped_command, "cmd /C \"", 9); strncat(wrapped_command, command, strlen(command)); diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 0e83bf128..0f3242345 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -181,7 +181,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t } */ - // extract MFU-C KEY when written. + // extract UL-C KEY when written. switch (frame[0]) { case MIFARE_ULC_AUTH_1: { diff --git a/client/src/fileutils.c b/client/src/fileutils.c index 3bea9f3c4..f4c76f049 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -2353,7 +2353,7 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo keylen = 6; } - size_t block_size = 10 * keylen; + size_t block_size = 1000 * keylen; // double up since its chars keylen <<= 1; @@ -2428,10 +2428,9 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo continue; } - if (hex_to_bytes( - line, - (uint8_t *)*pdata + (*keycnt * (keylen >> 1)), - keylen >> 1) != (keylen >> 1)) { + int ret = hex_to_bytes(line, (uint8_t *)*pdata + (*keycnt * (keylen >> 1)), keylen >> 1); + if (ret != (keylen >> 1)) { + PrintAndLogEx(INFO, "hex to bytes wrong %i", ret); continue; } @@ -2450,16 +2449,16 @@ out: return retval; } -int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen) { +int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen, bool verbose) { char *path; int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, suffix, false); if (res != PM3_SUCCESS) { - return PM3_EFILE; + return PM3_ENOFILE; } FILE *f = fopen(path, "rb"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); free(path); return PM3_EFILE; @@ -2502,7 +2501,9 @@ int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya *blen = fread(*keyb, 1, fsize, f); fclose(f); - PrintAndLogEx(SUCCESS, "Loaded binary key file `" _YELLOW_("%s") "`", path); + if (verbose) { + PrintAndLogEx(SUCCESS, "Loaded binary key file `" _YELLOW_("%s") "`", path); + } free(path); return PM3_SUCCESS; } diff --git a/client/src/fileutils.h b/client/src/fileutils.h index 3e8b022c1..cc6d9ee14 100644 --- a/client/src/fileutils.h +++ b/client/src/fileutils.h @@ -307,7 +307,7 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo */ int loadFileXML_safe(const char *preferredName, const char *suffix, void **pdata, size_t *datalen); -int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen); +int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen, bool verbose); /** * @brief Utility function to check and convert plain mfu dump format to new mfu binary format. diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index f6fc0b07a..bdac928d5 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -64,7 +64,7 @@ int mf_dark_side(uint8_t blockno, uint8_t key_type, uint64_t *key) { //flush queue while (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(WARNING, "Aborted via keyboard"); + PrintAndLogEx(WARNING, "aborted via keyboard"); return PM3_EOPABORTED; } @@ -90,7 +90,7 @@ int mf_dark_side(uint8_t blockno, uint8_t key_type, uint64_t *key) { //TODO: Not really stopping the command in time. if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(WARNING, "\nAborted via keyboard"); + PrintAndLogEx(WARNING, "\naborted via keyboard"); return PM3_EOPABORTED; } diff --git a/client/src/pthread_spin_lock_shim.h b/client/src/pthread_spin_lock_shim.h index 131598b02..243771a6c 100644 --- a/client/src/pthread_spin_lock_shim.h +++ b/client/src/pthread_spin_lock_shim.h @@ -18,38 +18,38 @@ typedef int pthread_spinlock_t; #endif static inline int pthread_spin_init(pthread_spinlock_t *lock, int pshared) { - __asm__ __volatile__ ("" ::: "memory"); - *lock = 0; - return 0; + __asm__ __volatile__("" ::: "memory"); + *lock = 0; + return 0; } static inline int pthread_spin_destroy(pthread_spinlock_t *lock) { - return 0; + return 0; } static inline int pthread_spin_lock(pthread_spinlock_t *lock) { - while (1) { - int i; - for (i=0; i < 10000; i++) { - if (__sync_bool_compare_and_swap(lock, 0, 1)) { - return 0; - } - } - sched_yield(); - } + while (1) { + int i; + for (i = 0; i < 10000; i++) { + if (__sync_bool_compare_and_swap(lock, 0, 1)) { + return 0; + } + } + sched_yield(); + } } static inline int pthread_spin_trylock(pthread_spinlock_t *lock) { - if (__sync_bool_compare_and_swap(lock, 0, 1)) { - return 0; - } - return 16; // EBUSY; + if (__sync_bool_compare_and_swap(lock, 0, 1)) { + return 0; + } + return 16; // EBUSY; } static inline int pthread_spin_unlock(pthread_spinlock_t *lock) { - __asm__ __volatile__ ("" ::: "memory"); - *lock = 0; - return 0; + __asm__ __volatile__("" ::: "memory"); + *lock = 0; + return 0; } #endif diff --git a/common/commonutil.c b/common/commonutil.c index 48ce64bfa..ff0782514 100644 --- a/common/commonutil.c +++ b/common/commonutil.c @@ -450,6 +450,43 @@ void lslx(uint8_t *d, size_t n, uint8_t shifts) { } } +// right shift an array of length one bit +void rsl(uint8_t *d, size_t n) { + + uint8_t carry = 0; + + for (size_t i = 0; i < n; i++) { + + // Save the LSB before shifting + uint8_t new_carry = d[i] & 0x1; + + // Shift current byte right and incorporate previous carry + d[i] = (d[i] >> 1) | (carry ? 0x80 : 0); + + // Update carry for next byte + carry = new_carry; + } +} + +void rslx(uint8_t *d, size_t n, uint8_t shifts) { + + uint8_t carry = 0; + for (uint8_t j = 0; j < shifts; j++) { + + for (size_t i = 0; i < n; i++) { + + // Save the LSB before shifting + uint8_t new_carry = d[i] & 0x1; + + // Shift current byte right and incorporate previous carry + d[i] = (d[i] >> 1) | (carry ? 0x80 : 0); + + // Update carry for next byte + carry = new_carry; + } + } +} + // BSWAP24 of array[3] uint32_t le24toh(const uint8_t data[3]) { diff --git a/common/commonutil.h b/common/commonutil.h index f963805bb..00949b851 100644 --- a/common/commonutil.h +++ b/common/commonutil.h @@ -135,6 +135,9 @@ void xor(uint8_t *dest, const uint8_t *src, size_t n); void lsl(uint8_t *d, size_t n); void lslx(uint8_t *d, size_t n, uint8_t shifts); +void rsl(uint8_t *d, size_t n); +void rslx(uint8_t *d, size_t n, uint8_t shifts); + uint32_t le24toh(const uint8_t data[3]); void htole24(uint32_t val, uint8_t data[3]); diff --git a/doc/commands.json b/doc/commands.json index 034e055b1..4ef2a2aa0 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -3686,9 +3686,10 @@ "-p, --prevent fake epurse update", "--shallow shallow mod", "-d, --data DER encoded command to send to SAM", + "-s, --snmp data is in snmp format without headers", "--info get SAM infos (version, serial number)" ], - "usage": "hf iclass sam [-hvkntp] [--break] [--shallow] [-d ]... [--info]" + "usage": "hf iclass sam [-hvkntps] [--break] [--shallow] [-d ]... [--info]" }, "hf iclass sim": { "command": "hf iclass sim", @@ -12123,7 +12124,7 @@ }, "mem load": { "command": "mem load", - "description": "Loads binary file into flash memory on device Warning: mem area to be written must have been wiped first ( dictionaries are serviced as files in spiffs so no wipe is needed )", + "description": "Loads binary file into flash memory on device Warning! - mem area to be written must have been wiped first OBS! - dictionaries are serviced as files in spiffs so no wipe is needed", "notes": [ "mem load -f myfile -> upload file myfile values at default offset 0", "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024", @@ -12136,14 +12137,14 @@ "options": [ "-h, --help This help", "-o, --offset offset in memory", - "-m, --mifare, --mfc upload 6 bytes keys (mifare key dictionary)", - "-i, --iclass upload 8 bytes keys (iClass key dictionary)", - "-t, --t55xx upload 4 bytes keys (password dictionary)", - "--ulc upload 16 bytes keys (mifare UL-C key dictionary)", - "--ulaes upload 16 bytes keys (mifare UL-AES key dictionary)", + "-m, --mfc upload 6 bytes keys (MIFARE Classic dictionary)", + "-i, --iclass upload 8 bytes keys (iClass dictionary)", + "-t, --t55xx upload 4 bytes keys (T55xx dictionary)", + "--ulc upload 16 bytes keys (MIFARE UL-C dictionary)", + "--aes upload 16 bytes keys (MIFARE UL-AES dictionary)", "-f, --file file name" ], - "usage": "mem load [-hmit] [-o ] [--ulc] [--ulaes] -f " + "usage": "mem load [-hmit] [-o ] [--ulc] [--aes] -f " }, "mem spiffs check": { "command": "mem spiffs check", @@ -13374,6 +13375,6 @@ "metadata": { "commands_extracted": 768, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-06-19T15:01:51" + "extracted_on": "2025-06-22T18:30:29" } } diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index bb9123d7d..cbce45a24 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -964,6 +964,8 @@ typedef struct { // Cryptographic error client/pm3: cryptographic operation failed #define PM3_ECRYPTO -29 +// File error client: error related to file does not exist in search paths +#define PM3_ENOFILE -30 // No data client/pm3: no data available, no host frame available (not really an error) #define PM3_ENODATA -98 // Quit program client: reserved, order to quit the program From f94a2cb964a3b5dbe811a5b84aeed3f808a26461 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 27 Jun 2025 09:55:58 +0800 Subject: [PATCH 034/149] Updated sam firmware version to be in decimal digits Updated sam firmware version to be in decimal digits --- armsrc/sam_common.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/armsrc/sam_common.c b/armsrc/sam_common.c index 75a5c3527..bfa959bba 100644 --- a/armsrc/sam_common.c +++ b/armsrc/sam_common.c @@ -229,7 +229,8 @@ int sam_get_version(bool info) { uint16_t response_len = ISO7816_MAX_FRAME; uint8_t payload[] = { - 0xa0, 0x02, // <- SAM command + 0xa0, // <- SAM command + 0x02, // <- Length 0x82, 0x00 // <- get version }; uint16_t payload_len = sizeof(payload); @@ -278,7 +279,7 @@ int sam_get_version(bool info) { } if (g_dbglevel >= DBG_INFO || info) { DbpString(_BLUE_("-- SAM Information --")); - Dbprintf(_YELLOW_("Firmware version: ")"%X.%X", sam_version_an[2], sam_version_an[3]); + Dbprintf(_YELLOW_("Firmware version: ")"%d.%d", sam_version_an[2], sam_version_an[3]); Dbprintf(_YELLOW_("Firmware ID: ")); Dbhexdump(sam_build_an[1], sam_build_an + 2, false); } @@ -309,7 +310,8 @@ int sam_get_serial_number(void) { uint16_t response_len = ISO7816_MAX_FRAME; uint8_t payload[] = { - 0xa0, 0x02, // <- SAM command + 0xa0, // <- SAM command + 0x02, // <- Length 0x96, 0x00 // <- get serial number }; uint16_t payload_len = sizeof(payload); From 89465db4b127ed9d4a3b77fd84f252dfa50f426c Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 27 Jun 2025 10:30:40 +0800 Subject: [PATCH 035/149] Update hf iclass unhash to check lsb Updated hf iclass unhash to check lsb to be 4x 0 and 4x 1. If it doesn't respect that format it means it never went through hash0 (as hash0 forces the key format to have 4x LSB set to 1 and 4x LSB set to 0) and likely to be an AES based key. --- client/src/cmdhficlass.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index ffa5d8f7d..786049787 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -5058,6 +5058,24 @@ static int CmdHFiClassUnhash(const char *Cmd) { return PM3_EINVARG; } + //check if divkey respects hash0 rules (legacy format) or if it could be AES Based + + int count_lsb0 = 0; + int count_lsb1 = 0; + + for (int i = 0; i < PICOPASS_BLOCK_SIZE; i++) { + if ((div_key[i] & 0x01) == 0) { + count_lsb0++; + } else { + count_lsb1++; + } + } + + if(count_lsb0 != 4 || count_lsb1 != 4){ + PrintAndLogEx(INFO, _RED_("Incorrect LSB Distribution, unable to unhash - the key might be AES based.")); + return PM3_SUCCESS; + } + PrintAndLogEx(INFO, "Diversified key... %s", sprint_hex_inrow(div_key, sizeof(div_key))); PrintAndLogEx(INFO, "-----------------------------------"); invert_hash0(div_key); From 3625ee318a844f438efcce10a273143ed0001900 Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Fri, 27 Jun 2025 11:57:26 +0200 Subject: [PATCH 036/149] Fix SEGV in cmdhf15 due to argtable size being miscomputed --- client/src/cmdhf15.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index c2727fa38..383aa0d7c 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -2624,7 +2624,7 @@ static int CmdHF15Restore(const char *Cmd) { "hf 15 restore -u E011223344556677 -f hf-15-my-dump.bin" ); - void *argtable[6 + 5] = {0}; + void *argtable[6 + 4] = {0}; uint8_t arglen = arg_add_default(argtable); argtable[arglen++] = arg_str0("f", "file", "", "Specify a filename for dump file"); argtable[arglen++] = arg_int0("r", "retry", "", "number of retries (def 3)"); From 6c402791f12c09eef4bc2f898afe58935b9f2602 Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Fri, 27 Jun 2025 12:04:27 +0200 Subject: [PATCH 037/149] Fix comma instead of semicolon --- client/src/cmdhf15.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 383aa0d7c..0e19c9192 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -3498,8 +3498,8 @@ static int CmdHF15Wipe(const char *Cmd) { ); void *argtable[6 + 3] = {0}; uint8_t arglen = arg_add_default(argtable); - argtable[arglen++] = arg_int0(NULL, "bs", "", "block size (def 4)"), - argtable[arglen++] = arg_lit0("v", "verbose", "verbose output"); + argtable[arglen++] = arg_int0(NULL, "bs", "", "block size (def 4)"); + argtable[arglen++] = arg_lit0("v", "verbose", "verbose output"); argtable[arglen++] = arg_param_end; CLIExecWithReturn(ctx, Cmd, argtable, true); From a33ea4dc6e146f084af0e439066c727b86bd8c28 Mon Sep 17 00:00:00 2001 From: Benjamin DELPY Date: Fri, 27 Jun 2025 22:04:08 +0200 Subject: [PATCH 038/149] [st25tb] Update intertic.py to support Colmar (Trace / Keolis) ID Signed-off-by: Benjamin DELPY --- client/pyscripts/intertic.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/pyscripts/intertic.py b/client/pyscripts/intertic.py index 34a02800c..8f2690dd7 100644 --- a/client/pyscripts/intertic.py +++ b/client/pyscripts/intertic.py @@ -305,6 +305,9 @@ FRA_OrganizationalAuthority_Contract_Provider = { 0x021: { 1: InterticHelper('Bordeaux', 'TBM / Keolis', Describe_Usage_1_1), }, + 0x040: { + 28: InterticHelper('Colmar', 'Trace / Keolis', Describe_Usage_1_1), + }, 0x057: { 1: InterticHelper('Lyon', 'TCL / Keolis', Describe_Usage_1), # Strange usage ?, kept on generic 1 }, From 5b37fe8af66e8ed0e726d80239654f8e8030b04a Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 29 Jun 2025 23:38:27 +0200 Subject: [PATCH 039/149] hf mf info - dont try fudan cards --- armsrc/mifarecmd.c | 2 +- client/src/cmdhf14a.c | 8 ++++---- client/src/cmdhfmf.c | 17 ++++++++++++----- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 4386f5003..010277873 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -3220,7 +3220,7 @@ void MifareHasStaticNonce(void) { } if (counter) { - Dbprintf("Static nonce......... " _YELLOW_("%08x"), nt); + Dbprintf("Static nonce....... " _YELLOW_("%08x"), nt); data[0] = NONCE_STATIC; } else { data[0] = NONCE_NORMAL; diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 8d7ec5d5b..42bee9388 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -3147,18 +3147,18 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (isMifareClassic || isMifareMini) { res = detect_classic_static_nonce(); if (res == NONCE_STATIC) { - PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); + PrintAndLogEx(SUCCESS, "Static nonce....... " _YELLOW_("yes")); } if (res == NONCE_NORMAL) { // not static res = detect_classic_prng(); if (res == 1) { - PrintAndLogEx(SUCCESS, "Prng detection....... " _GREEN_("weak")); + PrintAndLogEx(SUCCESS, "Prng detection..... " _GREEN_("weak")); } else if (res == 0) { - PrintAndLogEx(SUCCESS, "Prng detection....... " _YELLOW_("hard")); + PrintAndLogEx(SUCCESS, "Prng detection..... " _YELLOW_("hard")); } else { - PrintAndLogEx(FAILED, "Prng detection........ " _RED_("fail")); + PrintAndLogEx(FAILED, "Prng detection...... " _RED_("fail")); } if (do_nack_test) { diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 038180ff1..f52f2ba19 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -10237,7 +10237,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " ---------------------"); + PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " -----------------------------"); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]); PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card.sak, resp.oldarg[0]); @@ -10292,6 +10292,13 @@ static int CmdHF14AMfInfo(const char *Cmd) { PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("emv info") "`"); } + if ((card_type & MTFUDAN) == MTFUDAN) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "FUDAN FM11RF005 detected"); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf fudan dump") "`"); + goto out; + } + if (setDeviceDebugLevel(verbose ? MAX(dbg_curr, DBG_INFO) : DBG_NONE, false) != PM3_SUCCESS) { return PM3_EFAILED; } @@ -10764,7 +10771,7 @@ static int CmdHF14AMfISEN(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " ---------------------"); + PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " -----------------------------"); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]); PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card.sak, resp.oldarg[0]); @@ -10776,11 +10783,11 @@ static int CmdHF14AMfISEN(const char *Cmd) { int res = detect_classic_static_encrypted_nonce_ex(blockn, keytype, key, blockn_nested, keytype_nested, key_nested, nr_nested, reset, hardreset, addread, addauth, incblk2, corruptnrar, corruptnrarparity, true); if (res == NONCE_STATIC) { - PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); + PrintAndLogEx(SUCCESS, "Static nonce....... " _YELLOW_("yes")); } else if (res == NONCE_SUPERSTATIC) { - PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes, even when nested")); + PrintAndLogEx(SUCCESS, "Static nonce....... " _YELLOW_("yes, even when nested")); } else if (res == NONCE_STATIC_ENC) { - PrintAndLogEx(SUCCESS, "Static enc nonce..... " _RED_("yes")); + PrintAndLogEx(SUCCESS, "Static enc nonce... " _RED_("yes")); } if (res == NONCE_STATIC_ENC) { From 16cbb4a446a6d57e78ce5a9cad94f533e2cfa22b Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 30 Jun 2025 14:28:24 +0200 Subject: [PATCH 040/149] style --- client/src/cmdhf15.c | 133 ++++++++++++++++++--------------------- client/src/cmdhf15.h | 17 ++++- client/src/cmdhficlass.c | 2 +- client/src/proxmark3.c | 6 +- include/iso15.h | 8 ++- 5 files changed, 88 insertions(+), 78 deletions(-) diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 0e19c9192..5104ddedc 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -50,22 +50,6 @@ #define Logic1 Iso15693Logic1 #define FrameEOF Iso15693FrameEOF #define CARD_MEMORY_SIZE 4096 -#define HF15_UID_LENGTH 8 - -#ifndef Crc15 -# define Crc15(data, len) Crc16ex(CRC_15693, (data), (len)) -#endif -#ifndef CheckCrc15 -# define CheckCrc15(data, len) check_crc(CRC_15693, (data), (len)) -#endif -#ifndef AddCrc15 -#define AddCrc15(data, len) compute_crc(CRC_15693, (data), (len), (data)+(len), (data)+(len)+1) -#endif - -#ifndef ISO15_RAW_LEN -#define ISO15_RAW_LEN(x) (sizeof(iso15_raw_cmd_t) + (x)) -#endif - #ifndef ISO15_ERROR_HANDLING_RESPONSE #define ISO15_ERROR_HANDLING_RESPONSE { \ @@ -98,6 +82,11 @@ } #endif +typedef struct { + uint8_t lock; + uint8_t block[8]; +} t15memory_t; + // structure and database for uid -> tagtype lookups typedef struct { uint64_t uid; @@ -474,7 +463,7 @@ static int getUID(bool verbose, bool loop, uint8_t *buf) { // used with 'hf search' bool readHF15Uid(bool loop, bool verbose) { - uint8_t uid[HF15_UID_LENGTH] = {0}; + uint8_t uid[ISO15693_UID_LENGTH] = {0}; if (getUID(verbose, loop, uid) != PM3_SUCCESS) { return false; } @@ -665,7 +654,7 @@ static int NxpTestEAS(const uint8_t *uid) { return PM3_EINVARG; } - uint8_t approxlen = 3 + HF15_UID_LENGTH + 2; + uint8_t approxlen = 3 + ISO15693_UID_LENGTH + 2; iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); if (packet == NULL) { PrintAndLogEx(WARNING, "Failed to allocate memory"); @@ -677,8 +666,8 @@ static int NxpTestEAS(const uint8_t *uid) { packet->raw[packet->rawlen++] = ISO15693_EAS_ALARM; packet->raw[packet->rawlen++] = 0x04; // IC manufacturer code - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); // add UID - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); // add UID + packet->rawlen += ISO15693_UID_LENGTH; AddCrc15(packet->raw, packet->rawlen); packet->rawlen += 2; @@ -720,7 +709,7 @@ static int NxpCheckSig(uint8_t *uid) { return PM3_EINVARG; } - uint8_t approxlen = 3 + HF15_UID_LENGTH + 2; + uint8_t approxlen = 3 + ISO15693_UID_LENGTH + 2; iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); if (packet == NULL) { PrintAndLogEx(WARNING, "Failed to allocate memory"); @@ -733,8 +722,8 @@ static int NxpCheckSig(uint8_t *uid) { packet->raw[packet->rawlen++] = ISO15693_READ_SIGNATURE; packet->raw[packet->rawlen++] = 0x04; // IC manufacturer code - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); // add UID - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); // add UID + packet->rawlen += ISO15693_UID_LENGTH; AddCrc15(packet->raw, packet->rawlen); packet->rawlen += 2; @@ -787,7 +776,7 @@ static int NxpSysInfo(uint8_t *uid) { packet->raw[packet->rawlen++] = 0x04; // IC manufacturer code memcpy(packet->raw + 3, uid, 8); // add UID - packet->rawlen += HF15_UID_LENGTH; + packet->rawlen += ISO15693_UID_LENGTH; AddCrc15(packet->raw, packet->rawlen); packet->rawlen += 2; @@ -900,11 +889,11 @@ static int StCheckSig(uint8_t *uid) { } // ISO15693 Protocol params - packet->raw[packet->rawlen++] = arg_get_raw_flag(HF15_UID_LENGTH, false, false, false); + packet->raw[packet->rawlen++] = arg_get_raw_flag(ISO15693_UID_LENGTH, false, false, false); packet->raw[packet->rawlen++] = ISO15693_READBLOCK; // add UID (scan, uid) - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; packet->flags = (ISO15_CONNECT | ISO15_READ_RESPONSE | ISO15_NO_DISCONNECT); uint16_t blkoff = packet->rawlen; char signature_hex[65] = {0}; @@ -943,9 +932,9 @@ static int StCheckSig(uint8_t *uid) { uint8_t signature[16]; size_t signature_len; hexstr_to_byte_array(signature_hex, signature, &signature_len); - uint8_t uid_swap[HF15_UID_LENGTH]; - reverse_array_copy(uid, HF15_UID_LENGTH, uid_swap); - int index = originality_check_verify_ex(uid_swap, HF15_UID_LENGTH, signature, signature_len, PK_ST25TV, false, true); + uint8_t uid_swap[ISO15693_UID_LENGTH]; + reverse_array_copy(uid, ISO15693_UID_LENGTH, uid_swap); + int index = originality_check_verify_ex(uid_swap, ISO15693_UID_LENGTH, signature, signature_len, PK_ST25TV, false, true); PrintAndLogEx(NORMAL, ""); return originality_check_print(signature, signature_len, index); } @@ -970,7 +959,7 @@ static int CmdHF15Info(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); bool unaddressed = arg_get_lit(ctx, 2); @@ -987,7 +976,7 @@ static int CmdHF15Info(const char *Cmd) { } // default fallback to scan for tag. - if (unaddressed == false && uidlen != HF15_UID_LENGTH) { + if (unaddressed == false && uidlen != ISO15693_UID_LENGTH) { scan = true; } @@ -1014,10 +1003,10 @@ static int CmdHF15Info(const char *Cmd) { free(packet); return PM3_EINVARG; } - uidlen = HF15_UID_LENGTH; + uidlen = ISO15693_UID_LENGTH; } - if (uidlen == HF15_UID_LENGTH) { + if (uidlen == ISO15693_UID_LENGTH) { // add UID (scan, uid) memcpy(packet->raw + packet->rawlen, uid, uidlen); packet->rawlen += uidlen; @@ -1486,7 +1475,7 @@ static int CmdHF15Sim(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); struct { - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; uint8_t block_size; } PACKED payload; memset(&payload, 0, sizeof(payload)); @@ -1497,7 +1486,7 @@ static int CmdHF15Sim(const char *Cmd) { CLIParserFree(ctx); // sanity checks - if (uidlen != 0 && uidlen != HF15_UID_LENGTH) { + if (uidlen != 0 && uidlen != ISO15693_UID_LENGTH) { PrintAndLogEx(WARNING, "UID must include 8 hex bytes, got ( " _RED_("%i") " )", uidlen); return PM3_EINVARG; } @@ -1624,7 +1613,7 @@ static int CmdHF15WriteAfi(const char *Cmd) { struct { uint8_t pwd[4]; bool use_pwd; - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; bool use_uid; uint8_t afi; } PACKED payload; @@ -1645,7 +1634,7 @@ static int CmdHF15WriteAfi(const char *Cmd) { } payload.use_uid = false; - if (uidlen == HF15_UID_LENGTH) { + if (uidlen == ISO15693_UID_LENGTH) { payload.use_uid = true; } @@ -1703,7 +1692,7 @@ static int CmdHF15WriteDsfid(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[HF15_UID_LENGTH] = {0}; + uint8_t uid[ISO15693_UID_LENGTH] = {0}; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); @@ -1742,10 +1731,10 @@ static int CmdHF15WriteDsfid(const char *Cmd) { free(packet); return PM3_EINVARG; } - uidlen = HF15_UID_LENGTH; + uidlen = ISO15693_UID_LENGTH; } - if (uidlen == HF15_UID_LENGTH) { + if (uidlen == ISO15693_UID_LENGTH) { // add UID (scan, uid) memcpy(packet->raw + packet->rawlen, uid, uidlen); packet->rawlen += uidlen; @@ -1807,7 +1796,7 @@ static int CmdHF15Dump(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t uid[HF15_UID_LENGTH] = {0}; + uint8_t uid[ISO15693_UID_LENGTH] = {0}; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); @@ -1838,7 +1827,7 @@ static int CmdHF15Dump(const char *Cmd) { } // default fallback to scan for tag. - if (uidlen != HF15_UID_LENGTH && !unaddressed) { + if (uidlen != ISO15693_UID_LENGTH && !unaddressed) { scan = true; } @@ -1874,11 +1863,11 @@ static int CmdHF15Dump(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } // add UID (scan, uid) - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; used_uid = true; } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -2178,10 +2167,10 @@ static int CmdHF15Readmulti(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[HF15_UID_LENGTH] = {0x00}; + uint8_t uid[ISO15693_UID_LENGTH] = {0x00}; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool uid_set = (uidlen == ISO15693_UID_LENGTH) ? true : false; bool unaddressed = arg_get_lit(ctx, 2); bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; //Default fallback to scan for tag. Overriding unaddressed parameter. @@ -2239,11 +2228,11 @@ static int CmdHF15Readmulti(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } // add UID (scan, uid) - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -2336,10 +2325,10 @@ static int CmdHF15Readblock(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool uid_set = (uidlen == ISO15693_UID_LENGTH) ? true : false; bool unaddressed = arg_get_lit(ctx, 2); bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; // Default fallback to scan for tag. Overriding unaddressed parameter. @@ -2393,11 +2382,11 @@ static int CmdHF15Readblock(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } // add UID (scan, uid) - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -2490,8 +2479,8 @@ static int hf_15_write_blk(const uint8_t *pm3flags, uint16_t flags, const uint8_ // add UID if (uid) { - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; } packet->raw[packet->rawlen++] = blockno; @@ -2550,10 +2539,10 @@ static int CmdHF15Write(const char *Cmd) { argtable[arglen++] = arg_param_end; CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool uid_set = (uidlen == ISO15693_UID_LENGTH) ? true : false; bool unaddressed = arg_get_lit(ctx, 2); bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; // Default fallback to scan for tag. Overriding unaddressed parameter. @@ -2589,7 +2578,7 @@ static int CmdHF15Write(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -2632,7 +2621,7 @@ static int CmdHF15Restore(const char *Cmd) { argtable[arglen++] = arg_param_end; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); @@ -2662,7 +2651,7 @@ static int CmdHF15Restore(const char *Cmd) { // default fallback to scan for tag. // overriding unaddress parameter :) - if (uidlen != HF15_UID_LENGTH) { + if (uidlen != ISO15693_UID_LENGTH) { scan = true; } @@ -2674,7 +2663,7 @@ static int CmdHF15Restore(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -2809,7 +2798,7 @@ static int CmdHF15CSetUID(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); struct { - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; } PACKED payload; int uidlen = 0; @@ -2817,7 +2806,7 @@ static int CmdHF15CSetUID(const char *Cmd) { bool use_v2 = arg_get_lit(ctx, 2); CLIParserFree(ctx); - if (uidlen != HF15_UID_LENGTH) { + if (uidlen != ISO15693_UID_LENGTH) { PrintAndLogEx(WARNING, "UID must include 8 hex bytes, got " _RED_("%i"), uidlen); return PM3_EINVARG; } @@ -2831,7 +2820,7 @@ static int CmdHF15CSetUID(const char *Cmd) { PrintAndLogEx(INFO, "Get current tag"); - uint8_t carduid[HF15_UID_LENGTH] = {0x00}; + uint8_t carduid[ISO15693_UID_LENGTH] = {0x00}; if (getUID(true, false, carduid) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "no tag found"); return PM3_ESOFT; @@ -2861,10 +2850,10 @@ static int CmdHF15CSetUID(const char *Cmd) { } // reverse cardUID to compare - uint8_t revuid[HF15_UID_LENGTH] = {0}; + uint8_t revuid[ISO15693_UID_LENGTH] = {0}; reverse_array_copy(carduid, sizeof(carduid), revuid); - if (memcmp(revuid, payload.uid, HF15_UID_LENGTH) == 0) { + if (memcmp(revuid, payload.uid, ISO15693_UID_LENGTH) == 0) { PrintAndLogEx(SUCCESS, "Setting new UID ( " _GREEN_("ok") " )"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS;; @@ -3503,10 +3492,10 @@ static int CmdHF15Wipe(const char *Cmd) { argtable[arglen++] = arg_param_end; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool uid_set = (uidlen == ISO15693_UID_LENGTH) ? true : false; bool unaddressed = arg_get_lit(ctx, 2); bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; // Default fallback to scan for tag. Overriding unaddressed parameter. @@ -3538,7 +3527,7 @@ static int CmdHF15Wipe(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } } else { PrintAndLogEx(INFO, "Using unaddressed mode"); diff --git a/client/src/cmdhf15.h b/client/src/cmdhf15.h index 8803a58b4..b6f7d250b 100644 --- a/client/src/cmdhf15.h +++ b/client/src/cmdhf15.h @@ -20,9 +20,24 @@ #define CMDHF15_H__ #include "common.h" +#include "crc16.h" +#include "iso15.h" // typedef structs / enum + +#ifndef Crc15 +# define Crc15(data, len) Crc16ex(CRC_15693, (data), (len)) +#endif +#ifndef CheckCrc15 +# define CheckCrc15(data, len) check_crc(CRC_15693, (data), (len)) +#endif +#ifndef AddCrc15 +#define AddCrc15(data, len) compute_crc(CRC_15693, (data), (len), (data)+(len), (data)+(len)+1) +#endif + +#ifndef ISO15_RAW_LEN +#define ISO15_RAW_LEN(x) (sizeof(iso15_raw_cmd_t) + (x)) +#endif int CmdHF15(const char *Cmd); - bool readHF15Uid(bool loop, bool verbose); #endif diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 786049787..880cea140 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -5071,7 +5071,7 @@ static int CmdHFiClassUnhash(const char *Cmd) { } } - if(count_lsb0 != 4 || count_lsb1 != 4){ + if (count_lsb0 != 4 || count_lsb1 != 4) { PrintAndLogEx(INFO, _RED_("Incorrect LSB Distribution, unable to unhash - the key might be AES based.")); return PM3_SUCCESS; } diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index b916f0b71..2481803fa 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -949,10 +949,13 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *file } finish: - if (ret != PM3_SUCCESS) + if (ret != PM3_SUCCESS) { PrintAndLogEx(WARNING, "The flashing procedure failed, follow the suggested steps!"); + } + ret = flash_stop_flashing(); CloseProxmark(g_session.current_device); + finish2: for (int i = 0 ; i < num_files; ++i) { flash_free(&files[i]); @@ -1001,7 +1004,6 @@ void pm3_init(void) { // set global variables soon enough to get the log path set_my_executable_path(); set_my_user_directory(); - } #ifndef LIBPM3 diff --git a/include/iso15.h b/include/iso15.h index 0d19c1756..9c39db3a3 100644 --- a/include/iso15.h +++ b/include/iso15.h @@ -20,10 +20,14 @@ #define _ISO15_H_ #include "common.h" + +#define ISO15693_UID_LENGTH 8 +#define ISO15693_ATQB_LENGTH 7 + typedef struct { - uint8_t uid[8]; + uint8_t uid[ISO15693_UID_LENGTH]; uint8_t uidlen; - uint8_t atqb[7]; + uint8_t atqb[ISO15693_ATQB_LENGTH]; uint8_t chipid; uint8_t cid; } PACKED iso15_card_select_t; From 7e2aa07b273751990587d83274f0b1b90294ef66 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 30 Jun 2025 14:29:24 +0200 Subject: [PATCH 041/149] revamped --- client/resources/iceman.txt | 8 ++++ client/src/flash.c | 77 ++++++++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 14 deletions(-) create mode 100644 client/resources/iceman.txt diff --git a/client/resources/iceman.txt b/client/resources/iceman.txt new file mode 100644 index 000000000..ad58ac814 --- /dev/null +++ b/client/resources/iceman.txt @@ -0,0 +1,8 @@ +$$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\  +\_$$ _|$$ __$$\ $$ _____|$$$\ $$$ |$$ __$$\ $$$\ $$ | + $$ | $$ / \__|$$ | $$$$\ $$$$ |$$ / $$ |$$$$\ $$ | + $$ | $$ | $$$$$\ $$\$$\$$ $$ |$$$$$$$$ |$$ $$\$$ | + $$ | $$ | $$ __| $$ \$$$ $$ |$$ __$$ |$$ \$$$$ | + $$ | $$ | $$\ $$ | $$ |\$ /$$ |$$ | $$ |$$ |\$$$ | +$$$$$$\ \$$$$$$ |$$$$$$$$\ $$ | \_/ $$ |$$ | $$ |$$ | \$$ | +\______| \______/ \________|\__| \__|\__| \__|\__| \__| \ No newline at end of file diff --git a/client/src/flash.c b/client/src/flash.c index ff14309a0..1672a4497 100644 --- a/client/src/flash.c +++ b/client/src/flash.c @@ -30,6 +30,7 @@ #include "util_posix.h" #include "comms.h" #include "commonutil.h" +#include "fileutils.h" #define FLASH_START 0x100000 @@ -266,7 +267,7 @@ int flash_load(flash_file_t *ctx, bool force) { int res = PM3_EUNDEF; fd = fopen(ctx->filename, "rb"); - if (!fd) { + if (fd == NULL) { PrintAndLogEx(ERR, _RED_("Could not open file") " %s >>> ", ctx->filename); res = PM3_EFILE; goto fail; @@ -652,17 +653,41 @@ static const char ice[] = " !!: :!! !!: !!: !!: !!: !!! !!: !!!\n : :: :: : : :: ::: : : : : : :: : \n" _RED_(" . .. .. . . .. ... . . . . . .. . "); + +#define ICEMAN_LOGO_FN "iceman.txt" +#define ICEMAN_LOGO_SIZE (5000) // Write a file's segments to Flash int flash_write(flash_file_t *ctx) { - int len = 0; + + char ice2[ICEMAN_LOGO_SIZE] = {0}; + char ice3[ICEMAN_LOGO_SIZE] = {0}; + + bool is_loaded = false; + if (g_session.supports_colors) { + + uint8_t *iraw = NULL; + size_t irawlen = 0; + int res = loadFile_safeEx(ICEMAN_LOGO_FN, "", (void **)&iraw, &irawlen, false); + if (res == PM3_SUCCESS && irawlen > ICEMAN_LOGO_SIZE) { + irawlen = ICEMAN_LOGO_SIZE; + } + if (res == PM3_SUCCESS) { + memcpy(ice3, iraw, irawlen); + free(iraw); + is_loaded = true; + } + } + + if (is_loaded == false) { + memcpy_filter_ansi(ice2, ice, sizeof(ice), !g_session.supports_colors); + memcpy_filter_emoji(ice3, ice2, sizeof(ice2), g_session.emoji_mode); + } + + size_t ice3len = strlen(ice3); PrintAndLogEx(SUCCESS, "Writing segments for file: %s", ctx->filename); - char ice2[sizeof(ice)] = {0}; - char ice3[sizeof(ice)] = {0}; - memcpy_filter_ansi(ice2, ice, sizeof(ice), !g_session.supports_colors); - memcpy_filter_emoji(ice3, ice2, sizeof(ice2), g_session.emoji_mode); - size_t ice3len = strlen(ice3); + int len = 0; for (int i = 0; i < ctx->num_segs; i++) { flash_seg_t *seg = &ctx->segments[i]; @@ -672,6 +697,9 @@ int flash_write(flash_file_t *ctx) { uint32_t end = seg->start + length; PrintAndLogEx(SUCCESS, " 0x%08x..0x%08x [0x%x / %u blocks]", seg->start, end - 1, length, blocks); + if (is_loaded) { + fprintf(stdout, "\n\n"); + } fflush(stdout); int block = 0; uint8_t *data = seg->data; @@ -693,15 +721,36 @@ int flash_write(flash_file_t *ctx) { length -= block_size; block++; - if (len < ice3len) { - fprintf(stdout, "%c", ice3[len++]); - } else { + if (is_loaded) { + if (len < ice3len) { + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + } else { - if ((len - ice3len) % 67 == 0) { - fprintf(stdout, "\n"); + if ((len - ice3len - 1) % 61 == 0) { + fprintf(stdout, "\n"); + } + fprintf(stdout, "."); + len++; + } + + } else { + if (len < ice3len) { + fprintf(stdout, "%c", ice3[len++]); + } else { + + if ((len - ice3len) % 67 == 0) { + fprintf(stdout, "\n"); + } + fprintf(stdout, "."); + len++; } - fprintf(stdout, "."); - len++; } fflush(stdout); } From 4268fe3ce1460f615f9e266e5f4d6a2601ef91a8 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 30 Jun 2025 14:29:49 +0200 Subject: [PATCH 042/149] text --- client/src/cmdwiegand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdwiegand.c b/client/src/cmdwiegand.c index d29521d53..c2d6bb3b1 100644 --- a/client/src/cmdwiegand.c +++ b/client/src/cmdwiegand.c @@ -201,7 +201,7 @@ int CmdWiegandDecode(const char *Cmd) { PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); return PM3_EINVARG; } - PrintAndLogEx(INFO, "Input bin len... %d", blen); + PrintAndLogEx(INFO, "#bits... %d", blen); } else if (plen) { From 95814cc5b81ad86170ecba0b432aaed1b21d26b4 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 1 Jul 2025 16:43:42 +0200 Subject: [PATCH 043/149] text --- client/src/cmdhf.c | 98 +++++++++++++++++----------------- client/src/cmdhfmf.c | 2 +- client/src/mifare/mifarehost.c | 8 +-- doc/commands.json | 5 +- 4 files changed, 57 insertions(+), 56 deletions(-) diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index fb9aae5b9..13c10d863 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -231,60 +231,60 @@ int CmdHFSearch(const char *Cmd) { } */ + DropField(); + PROMPT_CLEARLINE; if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, _RED_("No known/supported 13.56 MHz tags found")); - res = PM3_ESOFT; - } else { - - // no need to print 14A hints, since it will print itself - - if (success[THINFILM]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf thinfilm") "` commands\n"); - } - - if (success[LTO]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf lto") "` commands\n"); - } - - if (success[LEGIC]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf legic") "` commands\n"); - } - - if (success[TOPAZ]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf topaz") "` commands\n"); - } - - if (success[PROTO_TEXKOM]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf texkom") "` commands\n"); - } - - if (success[PROTO_XEROX]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf xerox") "` commands\n"); - } - - if (success[ISO_14443B]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 14b") "` commands\n"); - } - - if (success[ISO_15693]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 15") "` commands\n"); - } - - if (success[ICLASS]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass") "` commands\n"); - } - - if (success[FELICA]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf felica") "` commands\n"); - } - - if (success[PROTO_CRYPTORF]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf cryptorf") "` commands\n"); - } + return res; + } + + // no need to print 14A hints, since it will print itself + + if (success[THINFILM]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf thinfilm") "` commands\n"); + } + + if (success[LTO]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf lto") "` commands\n"); + } + + if (success[LEGIC]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf legic") "` commands\n"); + } + + if (success[TOPAZ]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf topaz") "` commands\n"); + } + + if (success[PROTO_TEXKOM]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf texkom") "` commands\n"); + } + + if (success[PROTO_XEROX]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf xerox") "` commands\n"); + } + + if (success[ISO_14443B]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 14b") "` commands\n"); + } + + if (success[ISO_15693]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 15") "` commands\n"); + } + + if (success[ICLASS]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass") "` commands\n"); + } + + if (success[FELICA]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf felica") "` commands\n"); + } + + if (success[PROTO_CRYPTORF]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf cryptorf") "` commands\n"); } - DropField(); return res; } diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index f52f2ba19..29eefbf0e 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -4651,7 +4651,7 @@ static int CmdHF14AMfSim(const char *Cmd) { "hf mf sim --1k -u 11223344556677 --> MIFARE Classic 1k with 7b UID\n" "hf mf sim --1k -u 11223344 -i -x --> Perform reader attack in interactive mode\n" "hf mf sim --2k --> MIFARE 2k\n" - "hf mf sim --4k --> MIFARE 4k" + "hf mf sim --4k --> MIFARE 4k\n" "hf mf sim --1k -x -e --> Keep simulation running and populate with found reader keys\n" ); diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index bdac928d5..d3e6a902b 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -1481,19 +1481,19 @@ int detect_classic_nackbug(bool verbose) { return PM3_SUCCESS; } case 2: { - PrintAndLogEx(SUCCESS, "NACK test: " _GREEN_("always leak NACK")); + PrintAndLogEx(SUCCESS, "NACK test... " _GREEN_("always leak NACK")); return PM3_SUCCESS; } case 1: { - PrintAndLogEx(SUCCESS, "NACK test: " _GREEN_("detected")); + PrintAndLogEx(SUCCESS, "NACK test... " _GREEN_("detected")); return PM3_SUCCESS; } case 0: { - PrintAndLogEx(SUCCESS, "NACK test: " _GREEN_("no bug")); + PrintAndLogEx(SUCCESS, "NACK test... " _GREEN_("no bug")); return PM3_SUCCESS; } default: { - PrintAndLogEx(ERR, "errorcode from device " _RED_("[%i]"), ok); + PrintAndLogEx(ERR, "errorcode from device (" _RED_("%u") " )", ok); return PM3_EUNDEF; } } diff --git a/doc/commands.json b/doc/commands.json index 4ef2a2aa0..6853203b4 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -5490,7 +5490,8 @@ "hf mf sim --1k -u 11223344556677 -> MIFARE Classic 1k with 7b UID", "hf mf sim --1k -u 11223344 -i -x -> Perform reader attack in interactive mode", "hf mf sim --2k -> MIFARE 2k", - "hf mf sim --4k -> MIFARE 4khf mf sim --1k -x -e --> Keep simulation running and populate with found reader keys" + "hf mf sim --4k -> MIFARE 4k", + "hf mf sim --1k -x -e -> Keep simulation running and populate with found reader keys" ], "offline": false, "options": [ @@ -13375,6 +13376,6 @@ "metadata": { "commands_extracted": 768, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-06-22T18:30:29" + "extracted_on": "2025-07-01T14:12:39" } } From 21bae5c73fc04424ea243f9c323a1b72d1105c5e Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 1 Jul 2025 16:45:18 +0200 Subject: [PATCH 044/149] fix hf 15 readmulti - wrong block count number. It is decreased before to be zero based, but in calc we need number of blocks --- CHANGELOG.md | 1 + client/src/cmdhf15.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aef0c3883..8aac2816a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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] +- Fixed `hf 15 readmulti` - fix block calculations (@iceman1001) - Changed `mem load` - now handles UL-C and UL-AES dictionary files (@iceman1001) - Changed `hf mfu sim` - now support UL-C simulation (@iceman1001) - Added `!` - run system commands from inside the client. Potentially dangerous if running client as SUDO, SU, ROOT (@iceman1001) diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 5104ddedc..1dbd32b97 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -2274,7 +2274,7 @@ static int CmdHF15Readmulti(const char *Cmd) { ISO15_ERROR_HANDLING_CARD_RESPONSE(d, resp.length) // 1 byte cmd, 1 lock byte, 4 / 8 bytes block size, 2 crc - if (resp.length > (1 + (blockcnt * (blocksize + 1)) + 2)) { + if (resp.length > (1 + ((blockcnt + 1) * (blocksize + 1)) + 2)) { PrintAndLogEx(WARNING, "got longer response. Check block size!"); } From 630708c3eb1be4cdeb61e483e3eea091461a6378 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 1 Jul 2025 16:45:55 +0200 Subject: [PATCH 045/149] support function --- client/src/util.c | 26 ++++++++++++++++++++++++++ client/src/util.h | 3 +++ 2 files changed, 29 insertions(+) diff --git a/client/src/util.c b/client/src/util.c index 541b67dd8..ff71b74e1 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -1212,6 +1212,29 @@ void binstr_2_bytes(uint8_t *target, size_t *targetlen, const char *src) { } } +void binstr_2_u8(char *src, uint8_t n, uint8_t *dest) { + + uint8_t b = 0; + // Process binary string + for (uint8_t i = 0; i < n; ++i) { + b = (b << 1) | (src[i] == '1'); + } + if (dest) { + *dest = b; + } +} + +void binstr_2_u16(char *src, uint8_t n, uint16_t *dest) { + uint16_t b = 0; + // Process binary string + for (uint8_t i = 0; i < n; ++i) { + b = (b << 1) | (src[i] == '1'); + } + if (dest) { + *dest = b; + } +} + void hex_xor(uint8_t *d, const uint8_t *x, int n) { while (n--) { d[n] ^= x[n]; @@ -1611,6 +1634,9 @@ size_t unduplicate(uint8_t *d, size_t n, const uint8_t item_n) { if (n == 0) { return 0; } + if (n == 1) { + return 1; + } int write_index = 0; diff --git a/client/src/util.h b/client/src/util.h index c80e602f4..186f2e2a7 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -141,6 +141,9 @@ int binstr_2_binarray(uint8_t *target, char *source, int length); void bytes_2_binstr(char *target, const uint8_t *source, size_t sourcelen); void binstr_2_bytes(uint8_t *target, size_t *targetlen, const char *src); +void binstr_2_u8(char *src, uint8_t n, uint8_t *dest); +void binstr_2_u16(char *src, uint8_t n, uint16_t *dest); + void hex_xor(uint8_t *d, const uint8_t *x, int n); void hex_xor_token(uint8_t *d, const uint8_t *x, int dn, int xn); From a440fbabda3e24b568bf7aec85a6dee14b670a54 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 1 Jul 2025 16:47:20 +0200 Subject: [PATCH 046/149] make clean - now removes all __pycache__ folders sprinkled all over the project when running some of the dedicated python scripts --- CHANGELOG.md | 1 + Makefile | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8aac2816a..73ddb1c6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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] +- Changed `make clean` - it now removes all __pycache__ folders (@iceman1001) - Fixed `hf 15 readmulti` - fix block calculations (@iceman1001) - Changed `mem load` - now handles UL-C and UL-AES dictionary files (@iceman1001) - Changed `hf mfu sim` - now support UL-C simulation (@iceman1001) diff --git a/Makefile b/Makefile index 39b823b89..c985246d5 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,8 @@ all clean install uninstall check: %: client/% bootrom/% armsrc/% recovery/% mfc # hitag2crack toolsuite is not yet integrated in "all", it must be called explicitly: "make hitag2crack" #all clean install uninstall check: %: hitag2crack/% clean: %: hitag2crack/% + find . -type d -name __pycache__ -exec rm -rfv \{\} + + INSTALLTOOLS=mfc/pm3_eml2lower.sh mfc/pm3_eml2upper.sh mfc/pm3_mfdread.py mfc/pm3_mfd2eml.py mfc/pm3_eml2mfd.py pm3_amii_bin2eml.pl pm3_reblay-emulating.py pm3_reblay-reading.py INSTALLSIMFW=sim011.bin sim011.sha512.txt sim013.bin sim013.sha512.txt sim014.bin sim014.sha512.txt From 0662c1a9c1a16c5bb8dae994eced53726658ae75 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Tue, 1 Jul 2025 23:53:54 +0800 Subject: [PATCH 047/149] Detecting response sc flag in sam response Added detection for Secure Channel Flag in Sam's response. --- armsrc/sam_picopass.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c index dc794a7da..8b5be59f0 100644 --- a/armsrc/sam_picopass.c +++ b/armsrc/sam_picopass.c @@ -244,6 +244,10 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re *response_len = sam_rx_buf[5 + 1] + 2; } + if (sam_rx_buf[5] == 0xBD && sam_rx_buf[4] != 0x00){ //secure channel flag is not 0x00 + Dbprintf(_YELLOW_("Secure channel flag set to: ")"%02x", sam_rx_buf[4]); + } + memcpy(response, sam_rx_buf + 5, *response_len); goto out; From 7373c383888e34c524ad9809b11d1603e5d685e8 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 2 Jul 2025 22:04:42 +0200 Subject: [PATCH 048/149] hf 15 dump had an logic bug when reading the sysinfo response. It is always fixed size but the logic for handling the Information byte flags made it skip bytes when it wasnt 0x0F --- CHANGELOG.md | 1 + client/src/cmdhf15.c | 67 ++++++++++++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73ddb1c6b..6c4b03df4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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] +- Fixed `hf 15 dump` - now reads sysinfo response correct (@iceman1001) - Changed `make clean` - it now removes all __pycache__ folders (@iceman1001) - Fixed `hf 15 readmulti` - fix block calculations (@iceman1001) - Changed `mem load` - now handles UL-C and UL-AES dictionary files (@iceman1001) diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 1dbd32b97..d27c8d049 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -1240,8 +1240,11 @@ static int CmdHF15ELoad(const char *Cmd) { ((tag->pagesCount * tag->bytesPerPage) > ISO15693_TAG_MAX_SIZE) || (tag->pagesCount == 0) || (tag->bytesPerPage == 0)) { + PrintAndLogEx(FAILED, "Tag size error: pagesCount=%d, bytesPerPage=%d", - tag->pagesCount, tag->bytesPerPage); + tag->pagesCount, + tag->bytesPerPage + ); free(tag); return PM3_EINVARG; } @@ -1904,35 +1907,49 @@ static int CmdHF15Dump(const char *Cmd) { uint8_t dCpt = 10; int res = iso15_error_handling_card_response(d, resp.length); - if (res != PM3_SUCCESS) { + if (res == PM3_ECRC) { free(tag); free(packet); return res; } - memcpy(tag->uid, &d[2], 8); + if (res == PM3_SUCCESS) { + memcpy(tag->uid, d + 2, 8); - if (d[1] & 0x01) { - tag->dsfid = d[dCpt++]; - } + if (d[1] & 0x01) { + tag->dsfid = d[dCpt]; + } + dCpt++; - if (d[1] & 0x02) { - tag->afi = d[dCpt++]; - } + if (d[1] & 0x02) { + tag->afi = d[dCpt]; + } + dCpt++; + + if (d[1] & 0x04) { + tag->pagesCount = d[dCpt] + 1; + tag->bytesPerPage = d[dCpt + 1] + 1; + } else { + // Set tag memory layout values (if can't be read in SYSINFO) + tag->bytesPerPage = blocksize; + tag->pagesCount = 128; + } + dCpt += 2; + + if (d[1] & 0x08) { + tag->ic = d[dCpt]; + } + dCpt++; - if (d[1] & 0x04) { - tag->pagesCount = d[dCpt++] + 1; - tag->bytesPerPage = d[dCpt++] + 1; } else { + tag->uid[0] = 0xE0; + tag->dsfid = 0; + tag->afi = 0; // Set tag memory layout values (if can't be read in SYSINFO) tag->bytesPerPage = blocksize; tag->pagesCount = 128; } - if (d[1] & 0x08) { - tag->ic = d[dCpt++]; - } - // add length for blockno (1) packet->rawlen++; packet->raw[0] |= ISO15_REQ_OPTION; // Add option to dump lock status @@ -2244,7 +2261,9 @@ static int CmdHF15Readmulti(const char *Cmd) { // 0 means 1 page, // 1 means 2 pages, ... - if (blockcnt > 0) blockcnt--; + if (blockcnt > 0) { + blockcnt--; + } packet->raw[packet->rawlen++] = blockno; packet->raw[packet->rawlen++] = blockcnt; @@ -2702,8 +2721,11 @@ static int CmdHF15Restore(const char *Cmd) { ((tag->pagesCount * tag->bytesPerPage) > ISO15693_TAG_MAX_SIZE) || (tag->pagesCount == 0) || (tag->bytesPerPage == 0)) { + PrintAndLogEx(FAILED, "Tag size error: pagesCount=%d, bytesPerPage=%d", - tag->pagesCount, tag->bytesPerPage); + tag->pagesCount, + tag->bytesPerPage + ); free(tag); return PM3_EINVARG; } @@ -2734,8 +2756,8 @@ static int CmdHF15Restore(const char *Cmd) { for (tried = 0; tried < retries; tried++) { - retval = hf_15_write_blk(&pm3flags, flags, uid, fast - , i, data, tag->bytesPerPage); + retval = hf_15_write_blk(&pm3flags, flags, uid, fast, i, data, tag->bytesPerPage); + if (retval == PM3_SUCCESS) { PrintAndLogEx(INPLACE, "blk %3d", i); @@ -3467,8 +3489,11 @@ static int CmdHF15View(const char *Cmd) { ((tag->pagesCount * tag->bytesPerPage) > ISO15693_TAG_MAX_SIZE) || (tag->pagesCount == 0) || (tag->bytesPerPage == 0)) { + PrintAndLogEx(FAILED, "Tag size error: pagesCount=%d, bytesPerPage=%d", - tag->pagesCount, tag->bytesPerPage); + tag->pagesCount, + tag->bytesPerPage + ); free(tag); return PM3_EINVARG; } From ea2796dc6cf1d8d76bf4f6a799cf1dbec7667dd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=81=AB=E5=B1=B1=E5=A4=A7=E9=9A=8A=E9=95=B7?= Date: Thu, 3 Jul 2025 05:09:54 +0800 Subject: [PATCH 049/149] Fix: magic detection for SAK=0x00 cards by forcing RATS --- armsrc/iso14443a.c | 21 +++++++++++++-------- armsrc/iso14443a.h | 3 ++- armsrc/mifarecmd.c | 8 ++++---- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 733b692a2..e4509113d 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2976,9 +2976,13 @@ static int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, const is int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats) { - return iso14443a_select_cardEx(uid_ptr, p_card, cuid_ptr, anticollision, num_cascades, no_rats, NULL); + return iso14443a_select_cardEx(uid_ptr, p_card, cuid_ptr, anticollision, num_cascades, no_rats, NULL, false); +} +int iso14443a_select_card_for_magic(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades){ + // Bug fix: When SAK is 0x00, `iso14443a_select_cardEx` would return too early at + // line "if (hf14aconfig.forcerats == 0)".`force_rats` is used to force RATS execution and ATS retrieval. + return iso14443a_select_cardEx(uid_ptr, p_card, cuid_ptr, anticollision, num_cascades, false, NULL, true); } - // performs iso14443a anticollision (optional) and card select procedure // fills the uid and cuid pointer unless NULL @@ -2988,7 +2992,7 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 // requests ATS unless no_rats is true int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats, - const iso14a_polling_parameters_t *polling_parameters) { + const iso14a_polling_parameters_t *polling_parameters, bool force_rats) { uint8_t resp[MAX_FRAME_SIZE] = {0}; // theoretically. A usual RATS will be much smaller @@ -3206,18 +3210,18 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint p_card->sak = sak; } - if (hf14aconfig.forcerats == 0) { + if (hf14aconfig.forcerats == 0 && force_rats == false) { // PICC compliant with iso14443a-4 ---> (SAK & 0x20 != 0) if ((sak & 0x20) == 0) { return 2; } - } else if (hf14aconfig.forcerats == 2) { + } else if (hf14aconfig.forcerats == 2 && force_rats == false) { if ((sak & 0x20) != 0) Dbprintf("Skipping RATS according to hf 14a config"); return 2; } // else force RATS - if ((sak & 0x20) == 0) Dbprintf("Forcing RATS according to hf 14a config"); + if ((sak & 0x20) == 0 && force_rats == false) Dbprintf("Forcing RATS according to hf 14a config"); // RATS, Request for answer to select if (no_rats == false) { @@ -3483,7 +3487,8 @@ void ReaderIso14443a(PacketCommandNG *c) { true, 0, ((param & ISO14A_NO_RATS) == ISO14A_NO_RATS), - ((param & ISO14A_USE_CUSTOM_POLLING) == ISO14A_USE_CUSTOM_POLLING) ? (iso14a_polling_parameters_t *)cmd : NULL + ((param & ISO14A_USE_CUSTOM_POLLING) == ISO14A_USE_CUSTOM_POLLING) ? (iso14a_polling_parameters_t *)cmd : NULL, + false ); // TODO: Improve by adding a cmd parser pointer and moving it by struct length to allow combining data with polling params FpgaDisableTracing(); @@ -4501,4 +4506,4 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, retval, NULL, 0); -} +} \ No newline at end of file diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index d3a73bd6a..5eb2e81ff 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -168,7 +168,8 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, u int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats, - const iso14a_polling_parameters_t *polling_parameters); + const iso14a_polling_parameters_t *polling_parameters, bool force_rats); +int iso14443a_select_card_for_magic(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades); int iso14443a_fast_select_card(const uint8_t *uid_ptr, uint8_t num_cascades); void iso14a_set_trigger(bool enable); diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 010277873..d421a2c85 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -83,14 +83,14 @@ static bool mifare_wakeup_auth(struct Crypto1State *pcs, MifareWakeupType wakeup break; } case MF_WAKE_WUPA: { - if (iso14443a_select_cardEx(NULL, NULL, &cuid, true, 0, true, &WUPA_POLLING_PARAMETERS) == 0) { + if (iso14443a_select_cardEx(NULL, NULL, &cuid, true, 0, true, &WUPA_POLLING_PARAMETERS, false) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); return false; }; break; } case MF_WAKE_REQA: { - if (iso14443a_select_cardEx(NULL, NULL, &cuid, true, 0, true, &REQA_POLLING_PARAMETERS) == 0) { + if (iso14443a_select_cardEx(NULL, NULL, &cuid, true, 0, true, &REQA_POLLING_PARAMETERS, false) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); return false; }; @@ -3022,8 +3022,8 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) { // reset card mf_reset_card(); - - res = iso14443a_select_card(uid, card, &cuid, true, 0, false); + // Use special magic detection function that always attempts RATS regardless of SAK + res = iso14443a_select_card_for_magic(uid, card, &cuid, true, 0); if (res) { if (cuid == 0xAA55C396) { flag |= MAGIC_FLAG_GEN_UNFUSED; From 02a4594a1b78b5b81772c61f6ee3d0e31b4ccc69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=81=AB=E5=B1=B1=E5=A4=A7=E9=9A=8A=E9=95=B7?= Date: Fri, 4 Jul 2025 02:35:10 +0800 Subject: [PATCH 050/149] fix: reset card state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reset card state after gen2 detection in ATS based magic identification Signed-off-by: 火山大隊長 --- armsrc/mifarecmd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index d421a2c85..a9c0f68f1 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -3025,6 +3025,7 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) { // Use special magic detection function that always attempts RATS regardless of SAK res = iso14443a_select_card_for_magic(uid, card, &cuid, true, 0); if (res) { + mf_reset_card(); if (cuid == 0xAA55C396) { flag |= MAGIC_FLAG_GEN_UNFUSED; } From f8bd0b4bae31803b39476d7976b0b74bd92dae87 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 4 Jul 2025 16:47:11 +0800 Subject: [PATCH 051/149] Updated iclass restore to support privilege escalation Updated hf iclass restore to support privilege escalation to restore card's content using a single AA1 --nr mac value. This allows to write cards the debit key is not known. --- CHANGELOG.md | 1 + armsrc/iclass.c | 73 ++++++++++++++++++++++++++++------------ client/src/cmdhficlass.c | 4 ++- 3 files changed, 56 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c4b03df4..4c1083e3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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] +- Changed `hf iclass restore` - it now supports privilege escalation to restore card content using replay (@antiklesys) - Fixed `hf 15 dump` - now reads sysinfo response correct (@iceman1001) - Changed `make clean` - it now removes all __pycache__ folders (@iceman1001) - Fixed `hf 15 readmulti` - fix block calculations (@iceman1001) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 5a4466567..a0f2f7ebd 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -2552,7 +2552,38 @@ out: } } +static bool do_privilege_escalation(uint8_t *read_check_cc, size_t cc_len, uint32_t *eof_time){ + uint16_t resp_len = 0; + int res2; + uint8_t resp[10] = {0}; + int priv_esc_tries = 0; + bool priv_esc = false; + uint32_t start_time = 0; + while (!priv_esc) { + //The privilege escalation is done with a readcheck and not just a normal read! + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc, cc_len, &start_time, eof_time, false); + // expect a 8-byte response here + res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true, &resp_len); + if (res2 != PM3_SUCCESS || resp_len != 8) { + priv_esc_tries++; + } else { + return true; + } + if (priv_esc_tries == 5) { + DbpString(""); + DbpString(_RED_("Unable to complete privilege escalation! Stopping.")); + return false; + } + } + return false; +} + void iClass_Restore(iclass_restore_req_t *msg) { + bool priv_esc = false; + uint8_t read_check_cc[] = { 0x10 | ICLASS_CMD_READCHECK, 0x18 }; + uint8_t credit_key[8] = {0xFD, 0xCB, 0x5A, 0x52, 0xEA, 0x8F, 0x30, 0x90}; + uint8_t div_cc[8] = {0}; // sanitation if (msg == NULL) { @@ -2581,7 +2612,9 @@ void iClass_Restore(iclass_restore_req_t *msg) { if (res == false) { goto out; } + iclass_calc_div_key((uint8_t *)&hdr.csn, credit_key, div_cc, false); + read_check_cc[1] = ((uint8_t *)&hdr.conf)[0] + 1; //first block of AA2 // authenticate uint8_t mac[4] = {0}; uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; @@ -2594,6 +2627,13 @@ void iClass_Restore(iclass_restore_req_t *msg) { } } + if(msg->req.use_replay){ + priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); + if (!priv_esc){ + goto out; + } + } + // main loop bool use_mac; for (uint8_t i = 0; i < msg->item_cnt; i++) { @@ -2611,10 +2651,13 @@ void iClass_Restore(iclass_restore_req_t *msg) { wb[0] = item.blockno; memcpy(wb + 1, item.data, 8); - if (msg->req.use_credit_key) + if (msg->req.use_credit_key){ doMAC_N(wb, sizeof(wb), hdr.key_c, mac); - else + }else if (msg->req.use_replay){ + doMAC_N(wb, sizeof(wb), div_cc, mac); + }else{ doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + } } // data + mac @@ -2767,11 +2810,8 @@ void iClass_Recover(iclass_recover_req_t *msg) { while (bits_found == -1) { reinit_tentatives = 0; - int res2; - uint8_t resp[10] = {0}; uint8_t mac2[4] = {0}; res = false; - uint16_t resp_len = 0; if (BUTTON_PRESS() || loops > msg->loop) { if (loops > msg->loop) { @@ -2827,25 +2867,15 @@ void iClass_Recover(iclass_recover_req_t *msg) { } //Step2 Privilege Escalation: attempt to read AA2 with credentials for AA1 - int priv_esc_tries = 0; - while (!priv_esc) { - //The privilege escalation is done with a readcheck and not just a normal read! - start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); - // expect a 8-byte response here - res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - if (res2 != PM3_SUCCESS || resp_len != 8) { - priv_esc_tries++; - } else { - status_message = 3; //privilege escalation successful - priv_esc = true; - } - if (priv_esc_tries == 5) { - DbpString(""); - DbpString(_RED_("Unable to complete privilege escalation! Stopping.")); + if(!priv_esc){ + priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); + if (priv_esc){ + status_message = 3; + }else{ goto out; } } + if (priv_esc && status_message != 3) { start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); @@ -2882,6 +2912,7 @@ void iClass_Recover(iclass_recover_req_t *msg) { bool write_error = false; while (written == false && write_error == false) { //Step5 Perform Write + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; if (iclass_writeblock_sp(blockno, genkeyblock, mac2, shallow_mod, &start_time, &eof_time, short_delay)) { status_message = 4; //wrote new key on the card - unverified } diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 880cea140..b4d32ef6f 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -2569,6 +2569,7 @@ static int CmdHFiClassRestore(const char *Cmd) { arg_lit0(NULL, "raw", "no computations applied to key"), arg_lit0("v", "verbose", "verbose output"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), + arg_lit0(NULL, "nr", "replay of nr mac with privilege escalation"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -2619,6 +2620,7 @@ static int CmdHFiClassRestore(const char *Cmd) { bool rawkey = arg_get_lit(ctx, 8); bool verbose = arg_get_lit(ctx, 9); bool shallow_mod = arg_get_lit(ctx, 10); + bool use_replay = arg_get_lit(ctx, 11); CLIParserFree(ctx); @@ -2665,7 +2667,7 @@ static int CmdHFiClassRestore(const char *Cmd) { payload->req.use_raw = rawkey; payload->req.use_elite = elite; payload->req.use_credit_key = use_credit_key; - payload->req.use_replay = false; + payload->req.use_replay = use_replay; payload->req.blockno = startblock; payload->req.send_reply = true; payload->req.do_auth = true; From 649de11a9abe5f6f3d7aefd86e81bc69f3dad91d Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 4 Jul 2025 17:28:01 +0800 Subject: [PATCH 052/149] Update iclass.c Signed-off-by: Antiklesys --- armsrc/iclass.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index a0f2f7ebd..46d8616ae 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -2553,13 +2553,13 @@ out: } static bool do_privilege_escalation(uint8_t *read_check_cc, size_t cc_len, uint32_t *eof_time){ - uint16_t resp_len = 0; - int res2; - uint8_t resp[10] = {0}; int priv_esc_tries = 0; bool priv_esc = false; uint32_t start_time = 0; while (!priv_esc) { + uint16_t resp_len = 0; + int res2; + uint8_t resp[10] = {0}; //The privilege escalation is done with a readcheck and not just a normal read! start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; iclass_send_as_reader(read_check_cc, cc_len, &start_time, eof_time, false); @@ -2612,7 +2612,7 @@ void iClass_Restore(iclass_restore_req_t *msg) { if (res == false) { goto out; } - iclass_calc_div_key((uint8_t *)&hdr.csn, credit_key, div_cc, false); + iclass_calc_div_key(hdr.csn, credit_key, div_cc, false); read_check_cc[1] = ((uint8_t *)&hdr.conf)[0] + 1; //first block of AA2 // authenticate From 8e4b9b46a013301eb40fa0be5457ba9b33273f00 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 4 Jul 2025 17:48:23 +0800 Subject: [PATCH 053/149] Update iclass.c Signed-off-by: Antiklesys --- armsrc/iclass.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 46d8616ae..043ca0299 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -415,8 +415,8 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { // e-Purse (blk 2) // 18: Takes 2 bytes for SOF/EOF and 8 * 2 = 16 bytes (2 bytes/bit) - uint8_t *resp_cc = BigBuf_calloc(18); - int resp_cc_len; + uint8_t *resp = BigBuf_calloc(18); + int resp_len; // Kd, Kc (blocks 3 and 4). Cannot be read. Always respond with 0xff bytes only uint8_t *resp_ff = BigBuf_calloc(22); @@ -2614,7 +2614,7 @@ void iClass_Restore(iclass_restore_req_t *msg) { } iclass_calc_div_key(hdr.csn, credit_key, div_cc, false); - read_check_cc[1] = ((uint8_t *)&hdr.conf)[0] + 1; //first block of AA2 + read_check_cc[1] = hdr.conf.app_limit + 1; //first block of AA2 // authenticate uint8_t mac[4] = {0}; uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; @@ -2784,7 +2784,7 @@ void iClass_Recover(iclass_recover_req_t *msg) { } //Step 0A - The read_check_cc block has to be in AA2, set it by checking the card configuration - read_check_cc[1] = ((uint8_t *)&hdr.conf)[0] + 1; //first block of AA2 + read_check_cc[1] = hdr.conf.app_limit + 1; //first block of AA2 //Step1 Authenticate with AA1 using trace if (card_select) { From f5820999b4122b3374eb366ddf233561f39c15c4 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 4 Jul 2025 17:52:43 +0800 Subject: [PATCH 054/149] Update iclass.c Signed-off-by: Antiklesys --- armsrc/iclass.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 043ca0299..d63b1442c 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -415,8 +415,8 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { // e-Purse (blk 2) // 18: Takes 2 bytes for SOF/EOF and 8 * 2 = 16 bytes (2 bytes/bit) - uint8_t *resp = BigBuf_calloc(18); - int resp_len; + uint8_t *resp_cc = BigBuf_calloc(18); + int resp_cc_len; // Kd, Kc (blocks 3 and 4). Cannot be read. Always respond with 0xff bytes only uint8_t *resp_ff = BigBuf_calloc(22); From 24d80f51a9fd88d8125220dc60000e4ac09a9457 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 4 Jul 2025 12:22:09 +0200 Subject: [PATCH 055/149] style --- armsrc/iclass.c | 164 ++++++++++++++++++++++-------------------- armsrc/iso14443a.c | 6 +- armsrc/iso15693.c | 8 ++- armsrc/sam_picopass.c | 2 +- doc/commands.json | 7 +- 5 files changed, 101 insertions(+), 86 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index d63b1442c..768fbecbb 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -2552,29 +2552,28 @@ out: } } -static bool do_privilege_escalation(uint8_t *read_check_cc, size_t cc_len, uint32_t *eof_time){ - int priv_esc_tries = 0; - bool priv_esc = false; - uint32_t start_time = 0; - while (!priv_esc) { +static bool do_privilege_escalation(uint8_t *read_check_cc, size_t cc_len, uint32_t *eof_time) { + + int priv_esc_tries = 5; + + while (priv_esc_tries--) { + uint16_t resp_len = 0; - int res2; uint8_t resp[10] = {0}; //The privilege escalation is done with a readcheck and not just a normal read! - start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + uint32_t start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc, cc_len, &start_time, eof_time, false); // expect a 8-byte response here - res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true, &resp_len); - if (res2 != PM3_SUCCESS || resp_len != 8) { - priv_esc_tries++; - } else { + int res = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true, &resp_len); + if (res == PM3_SUCCESS && resp_len == 8) { return true; } - if (priv_esc_tries == 5) { - DbpString(""); - DbpString(_RED_("Unable to complete privilege escalation! Stopping.")); - return false; - } + } + + if (g_dbglevel == DBG_INFO) { + DbpString(""); + DbpString(_RED_("Unable to complete privilege escalation! Stopping.")); } return false; } @@ -2627,9 +2626,9 @@ void iClass_Restore(iclass_restore_req_t *msg) { } } - if(msg->req.use_replay){ + if (msg->req.use_replay) { priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); - if (!priv_esc){ + if (priv_esc == false) { goto out; } } @@ -2651,11 +2650,11 @@ void iClass_Restore(iclass_restore_req_t *msg) { wb[0] = item.blockno; memcpy(wb + 1, item.data, 8); - if (msg->req.use_credit_key){ + if (msg->req.use_credit_key) { doMAC_N(wb, sizeof(wb), hdr.key_c, mac); - }else if (msg->req.use_replay){ + } else if (msg->req.use_replay) { doMAC_N(wb, sizeof(wb), div_cc, mac); - }else{ + } else { doMAC_N(wb, sizeof(wb), hdr.key_d, mac); } } @@ -2681,7 +2680,7 @@ out: static void generate_single_key_block_inverted_opt(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) { uint8_t bits_index = index / 16383; - uint8_t ending_bits[] = { //all possible 70 combinations of 4x0 and 4x1 as key ending bits + uint8_t ending_bits[] = { // all possible 70 combinations of 4x0 and 4x1 as key ending bits 0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D, 0x2E, 0x33, 0x35, 0x36, 0x39, 0x3A, 0x3C, 0x47, 0x4B, 0x4D, 0x4E, 0x53, 0x55, 0x56, 0x59, 0x5A, 0x5C, 0x63, 0x65, 0x66, 0x69, 0x6A, @@ -2765,10 +2764,10 @@ void iClass_Recover(iclass_recover_req_t *msg) { uint8_t original_mac[8] = {0}; uint8_t mac1[4] = {0}; - while (!card_select || !card_auth) { + while ((card_select == false) || (card_auth == false)) { Iso15693InitReader(); //has to be at the top as it starts tracing - if (!msg->debug) { + if (msg->debug == false) { set_tracing(false); //disable tracing to prevent crashes - set to true for debugging } else { if (loops == 1) { @@ -2796,10 +2795,12 @@ void iClass_Recover(iclass_recover_req_t *msg) { card_auth = true; } } - if (!card_auth || !card_select) { + + if ((card_select == false) || (card_auth == false)) { reinit_tentatives++; switch_off(); } + if (reinit_tentatives == 5) { DbpString(""); DbpString(_RED_("Unable to select or authenticate with card multiple times! Stopping.")); @@ -2827,25 +2828,25 @@ void iClass_Recover(iclass_recover_req_t *msg) { if (msg->test) { Dbprintf(_YELLOW_("*Cycled Reader*") " TEST Index - Loops: "_YELLOW_("%3d / %3d") " *", loops, msg->loop); - } else if (msg->debug || (!card_select && !card_auth)) { + } else if (msg->debug || ((card_select == false) || (card_auth == false))) { Dbprintf(_YELLOW_("*Cycled Reader*") " Index: "_RED_("%3d")" Loops: "_YELLOW_("%3d / %3d") " *", index, loops, msg->loop); } else { DbprintfEx(FLAG_INPLACE, "[" _BLUE_("#") "] Index: "_CYAN_("%3d")" Loops: "_YELLOW_("%3d / %3d")" ", index, loops, msg->loop); } - while (!card_select || !card_auth) { + while ((card_select == false) || (card_auth == false)) { - Iso15693InitReader(); //has to be at the top as it starts tracing - set_tracing(false); //disable tracing to prevent crashes - set to true for debugging - //Step0 Card Select Routine - eof_time = 0; //reset eof time + Iso15693InitReader(); // has to be at the top as it starts tracing + set_tracing(false); // disable tracing to prevent crashes - set to true for debugging + // Step0 Card Select Routine + eof_time = 0; // reset eof time res = select_iclass_tag(&hdr, false, &eof_time, shallow_mod); if (res) { - status_message = 1; //card select successful + status_message = 1; // card select successful card_select = true; } - //Step1 Authenticate with AA1 using trace + // Step1 Authenticate with AA1 using trace if (card_select) { memcpy(original_mac, msg->req.key, 8); start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; @@ -2855,10 +2856,12 @@ void iClass_Recover(iclass_recover_req_t *msg) { card_auth = true; } } - if (!card_auth || !card_select) { + + if ((card_select == false) || (card_auth == false)) { reinit_tentatives++; switch_off(); } + if (reinit_tentatives == 5) { DbpString(""); DbpString(_RED_("Unable to select or authenticate with card multiple times! Stopping.")); @@ -2866,12 +2869,12 @@ void iClass_Recover(iclass_recover_req_t *msg) { } } - //Step2 Privilege Escalation: attempt to read AA2 with credentials for AA1 - if(!priv_esc){ + // Step2 Privilege Escalation: attempt to read AA2 with credentials for AA1 + if (priv_esc == false) { priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); - if (priv_esc){ + if (priv_esc) { status_message = 3; - }else{ + } else { goto out; } } @@ -2882,26 +2885,29 @@ void iClass_Recover(iclass_recover_req_t *msg) { status_message = 3; } - //Step3 Calculate New Key (Optimised Algo V2) + // Step3 Calculate New Key (Optimised Algo V2) generate_single_key_block_inverted_opt(zero_key, index, genkeyblock); if (msg->test) { memcpy(genkeyblock, zero_key, PICOPASS_BLOCK_SIZE); } - if (msg->fast) { //if we're skipping restoring the original key to gain speed, xor the new index key with the previous index key and update the difference and track restore values differently + if (msg->fast) { // if we're skipping restoring the original key to gain speed, xor the new index key with the previous index key and update the difference and track restore values differently + if (index > 0 && loops > 1) { generate_single_key_block_inverted_opt(zero_key, index - 1, fast_previous_key); } else { memcpy(fast_previous_key, zero_key, PICOPASS_BLOCK_SIZE); } + for (int i = 0; i < PICOPASS_BLOCK_SIZE; i++) { fast_current_key[i] = genkeyblock[i] ^ fast_previous_key[i]; fast_restore_key[i] = fast_restore_key[i] ^ fast_current_key[i]; } + memcpy(genkeyblock, fast_current_key, PICOPASS_BLOCK_SIZE); } - //Step4 Calculate New Mac + // Step4 Calculate New Mac uint8_t wb[9] = {0}; uint8_t blockno = 3; @@ -2910,17 +2916,18 @@ void iClass_Recover(iclass_recover_req_t *msg) { doMAC_N(wb, sizeof(wb), div_key2, mac2); bool written = false; bool write_error = false; + while (written == false && write_error == false) { - //Step5 Perform Write + // Step5 Perform Write start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; if (iclass_writeblock_sp(blockno, genkeyblock, mac2, shallow_mod, &start_time, &eof_time, short_delay)) { - status_message = 4; //wrote new key on the card - unverified + status_message = 4; // wrote new key on the card - unverified } - if (!msg->fast) { //if we're going slow we check at every write that the write actually happened - //Reset cypher state + if (msg->fast == false) { // if we're going slow we check at every write that the write actually happened + // Reset cypher state start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); - //try to authenticate with the original mac to verify the write happened + // try to authenticate with the original mac to verify the write happened memcpy(msg->req.key, original_mac, 8); start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); @@ -2937,24 +2944,24 @@ void iClass_Recover(iclass_recover_req_t *msg) { } } else { if (res) { - write_error = true; //failed to update the key, the card's key is the original one + write_error = true; // failed to update the key, the card's key is the original one } else { - status_message = 5; //verified the card key was updated to the new one + status_message = 5; // verified the card key was updated to the new one written = true; } } - } else { //if we're going fast we can skip the above checks as we're just xorring the key over and over + } else { // if we're going fast we can skip the above checks as we're just xorring the key over and over status_message = 5; written = true; } } if (write_error == false) { - //Step6 Perform 8 authentication attempts + 1 to verify if we found the weak key + // Step6 Perform 8 authentication attempts + 1 to verify if we found the weak key for (int i = 0; i < 8 ; ++i) { start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); - //need to craft the authentication payload accordingly + // need to craft the authentication payload accordingly memcpy(msg->req.key, iclass_mac_table[i], 8); start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); //mac1 here shouldn't matter @@ -2966,29 +2973,31 @@ void iClass_Recover(iclass_recover_req_t *msg) { bool reverted = false; uint8_t revert_retries = 0; - if (msg->fast) { //if we're going fast only restore the original key at the end + if (msg->fast) { // if we're going fast only restore the original key at the end if (recovered) { goto fast_restore; } } else { - //if we're NOT going fast, regardless of bits being found, restore the original key and verify it - while (!reverted) { - //Regain privilege escalation with a readcheck + // if we're NOT going fast, regardless of bits being found, restore the original key and verify it + while (reverted == false) { + // Regain privilege escalation with a readcheck start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; if (iclass_writeblock_sp(blockno, genkeyblock, mac2, shallow_mod, &start_time, &eof_time, short_delay)) { - status_message = 6; //restore of original key successful but unverified + status_message = 6; // restore of original key successful but unverified } - //Do a readcheck first to reset the cypher state + // Do a readcheck first to reset the cypher state start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); - //need to craft the authentication payload accordingly + + // need to craft the authentication payload accordingly memcpy(msg->req.key, original_mac, 8); start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); if (res == true) { - status_message = 7; //restore of original key verified - card usable again + status_message = 7; // restore of original key verified - card usable again reverted = true; if (recovered) { goto restore; @@ -2996,7 +3005,7 @@ void iClass_Recover(iclass_recover_req_t *msg) { } revert_retries++; - if (revert_retries >= 7) { //must always be an odd number! + if (revert_retries >= 7) { // must always be an odd number! DbpString(""); DbpString(_CYAN_("Last Written Key: ")); Dbhexdump(8, genkeyblock, false); @@ -3005,8 +3014,6 @@ void iClass_Recover(iclass_recover_req_t *msg) { } } } - - } if (msg->debug) { @@ -3035,7 +3042,7 @@ void iClass_Recover(iclass_recover_req_t *msg) { } } - if (write_error && (msg->debug || msg->test)) { //if there was a write error, re-run the loop for the same key index + if (write_error && (msg->debug || msg->test)) { // if there was a write error, re-run the loop for the same key index DbpString("Loop Error: "_RED_("Repeating Loop!")); card_select = false; card_auth = false; @@ -3046,39 +3053,45 @@ void iClass_Recover(iclass_recover_req_t *msg) { status_message = 2; } - }//end while + }// end while fast_restore: - ;//empty statement for compilation + ;// empty statement for compilation uint8_t mac2[4] = {0}; uint8_t wb[9] = {0}; uint8_t blockno = 3; wb[0] = blockno; bool reverted = false; uint8_t revert_retries = 0; - while (!reverted) { - //Regain privilege escalation with a readcheck + + while (reverted == false) { + // Regain privilege escalation with a readcheck start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); memcpy(wb + 1, fast_restore_key, 8); doMAC_N(wb, sizeof(wb), div_key2, mac2); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; if (iclass_writeblock_sp(blockno, fast_restore_key, mac2, shallow_mod, &start_time, &eof_time, short_delay)) { - status_message = 6; //restore of original key successful but unverified + status_message = 6; // restore of original key successful but unverified } - //Do a readcheck first to reset the cypher state + + // Do a readcheck first to reset the cypher state start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); - //need to craft the authentication payload accordingly + + // need to craft the authentication payload accordingly memcpy(msg->req.key, original_mac, 8); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); if (res == true) { - status_message = 7; //restore of original key verified - card usable again + status_message = 7; // restore of original key verified - card usable again reverted = true; } + revert_retries++; - if (revert_retries >= 7) { //must always be an odd number! + if (revert_retries >= 7) { // must always be an odd number! DbpString(""); DbpString(_CYAN_("Last Written Key (fast): ")); Dbhexdump(8, fast_restore_key, false); @@ -3092,7 +3105,7 @@ fast_restore: } restore: - ;//empty statement for compilation + ;// empty statement for compilation uint8_t partialkey[PICOPASS_BLOCK_SIZE] = {0}; for (int i = 0; i < PICOPASS_BLOCK_SIZE; i++) { @@ -3103,11 +3116,11 @@ restore: } } - //Print the bits decimal value + // Print the bits decimal value DbpString(""); DbpString(_RED_("--------------------------------------------------------")); Dbprintf("Decimal Value of last 3 bits: " _GREEN_("[%3d]"), bits_found); - //Print the 24 bits found from k1 + // Print the 24 bits found from k1 DbpString(_RED_("--------------------------------------------------------")); DbpString(_RED_("SUCCESS! Raw Key Partial Bytes: ")); Dbhexdump(8, partialkey, false); @@ -3126,5 +3139,4 @@ out: } else { reply_ng(CMD_HF_ICLASS_RECOVER, PM3_ESOFT, NULL, 0); } - } diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index e4509113d..c8fe0ce7f 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2978,8 +2978,8 @@ static int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, const is int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats) { return iso14443a_select_cardEx(uid_ptr, p_card, cuid_ptr, anticollision, num_cascades, no_rats, NULL, false); } -int iso14443a_select_card_for_magic(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades){ - // Bug fix: When SAK is 0x00, `iso14443a_select_cardEx` would return too early at +int iso14443a_select_card_for_magic(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades) { + // Bug fix: When SAK is 0x00, `iso14443a_select_cardEx` would return too early at // line "if (hf14aconfig.forcerats == 0)".`force_rats` is used to force RATS execution and ATS retrieval. return iso14443a_select_cardEx(uid_ptr, p_card, cuid_ptr, anticollision, num_cascades, false, NULL, true); } @@ -4506,4 +4506,4 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, retval, NULL, 0); -} \ No newline at end of file +} diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 57e41a431..a1ff3c721 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -985,10 +985,11 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo DecodeTagFSK_t dtfm = { 0 }; DecodeTagFSK_t *dtf = &dtfm; - if (fsk) + if (fsk) { DecodeTagFSKInit(dtf, response, max_len); - else + } else { DecodeTagInit(dt, response, max_len); + } // wait for last transfer to complete while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); @@ -1014,8 +1015,9 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo for (;;) { volatile uint16_t behindBy = ((uint16_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); - if (behindBy == 0) + if (behindBy == 0) { continue; + } samples++; if (samples == 1) { diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c index 8b5be59f0..84ddf549b 100644 --- a/armsrc/sam_picopass.c +++ b/armsrc/sam_picopass.c @@ -244,7 +244,7 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re *response_len = sam_rx_buf[5 + 1] + 2; } - if (sam_rx_buf[5] == 0xBD && sam_rx_buf[4] != 0x00){ //secure channel flag is not 0x00 + if (sam_rx_buf[5] == 0xBD && sam_rx_buf[4] != 0x00) { //secure channel flag is not 0x00 Dbprintf(_YELLOW_("Secure channel flag set to: ")"%02x", sam_rx_buf[4]); } diff --git a/doc/commands.json b/doc/commands.json index 6853203b4..8bc8b21f2 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -3663,9 +3663,10 @@ "--elite elite computations applied to key", "--raw no computations applied to key", "-v, --verbose verbose output", - "--shallow use shallow (ASK) reader modulation instead of OOK" + "--shallow use shallow (ASK) reader modulation instead of OOK", + "--nr replay of nr mac with privilege escalation" ], - "usage": "hf iclass restore [-hv] -f [-k ] [--ki ] --first --last [--credit] [--elite] [--raw] [--shallow]" + "usage": "hf iclass restore [-hv] -f [-k ] [--ki ] --first --last [--credit] [--elite] [--raw] [--shallow] [--nr]" }, "hf iclass sam": { "command": "hf iclass sam", @@ -13376,6 +13377,6 @@ "metadata": { "commands_extracted": 768, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-07-01T14:12:39" + "extracted_on": "2025-07-04T10:19:21" } } From f5e61410c6d67acee2f767766e5c915a1e88e635 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Fri, 4 Jul 2025 18:37:46 +0800 Subject: [PATCH 056/149] Fixing style fix Fixing https://github.com/Antiklesys/proxmark3/commit/24d80f51a9fd88d8125220dc60000e4ac09a9457 where an AND was switched to an OR --- armsrc/iclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 768fbecbb..2bbde9dae 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -2828,7 +2828,7 @@ void iClass_Recover(iclass_recover_req_t *msg) { if (msg->test) { Dbprintf(_YELLOW_("*Cycled Reader*") " TEST Index - Loops: "_YELLOW_("%3d / %3d") " *", loops, msg->loop); - } else if (msg->debug || ((card_select == false) || (card_auth == false))) { + } else if (msg->debug || ((card_select == false) && (card_auth == false))) { Dbprintf(_YELLOW_("*Cycled Reader*") " Index: "_RED_("%3d")" Loops: "_YELLOW_("%3d / %3d") " *", index, loops, msg->loop); } else { DbprintfEx(FLAG_INPLACE, "[" _BLUE_("#") "] Index: "_CYAN_("%3d")" Loops: "_YELLOW_("%3d / %3d")" ", index, loops, msg->loop); From 7ad3f6eaf2768b1c8cd6315990df36bd9bae7125 Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Sat, 5 Jul 2025 13:25:18 +0800 Subject: [PATCH 057/149] Updated hf iclass wrbl replay replay behavior to use privilege escalation instead of having to generate specific block/content macs for hf iclass wrbl --- CHANGELOG.md | 1 + armsrc/iclass.c | 101 +++++++++++++++++++++------------------ client/src/cmdhficlass.c | 2 +- 3 files changed, 56 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c1083e3d..e1e2b5994 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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] +- Changed `hf iclass wrbl` - replay behavior to use privilege escalation instead of having to generate specific block/content macs(@antiklesys) - Changed `hf iclass restore` - it now supports privilege escalation to restore card content using replay (@antiklesys) - Fixed `hf 15 dump` - now reads sysinfo response correct (@iceman1001) - Changed `make clean` - it now removes all __pycache__ folders (@iceman1001) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 2bbde9dae..191d0194f 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1857,8 +1857,39 @@ static bool iclass_writeblock_sp(uint8_t blockno, uint8_t *data, uint8_t *mac, b return true; } +uint8_t credit_key[8] = {0xFD, 0xCB, 0x5A, 0x52, 0xEA, 0x8F, 0x30, 0x90}; + +static bool do_privilege_escalation(uint8_t *read_check_cc, size_t cc_len, uint32_t *eof_time) { + + int priv_esc_tries = 5; + + while (priv_esc_tries--) { + + uint16_t resp_len = 0; + uint8_t resp[10] = {0}; + //The privilege escalation is done with a readcheck and not just a normal read! + uint32_t start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + iclass_send_as_reader(read_check_cc, cc_len, &start_time, eof_time, false); + // expect a 8-byte response here + int res = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true, &resp_len); + if (res == PM3_SUCCESS && resp_len == 8) { + return true; + } + } + + if (g_dbglevel == DBG_INFO) { + DbpString(""); + DbpString(_RED_("Unable to complete privilege escalation! Stopping.")); + } + return false; +} + // turn off afterwards void iClass_WriteBlock(uint8_t *msg) { + bool priv_esc = false; + uint8_t read_check_cc[] = { 0x10 | ICLASS_CMD_READCHECK, 0x18 }; + uint8_t div_cc[8] = {0}; LED_A_ON(); @@ -1878,6 +1909,9 @@ void iClass_WriteBlock(uint8_t *msg) { goto out; } + iclass_calc_div_key(hdr.csn, credit_key, div_cc, false); + read_check_cc[1] = hdr.conf.app_limit + 1; //first block of AA2 + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; uint8_t mac[4] = {0}; @@ -1904,21 +1938,24 @@ void iClass_WriteBlock(uint8_t *msg) { write_len -= 2; } else { - if (payload->req.use_replay) { - memcpy(write + 10, payload->mac, sizeof(payload->mac)); - } else { - // Secure tags uses MAC - uint8_t wb[9]; - wb[0] = payload->req.blockno; - memcpy(wb + 1, payload->data, PICOPASS_BLOCK_SIZE); + // Secure tags uses MAC + uint8_t wb[9]; + wb[0] = payload->req.blockno; + memcpy(wb + 1, payload->data, PICOPASS_BLOCK_SIZE); - if (payload->req.use_credit_key) - doMAC_N(wb, sizeof(wb), hdr.key_c, mac); - else - doMAC_N(wb, sizeof(wb), hdr.key_d, mac); - - memcpy(write + 10, mac, sizeof(mac)); + if (payload->req.use_credit_key){ + doMAC_N(wb, sizeof(wb), hdr.key_c, mac); + } else if (payload->req.use_replay) { + priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); + if (priv_esc == false) { + goto out; + } + doMAC_N(wb, sizeof(wb), div_cc, mac); + }else{ + doMAC_N(wb, sizeof(wb), hdr.key_d, mac); } + memcpy(write + 10, mac, sizeof(mac)); + } start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; @@ -2552,36 +2589,9 @@ out: } } -static bool do_privilege_escalation(uint8_t *read_check_cc, size_t cc_len, uint32_t *eof_time) { - - int priv_esc_tries = 5; - - while (priv_esc_tries--) { - - uint16_t resp_len = 0; - uint8_t resp[10] = {0}; - //The privilege escalation is done with a readcheck and not just a normal read! - uint32_t start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - - iclass_send_as_reader(read_check_cc, cc_len, &start_time, eof_time, false); - // expect a 8-byte response here - int res = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true, &resp_len); - if (res == PM3_SUCCESS && resp_len == 8) { - return true; - } - } - - if (g_dbglevel == DBG_INFO) { - DbpString(""); - DbpString(_RED_("Unable to complete privilege escalation! Stopping.")); - } - return false; -} - void iClass_Restore(iclass_restore_req_t *msg) { bool priv_esc = false; uint8_t read_check_cc[] = { 0x10 | ICLASS_CMD_READCHECK, 0x18 }; - uint8_t credit_key[8] = {0xFD, 0xCB, 0x5A, 0x52, 0xEA, 0x8F, 0x30, 0x90}; uint8_t div_cc[8] = {0}; // sanitation @@ -2626,13 +2636,6 @@ void iClass_Restore(iclass_restore_req_t *msg) { } } - if (msg->req.use_replay) { - priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); - if (priv_esc == false) { - goto out; - } - } - // main loop bool use_mac; for (uint8_t i = 0; i < msg->item_cnt; i++) { @@ -2653,6 +2656,10 @@ void iClass_Restore(iclass_restore_req_t *msg) { if (msg->req.use_credit_key) { doMAC_N(wb, sizeof(wb), hdr.key_c, mac); } else if (msg->req.use_replay) { + priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); + if (priv_esc == false) { + goto out; + } doMAC_N(wb, sizeof(wb), div_cc, mac); } else { doMAC_N(wb, sizeof(wb), hdr.key_d, mac); diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index b4d32ef6f..584edf662 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -2329,7 +2329,7 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key"), - arg_lit0(NULL, "nr", "replay of NR/MAC"), + arg_lit0(NULL, "nr", "replay of NR/MAC using privilege escalation"), arg_lit0("v", "verbose", "verbose output"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_param_end From fa2d52205b42ce14c22d41b348395c6ce82a6f3c Mon Sep 17 00:00:00 2001 From: ry4000 <154689120+ry4000@users.noreply.github.com> Date: Sat, 5 Jul 2025 15:39:12 +1000 Subject: [PATCH 058/149] R&Y: Added `GEG Connect` to `aid_desfire.json` ### Added - GEG Connect Card (F21400) Signed-off-by: ry4000 <154689120+ry4000@users.noreply.github.com> --- client/resources/aid_desfire.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index cd0b4fbc5..78b4ab5ea 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -1575,6 +1575,14 @@ "Description": "PVD Wave Smart Card", "Type": "transport" }, + { + "AID": "F21400", + "Vendor": "Spokane Transit Authority (STA) via INIT", + "Country": "US", + "Name": "Connect Card (GEG)", + "Description": "GEG Connect Card", + "Type": "transport" + }, { "AID": "F213F0", "Vendor": "Puget Sound Transit Agencies via Vix Technologies", From 66b7e27dec3d32c1e461ef2afa09164a77e6cb28 Mon Sep 17 00:00:00 2001 From: ry4000 <154689120+ry4000@users.noreply.github.com> Date: Sat, 5 Jul 2025 15:40:59 +1000 Subject: [PATCH 059/149] R&Y: Re-Ordered `aid_desfire.json` ### Updated - GEG Connect Card to in between SEA ORCA and ITSO AIDs Signed-off-by: ry4000 <154689120+ry4000@users.noreply.github.com> --- client/resources/aid_desfire.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index 78b4ab5ea..cb10384f4 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -1575,14 +1575,6 @@ "Description": "PVD Wave Smart Card", "Type": "transport" }, - { - "AID": "F21400", - "Vendor": "Spokane Transit Authority (STA) via INIT", - "Country": "US", - "Name": "Connect Card (GEG)", - "Description": "GEG Connect Card", - "Type": "transport" - }, { "AID": "F213F0", "Vendor": "Puget Sound Transit Agencies via Vix Technologies", @@ -1591,6 +1583,14 @@ "Description": "One Regional Card for All // FIDs 00: Standard Data; 01: Backup Data", "Type": "transport" }, + { + "AID": "F21400", + "Vendor": "Spokane Transit Authority (STA) via INIT", + "Country": "US", + "Name": "Connect Card (GEG)", + "Description": "GEG Connect Card", + "Type": "transport" + }, { "AID": "F40110", "Vendor": "ITSO Ltd", From 33c3988a94c1302cb4da76ccb17e1701d0baf62c Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Sat, 5 Jul 2025 19:26:22 +0800 Subject: [PATCH 060/149] Fix broken older functionality Updated to still maintain older functionality when the macs field is passed --- CHANGELOG.md | 2 +- armsrc/iclass.c | 6 +++++- client/src/cmdhficlass.c | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1e2b5994..73dcf3e25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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] -- Changed `hf iclass wrbl` - replay behavior to use privilege escalation instead of having to generate specific block/content macs(@antiklesys) +- Changed `hf iclass wrbl` - replay behavior to use privilege escalation if the macs field is not passed empty(@antiklesys) - Changed `hf iclass restore` - it now supports privilege escalation to restore card content using replay (@antiklesys) - Fixed `hf 15 dump` - now reads sysinfo response correct (@iceman1001) - Changed `make clean` - it now removes all __pycache__ folders (@iceman1001) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 191d0194f..522dfdfe9 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1938,6 +1938,9 @@ void iClass_WriteBlock(uint8_t *msg) { write_len -= 2; } else { + if (payload->req.use_replay && sizeof(payload->mac) > 0) { + memcpy(write + 10, payload->mac, sizeof(payload->mac)); + } else { // Secure tags uses MAC uint8_t wb[9]; wb[0] = payload->req.blockno; @@ -1954,8 +1957,9 @@ void iClass_WriteBlock(uint8_t *msg) { }else{ doMAC_N(wb, sizeof(wb), hdr.key_d, mac); } - memcpy(write + 10, mac, sizeof(mac)); + memcpy(write + 10, mac, sizeof(mac)); + } } start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 584edf662..f305c33e5 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -2329,7 +2329,7 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key"), - arg_lit0(NULL, "nr", "replay of NR/MAC using privilege escalation"), + arg_lit0(NULL, "nr", "replay of NR/MAC block write or use privilege escalation if mac is empty"), arg_lit0("v", "verbose", "verbose output"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_param_end From 75c3ce61dd50f81991339ed43ecd40aa937502ae Mon Sep 17 00:00:00 2001 From: Antiklesys Date: Sat, 5 Jul 2025 19:35:41 +0800 Subject: [PATCH 061/149] Update iclass.c Fixed correctly, in the previous fix I'm checking the length of the mac, but the mac is always 4 0 bytes (set from client side as part of the variable size) and the only actual check happens on client side. I'll have to check for the mac value to be != from 00000000 --- armsrc/iclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 522dfdfe9..3837d6931 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1938,7 +1938,7 @@ void iClass_WriteBlock(uint8_t *msg) { write_len -= 2; } else { - if (payload->req.use_replay && sizeof(payload->mac) > 0) { + if (payload->req.use_replay && (memcmp(payload->mac, "\x00\x00\x00\x00", 4) != 0)) { memcpy(write + 10, payload->mac, sizeof(payload->mac)); } else { // Secure tags uses MAC From b27d3edb1a6904175b7619d0ad9587897876c6c8 Mon Sep 17 00:00:00 2001 From: ry4000 <154689120+ry4000@users.noreply.github.com> Date: Sat, 5 Jul 2025 23:59:19 +1000 Subject: [PATCH 062/149] Update aid_desfire.json Signed-off-by: ry4000 <154689120+ry4000@users.noreply.github.com> --- client/resources/aid_desfire.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index cb10384f4..8166ba9e6 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -1465,7 +1465,7 @@ }, { "AID": "F21050", - "Vendor": "Metro Christchurch via INIT / Arc", + "Vendor": "Metro Christchurch via INIT / Arc via Vix Technologies", "Country": "NZ / CA", "Name": "Metrocard (CHC) / Arc (YEG)", "Description": "CHC FIDs: 00: Backup Data; 01/02: Trip History; 03: Card Balance", @@ -1473,7 +1473,7 @@ }, { "AID": "F210E0", - "Vendor": "TriMet", + "Vendor": "TriMet via INIT", "Country": "US", "Name": "hop fastpass (PDX)", "Description": "PDX hop fastpass Card", @@ -1521,7 +1521,7 @@ }, { "AID": "F21201", - "Vendor": "Green Bay Metro Transit via Genfare / Winnipeg Transit", + "Vendor": "Green Bay Metro Transit via Genfare / Winnipeg Transit via Genfare", "Country": "US / CA", "Name": "Tap-N-Go Card (GRB) / peggo card (YWG)", "Description": "GRB Tap-N-Go Card / YWG peggo card", @@ -1537,7 +1537,7 @@ }, { "AID": "F212A0", - "Vendor": "CTtransit", + "Vendor": "CTtransit via Genfare", "Country": "US", "Name": "Go CT Card (BDL)", "Description": "BDL Go CT Card", @@ -1545,7 +1545,7 @@ }, { "AID": "F21360", - "Vendor": "INIT", + "Vendor": "The City and County of Honolulu via INIT", "Country": "US", "Name": "HOLO Card (HNL)", "Description": "HNL HOLO Card", @@ -1561,7 +1561,7 @@ }, { "AID": "F21390", - "Vendor": "Multiple NZ Transit Agencies via Otago Regional Council", + "Vendor": "Otago Regional Council via INIT", "Country": "NZ", "Name": "Bee Card (DUD)", "Description": "Multi-Modal Transit #0 // FIDs 00: Backup Data; 01-02: Trip History; 03: Card Balance", @@ -1625,7 +1625,7 @@ }, { "AID": "FF30FF", - "Vendor": "Metrolinx", + "Vendor": "Metrolinx via Accenture", "Country": "CA", "Name": "PRESTO Card (YYZ/YHM/YOW)", "Description": "FID 08: Standard Data", From 7717dfc04dfff873f97f706ac872d07df4d9e6e7 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 6 Jul 2025 20:12:13 +0200 Subject: [PATCH 063/149] text --- armsrc/iclass.c | 30 +++++++++++++++--------------- client/src/flash.c | 16 +++++++++++++++- doc/commands.json | 4 ++-- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 3837d6931..3eb9df188 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1941,24 +1941,24 @@ void iClass_WriteBlock(uint8_t *msg) { if (payload->req.use_replay && (memcmp(payload->mac, "\x00\x00\x00\x00", 4) != 0)) { memcpy(write + 10, payload->mac, sizeof(payload->mac)); } else { - // Secure tags uses MAC - uint8_t wb[9]; - wb[0] = payload->req.blockno; - memcpy(wb + 1, payload->data, PICOPASS_BLOCK_SIZE); + // Secure tags uses MAC + uint8_t wb[9]; + wb[0] = payload->req.blockno; + memcpy(wb + 1, payload->data, PICOPASS_BLOCK_SIZE); - if (payload->req.use_credit_key){ - doMAC_N(wb, sizeof(wb), hdr.key_c, mac); - } else if (payload->req.use_replay) { - priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); - if (priv_esc == false) { - goto out; + if (payload->req.use_credit_key) { + doMAC_N(wb, sizeof(wb), hdr.key_c, mac); + } else if (payload->req.use_replay) { + priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); + if (priv_esc == false) { + goto out; + } + doMAC_N(wb, sizeof(wb), div_cc, mac); + } else { + doMAC_N(wb, sizeof(wb), hdr.key_d, mac); } - doMAC_N(wb, sizeof(wb), div_cc, mac); - }else{ - doMAC_N(wb, sizeof(wb), hdr.key_d, mac); - } - memcpy(write + 10, mac, sizeof(mac)); + memcpy(write + 10, mac, sizeof(mac)); } } diff --git a/client/src/flash.c b/client/src/flash.c index 1672a4497..25edb7c0f 100644 --- a/client/src/flash.c +++ b/client/src/flash.c @@ -698,8 +698,13 @@ int flash_write(flash_file_t *ctx) { PrintAndLogEx(SUCCESS, " 0x%08x..0x%08x [0x%x / %u blocks]", seg->start, end - 1, length, blocks); if (is_loaded) { - fprintf(stdout, "\n\n"); + if (blocks < 50) { + PrintAndLogEx(SUCCESS, "" NOLF); + } else { + fprintf(stdout, "\n\n"); + } } + fflush(stdout); int block = 0; uint8_t *data = seg->data; @@ -721,6 +726,15 @@ int flash_write(flash_file_t *ctx) { length -= block_size; block++; + // small files, like bootrom + if (blocks < 50) { + fprintf(stdout, "."); + len++; + fflush(stdout); + continue; + } + + // large fullimage write if (is_loaded) { if (len < ice3len) { fprintf(stdout, "%c", ice3[len++]); diff --git a/doc/commands.json b/doc/commands.json index 8bc8b21f2..bcc254785 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -3810,7 +3810,7 @@ "--credit key is assumed to be the credit key", "--elite elite computations applied to key", "--raw no computations applied to key", - "--nr replay of NR/MAC", + "--nr replay of NR/MAC block write or use privilege escalation if mac is empty", "-v, --verbose verbose output", "--shallow use shallow (ASK) reader modulation instead of OOK" ], @@ -13377,6 +13377,6 @@ "metadata": { "commands_extracted": 768, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-07-04T10:19:21" + "extracted_on": "2025-07-06T18:10:18" } } From ed504a76ca9e5e348066a9a95829923b1b41df71 Mon Sep 17 00:00:00 2001 From: Lucifer Voeltner Date: Tue, 8 Jul 2025 00:15:43 +0700 Subject: [PATCH 064/149] Fix incorrect formula for calculating Saflok years --- client/src/cmdhfmf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 29eefbf0e..63229caf9 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -267,7 +267,7 @@ static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t uint16_t property_id = ((decodedBA[14] & 0x0F) << 8) | decodedBA[15]; // Bytes 11-13: Creation date since SAFLOK_YEAR_OFFSET Jan 1st - uint16_t creation_year = (((decodedBA[11] & 0xF0) >> 4) + SAFLOK_YEAR_OFFSET) | creation_year_high_bits; + uint16_t creation_year = (creation_year_high_bits | (decodedBA[11] >> 4)) + SAFLOK_YEAR_OFFSET; uint8_t creation_month = decodedBA[11] & 0x0F; uint8_t creation_day = (decodedBA[12] >> 3) & 0x1F; uint8_t creation_hour = ((decodedBA[12] & 0x07) << 2) | ((decodedBA[13] & 0xC0) >> 6); @@ -329,7 +329,7 @@ static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t bool checksum_valid = (checksum_calculated == checksum); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("SAFLOK details")); + PrintAndLogEx(INFO, "--- " _CYAN_("Saflok details")); PrintAndLogEx(SUCCESS, "Key Level............. %u (%s)", saflok_key_levels[key_level].level_num, saflok_key_levels[key_level].level_name); PrintAndLogEx(SUCCESS, "LED Warning........... %s", led_warning ? "Yes" : "No"); PrintAndLogEx(SUCCESS, "Key ID................ %u (0x%02X)", key_id, key_id); From 138039b2325f65f16c2f97d59864c65944f59e8f Mon Sep 17 00:00:00 2001 From: ry4000 <154689120+ry4000@users.noreply.github.com> Date: Tue, 8 Jul 2025 17:08:10 +1000 Subject: [PATCH 065/149] R&Y: Removed Gallagher `Alternative Endian` in `aid_desfire.json` ### Updates - Removed Alternative Endian - AIDs previously designated as `(Alternative Endian)` are now correctly listed as Gallagher AIDs Signed-off-by: ry4000 <154689120+ry4000@users.noreply.github.com> --- client/resources/aid_desfire.json | 160 +++--------------------------- 1 file changed, 16 insertions(+), 144 deletions(-) diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index 8166ba9e6..0a6c78822 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -36,7 +36,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -44,7 +44,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -52,7 +52,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -60,7 +60,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -68,7 +68,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -76,7 +76,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -84,7 +84,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -92,7 +92,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -100,7 +100,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -108,7 +108,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -116,7 +116,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -124,7 +124,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application (Alternative Endian)", + "Description": "Cardax Card Data Application", "Type": "pacs" }, { @@ -132,7 +132,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Unused Cardax Card Data Application (Alternative Endian)", + "Description": "Unused Cardax Card Data Application", "Type": "pacs" }, { @@ -140,7 +140,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Unused Cardax Card Data Application (Alternative Endian)", + "Description": "Unused Cardax Card Data Application", "Type": "pacs" }, { @@ -148,7 +148,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Unused Cardax Card Data Application (Alternative Endian)", + "Description": "Unused Cardax Card Data Application", "Type": "pacs" }, { @@ -156,7 +156,7 @@ "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", "Country": "NZ", "Name": "Gallagher Security Credential", - "Description": "Card Application Directory (CAD) (Alternative Endian)", + "Description": "Card Application Directory (CAD)", "Type": "pacs" }, { @@ -223,134 +223,6 @@ "Description": "Securitron DESFire EV2 Credential", "Type": "pacs" }, - { - "AID": "F48120", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F48121", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F48122", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F48123", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F48124", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F48125", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F48126", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F48127", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F48128", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F48129", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F4812A", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F4812B", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F4812C", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Unused Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F4812D", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Unused Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F4812E", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Unused Cardax Card Data Application", - "Type": "pacs" - }, - { - "AID": "F4812F", - "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", - "Country": "NZ", - "Name": "Gallagher Security Credential", - "Description": "Card Application Directory (CAD)", - "Type": "pacs" - }, { "AID": "F484D1", "Vendor": "HID", From c7cf62fcf10a7bf332f913052bdcddc8d05db04e Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 8 Jul 2025 21:13:49 +0200 Subject: [PATCH 066/149] Added support for sending and receiving MQTT messages. It enables end user to quickly upload or share JSON files between them. Given the nature of MQTT, I also worked with @KevTheHermit who has proxdump.com site, where you can upload pm3 dump files and browse. He added support for MQTT uploads, so the process to share a file and it gets also uploaded to proxdump.com is very smooth. Feel free to improve this functionality with TLS etc. For now it looks quite promising. --- CHANGELOG.md | 1 + client/CMakeLists.txt | 2 + client/Makefile | 18 + client/deps/CMakeLists.txt | 3 + client/deps/mqtt.cmake | 9 + client/deps/mqtt/LICENSE | 21 + client/deps/mqtt/Makefile | 14 + client/deps/mqtt/mbedtls_sockets.h | 152 +++ client/deps/mqtt/mqtt.c | 1770 ++++++++++++++++++++++++++++ client/deps/mqtt/mqtt.h | 1640 ++++++++++++++++++++++++++ client/deps/mqtt/mqtt_pal.c | 235 ++++ client/deps/mqtt/mqtt_pal.h | 173 +++ client/deps/mqtt/posix_sockets.h | 94 ++ client/deps/mqtt/readme.md | 15 + client/src/cmdmain.c | 2 + client/src/cmdmqtt.c | 385 ++++++ client/src/cmdmqtt.h | 26 + client/src/fileutils.c | 101 +- client/src/fileutils.h | 35 +- client/src/pm3line_vocabulary.h | 3 + doc/commands.json | 42 +- doc/commands.md | 11 + 22 files changed, 4742 insertions(+), 10 deletions(-) create mode 100644 client/deps/mqtt.cmake create mode 100644 client/deps/mqtt/LICENSE create mode 100644 client/deps/mqtt/Makefile create mode 100644 client/deps/mqtt/mbedtls_sockets.h create mode 100644 client/deps/mqtt/mqtt.c create mode 100644 client/deps/mqtt/mqtt.h create mode 100644 client/deps/mqtt/mqtt_pal.c create mode 100644 client/deps/mqtt/mqtt_pal.h create mode 100644 client/deps/mqtt/posix_sockets.h create mode 100644 client/deps/mqtt/readme.md create mode 100644 client/src/cmdmqtt.c create mode 100644 client/src/cmdmqtt.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 73dcf3e25..5875edeec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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 `mqtt` - the pm3 client can now send and receive MQTT messages or json files. (@iceman1001) - Changed `hf iclass wrbl` - replay behavior to use privilege escalation if the macs field is not passed empty(@antiklesys) - Changed `hf iclass restore` - it now supports privilege escalation to restore card content using replay (@antiklesys) - Fixed `hf 15 dump` - now reads sysinfo response correct (@iceman1001) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 854828df9..1f0410e31 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -402,6 +402,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdlfvisa2000.c ${PM3_ROOT}/client/src/cmdlfzx8211.c ${PM3_ROOT}/client/src/cmdmain.c + ${PM3_ROOT}/client/src/cmdmqtt.c ${PM3_ROOT}/client/src/cmdnfc.c ${PM3_ROOT}/client/src/cmdparser.c ${PM3_ROOT}/client/src/cmdpiv.c @@ -772,6 +773,7 @@ target_link_libraries(proxmark3 PRIVATE pm3rrg_rdv4_reveng pm3rrg_rdv4_hardnested pm3rrg_rdv4_id48 + pm3rrg_rdv4_mqtt ${ADDITIONAL_LNK}) if (NOT SKIPPTHREAD EQUAL 1) diff --git a/client/Makefile b/client/Makefile index 014211325..2c0f7ef04 100644 --- a/client/Makefile +++ b/client/Makefile @@ -131,6 +131,12 @@ WHEREAMILIBINC = -I$(WHEREAMILIBPATH) WHEREAMILIB = $(WHEREAMILIBPATH)/libwhereami.a WHEREAMILIBLD = +## MQTT +MQTTLIBPATH = ./deps/mqtt +MQTTLIBINC = -I$(MQTTLIBPATH) +MQTTLIB = $(MQTTLIBPATH)/mqtt.a +MQTTLIBLD = + ########################## # common local libraries # ########################## @@ -239,6 +245,12 @@ STATICLIBS += $(WHEREAMILIB) LDLIBS += $(WHEREAMILIBLD) PM3INCLUDES += $(WHEREAMILIBINC) +## MQTT +# not distributed as system library +STATICLIBS += $(MQTTLIB) +LDLIBS += $(MQTTLIBLD) +PM3INCLUDES += $(MQTTLIBINC) + #################### # system libraries # #################### @@ -682,6 +694,7 @@ SRCS = mifare/aiddesfire.c \ cmdlfvisa2000.c \ cmdlfzx8211.c \ cmdmain.c \ + cmdmqtt.c \ cmdnfc.c \ cmdparser.c \ cmdpiv.c \ @@ -877,6 +890,7 @@ endif $(Q)$(MAKE) --no-print-directory -C $(REVENGLIBPATH) clean $(Q)$(MAKE) --no-print-directory -C $(TINYCBORLIBPATH) clean $(Q)$(MAKE) --no-print-directory -C $(WHEREAMILIBPATH) clean + $(Q)$(MAKE) --no-print-directory -C $(MQTTLIBPATH) clean @# Just in case someone compiled within these dirs: $(Q)$(MAKE) --no-print-directory -C $(MBEDTLSLIBPATH) clean @@ -974,6 +988,10 @@ ifneq ($(WHEREAMI_FOUND),1) $(Q)$(MAKE) --no-print-directory -C $(WHEREAMILIBPATH) all endif +$(MQTTLIB): .FORCE + $(info [*] MAKE $@) + $(Q)$(MAKE) --no-print-directory -C $(MQTTLIBPATH) all + ######## # SWIG # ######## diff --git a/client/deps/CMakeLists.txt b/client/deps/CMakeLists.txt index 99a843d66..c8f5b78c5 100644 --- a/client/deps/CMakeLists.txt +++ b/client/deps/CMakeLists.txt @@ -31,3 +31,6 @@ endif() if (NOT TARGET pm3rrg_rdv4_whereami) include(whereami.cmake) endif() +if (NOT TARGET pm3rrg_rdv4_mqtt) + include(mqtt.cmake) +endif() \ No newline at end of file diff --git a/client/deps/mqtt.cmake b/client/deps/mqtt.cmake new file mode 100644 index 000000000..f30e761c3 --- /dev/null +++ b/client/deps/mqtt.cmake @@ -0,0 +1,9 @@ +add_library(pm3rrg_rdv4_mqtt STATIC + mqtt/mqtt.c + mqtt/mqtt_pal.c + ) + +target_compile_definitions(pm3rrg_rdv4_mqtt PRIVATE WAI_PM3_TUNED) +target_include_directories(pm3rrg_rdv4_mqtt INTERFACE mqtt) +target_compile_options(pm3rrg_rdv4_mqtt PRIVATE -Wall -Werror -O3) +set_property(TARGET pm3rrg_rdv4_mqtt PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/mqtt/LICENSE b/client/deps/mqtt/LICENSE new file mode 100644 index 000000000..0bbb84557 --- /dev/null +++ b/client/deps/mqtt/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Liam Bindle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/client/deps/mqtt/Makefile b/client/deps/mqtt/Makefile new file mode 100644 index 000000000..af4679674 --- /dev/null +++ b/client/deps/mqtt/Makefile @@ -0,0 +1,14 @@ +MYSRCPATHS = +MYINCLUDES = +MYCFLAGS = -Wno-bad-function-cast -Wno-switch-enum +MYDEFS = -DWAI_PM3_TUNED +MYSRCS = \ + mqtt.c \ + mqtt_pal.c \ + +LIB_A = mqtt.a + +# Transition: remove old directories and objects +MYCLEANOLDPATH = ../../mqtt + +include ../../../Makefile.host diff --git a/client/deps/mqtt/mbedtls_sockets.h b/client/deps/mqtt/mbedtls_sockets.h new file mode 100644 index 000000000..1295152f9 --- /dev/null +++ b/client/deps/mqtt/mbedtls_sockets.h @@ -0,0 +1,152 @@ +#if !defined(__MBEDTLS_SOCKET_TEMPLATE_H__) +#define __MBEDTLS_SOCKET_TEMPLATE_H__ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if !defined(MBEDTLS_NET_POLL_READ) +/* compat for older mbedtls */ +#define MBEDTLS_NET_POLL_READ 1 +#define MBEDTLS_NET_POLL_WRITE 1 + +int mbedtls_net_poll(mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout) { + /* XXX this is not ideal but good enough for an example */ + msleep(300); + return 1; +} +#endif + +struct mbedtls_context { + mbedtls_net_context net_ctx; + mbedtls_ssl_context ssl_ctx; + mbedtls_ssl_config ssl_conf; + mbedtls_x509_crt ca_crt; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; +}; + +void failed(const char *fn, int rv); +void cert_verify_failed(uint32_t rv); +void open_nb_socket(struct mbedtls_context *ctx, + const char *hostname, + const char *port, + const char *ca_file); + + +void failed(const char *fn, int rv) { + char buf[100]; + mbedtls_strerror(rv, buf, sizeof(buf)); + printf("%s failed with %x (%s)\n", fn, -rv, buf); + exit(1); +} + +void cert_verify_failed(uint32_t rv) { + char buf[512]; + mbedtls_x509_crt_verify_info(buf, sizeof(buf), "\t", rv); + printf("Certificate verification failed (%0" PRIx32 ")\n%s\n", rv, buf); + exit(1); +} + +/* + A template for opening a non-blocking mbed TLS connection. +*/ +void open_nb_socket(struct mbedtls_context *ctx, + const char *hostname, + const char *port, + const char *ca_file) { + + const unsigned char *additional = (const unsigned char *)"Pm3 Client"; + size_t additional_len = 6; + int rv; + + mbedtls_net_context *net_ctx = &ctx->net_ctx; + mbedtls_ssl_context *ssl_ctx = &ctx->ssl_ctx; + mbedtls_ssl_config *ssl_conf = &ctx->ssl_conf; + mbedtls_x509_crt *ca_crt = &ctx->ca_crt; + mbedtls_entropy_context *entropy = &ctx->entropy; + mbedtls_ctr_drbg_context *ctr_drbg = &ctx->ctr_drbg; + + mbedtls_entropy_init(entropy); + mbedtls_ctr_drbg_init(ctr_drbg); + rv = mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, entropy, + additional, additional_len); + if (rv != 0) { + failed("mbedtls_ctr_drbg_seed", rv); + } + + mbedtls_x509_crt_init(ca_crt); + rv = mbedtls_x509_crt_parse_file(ca_crt, ca_file); + if (rv != 0) { + failed("mbedtls_x509_crt_parse_file", rv); + } + + mbedtls_ssl_config_init(ssl_conf); + rv = mbedtls_ssl_config_defaults(ssl_conf, MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (rv != 0) { + failed("mbedtls_ssl_config_defaults", rv); + } + mbedtls_ssl_conf_ca_chain(ssl_conf, ca_crt, NULL); + mbedtls_ssl_conf_authmode(ssl_conf, MBEDTLS_SSL_VERIFY_OPTIONAL); + mbedtls_ssl_conf_rng(ssl_conf, mbedtls_ctr_drbg_random, ctr_drbg); + + mbedtls_net_init(net_ctx); + rv = mbedtls_net_connect(net_ctx, hostname, port, MBEDTLS_NET_PROTO_TCP); + if (rv != 0) { + failed("mbedtls_net_connect", rv); + } + rv = mbedtls_net_set_nonblock(net_ctx); + if (rv != 0) { + failed("mbedtls_net_set_nonblock", rv); + } + + mbedtls_ssl_init(ssl_ctx); + rv = mbedtls_ssl_setup(ssl_ctx, ssl_conf); + if (rv != 0) { + failed("mbedtls_ssl_setup", rv); + } + rv = mbedtls_ssl_set_hostname(ssl_ctx, hostname); + if (rv != 0) { + failed("mbedtls_ssl_set_hostname", rv); + } + mbedtls_ssl_set_bio(ssl_ctx, net_ctx, + mbedtls_net_send, mbedtls_net_recv, NULL); + + for (;;) { + rv = mbedtls_ssl_handshake(ssl_ctx); + uint32_t want = 0; + if (rv == MBEDTLS_ERR_SSL_WANT_READ) { + want |= MBEDTLS_NET_POLL_READ; + } else if (rv == MBEDTLS_ERR_SSL_WANT_WRITE) { + want |= MBEDTLS_NET_POLL_WRITE; + } else { + break; + } + rv = mbedtls_net_poll(net_ctx, want, (uint32_t) -1); + if (rv < 0) { + failed("mbedtls_net_poll", rv); + } + } + if (rv != 0) { + failed("mbedtls_ssl_handshake", rv); + } + uint32_t result = mbedtls_ssl_get_verify_result(ssl_ctx); + if (result != 0) { + if (result == (uint32_t) -1) { + failed("mbedtls_ssl_get_verify_result", (int)result); + } else { + cert_verify_failed(result); + } + } +} + +#endif diff --git a/client/deps/mqtt/mqtt.c b/client/deps/mqtt/mqtt.c new file mode 100644 index 000000000..b29f9d9d4 --- /dev/null +++ b/client/deps/mqtt/mqtt.c @@ -0,0 +1,1770 @@ +/* +MIT License + +Copyright(c) 2018 Liam Bindle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "mqtt.h" + +/** + * @file + * @brief Implements the functionality of MQTT-C. + * @note The only files that are included are mqtt.h and mqtt_pal.h. + * + * @cond Doxygen_Suppress + */ + +enum MQTTErrors mqtt_sync(struct mqtt_client *client) { + /* Recover from any errors */ + enum MQTTErrors err; + int reconnecting = 0; + MQTT_PAL_MUTEX_LOCK(&client->mutex); + if (client->error != MQTT_ERROR_RECONNECTING && client->error != MQTT_OK && client->reconnect_callback != NULL) { + client->reconnect_callback(client, &client->reconnect_state); + if (client->error != MQTT_OK) { + client->error = MQTT_ERROR_RECONNECT_FAILED; + + /* normally unlocked during CONNECT */ + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + } + err = client->error; + + if (err != MQTT_OK) return err; + } else { + /* mqtt_reconnect will have queued the disconnect packet - that needs to be sent and then call reconnect */ + if (client->error == MQTT_ERROR_RECONNECTING) { + reconnecting = 1; + client->error = MQTT_OK; + } + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + } + + /* Call inspector callback if necessary */ + + if (client->inspector_callback != NULL) { + MQTT_PAL_MUTEX_LOCK(&client->mutex); + err = client->inspector_callback(client); + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + if (err != MQTT_OK) return err; + } + + /* Call receive */ + err = (enum MQTTErrors)__mqtt_recv(client); + if (err != MQTT_OK) return err; + + /* Call send */ + err = (enum MQTTErrors)__mqtt_send(client); + + /* mqtt_reconnect will essentially be a disconnect if there is no callback */ + if (reconnecting && client->reconnect_callback != NULL) { + MQTT_PAL_MUTEX_LOCK(&client->mutex); + client->reconnect_callback(client, &client->reconnect_state); + } + + return err; +} + +uint16_t __mqtt_next_pid(struct mqtt_client *client) { + int pid_exists = 0; + if (client->pid_lfsr == 0) { + client->pid_lfsr = 163u; + } + /* LFSR taps taken from: https://en.wikipedia.org/wiki/Linear-feedback_shift_register */ + + do { + struct mqtt_queued_message *curr; + unsigned lsb = client->pid_lfsr & 1; + (client->pid_lfsr) >>= 1; + if (lsb) { + client->pid_lfsr ^= 0xB400u; + } + + /* check that the PID is unique */ + pid_exists = 0; + for (curr = mqtt_mq_get(&(client->mq), 0); curr >= client->mq.queue_tail; --curr) { + if (curr->packet_id == client->pid_lfsr) { + pid_exists = 1; + break; + } + } + + } while (pid_exists); + return client->pid_lfsr; +} + +enum MQTTErrors mqtt_init(struct mqtt_client *client, + mqtt_pal_socket_handle sockfd, + uint8_t *sendbuf, size_t sendbufsz, + uint8_t *recvbuf, size_t recvbufsz, + void (*publish_response_callback)(void **state, struct mqtt_response_publish *publish)) { + if (client == NULL || sendbuf == NULL || recvbuf == NULL) { + return MQTT_ERROR_NULLPTR; + } + + /* initialize mutex */ + MQTT_PAL_MUTEX_INIT(&client->mutex); + MQTT_PAL_MUTEX_LOCK(&client->mutex); /* unlocked during CONNECT */ + + client->socketfd = sockfd; + + mqtt_mq_init(&client->mq, sendbuf, sendbufsz); + + client->recv_buffer.mem_start = recvbuf; + client->recv_buffer.mem_size = recvbufsz; + client->recv_buffer.curr = client->recv_buffer.mem_start; + client->recv_buffer.curr_sz = client->recv_buffer.mem_size; + + client->error = MQTT_ERROR_CONNECT_NOT_CALLED; + client->response_timeout = 30; + client->number_of_timeouts = 0; + client->number_of_keep_alives = 0; + client->typical_response_time = -1.0f; + client->publish_response_callback = publish_response_callback; + client->pid_lfsr = 0; + client->send_offset = 0; + + client->inspector_callback = NULL; + client->reconnect_callback = NULL; + client->reconnect_state = NULL; + + return MQTT_OK; +} + +void mqtt_init_reconnect(struct mqtt_client *client, + void (*reconnect)(struct mqtt_client *, void **), + void *reconnect_state, + void (*publish_response_callback)(void **state, struct mqtt_response_publish *publish)) { + /* initialize mutex */ + MQTT_PAL_MUTEX_INIT(&client->mutex); + + client->socketfd = (mqtt_pal_socket_handle) - 1; + + mqtt_mq_init(&client->mq, NULL, 0uL); + + client->recv_buffer.mem_start = NULL; + client->recv_buffer.mem_size = 0; + client->recv_buffer.curr = NULL; + client->recv_buffer.curr_sz = 0; + + client->error = MQTT_ERROR_INITIAL_RECONNECT; + client->response_timeout = 30; + client->number_of_timeouts = 0; + client->number_of_keep_alives = 0; + client->typical_response_time = -1.0f; + client->publish_response_callback = publish_response_callback; + client->pid_lfsr = 0; + client->send_offset = 0; + + client->inspector_callback = NULL; + client->reconnect_callback = reconnect; + client->reconnect_state = reconnect_state; +} + +void mqtt_reinit(struct mqtt_client *client, + mqtt_pal_socket_handle socketfd, + uint8_t *sendbuf, size_t sendbufsz, + uint8_t *recvbuf, size_t recvbufsz) { + client->error = MQTT_ERROR_CONNECT_NOT_CALLED; + client->socketfd = socketfd; + + mqtt_mq_init(&client->mq, sendbuf, sendbufsz); + + client->recv_buffer.mem_start = recvbuf; + client->recv_buffer.mem_size = recvbufsz; + client->recv_buffer.curr = client->recv_buffer.mem_start; + client->recv_buffer.curr_sz = client->recv_buffer.mem_size; +} + +/** + * A macro function that: + * 1) Checks that the client isn't in an error state. + * 2) Attempts to pack to client's message queue. + * a) handles errors + * b) if mq buffer is too small, cleans it and tries again + * 3) Upon successful pack, registers the new message. + */ +#define MQTT_CLIENT_TRY_PACK(tmp, msg, client, pack_call, release) \ + if (client->error < 0) { \ + if (release) MQTT_PAL_MUTEX_UNLOCK(&client->mutex); \ + return client->error; \ + } \ + tmp = pack_call; \ + if (tmp < 0) { \ + client->error = (enum MQTTErrors)tmp; \ + if (release) MQTT_PAL_MUTEX_UNLOCK(&client->mutex); \ + return (enum MQTTErrors)tmp; \ + } else if (tmp == 0) { \ + mqtt_mq_clean(&client->mq); \ + tmp = pack_call; \ + if (tmp < 0) { \ + client->error = (enum MQTTErrors)tmp; \ + if (release) MQTT_PAL_MUTEX_UNLOCK(&client->mutex); \ + return (enum MQTTErrors)tmp; \ + } else if(tmp == 0) { \ + client->error = MQTT_ERROR_SEND_BUFFER_IS_FULL; \ + if (release) MQTT_PAL_MUTEX_UNLOCK(&client->mutex); \ + return (enum MQTTErrors)MQTT_ERROR_SEND_BUFFER_IS_FULL; \ + } \ + } \ + msg = mqtt_mq_register(&client->mq, (size_t)tmp); \ + + +enum MQTTErrors mqtt_connect(struct mqtt_client *client, + const char *client_id, + const char *will_topic, + const void *will_message, + size_t will_message_size, + const char *user_name, + const char *password, + uint8_t connect_flags, + uint16_t keep_alive) { + ssize_t rv; + struct mqtt_queued_message *msg; + + /* Note: Current thread already has mutex locked. */ + + /* update the client's state */ + client->keep_alive = keep_alive; + if (client->error == MQTT_ERROR_CONNECT_NOT_CALLED) { + client->error = MQTT_OK; + } + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK(rv, msg, client, + mqtt_pack_connection_request( + client->mq.curr, client->mq.curr_sz, + client_id, will_topic, will_message, + will_message_size, user_name, password, + connect_flags, keep_alive + ), + 1 + ); + /* save the control type of the message */ + msg->control_type = MQTT_CONTROL_CONNECT; + + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_OK; +} + +enum MQTTErrors mqtt_publish(struct mqtt_client *client, + const char *topic_name, + const void *application_message, + size_t application_message_size, + uint8_t publish_flags) { + struct mqtt_queued_message *msg; + ssize_t rv; + uint16_t packet_id; + MQTT_PAL_MUTEX_LOCK(&client->mutex); + packet_id = __mqtt_next_pid(client); + + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_publish_request( + client->mq.curr, client->mq.curr_sz, + topic_name, + packet_id, + application_message, + application_message_size, + publish_flags + ), + 1 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_PUBLISH; + msg->packet_id = packet_id; + + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_OK; +} + +ssize_t __mqtt_puback(struct mqtt_client *client, uint16_t packet_id) { + ssize_t rv; + struct mqtt_queued_message *msg; + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_pubxxx_request( + client->mq.curr, client->mq.curr_sz, + MQTT_CONTROL_PUBACK, + packet_id + ), + 0 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_PUBACK; + msg->packet_id = packet_id; + + return MQTT_OK; +} + +ssize_t __mqtt_pubrec(struct mqtt_client *client, uint16_t packet_id) { + ssize_t rv; + struct mqtt_queued_message *msg; + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_pubxxx_request( + client->mq.curr, client->mq.curr_sz, + MQTT_CONTROL_PUBREC, + packet_id + ), + 0 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_PUBREC; + msg->packet_id = packet_id; + + return MQTT_OK; +} + +ssize_t __mqtt_pubrel(struct mqtt_client *client, uint16_t packet_id) { + ssize_t rv; + struct mqtt_queued_message *msg; + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_pubxxx_request( + client->mq.curr, client->mq.curr_sz, + MQTT_CONTROL_PUBREL, + packet_id + ), + 0 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_PUBREL; + msg->packet_id = packet_id; + + return MQTT_OK; +} + +ssize_t __mqtt_pubcomp(struct mqtt_client *client, uint16_t packet_id) { + ssize_t rv; + struct mqtt_queued_message *msg; + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_pubxxx_request( + client->mq.curr, client->mq.curr_sz, + MQTT_CONTROL_PUBCOMP, + packet_id + ), + 0 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_PUBCOMP; + msg->packet_id = packet_id; + + return MQTT_OK; +} + +enum MQTTErrors mqtt_subscribe(struct mqtt_client *client, + const char *topic_name, + int max_qos_level) { + ssize_t rv; + uint16_t packet_id; + struct mqtt_queued_message *msg; + MQTT_PAL_MUTEX_LOCK(&client->mutex); + packet_id = __mqtt_next_pid(client); + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_subscribe_request( + client->mq.curr, client->mq.curr_sz, + packet_id, + topic_name, + max_qos_level, + (const char *)NULL + ), + 1 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_SUBSCRIBE; + msg->packet_id = packet_id; + + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_OK; +} + +enum MQTTErrors mqtt_unsubscribe(struct mqtt_client *client, + const char *topic_name) { + uint16_t packet_id = __mqtt_next_pid(client); + ssize_t rv; + struct mqtt_queued_message *msg; + MQTT_PAL_MUTEX_LOCK(&client->mutex); + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_unsubscribe_request( + client->mq.curr, client->mq.curr_sz, + packet_id, + topic_name, + (const char *)NULL + ), + 1 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_UNSUBSCRIBE; + msg->packet_id = packet_id; + + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_OK; +} + +enum MQTTErrors mqtt_ping(struct mqtt_client *client) { + enum MQTTErrors rv; + MQTT_PAL_MUTEX_LOCK(&client->mutex); + rv = __mqtt_ping(client); + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return rv; +} + +enum MQTTErrors __mqtt_ping(struct mqtt_client *client) { + ssize_t rv; + struct mqtt_queued_message *msg; + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_ping_request( + client->mq.curr, client->mq.curr_sz + ), + 0 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_PINGREQ; + + + return MQTT_OK; +} + +enum MQTTErrors mqtt_reconnect(struct mqtt_client *client) { + enum MQTTErrors err = mqtt_disconnect(client); + + if (err == MQTT_OK) { + MQTT_PAL_MUTEX_LOCK(&client->mutex); + client->error = MQTT_ERROR_RECONNECTING; + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + } + return err; +} + +enum MQTTErrors mqtt_disconnect(struct mqtt_client *client) { + ssize_t rv; + struct mqtt_queued_message *msg; + MQTT_PAL_MUTEX_LOCK(&client->mutex); + + /* try to pack the message */ + MQTT_CLIENT_TRY_PACK( + rv, msg, client, + mqtt_pack_disconnect( + client->mq.curr, client->mq.curr_sz + ), + 1 + ); + /* save the control type and packet id of the message */ + msg->control_type = MQTT_CONTROL_DISCONNECT; + + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_OK; +} + +ssize_t __mqtt_send(struct mqtt_client *client) { + uint8_t inspected; + ssize_t len; + int inflight_qos2 = 0; + int i = 0; + + MQTT_PAL_MUTEX_LOCK(&client->mutex); + + if (client->error < 0 && client->error != MQTT_ERROR_SEND_BUFFER_IS_FULL) { + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return client->error; + } + + /* loop through all messages in the queue */ + len = mqtt_mq_length(&client->mq); + for (; i < len; ++i) { + struct mqtt_queued_message *msg = mqtt_mq_get(&client->mq, i); + int resend = 0; + if (msg->state == MQTT_QUEUED_UNSENT) { + /* message has not been sent to lets send it */ + resend = 1; + } else if (msg->state == MQTT_QUEUED_AWAITING_ACK) { + /* check for timeout */ + if (MQTT_PAL_TIME() > msg->time_sent + client->response_timeout) { + resend = 1; + client->number_of_timeouts += 1; + client->send_offset = 0; + } + } + + /* only send QoS 2 message if there are no inflight QoS 2 PUBLISH messages */ + if (msg->control_type == MQTT_CONTROL_PUBLISH + && (msg->state == MQTT_QUEUED_UNSENT || msg->state == MQTT_QUEUED_AWAITING_ACK)) { + inspected = 0x03 & ((msg->start[0]) >> 1); /* qos */ + if (inspected == 2) { + if (inflight_qos2) { + resend = 0; + } + inflight_qos2 = 1; + } + } + + /* goto next message if we don't need to send */ + if (!resend) { + continue; + } + + /* we're sending the message */ + { + ssize_t tmp = mqtt_pal_sendall(client->socketfd, msg->start + client->send_offset, msg->size - client->send_offset, 0); + if (tmp < 0) { + client->error = (enum MQTTErrors)tmp; + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return tmp; + } else { + client->send_offset += (unsigned long)tmp; + if (client->send_offset < msg->size) { + /* partial sent. Await additional calls */ + break; + } else { + /* whole message has been sent */ + client->send_offset = 0; + } + + } + + } + + /* update timeout watcher */ + client->time_of_last_send = MQTT_PAL_TIME(); + msg->time_sent = client->time_of_last_send; + + /* + Determine the state to put the message in. + Control Types: + MQTT_CONTROL_CONNECT -> awaiting + MQTT_CONTROL_CONNACK -> n/a + MQTT_CONTROL_PUBLISH -> qos == 0 ? complete : awaiting + MQTT_CONTROL_PUBACK -> complete + MQTT_CONTROL_PUBREC -> awaiting + MQTT_CONTROL_PUBREL -> awaiting + MQTT_CONTROL_PUBCOMP -> complete + MQTT_CONTROL_SUBSCRIBE -> awaiting + MQTT_CONTROL_SUBACK -> n/a + MQTT_CONTROL_UNSUBSCRIBE -> awaiting + MQTT_CONTROL_UNSUBACK -> n/a + MQTT_CONTROL_PINGREQ -> awaiting + MQTT_CONTROL_PINGRESP -> n/a + MQTT_CONTROL_DISCONNECT -> complete + */ + switch (msg->control_type) { + case MQTT_CONTROL_PUBACK: + case MQTT_CONTROL_PUBCOMP: + case MQTT_CONTROL_DISCONNECT: + msg->state = MQTT_QUEUED_COMPLETE; + break; + case MQTT_CONTROL_PUBLISH: + inspected = (MQTT_PUBLISH_QOS_MASK & (msg->start[0])) >> 1; /* qos */ + if (inspected == 0) { + msg->state = MQTT_QUEUED_COMPLETE; + } else if (inspected == 1) { + msg->state = MQTT_QUEUED_AWAITING_ACK; + /*set DUP flag for subsequent sends [Spec MQTT-3.3.1-1] */ + msg->start[0] |= MQTT_PUBLISH_DUP; + } else { + msg->state = MQTT_QUEUED_AWAITING_ACK; + } + break; + case MQTT_CONTROL_CONNECT: + case MQTT_CONTROL_PUBREC: + case MQTT_CONTROL_PUBREL: + case MQTT_CONTROL_SUBSCRIBE: + case MQTT_CONTROL_UNSUBSCRIBE: + case MQTT_CONTROL_PINGREQ: + msg->state = MQTT_QUEUED_AWAITING_ACK; + break; + default: + client->error = MQTT_ERROR_MALFORMED_REQUEST; + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_ERROR_MALFORMED_REQUEST; + } + } + + /* check for keep-alive */ + { + mqtt_pal_time_t keep_alive_timeout = client->time_of_last_send + (mqtt_pal_time_t)((float)(client->keep_alive)); + if (MQTT_PAL_TIME() > keep_alive_timeout) { + ssize_t rv = __mqtt_ping(client); + if (rv != MQTT_OK) { + client->error = (enum MQTTErrors)rv; + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return rv; + } + } + } + + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_OK; +} + +ssize_t __mqtt_recv(struct mqtt_client *client) { + struct mqtt_response response; + ssize_t mqtt_recv_ret = MQTT_OK; + MQTT_PAL_MUTEX_LOCK(&client->mutex); + + /* read until there is nothing left to read, or there was an error */ + while (mqtt_recv_ret == MQTT_OK) { + /* read in as many bytes as possible */ + ssize_t rv, consumed; + struct mqtt_queued_message *msg = NULL; + + rv = mqtt_pal_recvall(client->socketfd, client->recv_buffer.curr, client->recv_buffer.curr_sz, 0); + if (rv < 0) { + /* an error occurred */ + client->error = (enum MQTTErrors)rv; + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return rv; + } else { + client->recv_buffer.curr += rv; + client->recv_buffer.curr_sz -= (unsigned long)rv; + } + + /* attempt to parse */ + consumed = mqtt_unpack_response(&response, client->recv_buffer.mem_start, (size_t)(client->recv_buffer.curr - client->recv_buffer.mem_start)); + + if (consumed < 0) { + client->error = (enum MQTTErrors)consumed; + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return consumed; + } else if (consumed == 0) { + /* if curr_sz is 0 then the buffer is too small to ever fit the message */ + if (client->recv_buffer.curr_sz == 0) { + client->error = MQTT_ERROR_RECV_BUFFER_TOO_SMALL; + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_ERROR_RECV_BUFFER_TOO_SMALL; + } + + /* just need to wait for the rest of the data */ + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return MQTT_OK; + } + + /* response was unpacked successfully */ + + /* + The switch statement below manages how the client responds to messages from the broker. + + Control Types (that we expect to receive from the broker): + MQTT_CONTROL_CONNACK: + -> release associated CONNECT + -> handle response + MQTT_CONTROL_PUBLISH: + -> stage response, none if qos==0, PUBACK if qos==1, PUBREC if qos==2 + -> call publish callback + MQTT_CONTROL_PUBACK: + -> release associated PUBLISH + MQTT_CONTROL_PUBREC: + -> release PUBLISH + -> stage PUBREL + MQTT_CONTROL_PUBREL: + -> release associated PUBREC + -> stage PUBCOMP + MQTT_CONTROL_PUBCOMP: + -> release PUBREL + MQTT_CONTROL_SUBACK: + -> release SUBSCRIBE + -> handle response + MQTT_CONTROL_UNSUBACK: + -> release UNSUBSCRIBE + MQTT_CONTROL_PINGRESP: + -> release PINGREQ + */ + switch (response.fixed_header.control_type) { + case MQTT_CONTROL_CONNACK: + /* release associated CONNECT */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_CONNECT, NULL); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* initialize typical response time */ + client->typical_response_time = (float)(MQTT_PAL_TIME() - msg->time_sent); + /* check that connection was successful */ + if (response.decoded.connack.return_code != MQTT_CONNACK_ACCEPTED) { + if (response.decoded.connack.return_code == MQTT_CONNACK_REFUSED_IDENTIFIER_REJECTED) { + client->error = MQTT_ERROR_CONNECT_CLIENT_ID_REFUSED; + mqtt_recv_ret = MQTT_ERROR_CONNECT_CLIENT_ID_REFUSED; + } else { + client->error = MQTT_ERROR_CONNECTION_REFUSED; + mqtt_recv_ret = MQTT_ERROR_CONNECTION_REFUSED; + } + break; + } + break; + case MQTT_CONTROL_PUBLISH: + /* stage response, none if qos==0, PUBACK if qos==1, PUBREC if qos==2 */ + if (response.decoded.publish.qos_level == 1) { + rv = __mqtt_puback(client, response.decoded.publish.packet_id); + if (rv != MQTT_OK) { + client->error = (enum MQTTErrors)rv; + mqtt_recv_ret = rv; + break; + } + } else if (response.decoded.publish.qos_level == 2) { + /* check if this is a duplicate */ + if (mqtt_mq_find(&client->mq, MQTT_CONTROL_PUBREC, &response.decoded.publish.packet_id) != NULL) { + break; + } + + rv = __mqtt_pubrec(client, response.decoded.publish.packet_id); + if (rv != MQTT_OK) { + client->error = (enum MQTTErrors)rv; + mqtt_recv_ret = rv; + break; + } + } + /* call publish callback */ + client->publish_response_callback(&client->publish_response_callback_state, &response.decoded.publish); + break; + case MQTT_CONTROL_PUBACK: + /* release associated PUBLISH */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_PUBLISH, &response.decoded.puback.packet_id); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* update response time */ + client->typical_response_time = 0.875f * (client->typical_response_time) + 0.125f * (float)(MQTT_PAL_TIME() - msg->time_sent); + break; + case MQTT_CONTROL_PUBREC: + /* check if this is a duplicate */ + if (mqtt_mq_find(&client->mq, MQTT_CONTROL_PUBREL, &response.decoded.pubrec.packet_id) != NULL) { + break; + } + /* release associated PUBLISH */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_PUBLISH, &response.decoded.pubrec.packet_id); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* update response time */ + client->typical_response_time = 0.875f * (client->typical_response_time) + 0.125f * (float)(MQTT_PAL_TIME() - msg->time_sent); + /* stage PUBREL */ + rv = __mqtt_pubrel(client, response.decoded.pubrec.packet_id); + if (rv != MQTT_OK) { + client->error = (enum MQTTErrors)rv; + mqtt_recv_ret = rv; + break; + } + break; + case MQTT_CONTROL_PUBREL: + /* release associated PUBREC */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_PUBREC, &response.decoded.pubrel.packet_id); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* update response time */ + client->typical_response_time = 0.875f * (client->typical_response_time) + 0.125f * (float)(MQTT_PAL_TIME() - msg->time_sent); + /* stage PUBCOMP */ + rv = __mqtt_pubcomp(client, response.decoded.pubrec.packet_id); + if (rv != MQTT_OK) { + client->error = (enum MQTTErrors)rv; + mqtt_recv_ret = rv; + break; + } + break; + case MQTT_CONTROL_PUBCOMP: + /* release associated PUBREL */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_PUBREL, &response.decoded.pubcomp.packet_id); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* update response time */ + client->typical_response_time = 0.875f * (client->typical_response_time) + 0.125f * (float)(MQTT_PAL_TIME() - msg->time_sent); + break; + case MQTT_CONTROL_SUBACK: + /* release associated SUBSCRIBE */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_SUBSCRIBE, &response.decoded.suback.packet_id); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* update response time */ + client->typical_response_time = 0.875f * (client->typical_response_time) + 0.125f * (float)(MQTT_PAL_TIME() - msg->time_sent); + /* check that subscription was successful (not currently only one subscribe at a time) */ + if (response.decoded.suback.return_codes[0] == MQTT_SUBACK_FAILURE) { + client->error = MQTT_ERROR_SUBSCRIBE_FAILED; + mqtt_recv_ret = MQTT_ERROR_SUBSCRIBE_FAILED; + break; + } + break; + case MQTT_CONTROL_UNSUBACK: + /* release associated UNSUBSCRIBE */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_UNSUBSCRIBE, &response.decoded.unsuback.packet_id); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* update response time */ + client->typical_response_time = 0.875f * (client->typical_response_time) + 0.125f * (float)(MQTT_PAL_TIME() - msg->time_sent); + break; + case MQTT_CONTROL_PINGRESP: + /* release associated PINGREQ */ + msg = mqtt_mq_find(&client->mq, MQTT_CONTROL_PINGREQ, NULL); + if (msg == NULL) { + client->error = MQTT_ERROR_ACK_OF_UNKNOWN; + mqtt_recv_ret = MQTT_ERROR_ACK_OF_UNKNOWN; + break; + } + msg->state = MQTT_QUEUED_COMPLETE; + /* update response time */ + client->typical_response_time = 0.875f * (client->typical_response_time) + 0.125f * (float)(MQTT_PAL_TIME() - msg->time_sent); + break; + default: + client->error = MQTT_ERROR_MALFORMED_RESPONSE; + mqtt_recv_ret = MQTT_ERROR_MALFORMED_RESPONSE; + break; + } + { + /* we've handled the response, now clean the buffer */ + void *dest = (unsigned char *)client->recv_buffer.mem_start; + void *src = (unsigned char *)client->recv_buffer.mem_start + consumed; + size_t n = (size_t)(client->recv_buffer.curr - client->recv_buffer.mem_start - consumed); + memmove(dest, src, n); + client->recv_buffer.curr -= consumed; + client->recv_buffer.curr_sz += (unsigned long)consumed; + } + } + + /* In case there was some error handling the (well formed) message, we end up here */ + MQTT_PAL_MUTEX_UNLOCK(&client->mutex); + return mqtt_recv_ret; +} + +/* FIXED HEADER */ + +#define MQTT_BITFIELD_RULE_VIOLOATION(bitfield, rule_value, rule_mask) ((bitfield ^ rule_value) & rule_mask) + +struct mqtt_fixed_header_rules_s { + uint8_t control_type_is_valid[16]; + uint8_t required_flags[16]; + uint8_t mask_required_flags[16]; +} ; + +static const struct mqtt_fixed_header_rules_s mqtt_fixed_header_rules = { + { + /* boolean value, true if type is valid */ + 0x00, /* MQTT_CONTROL_RESERVED */ + 0x01, /* MQTT_CONTROL_CONNECT */ + 0x01, /* MQTT_CONTROL_CONNACK */ + 0x01, /* MQTT_CONTROL_PUBLISH */ + 0x01, /* MQTT_CONTROL_PUBACK */ + 0x01, /* MQTT_CONTROL_PUBREC */ + 0x01, /* MQTT_CONTROL_PUBREL */ + 0x01, /* MQTT_CONTROL_PUBCOMP */ + 0x01, /* MQTT_CONTROL_SUBSCRIBE */ + 0x01, /* MQTT_CONTROL_SUBACK */ + 0x01, /* MQTT_CONTROL_UNSUBSCRIBE */ + 0x01, /* MQTT_CONTROL_UNSUBACK */ + 0x01, /* MQTT_CONTROL_PINGREQ */ + 0x01, /* MQTT_CONTROL_PINGRESP */ + 0x01, /* MQTT_CONTROL_DISCONNECT */ + 0x00 /* MQTT_CONTROL_RESERVED */ + }, + { + /* flags that must be set for the associated control type */ + 0x00, /* MQTT_CONTROL_RESERVED */ + 0x00, /* MQTT_CONTROL_CONNECT */ + 0x00, /* MQTT_CONTROL_CONNACK */ + 0x00, /* MQTT_CONTROL_PUBLISH */ + 0x00, /* MQTT_CONTROL_PUBACK */ + 0x00, /* MQTT_CONTROL_PUBREC */ + 0x02, /* MQTT_CONTROL_PUBREL */ + 0x00, /* MQTT_CONTROL_PUBCOMP */ + 0x02, /* MQTT_CONTROL_SUBSCRIBE */ + 0x00, /* MQTT_CONTROL_SUBACK */ + 0x02, /* MQTT_CONTROL_UNSUBSCRIBE */ + 0x00, /* MQTT_CONTROL_UNSUBACK */ + 0x00, /* MQTT_CONTROL_PINGREQ */ + 0x00, /* MQTT_CONTROL_PINGRESP */ + 0x00, /* MQTT_CONTROL_DISCONNECT */ + 0x00 /* MQTT_CONTROL_RESERVED */ + }, + { + /* mask of flags that must be specific values for the associated control type*/ + 0x00, /* MQTT_CONTROL_RESERVED */ + 0x0F, /* MQTT_CONTROL_CONNECT */ + 0x0F, /* MQTT_CONTROL_CONNACK */ + 0x00, /* MQTT_CONTROL_PUBLISH */ + 0x0F, /* MQTT_CONTROL_PUBACK */ + 0x0F, /* MQTT_CONTROL_PUBREC */ + 0x0F, /* MQTT_CONTROL_PUBREL */ + 0x0F, /* MQTT_CONTROL_PUBCOMP */ + 0x0F, /* MQTT_CONTROL_SUBSCRIBE */ + 0x0F, /* MQTT_CONTROL_SUBACK */ + 0x0F, /* MQTT_CONTROL_UNSUBSCRIBE */ + 0x0F, /* MQTT_CONTROL_UNSUBACK */ + 0x0F, /* MQTT_CONTROL_PINGREQ */ + 0x0F, /* MQTT_CONTROL_PINGRESP */ + 0x0F, /* MQTT_CONTROL_DISCONNECT */ + 0x00 /* MQTT_CONTROL_RESERVED */ + } +}; + +static ssize_t mqtt_fixed_header_rule_violation(const struct mqtt_fixed_header *fixed_header) { + uint8_t control_type; + uint8_t control_flags; + uint8_t required_flags; + uint8_t mask_required_flags; + + /* get value and rules */ + control_type = (uint8_t)fixed_header->control_type; + control_flags = fixed_header->control_flags; + required_flags = mqtt_fixed_header_rules.required_flags[control_type]; + mask_required_flags = mqtt_fixed_header_rules.mask_required_flags[control_type]; + + /* check for valid type */ + if (!mqtt_fixed_header_rules.control_type_is_valid[control_type]) { + return MQTT_ERROR_CONTROL_FORBIDDEN_TYPE; + } + + /* check that flags are appropriate */ + if (MQTT_BITFIELD_RULE_VIOLOATION(control_flags, required_flags, mask_required_flags)) { + return MQTT_ERROR_CONTROL_INVALID_FLAGS; + } + + return 0; +} + +ssize_t mqtt_unpack_fixed_header(struct mqtt_response *response, const uint8_t *buf, size_t bufsz) { + struct mqtt_fixed_header *fixed_header; + const uint8_t *start = buf; + int lshift; + ssize_t errcode; + + /* check for null pointers or empty buffer */ + if (response == NULL || buf == NULL) { + return MQTT_ERROR_NULLPTR; + } + fixed_header = &(response->fixed_header); + + /* check that bufsz is not zero */ + if (bufsz == 0) return 0; + + /* parse control type and flags */ + fixed_header->control_type = (enum MQTTControlPacketType)(*buf >> 4); + fixed_header->control_flags = (uint8_t)(*buf & 0x0F); + + /* parse remaining size */ + fixed_header->remaining_length = 0; + + lshift = 0; + do { + + /* MQTT spec (2.2.3) says the maximum length is 28 bits */ + if (lshift == 28) + return MQTT_ERROR_INVALID_REMAINING_LENGTH; + + /* consume byte and assert at least 1 byte left */ + --bufsz; + ++buf; + if (bufsz == 0) return 0; + + /* parse next byte*/ + fixed_header->remaining_length += (uint32_t)((*buf & 0x7F) << lshift); + lshift += 7; + } while (*buf & 0x80); /* while continue bit is set */ + + /* consume last byte */ + --bufsz; + ++buf; + + /* check that the fixed header is valid */ + errcode = mqtt_fixed_header_rule_violation(fixed_header); + if (errcode) { + return errcode; + } + + /* check that the buffer size if GT remaining length */ + if (bufsz < fixed_header->remaining_length) { + return 0; + } + + /* return how many bytes were consumed */ + return buf - start; +} + +ssize_t mqtt_pack_fixed_header(uint8_t *buf, size_t bufsz, const struct mqtt_fixed_header *fixed_header) { + const uint8_t *start = buf; + ssize_t errcode; + uint32_t remaining_length; + + /* check for null pointers or empty buffer */ + if (fixed_header == NULL || buf == NULL) { + return MQTT_ERROR_NULLPTR; + } + + /* check that the fixed header is valid */ + errcode = mqtt_fixed_header_rule_violation(fixed_header); + if (errcode) { + return errcode; + } + + /* check that bufsz is not zero */ + if (bufsz == 0) return 0; + + /* pack control type and flags */ + *buf = (uint8_t)((((uint8_t) fixed_header->control_type) << 4) & 0xF0); + *buf = (uint8_t)(*buf | (((uint8_t) fixed_header->control_flags) & 0x0F)); + + remaining_length = fixed_header->remaining_length; + + /* MQTT spec (2.2.3) says maximum remaining length is 2^28-1 */ + if (remaining_length >= 256 * 1024 * 1024) + return MQTT_ERROR_INVALID_REMAINING_LENGTH; + + do { + /* consume byte and assert at least 1 byte left */ + --bufsz; + ++buf; + if (bufsz == 0) return 0; + + /* pack next byte */ + *buf = remaining_length & 0x7F; + if (remaining_length > 127) *buf |= 0x80; + remaining_length = remaining_length >> 7; + } while (*buf & 0x80); + + /* consume last byte */ + --bufsz; + ++buf; + + /* check that there's still enough space in buffer for packet */ + if (bufsz < fixed_header->remaining_length) { + return 0; + } + + /* return how many bytes were consumed */ + return buf - start; +} + +/* CONNECT */ +ssize_t mqtt_pack_connection_request(uint8_t *buf, size_t bufsz, + const char *client_id, + const char *will_topic, + const void *will_message, + size_t will_message_size, + const char *user_name, + const char *password, + uint8_t connect_flags, + uint16_t keep_alive) { + struct mqtt_fixed_header fixed_header; + size_t remaining_length; + const uint8_t *const start = buf; + ssize_t rv; + + /* pack the fixed headr */ + fixed_header.control_type = MQTT_CONTROL_CONNECT; + fixed_header.control_flags = 0x00; + + /* calculate remaining length and build connect_flags at the same time */ + connect_flags = (uint8_t)(connect_flags & ~MQTT_CONNECT_RESERVED); + remaining_length = 10; /* size of variable header */ + + if (client_id == NULL) { + client_id = ""; + } + /* For an empty client_id, a clean session is required */ + if (client_id[0] == '\0' && !(connect_flags & MQTT_CONNECT_CLEAN_SESSION)) { + return MQTT_ERROR_CLEAN_SESSION_IS_REQUIRED; + } + /* mqtt_string length is strlen + 2 */ + remaining_length += __mqtt_packed_cstrlen(client_id); + + if (will_topic != NULL) { + uint8_t temp; + /* there is a will */ + connect_flags |= MQTT_CONNECT_WILL_FLAG; + remaining_length += __mqtt_packed_cstrlen(will_topic); + + if (will_message == NULL) { + /* if there's a will there MUST be a will message */ + return MQTT_ERROR_CONNECT_NULL_WILL_MESSAGE; + } + remaining_length += 2 + will_message_size; /* size of will_message */ + + /* assert that the will QOS is valid (i.e. not 3) */ + temp = connect_flags & 0x18; /* mask to QOS */ + if (temp == 0x18) { + /* bitwise equality with QoS 3 (invalid)*/ + return MQTT_ERROR_CONNECT_FORBIDDEN_WILL_QOS; + } + } else { + /* there is no will so set all will flags to zero */ + connect_flags &= (uint8_t)~MQTT_CONNECT_WILL_FLAG; + connect_flags &= (uint8_t)~0x18; + connect_flags &= (uint8_t)~MQTT_CONNECT_WILL_RETAIN; + } + + if (user_name != NULL) { + /* a user name is present */ + connect_flags |= MQTT_CONNECT_USER_NAME; + remaining_length += __mqtt_packed_cstrlen(user_name); + } else { + connect_flags &= (uint8_t)~MQTT_CONNECT_USER_NAME; + } + + if (password != NULL) { + /* a password is present */ + connect_flags |= MQTT_CONNECT_PASSWORD; + remaining_length += __mqtt_packed_cstrlen(password); + } else { + connect_flags &= (uint8_t)~MQTT_CONNECT_PASSWORD; + } + + /* fixed header length is now calculated*/ + fixed_header.remaining_length = (uint32_t)remaining_length; + + /* pack fixed header and perform error checks */ + rv = mqtt_pack_fixed_header(buf, bufsz, &fixed_header); + if (rv <= 0) { + /* something went wrong */ + return rv; + } + buf += rv; + bufsz -= (size_t)rv; + + /* check that the buffer has enough space to fit the remaining length */ + if (bufsz < fixed_header.remaining_length) { + return 0; + } + + /* pack the variable header */ + *buf++ = 0x00; + *buf++ = 0x04; + *buf++ = (uint8_t) 'M'; + *buf++ = (uint8_t) 'Q'; + *buf++ = (uint8_t) 'T'; + *buf++ = (uint8_t) 'T'; + *buf++ = MQTT_PROTOCOL_LEVEL; + *buf++ = connect_flags; + buf += __mqtt_pack_uint16(buf, keep_alive); + + /* pack the payload */ + buf += __mqtt_pack_str(buf, client_id); + if (will_topic != NULL) { + buf += __mqtt_pack_str(buf, will_topic); + buf += __mqtt_pack_uint16(buf, (uint16_t)will_message_size); + memcpy(buf, will_message, will_message_size); + buf += will_message_size; + } + if (user_name != NULL) { + buf += __mqtt_pack_str(buf, user_name); + } + if (password != NULL) { + buf += __mqtt_pack_str(buf, password); + } + + /* return the number of bytes that were consumed */ + return buf - start; +} + +/* CONNACK */ +ssize_t mqtt_unpack_connack_response(struct mqtt_response *mqtt_response, const uint8_t *buf) { + const uint8_t *const start = buf; + struct mqtt_response_connack *response; + + /* check that remaining length is 2 */ + if (mqtt_response->fixed_header.remaining_length != 2) { + return MQTT_ERROR_MALFORMED_RESPONSE; + } + + response = &(mqtt_response->decoded.connack); + /* unpack */ + if (*buf & 0xFE) { + /* only bit 1 can be set */ + return MQTT_ERROR_CONNACK_FORBIDDEN_FLAGS; + } else { + response->session_present_flag = *buf++; + } + + if (*buf > 5u) { + /* only bit 1 can be set */ + return MQTT_ERROR_CONNACK_FORBIDDEN_CODE; + } else { + response->return_code = (enum MQTTConnackReturnCode) * buf++; + } + return buf - start; +} + +/* DISCONNECT */ +ssize_t mqtt_pack_disconnect(uint8_t *buf, size_t bufsz) { + struct mqtt_fixed_header fixed_header; + fixed_header.control_type = MQTT_CONTROL_DISCONNECT; + fixed_header.control_flags = 0; + fixed_header.remaining_length = 0; + return mqtt_pack_fixed_header(buf, bufsz, &fixed_header); +} + +/* PING */ +ssize_t mqtt_pack_ping_request(uint8_t *buf, size_t bufsz) { + struct mqtt_fixed_header fixed_header; + fixed_header.control_type = MQTT_CONTROL_PINGREQ; + fixed_header.control_flags = 0; + fixed_header.remaining_length = 0; + return mqtt_pack_fixed_header(buf, bufsz, &fixed_header); +} + +/* PUBLISH */ +ssize_t mqtt_pack_publish_request(uint8_t *buf, size_t bufsz, + const char *topic_name, + uint16_t packet_id, + const void *application_message, + size_t application_message_size, + uint8_t publish_flags) { + const uint8_t *const start = buf; + ssize_t rv; + struct mqtt_fixed_header fixed_header; + uint32_t remaining_length; + uint8_t inspected_qos; + + /* check for null pointers */ + if (buf == NULL || topic_name == NULL) { + return MQTT_ERROR_NULLPTR; + } + + /* inspect QoS level */ + inspected_qos = (publish_flags & MQTT_PUBLISH_QOS_MASK) >> 1; /* mask */ + + /* build the fixed header */ + fixed_header.control_type = MQTT_CONTROL_PUBLISH; + + /* calculate remaining length */ + remaining_length = (uint32_t)__mqtt_packed_cstrlen(topic_name); + if (inspected_qos > 0) { + remaining_length += 2; + } + remaining_length += (uint32_t)application_message_size; + fixed_header.remaining_length = remaining_length; + + /* force dup to 0 if qos is 0 [Spec MQTT-3.3.1-2] */ + if (inspected_qos == 0) { + publish_flags &= (uint8_t)~MQTT_PUBLISH_DUP; + } + + /* make sure that qos is not 3 [Spec MQTT-3.3.1-4] */ + if (inspected_qos == 3) { + return MQTT_ERROR_PUBLISH_FORBIDDEN_QOS; + } + fixed_header.control_flags = publish_flags & 0x7; + + /* pack fixed header */ + rv = mqtt_pack_fixed_header(buf, bufsz, &fixed_header); + if (rv <= 0) { + /* something went wrong */ + return rv; + } + buf += rv; + bufsz -= (size_t)rv; + + /* check that buffer is big enough */ + if (bufsz < remaining_length) { + return 0; + } + + /* pack variable header */ + buf += __mqtt_pack_str(buf, topic_name); + if (inspected_qos > 0) { + buf += __mqtt_pack_uint16(buf, packet_id); + } + + /* pack payload */ + memcpy(buf, application_message, application_message_size); + buf += application_message_size; + + return buf - start; +} + +ssize_t mqtt_unpack_publish_response(struct mqtt_response *mqtt_response, const uint8_t *buf) { + const uint8_t *const start = buf; + struct mqtt_fixed_header *fixed_header; + struct mqtt_response_publish *response; + + fixed_header = &(mqtt_response->fixed_header); + response = &(mqtt_response->decoded.publish); + + /* get flags */ + response->dup_flag = (fixed_header->control_flags & MQTT_PUBLISH_DUP) >> 3; + response->qos_level = (fixed_header->control_flags & MQTT_PUBLISH_QOS_MASK) >> 1; + response->retain_flag = fixed_header->control_flags & MQTT_PUBLISH_RETAIN; + + /* make sure that remaining length is valid */ + if (mqtt_response->fixed_header.remaining_length < 4) { + return MQTT_ERROR_MALFORMED_RESPONSE; + } + + /* parse variable header */ + response->topic_name_size = __mqtt_unpack_uint16(buf); + buf += 2; + response->topic_name = buf; + buf += response->topic_name_size; + + if (response->qos_level > 0) { + response->packet_id = __mqtt_unpack_uint16(buf); + buf += 2; + } + + /* get payload */ + response->application_message = buf; + if (response->qos_level == 0) { + response->application_message_size = fixed_header->remaining_length - response->topic_name_size - 2; + } else { + response->application_message_size = fixed_header->remaining_length - response->topic_name_size - 4; + } + buf += response->application_message_size; + + /* return number of bytes consumed */ + return buf - start; +} + +/* PUBXXX */ +ssize_t mqtt_pack_pubxxx_request(uint8_t *buf, size_t bufsz, + enum MQTTControlPacketType control_type, + uint16_t packet_id) { + const uint8_t *const start = buf; + struct mqtt_fixed_header fixed_header; + ssize_t rv; + if (buf == NULL) { + return MQTT_ERROR_NULLPTR; + } + + /* pack fixed header */ + fixed_header.control_type = control_type; + if (control_type == MQTT_CONTROL_PUBREL) { + fixed_header.control_flags = 0x02; + } else { + fixed_header.control_flags = 0; + } + fixed_header.remaining_length = 2; + rv = mqtt_pack_fixed_header(buf, bufsz, &fixed_header); + if (rv <= 0) { + return rv; + } + buf += rv; + bufsz -= (size_t)rv; + + if (bufsz < fixed_header.remaining_length) { + return 0; + } + + buf += __mqtt_pack_uint16(buf, packet_id); + + return buf - start; +} + +ssize_t mqtt_unpack_pubxxx_response(struct mqtt_response *mqtt_response, const uint8_t *buf) { + const uint8_t *const start = buf; + uint16_t packet_id; + + /* assert remaining length is correct */ + if (mqtt_response->fixed_header.remaining_length != 2) { + return MQTT_ERROR_MALFORMED_RESPONSE; + } + + /* parse packet_id */ + packet_id = __mqtt_unpack_uint16(buf); + buf += 2; + + if (mqtt_response->fixed_header.control_type == MQTT_CONTROL_PUBACK) { + mqtt_response->decoded.puback.packet_id = packet_id; + } else if (mqtt_response->fixed_header.control_type == MQTT_CONTROL_PUBREC) { + mqtt_response->decoded.pubrec.packet_id = packet_id; + } else if (mqtt_response->fixed_header.control_type == MQTT_CONTROL_PUBREL) { + mqtt_response->decoded.pubrel.packet_id = packet_id; + } else { + mqtt_response->decoded.pubcomp.packet_id = packet_id; + } + + return buf - start; +} + +/* SUBACK */ +ssize_t mqtt_unpack_suback_response(struct mqtt_response *mqtt_response, const uint8_t *buf) { + const uint8_t *const start = buf; + uint32_t remaining_length = mqtt_response->fixed_header.remaining_length; + + /* assert remaining length is at least 3 (for packet id and at least 1 topic) */ + if (remaining_length < 3) { + return MQTT_ERROR_MALFORMED_RESPONSE; + } + + /* unpack packet_id */ + mqtt_response->decoded.suback.packet_id = __mqtt_unpack_uint16(buf); + buf += 2; + remaining_length -= 2; + + /* unpack return codes */ + mqtt_response->decoded.suback.num_return_codes = (size_t) remaining_length; + mqtt_response->decoded.suback.return_codes = buf; + buf += remaining_length; + + return buf - start; +} + +/* SUBSCRIBE */ +ssize_t mqtt_pack_subscribe_request(uint8_t *buf, size_t bufsz, unsigned int packet_id, ...) { + va_list args; + const uint8_t *const start = buf; + ssize_t rv; + struct mqtt_fixed_header fixed_header; + unsigned int num_subs = 0; + unsigned int i; + const char *topic[MQTT_SUBSCRIBE_REQUEST_MAX_NUM_TOPICS]; + uint8_t max_qos[MQTT_SUBSCRIBE_REQUEST_MAX_NUM_TOPICS]; + + /* parse all subscriptions */ + va_start(args, packet_id); + for (;;) { + topic[num_subs] = va_arg(args, const char *); + if (topic[num_subs] == NULL) { + /* end of list */ + break; + } + + max_qos[num_subs] = (uint8_t) va_arg(args, unsigned int); + + ++num_subs; + if (num_subs >= MQTT_SUBSCRIBE_REQUEST_MAX_NUM_TOPICS) { + va_end(args); + return MQTT_ERROR_SUBSCRIBE_TOO_MANY_TOPICS; + } + } + va_end(args); + + /* build the fixed header */ + fixed_header.control_type = MQTT_CONTROL_SUBSCRIBE; + fixed_header.control_flags = 2u; + fixed_header.remaining_length = 2u; /* size of variable header */ + for (i = 0; i < num_subs; ++i) { + /* payload is topic name + max qos (1 byte) */ + fixed_header.remaining_length += __mqtt_packed_cstrlen(topic[i]) + 1; + } + + /* pack the fixed header */ + rv = mqtt_pack_fixed_header(buf, bufsz, &fixed_header); + if (rv <= 0) { + return rv; + } + buf += rv; + bufsz -= (unsigned long)rv; + + /* check that the buffer has enough space */ + if (bufsz < fixed_header.remaining_length) { + return 0; + } + + + /* pack variable header */ + buf += __mqtt_pack_uint16(buf, (uint16_t)packet_id); + + + /* pack payload */ + for (i = 0; i < num_subs; ++i) { + buf += __mqtt_pack_str(buf, topic[i]); + *buf++ = max_qos[i]; + } + + return buf - start; +} + +/* UNSUBACK */ +ssize_t mqtt_unpack_unsuback_response(struct mqtt_response *mqtt_response, const uint8_t *buf) { + const uint8_t *const start = buf; + + if (mqtt_response->fixed_header.remaining_length != 2) { + return MQTT_ERROR_MALFORMED_RESPONSE; + } + + /* parse packet_id */ + mqtt_response->decoded.unsuback.packet_id = __mqtt_unpack_uint16(buf); + buf += 2; + + return buf - start; +} + +/* UNSUBSCRIBE */ +ssize_t mqtt_pack_unsubscribe_request(uint8_t *buf, size_t bufsz, unsigned int packet_id, ...) { + va_list args; + const uint8_t *const start = buf; + ssize_t rv; + struct mqtt_fixed_header fixed_header; + unsigned int num_subs = 0; + unsigned int i; + const char *topic[MQTT_UNSUBSCRIBE_REQUEST_MAX_NUM_TOPICS]; + + /* parse all subscriptions */ + va_start(args, packet_id); + for (;;) { + topic[num_subs] = va_arg(args, const char *); + if (topic[num_subs] == NULL) { + /* end of list */ + break; + } + + ++num_subs; + if (num_subs >= MQTT_UNSUBSCRIBE_REQUEST_MAX_NUM_TOPICS) { + va_end(args); + return MQTT_ERROR_UNSUBSCRIBE_TOO_MANY_TOPICS; + } + } + va_end(args); + + /* build the fixed header */ + fixed_header.control_type = MQTT_CONTROL_UNSUBSCRIBE; + fixed_header.control_flags = 2u; + fixed_header.remaining_length = 2u; /* size of variable header */ + for (i = 0; i < num_subs; ++i) { + /* payload is topic name */ + fixed_header.remaining_length += __mqtt_packed_cstrlen(topic[i]); + } + + /* pack the fixed header */ + rv = mqtt_pack_fixed_header(buf, bufsz, &fixed_header); + if (rv <= 0) { + return rv; + } + buf += rv; + bufsz -= (unsigned long)rv; + + /* check that the buffer has enough space */ + if (bufsz < fixed_header.remaining_length) { + return 0; + } + + /* pack variable header */ + buf += __mqtt_pack_uint16(buf, (uint16_t)packet_id); + + + /* pack payload */ + for (i = 0; i < num_subs; ++i) { + buf += __mqtt_pack_str(buf, topic[i]); + } + + return buf - start; +} + +/* MESSAGE QUEUE */ +void mqtt_mq_init(struct mqtt_message_queue *mq, void *buf, size_t bufsz) { + mq->mem_start = buf; + mq->mem_end = (uint8_t *)buf + bufsz; + mq->curr = (uint8_t *)buf; + mq->queue_tail = (struct mqtt_queued_message *)mq->mem_end; + mq->curr_sz = buf == NULL ? 0 : mqtt_mq_currsz(mq); +} + +struct mqtt_queued_message *mqtt_mq_register(struct mqtt_message_queue *mq, size_t nbytes) { + /* make queued message header */ + --(mq->queue_tail); + mq->queue_tail->start = mq->curr; + mq->queue_tail->size = nbytes; + mq->queue_tail->state = MQTT_QUEUED_UNSENT; + + /* move curr and recalculate curr_sz */ + mq->curr += nbytes; + mq->curr_sz = (size_t)(mqtt_mq_currsz(mq)); + + return mq->queue_tail; +} + +void mqtt_mq_clean(struct mqtt_message_queue *mq) { + struct mqtt_queued_message *new_head; + + for (new_head = mqtt_mq_get(mq, 0); new_head >= mq->queue_tail; --new_head) { + if (new_head->state != MQTT_QUEUED_COMPLETE) break; + } + + /* check if everything can be removed */ + if (new_head < mq->queue_tail) { + mq->curr = (uint8_t *)mq->mem_start; + mq->queue_tail = (struct mqtt_queued_message *)mq->mem_end; + mq->curr_sz = (size_t)(mqtt_mq_currsz(mq)); + return; + } else if (new_head == mqtt_mq_get(mq, 0)) { + /* do nothing */ + return; + } + + /* move buffered data */ + { + size_t n = (size_t)(mq->curr - new_head->start); + size_t removing = (size_t)(new_head->start - (uint8_t *) mq->mem_start); + memmove(mq->mem_start, new_head->start, n); + mq->curr = (unsigned char *)mq->mem_start + n; + + + /* move queue */ + { + ssize_t new_tail_idx = new_head - mq->queue_tail; + memmove(mqtt_mq_get(mq, new_tail_idx), mq->queue_tail, sizeof(struct mqtt_queued_message) * (size_t)((new_tail_idx + 1))); + mq->queue_tail = mqtt_mq_get(mq, new_tail_idx); + + { + /* bump back start's */ + ssize_t i = 0; + for (; i < new_tail_idx + 1; ++i) { + mqtt_mq_get(mq, i)->start -= removing; + } + } + } + } + + /* get curr_sz */ + mq->curr_sz = (size_t)(mqtt_mq_currsz(mq)); +} + +struct mqtt_queued_message *mqtt_mq_find(const struct mqtt_message_queue *mq, enum MQTTControlPacketType control_type, const uint16_t *packet_id) { + struct mqtt_queued_message *curr; + for (curr = mqtt_mq_get(mq, 0); curr >= mq->queue_tail; --curr) { + if (curr->control_type == control_type) { + if ((packet_id == NULL && curr->state != MQTT_QUEUED_COMPLETE) || + (packet_id != NULL && *packet_id == curr->packet_id)) { + return curr; + } + } + } + return NULL; +} + + +/* RESPONSE UNPACKING */ +ssize_t mqtt_unpack_response(struct mqtt_response *response, const uint8_t *buf, size_t bufsz) { + const uint8_t *const start = buf; + ssize_t rv = mqtt_unpack_fixed_header(response, buf, bufsz); + if (rv <= 0) return rv; + else buf += rv; + switch (response->fixed_header.control_type) { + case MQTT_CONTROL_CONNACK: + rv = mqtt_unpack_connack_response(response, buf); + break; + case MQTT_CONTROL_PUBLISH: + rv = mqtt_unpack_publish_response(response, buf); + break; + case MQTT_CONTROL_PUBACK: + rv = mqtt_unpack_pubxxx_response(response, buf); + break; + case MQTT_CONTROL_PUBREC: + rv = mqtt_unpack_pubxxx_response(response, buf); + break; + case MQTT_CONTROL_PUBREL: + rv = mqtt_unpack_pubxxx_response(response, buf); + break; + case MQTT_CONTROL_PUBCOMP: + rv = mqtt_unpack_pubxxx_response(response, buf); + break; + case MQTT_CONTROL_SUBACK: + rv = mqtt_unpack_suback_response(response, buf); + break; + case MQTT_CONTROL_UNSUBACK: + rv = mqtt_unpack_unsuback_response(response, buf); + break; + case MQTT_CONTROL_PINGRESP: + return rv; + default: + return MQTT_ERROR_RESPONSE_INVALID_CONTROL_TYPE; + } + + if (rv < 0) return rv; + buf += rv; + return buf - start; +} + +/* EXTRA DETAILS */ +ssize_t __mqtt_pack_uint16(uint8_t *buf, uint16_t integer) { + uint16_t integer_htons = MQTT_PAL_HTONS(integer); + memcpy(buf, &integer_htons, 2uL); + return 2; +} + +uint16_t __mqtt_unpack_uint16(const uint8_t *buf) { + uint16_t integer_htons; + memcpy(&integer_htons, buf, 2uL); + return MQTT_PAL_NTOHS(integer_htons); +} + +ssize_t __mqtt_pack_str(uint8_t *buf, const char *str) { + uint16_t length = (uint16_t)strlen(str); + int i = 0; + /* pack string length */ + buf += __mqtt_pack_uint16(buf, length); + + /* pack string */ + for (; i < length; ++i) { + *(buf++) = (uint8_t)str[i]; + } + + /* return number of bytes consumed */ + return length + 2; +} + +static const char *const MQTT_ERRORS_STR[] = { + "MQTT_UNKNOWN_ERROR", + __ALL_MQTT_ERRORS(GENERATE_STRING) +}; + +const char *mqtt_error_str(enum MQTTErrors error) { + int offset = error - MQTT_ERROR_UNKNOWN; + if (offset >= 0) { + return MQTT_ERRORS_STR[offset]; + } else if (error == 0) { + return "MQTT_ERROR: Buffer too small."; + } else if (error > 0) { + return "MQTT_OK"; + } else { + return MQTT_ERRORS_STR[0]; + } +} + +/** @endcond*/ diff --git a/client/deps/mqtt/mqtt.h b/client/deps/mqtt/mqtt.h new file mode 100644 index 000000000..d3467cd27 --- /dev/null +++ b/client/deps/mqtt/mqtt.h @@ -0,0 +1,1640 @@ +#if !defined(__MQTT_H__) +#define __MQTT_H__ + +/* +MIT License + +Copyright(c) 2018 Liam Bindle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#if defined(__cplusplus) +extern "C" { +#endif + +// Users can override mqtt_pal.h with their own configuration by defining +// MQTTC_PAL_FILE as a header file to include (-DMQTTC_PAL_FILE=my_mqtt_pal.h). +// +// If MQTTC_PAL_FILE is used, none of the default utils will be emitted and must be +// provided by the config file. To start, I would suggest copying mqtt_pal.h +// and modifying as needed. +#if defined(MQTTC_PAL_FILE) +#define MQTTC_STR2(x) #x +#define MQTTC_STR(x) MQTTC_STR2(x) +#include MQTTC_STR(MQTTC_PAL_FILE) +#else +#include "mqtt_pal.h" +#endif /* MQTT_PAL_FILE */ + +/** + * @file + * @brief Declares all the MQTT-C functions and datastructures. + * + * @note You should \#include . + * + * @example simple_publisher.c + * A simple program to that publishes the current time whenever ENTER is pressed. + * + * Usage: + * \code{.sh} + * ./bin/simple_publisher [address [port [topic]]] + * \endcode + * + * Where \c address is the address of the MQTT broker, \c port is the port number the + * MQTT broker is running on, and \c topic is the name of the topic to publish with. Note + * that all these arguments are optional and the defaults are \c address = \c "test.mosquitto.org", + * \c port = \c "1883", and \c topic = "datetime". + * + * @example simple_subscriber.c + * A simple program that subscribes to a single topic and prints all updates that are received. + * + * Usage: + * \code{.sh} + * ./bin/simple_subscriber [address [port [topic]]] + * \endcode + * + * Where \c address is the address of the MQTT broker, \c port is the port number the + * MQTT broker is running on, and \c topic is the name of the topic subscribe to. Note + * that all these arguments are optional and the defaults are \c address = \c "test.mosquitto.org", + * \c port = \c "1883", and \c topic = "datetime". + * + * @example reconnect_subscriber.c + * Same program as \ref simple_subscriber.c, but using the automatic reconnect functionality. + * + * @example bio_publisher.c + * Same program as \ref simple_publisher.c, but uses a unencrypted BIO socket. + * + * @example openssl_publisher.c + * Same program as \ref simple_publisher.c, but over an encrypted connection using OpenSSL. + * + * Usage: + * \code{.sh} + * ./bin/openssl_publisher ca_file [address [port [topic]]] + * \endcode + * + * + * @defgroup api API + * @brief Documentation of everything you need to know to use the MQTT-C client. + * + * This module contains everything you need to know to use MQTT-C in your application. + * For usage examples see: + * - @ref simple_publisher.c + * - @ref simple_subscriber.c + * - @ref reconnect_subscriber.c + * - @ref bio_publisher.c + * - @ref openssl_publisher.c + * + * @note MQTT-C can be used in both single-threaded and multi-threaded applications. All + * the functions in \ref api are thread-safe. + * + * @defgroup packers Control Packet Serialization + * @brief Developer documentation of the functions and datastructures used for serializing MQTT + * control packets. + * + * @defgroup unpackers Control Packet Deserialization + * @brief Developer documentation of the functions and datastructures used for deserializing MQTT + * control packets. + * + * @defgroup details Utilities + * @brief Developer documentation for the utilities used to implement the MQTT-C client. + * + * @note To deserialize a packet from a buffer use \ref mqtt_unpack_response (it's the only + * function you need). + */ + + +/** + * @brief An enumeration of the MQTT control packet types. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: MQTT Control Packet Types + * + */ +enum MQTTControlPacketType { + MQTT_CONTROL_CONNECT = 1u, + MQTT_CONTROL_CONNACK = 2u, + MQTT_CONTROL_PUBLISH = 3u, + MQTT_CONTROL_PUBACK = 4u, + MQTT_CONTROL_PUBREC = 5u, + MQTT_CONTROL_PUBREL = 6u, + MQTT_CONTROL_PUBCOMP = 7u, + MQTT_CONTROL_SUBSCRIBE = 8u, + MQTT_CONTROL_SUBACK = 9u, + MQTT_CONTROL_UNSUBSCRIBE = 10u, + MQTT_CONTROL_UNSUBACK = 11u, + MQTT_CONTROL_PINGREQ = 12u, + MQTT_CONTROL_PINGRESP = 13u, + MQTT_CONTROL_DISCONNECT = 14u +}; + +/** + * @brief A structure that I will use to keep track of some data needed + * to setup the connection to the broker. + * + * An instance of this struct will be created in my \c main(). Then, whenever + * \ref reconnect_client is called, this instance will be passed. + */ +struct reconnect_state_t { + const char *hostname; + const char *port; + const char *topic; + uint8_t *sendbuf; + size_t sendbufsz; + uint8_t *recvbuf; + size_t recvbufsz; +}; + +/** + * @brief The fixed header of an MQTT control packet. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: Fixed Header + * + */ +struct mqtt_fixed_header { + /** The type of packet. */ + enum MQTTControlPacketType control_type; + + /** The packets control flags.*/ + uint32_t control_flags: 4; + + /** The remaining size of the packet in bytes (i.e. the size of variable header and payload).*/ + uint32_t remaining_length; +}; + +/** + * @brief The protocol identifier for MQTT v3.1.1. + * @ingroup packers + * + * @see + * MQTT v3.1.1: CONNECT Variable Header. + * + */ +#define MQTT_PROTOCOL_LEVEL 0x04 + +/** + * @brief A macro used to declare the enum MQTTErrors and associated + * error messages (the members of the num) at the same time. + */ +#define __ALL_MQTT_ERRORS(MQTT_ERROR) \ + MQTT_ERROR(MQTT_ERROR_NULLPTR) \ + MQTT_ERROR(MQTT_ERROR_CONTROL_FORBIDDEN_TYPE) \ + MQTT_ERROR(MQTT_ERROR_CONTROL_INVALID_FLAGS) \ + MQTT_ERROR(MQTT_ERROR_CONTROL_WRONG_TYPE) \ + MQTT_ERROR(MQTT_ERROR_CONNECT_CLIENT_ID_REFUSED) \ + MQTT_ERROR(MQTT_ERROR_CONNECT_NULL_WILL_MESSAGE) \ + MQTT_ERROR(MQTT_ERROR_CONNECT_FORBIDDEN_WILL_QOS) \ + MQTT_ERROR(MQTT_ERROR_CONNACK_FORBIDDEN_FLAGS) \ + MQTT_ERROR(MQTT_ERROR_CONNACK_FORBIDDEN_CODE) \ + MQTT_ERROR(MQTT_ERROR_PUBLISH_FORBIDDEN_QOS) \ + MQTT_ERROR(MQTT_ERROR_SUBSCRIBE_TOO_MANY_TOPICS) \ + MQTT_ERROR(MQTT_ERROR_MALFORMED_RESPONSE) \ + MQTT_ERROR(MQTT_ERROR_UNSUBSCRIBE_TOO_MANY_TOPICS) \ + MQTT_ERROR(MQTT_ERROR_RESPONSE_INVALID_CONTROL_TYPE) \ + MQTT_ERROR(MQTT_ERROR_CONNECT_NOT_CALLED) \ + MQTT_ERROR(MQTT_ERROR_SEND_BUFFER_IS_FULL) \ + MQTT_ERROR(MQTT_ERROR_SOCKET_ERROR) \ + MQTT_ERROR(MQTT_ERROR_MALFORMED_REQUEST) \ + MQTT_ERROR(MQTT_ERROR_RECV_BUFFER_TOO_SMALL) \ + MQTT_ERROR(MQTT_ERROR_ACK_OF_UNKNOWN) \ + MQTT_ERROR(MQTT_ERROR_NOT_IMPLEMENTED) \ + MQTT_ERROR(MQTT_ERROR_CONNECTION_REFUSED) \ + MQTT_ERROR(MQTT_ERROR_SUBSCRIBE_FAILED) \ + MQTT_ERROR(MQTT_ERROR_CONNECTION_CLOSED) \ + MQTT_ERROR(MQTT_ERROR_INITIAL_RECONNECT) \ + MQTT_ERROR(MQTT_ERROR_INVALID_REMAINING_LENGTH) \ + MQTT_ERROR(MQTT_ERROR_CLEAN_SESSION_IS_REQUIRED) \ + MQTT_ERROR(MQTT_ERROR_RECONNECT_FAILED) \ + MQTT_ERROR(MQTT_ERROR_RECONNECTING) + +/* todo: add more connection refused errors */ + +/** + * @brief A macro used to generate the enum MQTTErrors from + * \ref __ALL_MQTT_ERRORS + * @see __ALL_MQTT_ERRORS +*/ +#define GENERATE_ENUM(ENUM) ENUM, + +/** + * @brief A macro used to generate the error messages associated with + * MQTTErrors from \ref __ALL_MQTT_ERRORS + * @see __ALL_MQTT_ERRORS +*/ +#define GENERATE_STRING(STRING) #STRING, + + +/** + * @brief An enumeration of error codes. Error messages can be retrieved by calling \ref mqtt_error_str. + * @ingroup api + * + * @see mqtt_error_str + */ +enum MQTTErrors { + MQTT_ERROR_UNKNOWN = INT_MIN, + __ALL_MQTT_ERRORS(GENERATE_ENUM) + MQTT_OK = 1 +}; + +/** + * @brief Returns an error message for error code, \p error. + * @ingroup api + * + * @param[in] error the error code. + * + * @returns The associated error message. + */ +const char *mqtt_error_str(enum MQTTErrors error); + +/** + * @brief Pack a MQTT 16 bit integer, given a native 16 bit integer . + * + * @param[out] buf the buffer that the MQTT integer will be written to. + * @param[in] integer the native integer to be written to \p buf. + * + * @warning This function provides no error checking. + * + * @returns 2 +*/ +ssize_t __mqtt_pack_uint16(uint8_t *buf, uint16_t integer); + +/** + * @brief Unpack a MQTT 16 bit integer to a native 16 bit integer. + * + * @param[in] buf the buffer that the MQTT integer will be read from. + * + * @warning This function provides no error checking and does not modify \p buf. + * + * @returns The native integer +*/ +uint16_t __mqtt_unpack_uint16(const uint8_t *buf); + +/** + * @brief Pack a MQTT string, given a c-string \p str. + * + * @param[out] buf the buffer that the MQTT string will be written to. + * @param[in] str the c-string to be written to \p buf. + * + * @warning This function provides no error checking. + * + * @returns strlen(str) + 2 +*/ +ssize_t __mqtt_pack_str(uint8_t *buf, const char *str); + +/** @brief A macro to get the MQTT string length from a c-string. */ +#define __mqtt_packed_cstrlen(x) (2 + (unsigned int)strlen(x)) + +/* RESPONSES */ + +/** + * @brief An enumeration of the return codes returned in a CONNACK packet. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: CONNACK return codes. + * + */ +enum MQTTConnackReturnCode { + MQTT_CONNACK_ACCEPTED = 0u, + MQTT_CONNACK_REFUSED_PROTOCOL_VERSION = 1u, + MQTT_CONNACK_REFUSED_IDENTIFIER_REJECTED = 2u, + MQTT_CONNACK_REFUSED_SERVER_UNAVAILABLE = 3u, + MQTT_CONNACK_REFUSED_BAD_USER_NAME_OR_PASSWORD = 4u, + MQTT_CONNACK_REFUSED_NOT_AUTHORIZED = 5u +}; + +/** + * @brief A connection response datastructure. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: CONNACK - Acknowledgement connection response. + * + */ +struct mqtt_response_connack { + /** + * @brief Allows client and broker to check if they have a consistent view about whether there is + * already a stored session state. + */ + uint8_t session_present_flag; + + /** + * @brief The return code of the connection request. + * + * @see MQTTConnackReturnCode + */ + enum MQTTConnackReturnCode return_code; +}; + +/** + * @brief A publish packet received from the broker. + * @ingroup unpackers + * + * A publish packet is received from the broker when a client publishes to a topic that the + * \em {local client} is subscribed to. + * + * @see + * MQTT v3.1.1: PUBLISH - Publish Message. + * + */ +struct mqtt_response_publish { + /** + * @brief The DUP flag. DUP flag is 0 if its the first attempt to send this publish packet. A DUP flag + * of 1 means that this might be a re-delivery of the packet. + */ + uint8_t dup_flag; + + /** + * @brief The quality of service level. + * + * @see + * MQTT v3.1.1: QoS Definitions + * + */ + uint8_t qos_level; + + /** @brief The retain flag of this publish message. */ + uint8_t retain_flag; + + /** @brief Size of the topic name (number of characters). */ + uint16_t topic_name_size; + + /** + * @brief The topic name. + * @note topic_name is not null terminated. Therefore topic_name_size must be used to get the + * string length. + */ + const void *topic_name; + + /** @brief The publish message's packet ID. */ + uint16_t packet_id; + + /** @brief The publish message's application message.*/ + const void *application_message; + + /** @brief The size of the application message in bytes. */ + size_t application_message_size; +}; + +/** + * @brief A publish acknowledgement for messages that were published with QoS level 1. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: PUBACK - Publish Acknowledgement. + * + * + */ +struct mqtt_response_puback { + /** @brief The published messages packet ID. */ + uint16_t packet_id; +}; + +/** + * @brief The response packet to a PUBLISH packet with QoS level 2. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: PUBREC - Publish Received. + * + * + */ +struct mqtt_response_pubrec { + /** @brief The published messages packet ID. */ + uint16_t packet_id; +}; + +/** + * @brief The response to a PUBREC packet. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: PUBREL - Publish Release. + * + * + */ +struct mqtt_response_pubrel { + /** @brief The published messages packet ID. */ + uint16_t packet_id; +}; + +/** + * @brief The response to a PUBREL packet. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: PUBCOMP - Publish Complete. + * + * + */ +struct mqtt_response_pubcomp { + /** T@brief he published messages packet ID. */ + uint16_t packet_id; +}; + +/** + * @brief An enumeration of subscription acknowledgement return codes. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: SUBACK Return Codes. + * + */ +enum MQTTSubackReturnCodes { + MQTT_SUBACK_SUCCESS_MAX_QOS_0 = 0u, + MQTT_SUBACK_SUCCESS_MAX_QOS_1 = 1u, + MQTT_SUBACK_SUCCESS_MAX_QOS_2 = 2u, + MQTT_SUBACK_FAILURE = 128u +}; + +/** + * @brief The response to a subscription request. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: SUBACK - Subscription Acknowledgement. + * + */ +struct mqtt_response_suback { + /** @brief The published messages packet ID. */ + uint16_t packet_id; + + /** + * Array of return codes corresponding to the requested subscribe topics. + * + * @see MQTTSubackReturnCodes + */ + const uint8_t *return_codes; + + /** The number of return codes. */ + size_t num_return_codes; +}; + +/** + * @brief The brokers response to a UNSUBSCRIBE request. + * @ingroup unpackers + * + * @see + * MQTT v3.1.1: UNSUBACK - Unsubscribe Acknowledgement. + * + */ +struct mqtt_response_unsuback { + /** @brief The published messages packet ID. */ + uint16_t packet_id; +}; + +/** + * @brief The response to a ping request. + * @ingroup unpackers + * + * @note This response contains no members. + * + * @see + * MQTT v3.1.1: PINGRESP - Ping Response. + * + */ +struct mqtt_response_pingresp { + int dummy; +}; + +/** + * @brief A struct used to deserialize/interpret an incoming packet from the broker. + * @ingroup unpackers + */ +struct mqtt_response { + /** @brief The mqtt_fixed_header of the deserialized packet. */ + struct mqtt_fixed_header fixed_header; + + /** + * @brief A union of the possible responses from the broker. + * + * @note The fixed_header contains the control type. This control type corresponds to the + * member of this union that should be accessed. For example if + * fixed_header#control_type == \c MQTT_CONTROL_PUBLISH then + * decoded#publish should be accessed. + */ + union { + struct mqtt_response_connack connack; + struct mqtt_response_publish publish; + struct mqtt_response_puback puback; + struct mqtt_response_pubrec pubrec; + struct mqtt_response_pubrel pubrel; + struct mqtt_response_pubcomp pubcomp; + struct mqtt_response_suback suback; + struct mqtt_response_unsuback unsuback; + struct mqtt_response_pingresp pingresp; + } decoded; +}; + +/** + * @brief Deserialize the contents of \p buf into an mqtt_fixed_header object. + * @ingroup unpackers + * + * @note This function performs complete error checking and a positive return value + * means the entire mqtt_response can be deserialized from \p buf. + * + * @param[out] response the response who's \ref mqtt_response.fixed_header will be initialized. + * @param[in] buf the buffer. + * @param[in] bufsz the total number of bytes in the buffer. + * + * @returns The number of bytes that were consumed, or 0 if the buffer does not contain enough + * bytes to parse the packet, or a negative value if there was a protocol violation. + */ +ssize_t mqtt_unpack_fixed_header(struct mqtt_response *response, const uint8_t *buf, size_t bufsz); + +/** + * @brief Deserialize a CONNACK response from \p buf. + * @ingroup unpackers + * + * @pre \ref mqtt_unpack_fixed_header must have returned a positive value and the control packet type + * must be \c MQTT_CONTROL_CONNACK. + * + * @param[out] mqtt_response the mqtt_response that will be initialized. + * @param[in] buf the buffer that contains the variable header and payload of the packet. The + * first byte of \p buf should be the first byte of the variable header. + * + * @relates mqtt_response_connack + * + * @returns The number of bytes that were consumed, or 0 if the buffer does not contain enough + * bytes to parse the packet, or a negative value if there was a protocol violation. + */ +ssize_t mqtt_unpack_connack_response(struct mqtt_response *mqtt_response, const uint8_t *buf); + +/** + * @brief Deserialize a publish response from \p buf. + * @ingroup unpackers + * + * @pre \ref mqtt_unpack_fixed_header must have returned a positive value and the mqtt_response must + * have a control type of \c MQTT_CONTROL_PUBLISH. + * + * @param[out] mqtt_response the response that is initialized from the contents of \p buf. + * @param[in] buf the buffer with the incoming data. + * + * @relates mqtt_response_publish + * + * @returns The number of bytes that were consumed, or 0 if the buffer does not contain enough + * bytes to parse the packet, or a negative value if there was a protocol violation. + */ +ssize_t mqtt_unpack_publish_response(struct mqtt_response *mqtt_response, const uint8_t *buf); + +/** + * @brief Deserialize a PUBACK/PUBREC/PUBREL/PUBCOMP packet from \p buf. + * @ingroup unpackers + * + * @pre \ref mqtt_unpack_fixed_header must have returned a positive value and the mqtt_response must + * have a control type of \c MQTT_CONTROL_PUBACK, \c MQTT_CONTROL_PUBREC, \c MQTT_CONTROL_PUBREL + * or \c MQTT_CONTROL_PUBCOMP. + * + * @param[out] mqtt_response the response that is initialized from the contents of \p buf. + * @param[in] buf the buffer with the incoming data. + * + * @relates mqtt_response_puback mqtt_response_pubrec mqtt_response_pubrel mqtt_response_pubcomp + * + * @returns The number of bytes that were consumed, or 0 if the buffer does not contain enough + * bytes to parse the packet, or a negative value if there was a protocol violation. + */ +ssize_t mqtt_unpack_pubxxx_response(struct mqtt_response *mqtt_response, const uint8_t *buf); + +/** + * @brief Deserialize a SUBACK packet from \p buf. + * @ingroup unpacker + * + * @pre \ref mqtt_unpack_fixed_header must have returned a positive value and the mqtt_response must + * have a control type of \c MQTT_CONTROL_SUBACK. + * + * @param[out] mqtt_response the response that is initialized from the contents of \p buf. + * @param[in] buf the buffer with the incoming data. + * + * @relates mqtt_response_suback + * + * @returns The number of bytes that were consumed, or 0 if the buffer does not contain enough + * bytes to parse the packet, or a negative value if there was a protocol violation. + */ +ssize_t mqtt_unpack_suback_response(struct mqtt_response *mqtt_response, const uint8_t *buf); + +/** + * @brief Deserialize an UNSUBACK packet from \p buf. + * @ingroup unpacker + * + * @pre \ref mqtt_unpack_fixed_header must have returned a positive value and the mqtt_response must + * have a control type of \c MQTT_CONTROL_UNSUBACK. + * + * @param[out] mqtt_response the response that is initialized from the contents of \p buf. + * @param[in] buf the buffer with the incoming data. + * + * @relates mqtt_response_unsuback + * + * @returns The number of bytes that were consumed, or 0 if the buffer does not contain enough + * bytes to parse the packet, or a negative value if there was a protocol violation. + */ +ssize_t mqtt_unpack_unsuback_response(struct mqtt_response *mqtt_response, const uint8_t *buf); + +/** + * @brief Deserialize a packet from the broker. + * @ingroup unpackers + * + * @param[out] response the mqtt_response that will be initialize from \p buf. + * @param[in] buf the incoming data buffer. + * @param[in] bufsz the number of bytes available in the buffer. + * + * @relates mqtt_response + * + * @returns The number of bytes consumed on success, zero \p buf does not contain enough bytes + * to deserialize the packet, a negative value if a protocol violation was encountered. + */ +ssize_t mqtt_unpack_response(struct mqtt_response *response, const uint8_t *buf, size_t bufsz); + +/* REQUESTS */ + +/** +* @brief Serialize an mqtt_fixed_header and write it to \p buf. +* @ingroup packers +* +* @note This function performs complete error checking and a positive return value +* guarantees the entire packet will fit into the given buffer. +* +* @param[out] buf the buffer to write to. +* @param[in] bufsz the maximum number of bytes that can be put in to \p buf. +* @param[in] fixed_header the fixed header that will be serialized. +* +* @returns The number of bytes written to \p buf, or 0 if \p buf is too small, or a +* negative value if there was a protocol violation. +*/ +ssize_t mqtt_pack_fixed_header(uint8_t *buf, size_t bufsz, const struct mqtt_fixed_header *fixed_header); + +/** + * @brief An enumeration of CONNECT packet flags. + * @ingroup packers + * + * @see + * MQTT v3.1.1: CONNECT Variable Header. + * + */ +enum MQTTConnectFlags { + MQTT_CONNECT_RESERVED = 1u, + MQTT_CONNECT_CLEAN_SESSION = 2u, + MQTT_CONNECT_WILL_FLAG = 4u, + MQTT_CONNECT_WILL_QOS_0 = (0u & 0x03) << 3, + MQTT_CONNECT_WILL_QOS_1 = (1u & 0x03) << 3, + MQTT_CONNECT_WILL_QOS_2 = (2u & 0x03) << 3, + MQTT_CONNECT_WILL_RETAIN = 32u, + MQTT_CONNECT_PASSWORD = 64u, + MQTT_CONNECT_USER_NAME = 128u +}; + +/** + * @brief Serialize a connection request into a buffer. + * @ingroup packers + * + * @param[out] buf the buffer to pack the connection request packet into. + * @param[in] bufsz the number of bytes left in \p buf. + * @param[in] client_id the ID that identifies the local client. \p client_id can be NULL or an empty + * string for Anonymous clients. + * @param[in] will_topic the topic under which the local client's will message will be published. + * Set to \c NULL for no will message. If \p will_topic is not \c NULL a + * \p will_message must also be provided. + * @param[in] will_message the will message to be published upon a unsuccessful disconnection of + * the local client. Set to \c NULL if \p will_topic is \c NULL. + * \p will_message must \em not be \c NULL if \p will_topic is not + * \c NULL. + * @param[in] will_message_size The size of \p will_message in bytes. + * @param[in] user_name the username to be used to connect to the broker with. Set to \c NULL if + * no username is required. + * @param[in] password the password to be used to connect to the broker with. Set to \c NULL if + * no password is required. + * @param[in] connect_flags additional MQTTConnectFlags to be set. The only flags that need to be + * set manually are \c MQTT_CONNECT_CLEAN_SESSION, + * \c MQTT_CONNECT_WILL_QOS_X (for \c X ∈ {0, 1, 2}), and + * \c MQTT_CONNECT_WILL_RETAIN. Set to 0 if no additional flags are + * required. + * @param[in] keep_alive the keep alive time in seconds. It is the responsibility of the clinet + * to ensure packets are sent to the server \em {at least} this frequently. + * + * @note If there is a \p will_topic and no additional \p connect_flags are given, then by + * default \p will_message will be published at QoS level 0. + * + * @see + * MQTT v3.1.1: CONNECT - Client Requests a Connection to a Server. + * + * + * @returns The number of bytes put into \p buf, 0 if \p buf is too small to fit the CONNECT + * packet, a negative value if there was a protocol violation. + */ +ssize_t mqtt_pack_connection_request(uint8_t *buf, size_t bufsz, + const char *client_id, + const char *will_topic, + const void *will_message, + size_t will_message_size, + const char *user_name, + const char *password, + uint8_t connect_flags, + uint16_t keep_alive); + +/** + * @brief An enumeration of the PUBLISH flags. + * @ingroup packers + * + * @see + * MQTT v3.1.1: PUBLISH - Publish Message. + * + */ +enum MQTTPublishFlags { + MQTT_PUBLISH_DUP = 8u, + MQTT_PUBLISH_QOS_0 = ((0u << 1) & 0x06), + MQTT_PUBLISH_QOS_1 = ((1u << 1) & 0x06), + MQTT_PUBLISH_QOS_2 = ((2u << 1) & 0x06), + MQTT_PUBLISH_QOS_MASK = ((3u << 1) & 0x06), + MQTT_PUBLISH_RETAIN = 0x01 +}; + +/** + * @brief Serialize a PUBLISH request and put it in \p buf. + * @ingroup packers + * + * @param[out] buf the buffer to put the PUBLISH packet in. + * @param[in] bufsz the maximum number of bytes that can be put into \p buf. + * @param[in] topic_name the topic to publish \p application_message under. + * @param[in] packet_id this packets packet ID. + * @param[in] application_message the application message to be published. + * @param[in] application_message_size the size of \p application_message in bytes. + * @param[in] publish_flags The flags to publish \p application_message with. These include + * the \c MQTT_PUBLISH_DUP flag, \c MQTT_PUBLISH_QOS_X (\c X ∈ + * {0, 1, 2}), and \c MQTT_PUBLISH_RETAIN flag. + * + * @note The default QoS is level 0. + * + * @see + * MQTT v3.1.1: PUBLISH - Publish Message. + * + * + * @returns The number of bytes put into \p buf, 0 if \p buf is too small to fit the PUBLISH + * packet, a negative value if there was a protocol violation. + */ +ssize_t mqtt_pack_publish_request(uint8_t *buf, size_t bufsz, + const char *topic_name, + uint16_t packet_id, + const void *application_message, + size_t application_message_size, + uint8_t publish_flags); + +/** + * @brief Serialize a PUBACK, PUBREC, PUBREL, or PUBCOMP packet and put it in \p buf. + * @ingroup packers + * + * @param[out] buf the buffer to put the PUBXXX packet in. + * @param[in] bufsz the maximum number of bytes that can be put into \p buf. + * @param[in] control_type the type of packet. Must be one of: \c MQTT_CONTROL_PUBACK, + * \c MQTT_CONTROL_PUBREC, \c MQTT_CONTROL_PUBREL, + * or \c MQTT_CONTROL_PUBCOMP. + * @param[in] packet_id the packet ID of the packet being acknowledged. + * + * + * @see + * MQTT v3.1.1: PUBACK - Publish Acknowledgement. + * + * @see + * MQTT v3.1.1: PUBREC - Publish Received. + * + * @see + * MQTT v3.1.1: PUBREL - Publish Released. + * + * @see + * MQTT v3.1.1: PUBCOMP - Publish Complete. + * + * + * @returns The number of bytes put into \p buf, 0 if \p buf is too small to fit the PUBXXX + * packet, a negative value if there was a protocol violation. + */ +ssize_t mqtt_pack_pubxxx_request(uint8_t *buf, size_t bufsz, + enum MQTTControlPacketType control_type, + uint16_t packet_id); + +/** + * @brief The maximum number topics that can be subscribed to in a single call to + * mqtt_pack_subscribe_request. + * @ingroup packers + * + * @see mqtt_pack_subscribe_request + */ +#define MQTT_SUBSCRIBE_REQUEST_MAX_NUM_TOPICS 8 + +/** + * @brief Serialize a SUBSCRIBE packet and put it in \p buf. + * @ingroup packers + * + * @param[out] buf the buffer to put the SUBSCRIBE packet in. + * @param[in] bufsz the maximum number of bytes that can be put into \p buf. + * @param[in] packet_id the packet ID to be used. + * @param[in] ... \c NULL terminated list of (\c {const char *topic_name}, \c {int max_qos_level}) + * pairs. + * + * @note The variadic arguments, \p ..., \em must be followed by a \c NULL. For example: + * @code + * ssize_t n = mqtt_pack_subscribe_request(buf, bufsz, 1234, "topic_1", 0, "topic_2", 2, NULL); + * @endcode + * + * @see + * MQTT v3.1.1: SUBSCRIBE - Subscribe to Topics. + * + * + * @returns The number of bytes put into \p buf, 0 if \p buf is too small to fit the SUBSCRIBE + * packet, a negative value if there was a protocol violation. + */ +ssize_t mqtt_pack_subscribe_request(uint8_t *buf, size_t bufsz, + unsigned int packet_id, + ...); /* null terminated */ + +/** + * @brief The maximum number topics that can be subscribed to in a single call to + * mqtt_pack_unsubscribe_request. + * @ingroup packers + * + * @see mqtt_pack_unsubscribe_request + */ +#define MQTT_UNSUBSCRIBE_REQUEST_MAX_NUM_TOPICS 8 + +/** + * @brief Serialize a UNSUBSCRIBE packet and put it in \p buf. + * @ingroup packers + * + * @param[out] buf the buffer to put the UNSUBSCRIBE packet in. + * @param[in] bufsz the maximum number of bytes that can be put into \p buf. + * @param[in] packet_id the packet ID to be used. + * @param[in] ... \c NULL terminated list of \c {const char *topic_name}'s to unsubscribe from. + * + * @note The variadic arguments, \p ..., \em must be followed by a \c NULL. For example: + * @code + * ssize_t n = mqtt_pack_unsubscribe_request(buf, bufsz, 4321, "topic_1", "topic_2", NULL); + * @endcode + * + * @see + * MQTT v3.1.1: UNSUBSCRIBE - Unsubscribe from Topics. + * + * + * @returns The number of bytes put into \p buf, 0 if \p buf is too small to fit the UNSUBSCRIBE + * packet, a negative value if there was a protocol violation. + */ +ssize_t mqtt_pack_unsubscribe_request(uint8_t *buf, size_t bufsz, + unsigned int packet_id, + ...); /* null terminated */ + +/** + * @brief Serialize a PINGREQ and put it into \p buf. + * @ingroup packers + * + * @param[out] buf the buffer to put the PINGREQ packet in. + * @param[in] bufsz the maximum number of bytes that can be put into \p buf. + * + * @see + * MQTT v3.1.1: PINGREQ - Ping Request. + * + * + * @returns The number of bytes put into \p buf, 0 if \p buf is too small to fit the PINGREQ + * packet, a negative value if there was a protocol violation. + */ +ssize_t mqtt_pack_ping_request(uint8_t *buf, size_t bufsz); + +/** + * @brief Serialize a DISCONNECT and put it into \p buf. + * @ingroup packers + * + * @param[out] buf the buffer to put the DISCONNECT packet in. + * @param[in] bufsz the maximum number of bytes that can be put into \p buf. + * + * @see + * MQTT v3.1.1: DISCONNECT - Disconnect Notification. + * + * + * @returns The number of bytes put into \p buf, 0 if \p buf is too small to fit the DISCONNECT + * packet, a negative value if there was a protocol violation. + */ +ssize_t mqtt_pack_disconnect(uint8_t *buf, size_t bufsz); + + +/** + * @brief An enumeration of queued message states. + * @ingroup details + */ +enum MQTTQueuedMessageState { + MQTT_QUEUED_UNSENT, + MQTT_QUEUED_AWAITING_ACK, + MQTT_QUEUED_COMPLETE +}; + +/** + * @brief A message in a mqtt_message_queue. + * @ingroup details + */ +struct mqtt_queued_message { + /** @brief A pointer to the start of the message. */ + uint8_t *start; + + /** @brief The number of bytes in the message. */ + size_t size; + + + /** @brief The state of the message. */ + enum MQTTQueuedMessageState state; + + /** + * @brief The time at which the message was sent.. + * + * @note A timeout will only occur if the message is in + * the MQTT_QUEUED_AWAITING_ACK \c state. + */ + mqtt_pal_time_t time_sent; + + /** + * @brief The control type of the message. + */ + enum MQTTControlPacketType control_type; + + /** + * @brief The packet id of the message. + * + * @note This field is only used if the associate \c control_type has a + * \c packet_id field. + */ + uint16_t packet_id; +}; + +/** + * @brief A message queue. + * @ingroup details + * + * @note This struct is used internally to manage sending messages. + * @note The only members the user should use are \c curr and \c curr_sz. + */ +struct mqtt_message_queue { + /** + * @brief The start of the message queue's memory block. + * + * @warning This member should \em not be manually changed. + */ + void *mem_start; + + /** @brief The end of the message queue's memory block. */ + void *mem_end; + + /** + * @brief A pointer to the position in the buffer you can pack bytes at. + * + * @note Immediately after packing bytes at \c curr you \em must call + * mqtt_mq_register. + */ + uint8_t *curr; + + /** + * @brief The number of bytes that can be written to \c curr. + * + * @note curr_sz will decrease by more than the number of bytes you write to + * \c curr. This is because the mqtt_queued_message structs share the + * same memory (and thus, a mqtt_queued_message must be allocated in + * the message queue's memory whenever a new message is registered). + */ + size_t curr_sz; + + /** + * @brief The tail of the array of mqtt_queued_messages's. + * + * @note This member should not be used manually. + */ + struct mqtt_queued_message *queue_tail; +}; + +/** + * @brief Initialize a message queue. + * @ingroup details + * + * @param[out] mq The message queue to initialize. + * @param[in] buf The buffer for this message queue. + * @param[in] bufsz The number of bytes in the buffer. + * + * @relates mqtt_message_queue + */ +void mqtt_mq_init(struct mqtt_message_queue *mq, void *buf, size_t bufsz); + +/** + * @brief Clear as many messages from the front of the queue as possible. + * @ingroup details + * + * @note Calls to this function are the \em only way to remove messages from the queue. + * + * @param mq The message queue. + * + * @relates mqtt_message_queue + */ +void mqtt_mq_clean(struct mqtt_message_queue *mq); + +/** + * @brief Register a message that was just added to the buffer. + * @ingroup details + * + * @note This function should be called immediately following a call to a packer function + * that returned a positive value. The positive value (number of bytes packed) should + * be passed to this function. + * + * @param mq The message queue. + * @param[in] nbytes The number of bytes that were just packed. + * + * @note This function will step mqtt_message_queue::curr and update mqtt_message_queue::curr_sz. + * @relates mqtt_message_queue + * + * @returns The newly added struct mqtt_queued_message. + */ +struct mqtt_queued_message *mqtt_mq_register(struct mqtt_message_queue *mq, size_t nbytes); + +/** + * @brief Find a message in the message queue. + * @ingroup details + * + * @param mq The message queue. + * @param[in] control_type The control type of the message you want to find. + * @param[in] packet_id The packet ID of the message you want to find. Set to \c NULL if you + * don't want to specify a packet ID. + * + * @relates mqtt_message_queue + * @returns The found message. \c NULL if the message was not found. + */ +struct mqtt_queued_message *mqtt_mq_find(const struct mqtt_message_queue *mq, enum MQTTControlPacketType control_type, const uint16_t *packet_id); + +/** + * @brief Returns the mqtt_queued_message at \p index. + * @ingroup details + * + * @param mq_ptr A pointer to the message queue. + * @param index The index of the message. + * + * @returns The mqtt_queued_message at \p index. + */ +#define mqtt_mq_get(mq_ptr, index) (((struct mqtt_queued_message*) ((mq_ptr)->mem_end)) - 1 - index) + +/** + * @brief Returns the number of messages in the message queue, \p mq_ptr. + * @ingroup details + */ +#define mqtt_mq_length(mq_ptr) (((struct mqtt_queued_message*) ((mq_ptr)->mem_end)) - (mq_ptr)->queue_tail) + +/** + * @brief Used internally to recalculate the \c curr_sz. + * @ingroup details + */ +#define mqtt_mq_currsz(mq_ptr) (((mq_ptr)->curr >= (uint8_t*) ((mq_ptr)->queue_tail - 1)) ? 0 : ((uint8_t*) ((mq_ptr)->queue_tail - 1)) - (mq_ptr)->curr) + +/* CLIENT */ + +/** + * @brief An MQTT client. + * @ingroup details + * + * @note All members can be manipulated via the related functions. + */ +struct mqtt_client { + /** @brief The socket connecting to the MQTT broker. */ + mqtt_pal_socket_handle socketfd; + + /** @brief The LFSR state used to generate packet ID's. */ + uint16_t pid_lfsr; + + /** @brief The keep-alive time in seconds. */ + uint16_t keep_alive; + + /** + * @brief A counter counting pings that have been sent to keep the connection alive. + * @see keep_alive + */ + int number_of_keep_alives; + + /** + * @brief The current sent offset. + * + * This is used to allow partial send commands. + */ + size_t send_offset; + + /** + * @brief The timestamp of the last message sent to the buffer. + * + * This is used to detect the need for keep-alive pings. + * + * @see keep_alive + */ + mqtt_pal_time_t time_of_last_send; + + /** + * @brief The error state of the client. + * + * error should be MQTT_OK for the entirety of the connection. + * + * @note The error state will be MQTT_ERROR_CONNECT_NOT_CALLED until + * you call mqtt_connect. + */ + enum MQTTErrors error; + + /** + * @brief The timeout period in seconds. + * + * If the broker doesn't return an ACK within response_timeout seconds a timeout + * will occur and the message will be retransmitted. + * + * @note The default value is 30 [seconds] but you can change it at any time. + */ + int response_timeout; + + /** @brief A counter counting the number of timeouts that have occurred. */ + int number_of_timeouts; + + /** + * @brief Approximately much time it has typically taken to receive responses from the + * broker. + * + * @note This is tracked using a exponential-averaging. + */ + float typical_response_time; + + /** + * @brief The callback that is called whenever a publish is received from the broker. + * + * Any topics that you have subscribed to will be returned from the broker as + * mqtt_response_publish messages. All the publishes received from the broker will + * be passed to this function. + * + * @note A pointer to publish_response_callback_state is always passed to the callback. + * Use publish_response_callback_state to keep track of any state information you + * need. + */ + void (*publish_response_callback)(void **state, struct mqtt_response_publish *publish); + + /** + * @brief A pointer to any publish_response_callback state information you need. + * + * @note A pointer to this pointer will always be publish_response_callback upon + * receiving a publish message from the broker. + */ + void *publish_response_callback_state; + + /** + * @brief A user-specified callback, triggered on each \ref mqtt_sync, allowing + * the user to perform state inspections (and custom socket error detection) + * on the client. + * + * This callback is triggered on each call to \ref mqtt_sync. If it returns MQTT_OK + * then \ref mqtt_sync will continue normally (performing reads and writes). If it + * returns an error then \ref mqtt_sync will not call reads and writes. + * + * This callback can be used to perform custom error detection, namely platform + * specific socket error detection, and force the client into an error state. + * + * This member is always initialized to NULL but it can be manually set at any + * time. + */ + enum MQTTErrors(*inspector_callback)(struct mqtt_client *); + + /** + * @brief A callback that is called whenever the client is in an error state. + * + * This callback is responsible for: application level error handling, closing + * previous sockets, and reestabilishing the connection to the broker and + * session configurations (i.e. subscriptions). + */ + void (*reconnect_callback)(struct mqtt_client *, void **); + + /** + * @brief A pointer to some state. A pointer to this member is passed to + * \ref mqtt_client.reconnect_callback. + */ + void *reconnect_state; + + /** + * @brief The buffer where ingress data is temporarily stored. + */ + struct { + /** @brief The start of the receive buffer's memory. */ + uint8_t *mem_start; + + /** @brief The size of the receive buffer's memory. */ + size_t mem_size; + + /** @brief A pointer to the next writable location in the receive buffer. */ + uint8_t *curr; + + /** @brief The number of bytes that are still writable at curr. */ + size_t curr_sz; + } recv_buffer; + + /** + * @brief A variable passed to support thread-safety. + * + * A pointer to this variable is passed to \c MQTT_PAL_MUTEX_LOCK, and + * \c MQTT_PAL_MUTEX_UNLOCK. + */ + mqtt_pal_mutex_t mutex; + + /** @brief The sending message queue. */ + struct mqtt_message_queue mq; +}; + +/** + * @brief Generate a new next packet ID. + * @ingroup details + * + * Packet ID's are generated using a max-length LFSR. + * + * @param client The MQTT client. + * + * @returns The new packet ID that should be used. + */ +uint16_t __mqtt_next_pid(struct mqtt_client *client); + +/** + * @brief Handles egress client traffic. + * @ingroup details + * + * @param client The MQTT client. + * + * @returns MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +ssize_t __mqtt_send(struct mqtt_client *client); + +/** + * @brief Handles ingress client traffic. + * @ingroup details + * + * @param client The MQTT client. + * + * @returns MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +ssize_t __mqtt_recv(struct mqtt_client *client); + +/** + * @brief Function that does the actual sending and receiving of + * traffic from the network. + * @ingroup api + * + * All the other functions in the @ref api simply stage messages for + * being sent to the broker. This function does the actual sending of + * those messages. Additionally this function receives traffic (responses and + * acknowledgements) from the broker and responds to that traffic accordingly. + * Lastly this function also calls the \c publish_response_callback when + * any \c MQTT_CONTROL_PUBLISH messages are received. + * + * @pre mqtt_init must have been called. + * + * @param[in,out] client The MQTT client. + * + * @attention It is the responsibility of the application programmer to + * call this function periodically. All functions in the @ref api are + * thread-safe so it is perfectly reasonable to have a thread dedicated + * to calling this function every 200 ms or so. MQTT-C can be used in single + * threaded application though by simply calling this functino periodically + * inside your main thread. See @ref simple_publisher.c and @ref simple_subscriber.c + * for examples (specifically the \c client_refresher functions). + * + * @returns MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_sync(struct mqtt_client *client); + +/** + * @brief Initializes an MQTT client. + * @ingroup api + * + * This function \em must be called before any other API function calls. + * + * @pre None. + * + * @param[out] client The MQTT client. + * @param[in] sockfd The socket file descriptor (or equivalent socket handle, e.g. BIO pointer + * for OpenSSL sockets) connected to the MQTT broker. + * @param[in] sendbuf A buffer that will be used for sending messages to the broker. + * @param[in] sendbufsz The size of \p sendbuf in bytes. + * @param[in] recvbuf A buffer that will be used for receiving messages from the broker. + * @param[in] recvbufsz The size of \p recvbuf in bytes. + * @param[in] publish_response_callback The callback to call whenever application messages + * are received from the broker. + * + * @post mqtt_connect must be called. + * + * @note \p sockfd is a non-blocking TCP connection. + * @note If \p sendbuf fills up completely during runtime a \c MQTT_ERROR_SEND_BUFFER_IS_FULL + * error will be set. Similarly if \p recvbuf is ever to small to receive a message from + * the broker an MQTT_ERROR_RECV_BUFFER_TOO_SMALL error will be set. + * @note A pointer to \ref mqtt_client.publish_response_callback_state is always passed as the + * \c state argument to \p publish_response_callback. Note that the second argument is + * the mqtt_response_publish that was received from the broker. + * + * @attention Only initialize an MQTT client once (i.e. don't call \ref mqtt_init or + * \ref mqtt_init_reconnect more than once per client). + * @attention \p sendbuf internally mapped to client's message-to-send queue that actively uses + * pointer access. In the case of unaligned \p sendbuf, that may lead to + * Segmentation/Hard/Memory Faults on systems that do not support unaligned pointer + * access (e.g. ARMv6, ARMv7-M). To avoid that, you may use the following technique: + * \code{.c} + * // example for ARMv7-M that requires pointers to be word aligned (4 byte boundary) + * static unsigned char mqtt_tx_buffer[MAX_TX_BUFFER_SIZE] __attribute__((aligned(4))); + * static unsigned char mqtt_rx_buffer[MAX_RX_BUFFER_SIZE]; + * // ... + * int main(void) { + * // ... + * mqtt_init(p_client, p_client->socketfd, mqtt_tx_buffer, sizeof mqtt_tx_buffer, mqtt_rx_buffer, + * sizeof mqtt_rx_buffer, message_callback); + * // ... + * } + * \endcode + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_init(struct mqtt_client *client, + mqtt_pal_socket_handle sockfd, + uint8_t *sendbuf, size_t sendbufsz, + uint8_t *recvbuf, size_t recvbufsz, + void (*publish_response_callback)(void **state, struct mqtt_response_publish *publish)); + +/** + * @brief Initializes an MQTT client and enables automatic reconnections. + * @ingroup api + * + * An alternative to \ref mqtt_init that allows the client to automatically reconnect to the + * broker after an error occurs (e.g. socket error or internal buffer overflows). + * + * This is accomplished by calling the \p reconnect_callback whenever the client enters an error + * state. The job of the \p reconnect_callback is to: (1) perform error handling/logging, + * (2) clean up the old connection (i.e. close client->socketfd), (3) \ref mqtt_reinit the + * client, and (4) reconfigure the MQTT session by calling \ref mqtt_connect followed by other + * API calls such as \ref mqtt_subscribe. + * + * The first argument to the \p reconnect_callback is the client (which will be in an error + * state) and the second argument is a pointer to a void pointer where you can store some state + * information. Internally, MQTT-C calls the reconnect callback like so: + * + * \code + * client->reconnect_callback(client, &client->reconnect_state) + * \endcode + * + * Note that the \p reconnect_callback is also called to setup the initial session. After + * calling \ref mqtt_init_reconnect the client will be in the error state + * \c MQTT_ERROR_INITIAL_RECONNECT. + * + * @pre None. + * + * @param[in,out] client The MQTT client that will be initialized. + * @param[in] reconnect_callback The callback that will be called to connect/reconnect the + * client to the broker and perform application level error handling. + * @param[in] reconnect_state A pointer to some state data for your \p reconnect_callback. + * If your \p reconnect_callback does not require any state information set this + * to NULL. A pointer to the memory address where the client stores a copy of this + * pointer is passed as the second argumnet to \p reconnect_callback. + * @param[in] publish_response_callback The callback to call whenever application messages + * are received from the broker. + * + * @post Call \p reconnect_callback yourself, or call \ref mqtt_sync + * (which will trigger the call to \p reconnect_callback). + * + * @attention Only initialize an MQTT client once (i.e. don't call \ref mqtt_init or + * \ref mqtt_init_reconnect more than once per client). + * + */ +void mqtt_init_reconnect(struct mqtt_client *client, + void (*reconnect_callback)(struct mqtt_client *client, void **state), + void *reconnect_state, + void (*publish_response_callback)(void **state, struct mqtt_response_publish *publish)); + +/** + * @brief Safely assign/reassign a socket and buffers to an new/existing client. + * @ingroup api + * + * This function also clears the \p client error state. Upon exiting this function + * \c client->error will be \c MQTT_ERROR_CONNECT_NOT_CALLED (which will be cleared) + * as soon as \ref mqtt_connect is called. + * + * @pre This function must be called BEFORE \ref mqtt_connect. + * + * @param[in,out] client The MQTT client. + * @param[in] socketfd The new socket connected to the broker. + * @param[in] sendbuf The buffer that will be used to buffer egress traffic to the broker. + * @param[in] sendbufsz The size of \p sendbuf in bytes. + * @param[in] recvbuf The buffer that will be used to buffer ingress traffic from the broker. + * @param[in] recvbufsz The size of \p recvbuf in bytes. + * + * @post Call \ref mqtt_connect. + * + * @attention This function should be used in conjunction with clients that have been + * initialzed with \ref mqtt_init_reconnect. + */ +void mqtt_reinit(struct mqtt_client *client, + mqtt_pal_socket_handle socketfd, + uint8_t *sendbuf, size_t sendbufsz, + uint8_t *recvbuf, size_t recvbufsz); + +/** + * @brief Establishes a session with the MQTT broker. + * @ingroup api + * + * @pre mqtt_init must have been called. + * + * @param[in,out] client The MQTT client. + * @param[in] client_id The unique name identifying the client. (or NULL) + * @param[in] will_topic The topic name of client's \p will_message. If no will message is + * desired set to \c NULL. + * @param[in] will_message The application message (data) to be published in the event the + * client ungracefully disconnects. Set to \c NULL if \p will_topic is \c NULL. + * @param[in] will_message_size The size of \p will_message in bytes. + * @param[in] user_name The username to use when establishing the session with the MQTT broker. + * Set to \c NULL if a username is not required. + * @param[in] password The password to use when establishing the session with the MQTT broker. + * Set to \c NULL if a password is not required. + * @param[in] connect_flags Additional \ref MQTTConnectFlags to use when establishing the connection. + * These flags are for forcing the session to start clean, + * \c MQTT_CONNECT_CLEAN_SESSION, the QOS level to publish the \p will_message with + * (provided \c will_message != \c NULL), MQTT_CONNECT_WILL_QOS_[0,1,2], and whether + * or not the broker should retain the \c will_message, MQTT_CONNECT_WILL_RETAIN. + * @param[in] keep_alive The keep-alive time in seconds. A reasonable value for this is 400 [seconds]. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_connect(struct mqtt_client *client, + const char *client_id, + const char *will_topic, + const void *will_message, + size_t will_message_size, + const char *user_name, + const char *password, + uint8_t connect_flags, + uint16_t keep_alive); + +/* + todo: will_message should be a void* +*/ + +/** + * @brief Publish an application message. + * @ingroup api + * + * Publishes an application message to the MQTT broker. + * + * @pre mqtt_connect must have been called. + * + * @param[in,out] client The MQTT client. + * @param[in] topic_name The name of the topic. + * @param[in] application_message The data to be published. + * @param[in] application_message_size The size of \p application_message in bytes. + * @param[in] publish_flags \ref MQTTPublishFlags to be used, namely the QOS level to + * publish at (MQTT_PUBLISH_QOS_[0,1,2]) or whether or not the broker should + * retain the publish (MQTT_PUBLISH_RETAIN). + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_publish(struct mqtt_client *client, + const char *topic_name, + const void *application_message, + size_t application_message_size, + uint8_t publish_flags); + +/** + * @brief Acknowledge an ingree publish with QOS==1. + * @ingroup details + * + * @param[in,out] client The MQTT client. + * @param[in] packet_id The packet ID of the ingress publish being acknowledged. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +ssize_t __mqtt_puback(struct mqtt_client *client, uint16_t packet_id); + +/** + * @brief Acknowledge an ingree publish with QOS==2. + * @ingroup details + * + * @param[in,out] client The MQTT client. + * @param[in] packet_id The packet ID of the ingress publish being acknowledged. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +ssize_t __mqtt_pubrec(struct mqtt_client *client, uint16_t packet_id); + +/** + * @brief Acknowledge an ingree PUBREC packet. + * @ingroup details + * + * @param[in,out] client The MQTT client. + * @param[in] packet_id The packet ID of the ingress PUBREC being acknowledged. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +ssize_t __mqtt_pubrel(struct mqtt_client *client, uint16_t packet_id); + +/** + * @brief Acknowledge an ingree PUBREL packet. + * @ingroup details + * + * @param[in,out] client The MQTT client. + * @param[in] packet_id The packet ID of the ingress PUBREL being acknowledged. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +ssize_t __mqtt_pubcomp(struct mqtt_client *client, uint16_t packet_id); + + +/** + * @brief Subscribe to a topic. + * @ingroup api + * + * @pre mqtt_connect must have been called. + * + * @param[in,out] client The MQTT client. + * @param[in] topic_name The name of the topic to subscribe to. + * @param[in] max_qos_level The maximum QOS level with which the broker can send application + * messages for this topic. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_subscribe(struct mqtt_client *client, + const char *topic_name, + int max_qos_level); + +/** + * @brief Unsubscribe from a topic. + * @ingroup api + * + * @pre mqtt_connect must have been called. + * + * @param[in,out] client The MQTT client. + * @param[in] topic_name The name of the topic to unsubscribe from. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_unsubscribe(struct mqtt_client *client, + const char *topic_name); + +/** + * @brief Ping the broker. + * @ingroup api + * + * @pre mqtt_connect must have been called. + * + * @param[in,out] client The MQTT client. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_ping(struct mqtt_client *client); + +/** + * @brief Ping the broker without locking/unlocking the mutex. + * @see mqtt_ping + */ +enum MQTTErrors __mqtt_ping(struct mqtt_client *client); + +/** + * @brief Terminate the session with the MQTT broker. + * @ingroup api + * + * @pre mqtt_connect must have been called. + * + * @param[in,out] client The MQTT client. + * + * @note To re-establish the session, mqtt_connect must be called. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_disconnect(struct mqtt_client *client); + +/** + * @brief Terminate the session with the MQTT broker and prepare to + * reconnect. Client code should call \ref mqtt_sync immediately + * after this call to prevent message loss. + * @ingroup api + * + * @note The user must provide a reconnect callback function for this to + * work as expected. See \r mqtt_client_reconnect. + * + * @pre mqtt_connect must have been called +* + * @param[in,out] client The MQTT client. + * + * @returns \c MQTT_OK upon success, an \ref MQTTErrors otherwise. + */ +enum MQTTErrors mqtt_reconnect(struct mqtt_client *client); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/client/deps/mqtt/mqtt_pal.c b/client/deps/mqtt/mqtt_pal.c new file mode 100644 index 000000000..9ebab98b6 --- /dev/null +++ b/client/deps/mqtt/mqtt_pal.c @@ -0,0 +1,235 @@ +/* +MIT License + +Copyright(c) 2018 Liam Bindle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "mqtt.h" + +/** + * @file + * @brief Implements @ref mqtt_pal_sendall and @ref mqtt_pal_recvall and + * any platform-specific helpers you'd like. + * @cond Doxygen_Suppress + */ + +#if defined(MQTT_USE_CUSTOM_SOCKET_HANDLE) + +/* + * In case of MQTT_USE_CUSTOM_SOCKET_HANDLE, a pal implemantation is + * provided by the user. + */ + +/* Note: Some toolchains complain on an object without symbols */ + +int _mqtt_pal_dummy; + +#else /* defined(MQTT_USE_CUSTOM_SOCKET_HANDLE) */ + +#if defined(MQTT_USE_MBEDTLS) +#include + +ssize_t mqtt_pal_sendall(mqtt_pal_socket_handle fd, const void *buf, size_t len, int flags) { + enum MQTTErrors error = 0; + size_t sent = 0; + while (sent < len) { + int rv = mbedtls_ssl_write(fd, (const unsigned char *)buf + sent, len - sent); + if (rv < 0) { + if (rv == MBEDTLS_ERR_SSL_WANT_READ || + rv == MBEDTLS_ERR_SSL_WANT_WRITE +#if defined(MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) + || rv == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS +#endif +#if defined(MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) + || rv == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS +#endif + ) { + /* should call mbedtls_ssl_write later again */ + break; + } + error = MQTT_ERROR_SOCKET_ERROR; + break; + } + /* + * Note: rv can be 0 here eg. when mbedtls just flushed + * the previous incomplete record. + * + * Note: we never send an empty TLS record. + */ + sent += (size_t) rv; + } + if (sent == 0) { + return error; + } + return (ssize_t)sent; +} + +ssize_t mqtt_pal_recvall(mqtt_pal_socket_handle fd, void *buf, size_t bufsz, int flags) { + const void *const start = buf; + enum MQTTErrors error = 0; + int rv; + do { + rv = mbedtls_ssl_read(fd, (unsigned char *)buf, bufsz); + if (rv == 0) { + /* + * Note: mbedtls_ssl_read returns 0 when the underlying + * transport was closed without CloseNotify. + * + * Raise an error to trigger a reconnect. + */ + error = MQTT_ERROR_SOCKET_ERROR; + break; + } + if (rv < 0) { + if (rv == MBEDTLS_ERR_SSL_WANT_READ || + rv == MBEDTLS_ERR_SSL_WANT_WRITE +#if defined(MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) + || rv == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS +#endif +#if defined(MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) + || rv == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS +#endif + ) { + /* should call mbedtls_ssl_read later again */ + break; + } + /* Note: MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY is handled here. */ + error = MQTT_ERROR_SOCKET_ERROR; + break; + } + buf = (char *)buf + rv; + bufsz -= (unsigned long)rv; + } while (bufsz > 0); + if (buf == start) { + return error; + } + return (const char *)buf - (const char *)start; +} + +#elif defined(__unix__) || defined(__APPLE__) || defined(__NuttX__) + +#include + +ssize_t mqtt_pal_sendall(mqtt_pal_socket_handle fd, const void *buf, size_t len, int flags) { + enum MQTTErrors error = 0; + size_t sent = 0; + while (sent < len) { + ssize_t rv = send(fd, (const char *)buf + sent, len - sent, flags); + if (rv < 0) { + if (errno == EAGAIN) { + /* should call send later again */ + break; + } + error = MQTT_ERROR_SOCKET_ERROR; + break; + } + if (rv == 0) { + /* is this possible? maybe OS bug. */ + error = MQTT_ERROR_SOCKET_ERROR; + break; + } + sent += (size_t) rv; + } + if (sent == 0) { + return error; + } + return (ssize_t)sent; +} + +ssize_t mqtt_pal_recvall(mqtt_pal_socket_handle fd, void *buf, size_t bufsz, int flags) { + const void *const start = buf; + enum MQTTErrors error = 0; + ssize_t rv; + do { + rv = recv(fd, buf, bufsz, flags); + if (rv == 0) { + /* + * recv returns 0 when the socket is (half) closed by the peer. + * + * Raise an error to trigger a reconnect. + */ + error = MQTT_ERROR_SOCKET_ERROR; + break; + } + if (rv < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* should call recv later again */ + break; + } + /* an error occurred that wasn't "nothing to read". */ + error = MQTT_ERROR_SOCKET_ERROR; + break; + } + buf = (char *)buf + rv; + bufsz -= (unsigned long)rv; + } while (bufsz > 0); + if (buf == start) { + return error; + } + return (char *)buf - (const char *)start; +} + +#elif defined(_MSC_VER) || defined(WIN32) + +#include + +ssize_t mqtt_pal_sendall(mqtt_pal_socket_handle fd, const void *buf, size_t len, int flags) { + size_t sent = 0; + while (sent < len) { + ssize_t tmp = send(fd, (char *)buf + sent, len - sent, flags); + if (tmp < 1) { + return MQTT_ERROR_SOCKET_ERROR; + } + sent += (size_t) tmp; + } + return sent; +} + +ssize_t mqtt_pal_recvall(mqtt_pal_socket_handle fd, void *buf, size_t bufsz, int flags) { + const char *const start = buf; + ssize_t rv; + do { + rv = recv(fd, buf, bufsz, flags); + if (rv > 0) { + /* successfully read bytes from the socket */ + buf = (char *)buf + rv; + bufsz -= rv; + } else if (rv < 0) { + int err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) { + /* an error occurred that wasn't "nothing to read". */ + return MQTT_ERROR_SOCKET_ERROR; + } + } + } while (rv > 0 && bufsz > 0); + + return (ssize_t)((char *)buf - start); +} + +#else + +#error No PAL! + +#endif + +#endif /* defined(MQTT_USE_CUSTOM_SOCKET_HANDLE) */ + +/** @endcond */ diff --git a/client/deps/mqtt/mqtt_pal.h b/client/deps/mqtt/mqtt_pal.h new file mode 100644 index 000000000..87b84500b --- /dev/null +++ b/client/deps/mqtt/mqtt_pal.h @@ -0,0 +1,173 @@ +#if !defined(__MQTT_PAL_H__) +#define __MQTT_PAL_H__ + +/* +MIT License + +Copyright(c) 2018 Liam Bindle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * @file + * @brief Includes/supports the types/calls required by the MQTT-C client. + * + * @note This is the \em only file included in mqtt.h, and mqtt.c. It is therefore + * responsible for including/supporting all the required types and calls. + * + * @defgroup pal Platform abstraction layer + * @brief Documentation of the types and calls required to port MQTT-C to a new platform. + * + * mqtt_pal.h is the \em only header file included in mqtt.c. Therefore, to port MQTT-C to a + * new platform the following types, functions, constants, and macros must be defined in + * mqtt_pal.h: + * - Types: + * - \c size_t, \c ssize_t + * - \c uint8_t, \c uint16_t, \c uint32_t + * - \c va_list + * - \c mqtt_pal_time_t : return type of \c MQTT_PAL_TIME() + * - \c mqtt_pal_mutex_t : type of the argument that is passed to \c MQTT_PAL_MUTEX_LOCK and + * \c MQTT_PAL_MUTEX_RELEASE + * - Functions: + * - \c memcpy, \c strlen + * - \c va_start, \c va_arg, \c va_end + * - Constants: + * - \c INT_MIN + * + * Additionally, three macro's are required: + * - \c MQTT_PAL_HTONS(s) : host-to-network endian conversion for uint16_t. + * - \c MQTT_PAL_NTOHS(s) : network-to-host endian conversion for uint16_t. + * - \c MQTT_PAL_TIME() : returns [type: \c mqtt_pal_time_t] current time in seconds. + * - \c MQTT_PAL_MUTEX_LOCK(mtx_pointer) : macro that locks the mutex pointed to by \c mtx_pointer. + * - \c MQTT_PAL_MUTEX_RELEASE(mtx_pointer) : macro that unlocks the mutex pointed to by + * \c mtx_pointer. + * + * Lastly, \ref mqtt_pal_sendall and \ref mqtt_pal_recvall, must be implemented in mqtt_pal.c + * for sending and receiving data using the platforms socket calls. + */ + + +/* UNIX-like platform support */ +#if defined(__unix__) || defined(__APPLE__) || defined(__NuttX__) +#include +#include +#include +#include +#include +#include + +#define MQTT_PAL_HTONS(s) htons(s) +#define MQTT_PAL_NTOHS(s) ntohs(s) + +#define MQTT_PAL_TIME() time(NULL) + +typedef time_t mqtt_pal_time_t; +typedef pthread_mutex_t mqtt_pal_mutex_t; + +#define MQTT_PAL_MUTEX_INIT(mtx_ptr) pthread_mutex_init(mtx_ptr, NULL) +#define MQTT_PAL_MUTEX_LOCK(mtx_ptr) pthread_mutex_lock(mtx_ptr) +#define MQTT_PAL_MUTEX_UNLOCK(mtx_ptr) pthread_mutex_unlock(mtx_ptr) + +#if !defined(MQTT_USE_CUSTOM_SOCKET_HANDLE) +#if defined(MQTT_USE_MBEDTLS) +struct mbedtls_ssl_context; +typedef struct mbedtls_ssl_context *mqtt_pal_socket_handle; +#else +typedef int mqtt_pal_socket_handle; +#endif +#endif + +#elif defined(_MSC_VER) || defined(WIN32) +#include +#include +#include +#include +#include + +typedef SSIZE_T ssize_t; +#define MQTT_PAL_HTONS(s) htons(s) +#define MQTT_PAL_NTOHS(s) ntohs(s) + +#define MQTT_PAL_TIME() time(NULL) + +typedef time_t mqtt_pal_time_t; +typedef CRITICAL_SECTION mqtt_pal_mutex_t; + +#define MQTT_PAL_MUTEX_INIT(mtx_ptr) InitializeCriticalSection(mtx_ptr) +#define MQTT_PAL_MUTEX_LOCK(mtx_ptr) EnterCriticalSection(mtx_ptr) +#define MQTT_PAL_MUTEX_UNLOCK(mtx_ptr) LeaveCriticalSection(mtx_ptr) + + +#if !defined(MQTT_USE_CUSTOM_SOCKET_HANDLE) +typedef SOCKET mqtt_pal_socket_handle; +#endif + +#endif + +/** + * @brief Sends all the bytes in a buffer. + * @ingroup pal + * + * @param[in] fd The file-descriptor (or handle) of the socket. + * @param[in] buf A pointer to the first byte in the buffer to send. + * @param[in] len The number of bytes to send (starting at \p buf). + * @param[in] flags Flags which are passed to the underlying socket. + * + * @returns The number of bytes sent if successful, an \ref MQTTErrors otherwise. + * + * Note about the error handling: + * - On an error, if some bytes have been processed already, + * this function should return the number of bytes successfully + * processed. (partial success) + * - Otherwise, if the error is an equivalent of EAGAIN, return 0. + * - Otherwise, return MQTT_ERROR_SOCKET_ERROR. + */ +ssize_t mqtt_pal_sendall(mqtt_pal_socket_handle fd, const void *buf, size_t len, int flags); + +/** + * @brief Non-blocking receive all the byte available. + * @ingroup pal + * + * @param[in] fd The file-descriptor (or handle) of the socket. + * @param[in] buf A pointer to the receive buffer. + * @param[in] bufsz The max number of bytes that can be put into \p buf. + * @param[in] flags Flags which are passed to the underlying socket. + * + * @returns The number of bytes received if successful, an \ref MQTTErrors otherwise. + * + * Note about the error handling: + * - On an error, if some bytes have been processed already, + * this function should return the number of bytes successfully + * processed. (partial success) + * - Otherwise, if the error is an equivalent of EAGAIN, return 0. + * - Otherwise, return MQTT_ERROR_SOCKET_ERROR. + */ +ssize_t mqtt_pal_recvall(mqtt_pal_socket_handle fd, void *buf, size_t bufsz, int flags); + +#if defined(__cplusplus) +} +#endif + + +#endif diff --git a/client/deps/mqtt/posix_sockets.h b/client/deps/mqtt/posix_sockets.h new file mode 100644 index 000000000..ab13f863d --- /dev/null +++ b/client/deps/mqtt/posix_sockets.h @@ -0,0 +1,94 @@ +#if !defined(__POSIX_SOCKET_TEMPLATE_H__) +#define __POSIX_SOCKET_TEMPLATE_H__ + +#include +#include +#if !defined(WIN32) +#include +#include +#else +#include +#endif +#if defined(__VMS) +#include +#endif +#include +#include + +/* + A template for opening a non-blocking POSIX socket. +*/ +int open_nb_socket(const char *addr, const char *port); + +int open_nb_socket(const char *addr, const char *port) { + + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = AF_UNSPEC; /* IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Must be TCP */ + int sockfd = -1; + int rv; + struct addrinfo *p, *servinfo; + + /* get address information */ + rv = getaddrinfo(addr, port, &hints, &servinfo); + if (rv != 0) { + fprintf(stderr, "Failed to open socket (getaddrinfo): %s\n", gai_strerror(rv)); + return -1; + } + + /* open the first possible socket */ + for (p = servinfo; p != NULL; p = p->ai_next) { + sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (sockfd == -1) continue; + + /* connect to server */ + rv = connect(sockfd, p->ai_addr, p->ai_addrlen); + if (rv == -1) { + close(sockfd); + sockfd = -1; + continue; + } + break; + } + + /* free servinfo */ + freeaddrinfo(servinfo); + + /* make non-blocking */ +#if !defined(WIN32) + if (sockfd != -1) { + fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK); + } +#else + if (sockfd != INVALID_SOCKET) { + int iMode = 1; + ioctlsocket(sockfd, FIONBIO, &iMode); + } +#endif + +#if defined(__VMS) + /* + OpenVMS only partially implements fcntl. It works on file descriptors + but silently fails on socket descriptors. So we need to fall back on + to the older ioctl system to set non-blocking IO + */ + int on = 1; + if (sockfd != -1) { + ioctl(sockfd, FIONBIO, &on); + } +#endif + + /* return the new socket fd */ + return sockfd; +} + +void close_nb_socket(int sockfd); + +void close_nb_socket(int sockfd) { + if (sockfd != -1) { + close(sockfd); + } +} +#endif diff --git a/client/deps/mqtt/readme.md b/client/deps/mqtt/readme.md new file mode 100644 index 000000000..c7e437b7e --- /dev/null +++ b/client/deps/mqtt/readme.md @@ -0,0 +1,15 @@ + +# Information +Source: https://github.com/LiamBindle/MQTT-C +License: MIT +Authors: + +MQTT-C was initially developed as a CMPT 434 (Winter Term, 2018) final project at the University of Saskatchewan by: + + - Liam Bindle + - Demilade Adeoye + + +# about +MQTT-C is an MQTT v3.1.1 client written in C. MQTT is a lightweight publisher-subscriber-based messaging protocol that is commonly used in IoT and networking applications where high-latency and low data-rate links are expected. The purpose of MQTT-C is to provide a portable MQTT client, written in C, for embedded systems and PC's alike. MQTT-C does this by providing a transparent Platform Abstraction Layer (PAL) which makes porting to new platforms easy. MQTT-C is completely thread-safe but can also run perfectly fine on single-threaded systems making MQTT-C well-suited for embedded systems and microcontrollers. Finally, MQTT-C is small; there are only two source files totalling less than 2000 lines. + diff --git a/client/src/cmdmain.c b/client/src/cmdmain.c index 25bf6cce8..9ab88324b 100644 --- a/client/src/cmdmain.c +++ b/client/src/cmdmain.c @@ -48,6 +48,7 @@ #include "commonutil.h" // ARRAYLEN #include "preferences.h" #include "cliparser.h" +#include "cmdmqtt.h" static int CmdHelp(const char *Cmd); @@ -338,6 +339,7 @@ static command_t CommandTable[] = { {"hw", CmdHW, AlwaysAvailable, "{ Hardware commands... }"}, {"lf", CmdLF, AlwaysAvailable, "{ Low frequency commands... }"}, {"mem", CmdFlashMem, IfPm3Flash, "{ Flash memory manipulation... }"}, + {"mqtt", CmdMqtt, AlwaysAvailable, "{ MQTT commmands... }"}, {"nfc", CmdNFC, AlwaysAvailable, "{ NFC commands... }"}, {"piv", CmdPIV, AlwaysAvailable, "{ PIV commands... }"}, {"reveng", CmdRev, AlwaysAvailable, "{ CRC calculations from RevEng software... }"}, diff --git a/client/src/cmdmqtt.c b/client/src/cmdmqtt.c new file mode 100644 index 000000000..62397a91d --- /dev/null +++ b/client/src/cmdmqtt.c @@ -0,0 +1,385 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// MQTT commands +//----------------------------------------------------------------------------- +#include "cmdmqtt.h" + +#include "cmdparser.h" +#include "cliparser.h" +#include "mqtt.h" // MQTT support +//#include "mbedtls_sockets.h" // MQTT networkings examples +#include "posix_sockets.h" // MQTT networkings examples +#include "util_posix.h" // time +#include "fileutils.h" + + +#define MQTT_BUFFER_SIZE ( 1 << 16 ) + +static int CmdHelp(const char *Cmd); + +static void mqtt_publish_callback(void **unused, struct mqtt_response_publish *published) { + /* note that published->topic_name is NOT null-terminated (here we'll change it to a c-string) */ + char *topic_name = (char *) calloc(published->topic_name_size + 1, 1); + memcpy(topic_name, published->topic_name, published->topic_name_size); + + PrintAndLogEx(INFO, "rec.. %zu", published->application_message_size); + + const char *msg = published->application_message; + + char *ps = strstr(msg, "Created\": \"proxmark3"); + if (ps) { + int res = saveFileTXT("ice_mqtt", ".json", msg, published->application_message_size, spDefault); + if (res == PM3_SUCCESS) { + PrintAndLogEx(INFO, "Got a json file, save OK"); + } + } else { + PrintAndLogEx(SUCCESS, "[" _GREEN_("%s")"] " _YELLOW_("%s"), topic_name, msg); + } + free(topic_name); +} + +static void *mqtt_client_refresher(void *client) { + while (1) { + mqtt_sync((struct mqtt_client *) client); + msleep(100); + } + return NULL; +} + +static int mqtt_exit(int status, int sockfd, pthread_t *client_daemon) { + close_nb_socket(sockfd); + + if (client_daemon != NULL) { + pthread_cancel(*client_daemon); + pthread_join(*client_daemon, NULL); // Wait for the thread to finish + } + return status; +} + +/* +static void mqtt_reconnect_client(struct mqtt_client* client, void **reconnect_state_vptr) { + + struct reconnect_state_t *rs = *((struct reconnect_state_t**) reconnect_state_vptr); + + // Close the clients socket if this isn't the initial reconnect call + if (client->error != MQTT_ERROR_INITIAL_RECONNECT) { + close_nb_socket(client->socketfd); + } + + if (client->error != MQTT_ERROR_INITIAL_RECONNECT) { + PrintAndLogEx(INFO, "reconnect_client: called while client was in error state `%s`", mqtt_error_str(client->error)); + } + + int sockfd = open_nb_socket(rs->hostname, rs->port); + if (sockfd == -1) { + PrintAndLogEx(FAILED, "Failed to open socket"); + mqtt_exit(PM3_EFAILED, sockfd, NULL); + } + + // Reinitialize the client. + mqtt_reinit(client, sockfd, rs->sendbuf, rs->sendbufsz, rs->recvbuf, rs->recvbufsz); + + const char* client_id = NULL; + + uint8_t connect_flags = MQTT_CONNECT_CLEAN_SESSION; + + mqtt_connect(client, client_id, NULL, NULL, 0, NULL, NULL, connect_flags, 400); + + mqtt_subscribe(client, rs->topic, 0); +} +*/ + +static int mqtt_receive(const char *addr, const char *port, const char *topic) { + // open the non-blocking TCP socket (connecting to the broker) + int sockfd = open_nb_socket(addr, port); + if (sockfd == -1) { + PrintAndLogEx(FAILED, "Failed to open socket"); + return mqtt_exit(PM3_EFAILED, sockfd, NULL); + } + + uint8_t sendbuf[MQTT_BUFFER_SIZE]; // 64kb sendbuf should be large enough to hold multiple whole mqtt messages + uint8_t recvbuf[MQTT_BUFFER_SIZE]; // 64kb recvbuf should be large enough any whole mqtt message expected to be received + + struct mqtt_client client; + + /* + struct reconnect_state_t rs; + rs.hostname = addr; + rs.port = port; + rs.topic = topic; + rs.sendbuf = sendbuf; + rs.sendbufsz = sizeof(sendbuf); + rs.recvbuf = recvbuf; + rs.recvbufsz = sizeof(recvbuf); + mqtt_init_reconnect(&client, mqtt_reconnect_client, &rs, mqtt_publish_callback); + */ + + mqtt_init(&client, sockfd, sendbuf, sizeof(sendbuf), recvbuf, sizeof(recvbuf), mqtt_publish_callback); + + char cid[20] = "pm3_"; + sprintf(cid + strlen(cid), "%02x%02x%02x%02x" + , rand() % 0xFF + , rand() % 0xFF + , rand() % 0xFF + , rand() % 0xFF + ); + + // Ensure we have a clean session + uint8_t connect_flags = MQTT_CONNECT_CLEAN_SESSION; + // Send connection request to the broker + mqtt_connect(&client, cid, NULL, NULL, 0, NULL, NULL, connect_flags, 400); + + // check that we don't have any errors + if (client.error != MQTT_OK) { + PrintAndLogEx(FAILED, "error: %s", mqtt_error_str(client.error)); + return mqtt_exit(PM3_ESOFT, sockfd, NULL); + } + + // start a thread to refresh the client (handle egress and ingree client traffic) + pthread_t client_daemon; + if (pthread_create(&client_daemon, NULL, mqtt_client_refresher, &client)) { + PrintAndLogEx(FAILED, "Failed to start client daemon"); + return mqtt_exit(PM3_ESOFT, sockfd, NULL); + } + + // subscribe to a topic with a max QoS level of 0 + mqtt_subscribe(&client, topic, 0); + + PrintAndLogEx(INFO, _CYAN_("%s") " listening at " _CYAN_("%s:%s") " for " _YELLOW_("%s") " messages", cid, addr, port, topic); + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + + while (kbd_enter_pressed() == false) { + msleep(2000); + }; + + PrintAndLogEx(INFO, _CYAN_("%s") " disconnecting from " _CYAN_("%s"), cid, addr); + return mqtt_exit(PM3_SUCCESS, sockfd, &client_daemon); +} + +static int mqtt_send(const char *addr, const char *port, const char *topic, char *msg, const char *fn) { + + uint8_t *data; + size_t bytes_read = 0; + if (fn != NULL) { + int res = loadFile_TXTsafe(fn, "", (void **)&data, &bytes_read, true); + if (res != PM3_SUCCESS) { + return res; + } + } + + // open the non-blocking TCP socket (connecting to the broker) + int sockfd = open_nb_socket(addr, port); + + if (sockfd == -1) { + PrintAndLogEx(FAILED, "Failed to open socket"); + return mqtt_exit(PM3_EFAILED, sockfd, NULL); + } + + struct mqtt_client client; + uint8_t sendbuf[MQTT_BUFFER_SIZE]; + uint8_t recvbuf[MQTT_BUFFER_SIZE]; + mqtt_init(&client, sockfd, sendbuf, sizeof(sendbuf), recvbuf, sizeof(recvbuf), mqtt_publish_callback); + + char cid[20] = "pm3_"; + sprintf(cid + strlen(cid), "%02x%02x%02x%02x" + , rand() % 0xFF + , rand() % 0xFF + , rand() % 0xFF + , rand() % 0xFF + ); + + // Ensure we have a clean session + uint8_t connect_flags = MQTT_CONNECT_CLEAN_SESSION; + // Send connection request to the broker + mqtt_connect(&client, cid, NULL, NULL, 0, NULL, NULL, connect_flags, 400); + + // check that we don't have any errors + if (client.error != MQTT_OK) { + PrintAndLogEx(FAILED, "error: %s", mqtt_error_str(client.error)); + mqtt_exit(PM3_EFAILED, sockfd, NULL); + } + + // start a thread to refresh the client (handle egress and ingree client traffic) + pthread_t client_daemon; + if (pthread_create(&client_daemon, NULL, mqtt_client_refresher, &client)) { + PrintAndLogEx(FAILED, "Failed to start client daemon"); + mqtt_exit(PM3_EFAILED, sockfd, NULL); + + } + + PrintAndLogEx(INFO, _CYAN_("%s") " is ready", cid); + + if (fn != NULL) { + PrintAndLogEx(INFO, "Publishing file..."); + mqtt_publish(&client, topic, data, bytes_read, MQTT_PUBLISH_QOS_0); + } else { + PrintAndLogEx(INFO, "Publishing message..."); + mqtt_publish(&client, topic, msg, strlen(msg) + 1, MQTT_PUBLISH_QOS_0); + } + + if (client.error != MQTT_OK) { + PrintAndLogEx(INFO, "error: %s", mqtt_error_str(client.error)); + mqtt_exit(PM3_ESOFT, sockfd, &client_daemon); + } + + msleep(4000); + + PrintAndLogEx(INFO, _CYAN_("%s") " disconnecting from " _CYAN_("%s"), cid, addr); + return mqtt_exit(PM3_SUCCESS, sockfd, &client_daemon); +} + +static int CmdMqttSend(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mqtt send", + "This command send MQTT messages. You can send JSON file\n" + "Default server: proxdump.com:1883 topic: proxdump\n", + "mqtt send --msg \"Hello from Pm3\" --> sending msg to default server/port/topic\n" + "mqtt send -f myfile.json --> sending file to default server/port/topic\n" + "mqtt send --addr test.mosquitto.org -p 1883 --topic pm3 --msg \"custom mqtt server \"\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "addr", "", "MQTT server address"), + arg_str0("p", "port", "", "MQTT server port"), + arg_str0(NULL, "topic", "", "MQTT topic"), + arg_str0(NULL, "msg", "", "Message to send over MQTT"), + arg_str0("f", "file", "", "file to send"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int alen = 0; + char addr[256] = {0x00}; + int res = CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)addr, sizeof(addr), &alen); + + int plen = 0; + char port[10 + 1] = {0x00}; + res = CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)port, sizeof(port), &plen); + + int tlen = 0; + char topic[128] = {0x00}; + res = CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)topic, sizeof(topic), &tlen); + + int mlen = 0; + char msg[128] = {0x00}; + res = CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)msg, sizeof(msg), &mlen); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + CLIParserFree(ctx); + + // Error message if... an error occured. + if (res) { + PrintAndLogEx(FAILED, "Error parsing input strings"); + return PM3_EINVARG; + } + + if (alen == 0) { + strcpy(addr, "proxdump.com"); + } + if (plen == 0) { + strcpy(port, "1883"); + } + if (tlen == 0) { + strcpy(topic, "proxdump"); + } + + if (fnlen) { + return mqtt_send(addr, port, topic, NULL, filename); + } + + if (mlen) { + return mqtt_send(addr, port, topic, msg, NULL); + } + return PM3_SUCCESS; +} + +static int CmdMqttReceive(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mqtt receive", + "This command receives MQTT messages. JSON text will be saved to file if detected\n" + "Default server: proxdump.com:1883 topic: proxdump\n", + "mqtt receive --> listening to default server/port/topic\n" + "mqtt receive --addr test.mosquitto.org -p 1883 --topic pm3\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "addr", "", "MQTT server address"), + arg_str0("p", "port", "", "MQTT server port"), + arg_str0(NULL, "topic", "", "MQTT topic"), + arg_str0("f", "file", "", "file to send"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int alen = 0; + char addr[256] = {0x00}; + int res = CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)addr, sizeof(addr), &alen); + + int plen = 0; + char port[10 + 1] = {0x00}; + res = CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)port, sizeof(port), &plen); + + int tlen = 0; + char topic[128] = {0x00}; + res = CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)topic, sizeof(topic), &tlen); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + CLIParserFree(ctx); + + // Error message if... an error occured. + if (res) { + PrintAndLogEx(FAILED, "Error parsing input strings"); + return PM3_EINVARG; + } + + if (alen == 0) { + strcpy(addr, "proxdump.com"); + } + if (plen == 0) { + strcpy(port, "1883"); + } + if (tlen == 0) { + strcpy(topic, "proxdump"); + } + + return mqtt_receive(addr, port, topic); +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"send", CmdMqttSend, AlwaysAvailable, "Send messages or json file over MQTT"}, + {"receive", CmdMqttReceive, AlwaysAvailable, "Receive message or json file over MQTT"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return 0; +} + +int CmdMqtt(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdmqtt.h b/client/src/cmdmqtt.h new file mode 100644 index 000000000..fad58ff8f --- /dev/null +++ b/client/src/cmdmqtt.h @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// MQTT commands +//----------------------------------------------------------------------------- + +#ifndef CMDMQTT_H__ +#define CMDMQTT_H__ + +#include "common.h" + +int CmdMqtt(const char *Cmd); + +#endif diff --git a/client/src/fileutils.c b/client/src/fileutils.c index f4c76f049..805235e42 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -278,7 +278,7 @@ int saveFileEx(const char *preferredName, const char *suffix, const void *data, // Opening file for writing in binary mode FILE *f = fopen(fileName, "wb"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName); free(fileName); return PM3_EFILE; @@ -291,6 +291,33 @@ int saveFileEx(const char *preferredName, const char *suffix, const void *data, return PM3_SUCCESS; } +int saveFileTXT(const char *preferredName, const char *suffix, const void *data, size_t datalen, savePaths_t e_save_path) { + if (data == NULL || datalen == 0) { + return PM3_EINVARG; + } + + char *fileName = newfilenamemcopyEx(preferredName, suffix, e_save_path); + if (fileName == NULL) { + return PM3_EMALLOC; + } + + // We should have a valid filename now, e.g. dumpdata-3.txt + + // Opening file for writing in text mode + FILE *f = fopen(fileName, "w"); + if (f == NULL) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName); + free(fileName); + return PM3_EFILE; + } + fwrite(data, 1, datalen, f); + fflush(f); + fclose(f); + PrintAndLogEx(SUCCESS, "Saved " _YELLOW_("%zu") " bytes to text file `" _YELLOW_("%s") "`", datalen, fileName); + free(fileName); + return PM3_SUCCESS; +} + int prepareJSON(json_t *root, JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *)) { if (ftype != jsfCustom) { if (data == NULL || datalen == 0) { @@ -794,8 +821,9 @@ int saveFileJSONroot(const char *preferredName, void *root, size_t flags, bool v } int saveFileJSONrootEx(const char *preferredName, const void *root, size_t flags, bool verbose, bool overwrite, savePaths_t e_save_path) { - if (root == NULL) + if (root == NULL) { return PM3_EINVARG; + } char *filename = NULL; if (overwrite) @@ -975,7 +1003,7 @@ int loadFile_safeEx(const char *preferredName, const char *suffix, void **pdata, } FILE *f = fopen(path, "rb"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); free(path); return PM3_EFILE; @@ -1018,6 +1046,58 @@ int loadFile_safeEx(const char *preferredName, const char *suffix, void **pdata, return PM3_SUCCESS; } +int loadFile_TXTsafe(const char *preferredName, const char *suffix, void **pdata, size_t *datalen, bool verbose) { + + char *path; + int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, suffix, false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + FILE *f = fopen(path, "r"); + if (f == NULL) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + free(path); + + // get filesize in order to malloc memory + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + if (fsize <= 0) { + PrintAndLogEx(FAILED, "error, when getting filesize"); + fclose(f); + return PM3_EFILE; + } + + *pdata = calloc(fsize, sizeof(uint8_t)); + if (*pdata == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + fclose(f); + return PM3_EMALLOC; + } + + size_t bytes_read = fread(*pdata, 1, fsize, f); + + fclose(f); + + if (bytes_read != fsize) { + PrintAndLogEx(FAILED, "error, bytes read mismatch file size"); + free(*pdata); + return PM3_EFILE; + } + + *datalen = bytes_read; + + if (verbose) { + PrintAndLogEx(SUCCESS, "Loaded " _YELLOW_("%zu") " bytes from text file `" _YELLOW_("%s") "`", bytes_read, preferredName); + } + return PM3_SUCCESS; +} + int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { char *path; int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false); @@ -1121,7 +1201,7 @@ int loadFileNFC_safe(const char *preferredName, void *data, size_t maxdatalen, s } FILE *f = fopen(path, "r"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); free(path); return PM3_EFILE; @@ -1446,7 +1526,9 @@ int loadFileJSON(const char *preferredName, void *data, size_t maxdatalen, size_ } int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, bool verbose, void (*callback)(json_t *)) { - if (data == NULL) return PM3_EINVARG; + if (data == NULL) { + return PM3_EINVARG; + } *datalen = 0; int retval = PM3_SUCCESS; @@ -2629,6 +2711,10 @@ int detect_nfc_dump_format(const char *preferredName, nfc_df_e *dump_type, bool *dump_type = NFC_DF_14_4A; break; } + if (str_startswith(line, "device type: iso15693")) { + *dump_type = NFC_DF_15; + break; + } if (str_startswith(line, "filetype: flipper picopass device")) { *dump_type = NFC_DF_PICOPASS; break; @@ -2661,6 +2747,8 @@ int detect_nfc_dump_format(const char *preferredName, nfc_df_e *dump_type, bool case NFC_DF_PICOPASS: PrintAndLogEx(INFO, "Detected PICOPASS based dump format"); break; + case NFC_DF_15: + PrintAndLogEx(INFO, "Detected ISO15693 based dump format"); case NFC_DF_UNKNOWN: PrintAndLogEx(WARNING, "Failed to detected dump format"); break; @@ -3210,7 +3298,7 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl break; } - if (dumptype == NFC_DF_MFC || dumptype == NFC_DF_MFU || dumptype == NFC_DF_PICOPASS) { + if (dumptype == NFC_DF_MFC || dumptype == NFC_DF_MFU || dumptype == NFC_DF_PICOPASS || dumptype == NFC_DF_15) { *pdump = calloc(maxdumplen, sizeof(uint8_t)); if (*pdump == NULL) { @@ -3247,6 +3335,7 @@ int pm3_save_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft) { PrintAndLogEx(INFO, "No data to save, skipping..."); return PM3_EINVARG; } + saveFile(fn, ".bin", d, n); saveFileJSON(fn, jsft, d, n, NULL); return PM3_SUCCESS; diff --git a/client/src/fileutils.h b/client/src/fileutils.h index cc6d9ee14..bbb548fb9 100644 --- a/client/src/fileutils.h +++ b/client/src/fileutils.h @@ -100,9 +100,16 @@ typedef enum { NFC_DF_14_3A, NFC_DF_14_3B, NFC_DF_14_4A, + NFC_DF_15, NFC_DF_PICOPASS, } nfc_df_e; +typedef enum { + ISO15_DF_UNKNOWN, + ISO15_DF_V4_BIN, + ISO15_DF_V5_BIN +} iso15_df_e; + int fileExists(const char *filename); // set a path in the path list g_session.defaultPaths @@ -116,7 +123,7 @@ void truncate_filename(char *fn, uint16_t maxlen); /** * @brief Utility function to save data to a binary file. This method takes a preferred name, but if that * file already exists, it tries with another name until it finds something suitable. - * E.g. dumpdata-15.txt + * E.g. dumpdata-15.bin * * @param preferredName * @param suffix the file suffix. Including the ".". @@ -127,6 +134,19 @@ void truncate_filename(char *fn, uint16_t maxlen); int saveFile(const char *preferredName, const char *suffix, const void *data, size_t datalen); int saveFileEx(const char *preferredName, const char *suffix, const void *data, size_t datalen, savePaths_t e_save_path); +/** + * @brief Utility function to save data to a text file. This method takes a preferred name, but if that + * file already exists, it tries with another name until it finds something suitable. + * E.g. dumpdata-15.txt + * + * @param preferredName + * @param suffix the file suffix. Including the ".". + * @param data The binary data to write to the file + * @param datalen the length of the data + * @return 0 for ok, 1 for failz + */ +int saveFileTXT(const char *preferredName, const char *suffix, const void *data, size_t datalen, savePaths_t e_save_path); + /** STUB * @brief Utility function to save JSON data to a file. This method takes a preferred name, but if that * file already exists, it tries with another name until it finds something suitable. @@ -190,6 +210,19 @@ int createMfcKeyDump(const char *preferredName, uint8_t sectorsCnt, const sector */ int loadFile_safe(const char *preferredName, const char *suffix, void **pdata, size_t *datalen); int loadFile_safeEx(const char *preferredName, const char *suffix, void **pdata, size_t *datalen, bool verbose); + +/** + * @brief Utility function to load a text file. This method takes a preferred name. + * E.g. dumpdata-15.json, tries to search for it, and allocated memory. + * + * @param preferredName + * @param suffix the file suffix. Including the ".". + * @param data The data array to store the loaded bytes from file + * @param datalen the number of bytes loaded from file + * @return PM3_SUCCESS for ok, PM3_E* for failz +*/ +int loadFile_TXTsafe(const char *preferredName, const char *suffix, void **pdata, size_t *datalen, bool verbose); + /** * @brief Utility function to load data from a textfile (EML). This method takes a preferred name. * E.g. dumpdata-15.txt diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index a7cb8a8a2..7cae2a36a 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -832,6 +832,9 @@ const static vocabulary_t vocabulary[] = { { 0, "mem spiffs upload" }, { 0, "mem spiffs view" }, { 0, "mem spiffs wipe" }, + { 1, "mqtt help" }, + { 1, "mqtt send" }, + { 1, "mqtt receive" }, { 1, "nfc help" }, { 1, "nfc decode" }, { 0, "nfc type1 read" }, diff --git a/doc/commands.json b/doc/commands.json index bcc254785..0d4ab0c28 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -1136,7 +1136,7 @@ }, "help": { "command": "help", - "description": "help Use ` help` for details of a command prefs { Edit client/device preferences... } -------- ----------------------- Technology ----------------------- analyse { Analyse utils... } data { Plot window / data buffer manipulation... } emv { EMV ISO-14443 / ISO-7816... } hf { High frequency commands... } hw { Hardware commands... } lf { Low frequency commands... } nfc { NFC commands... } piv { PIV commands... } reveng { CRC calculations from RevEng software... } smart { Smart card ISO-7816 commands... } script { Scripting commands... } trace { Trace manipulation... } wiegand { Wiegand format manipulation... } -------- ----------------------- General ----------------------- clear Clear screen hints Turn hints on / off msleep Add a pause in milliseconds rem Add a text line in log file quit exit Exit program --------------------------------------------------------------------------------------- auto available offline: no Run LF SEARCH / HF SEARCH / DATA PLOT / DATA SAVE", + "description": "help Use ` help` for details of a command prefs { Edit client/device preferences... } -------- ----------------------- Technology ----------------------- analyse { Analyse utils... } data { Plot window / data buffer manipulation... } emv { EMV ISO-14443 / ISO-7816... } hf { High frequency commands... } hw { Hardware commands... } lf { Low frequency commands... } mqtt { MQTT commmands... } nfc { NFC commands... } piv { PIV commands... } reveng { CRC calculations from RevEng software... } smart { Smart card ISO-7816 commands... } script { Scripting commands... } trace { Trace manipulation... } wiegand { Wiegand format manipulation... } -------- ----------------------- General ----------------------- clear Clear screen hints Turn hints on / off msleep Add a pause in milliseconds rem Add a text line in log file quit exit Exit program --------------------------------------------------------------------------------------- auto available offline: no Run LF SEARCH / HF SEARCH / DATA PLOT / DATA SAVE", "notes": [ "auto" ], @@ -12331,6 +12331,42 @@ ], "usage": "mem wipe [-h] [-p ]" }, + "mqtt help": { + "command": "mqtt help", + "description": "help This help send Send messages or json file over MQTT receive Receive message or json file over MQTT --------------------------------------------------------------------------------------- mqtt send available offline: yes This command send MQTT messages. You can send JSON file Default server: proxdump.com:1883 topic: proxdump", + "notes": [ + "mqtt send --msg \"Hello from Pm3\" -> sending msg to default server/port/topic", + "mqtt send -f myfile.json -> sending file to default server/port/topic", + "mqtt send --addr test.mosquitto.org -p 1883 --topic pm3 --msg \"custom mqtt server \"" + ], + "offline": true, + "options": [ + "-h, --help This help", + "--addr MQTT server address", + "-p, --port MQTT server port", + "--topic MQTT topic", + "--msg Message to send over MQTT", + "-f, --file file to send" + ], + "usage": "mqtt send [-h] [--addr ] [-p ] [--topic ] [--msg ] [-f ]" + }, + "mqtt receive": { + "command": "mqtt receive", + "description": "This command receives MQTT messages. JSON text will be saved to file if detected Default server: proxdump.com:1883 topic: proxdump", + "notes": [ + "mqtt receive -> listening to default server/port/topic", + "mqtt receive --addr test.mosquitto.org -p 1883 --topic pm3" + ], + "offline": true, + "options": [ + "-h, --help This help", + "--addr MQTT server address", + "-p, --port MQTT server port", + "--topic MQTT topic", + "-f, --file file to send" + ], + "usage": "mqtt receive [-h] [--addr ] [-p ] [--topic ] [-f ]" + }, "msleep": { "command": "msleep", "description": "Sleep for given amount of milliseconds", @@ -13375,8 +13411,8 @@ } }, "metadata": { - "commands_extracted": 768, + "commands_extracted": 770, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-07-06T18:10:18" + "extracted_on": "2025-07-08T19:08:23" } } diff --git a/doc/commands.md b/doc/commands.md index 9cdcf7a47..96bb744d9 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -1414,6 +1414,17 @@ Check column "offline" for their availability. |`mem spiffs wipe `|N |`Wipe all files from SPIFFS file system * dangerous *` +### mqtt + + { MQTT commmands... } + +|command |offline |description +|------- |------- |----------- +|`mqtt help `|Y |`This help` +|`mqtt send `|Y |`Send messages or json file over MQTT` +|`mqtt receive `|Y |`Receive message or json file over MQTT` + + ### nfc { NFC commands... } From a3c2d2b8158ec1206a52246d501d5050b2dbb4a6 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 8 Jul 2025 21:14:29 +0200 Subject: [PATCH 067/149] style --- client/deps/jansson/dump.c | 43 ++++++++++++++++----------------- client/deps/mbedtls.cmake | 2 +- client/src/cmdhficlass.c | 2 +- client/src/cmdhflegic.c | 11 ++++++--- client/src/cmdlf.c | 6 ++--- client/src/cmdlfawid.c | 2 +- client/src/cmdlfem410x.c | 2 +- client/src/cmdlfguard.c | 2 +- client/src/cmdlfhid.c | 2 +- client/src/cmdlfhitag.c | 2 +- client/src/mifare/desfirecore.c | 14 +++++++---- client/src/proxmark3.c | 4 --- common/commonutil.h | 1 + common/hitag2/hitag2_crypto.h | 2 +- include/iclass_cmd.h | 10 +++++++- 15 files changed, 58 insertions(+), 47 deletions(-) diff --git a/client/deps/jansson/dump.c b/client/deps/jansson/dump.c index afdec8f7a..9c04063e0 100644 --- a/client/deps/jansson/dump.c +++ b/client/deps/jansson/dump.c @@ -440,33 +440,32 @@ int json_dumpfd(const json_t *json, int output, size_t flags) { } int json_dump_file(const json_t *json, const char *path, size_t flags) { - int result; - FILE *output = fopen(path, "w"); - if (!output) + FILE *f = fopen(path, "w"); + if (f == NULL) { return -1; - - result = json_dumpf(json, output, flags); - - if (fclose(output) != 0) - return -1; - - return result; -} - -int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags) { - int res; - hashtable_t parents_set; - - if (!(flags & JSON_ENCODE_ANY)) { - if (!json_is_array(json) && !json_is_object(json)) - return -1; } - if (hashtable_init(&parents_set)) + int res = json_dumpf(json, f, flags); + + if (fclose(f) != 0) return -1; - res = do_dump(json, flags, 0, &parents_set, callback, data); - hashtable_close(&parents_set); return res; } + +int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags) { + if (!(flags & JSON_ENCODE_ANY)) { + if (!json_is_array(json) && !json_is_object(json)) { + return -1; + } + } + + hashtable_t parents_set; + if (hashtable_init(&parents_set)) { + return -1; + } + int res = do_dump(json, flags, 0, &parents_set, callback, data); + hashtable_close(&parents_set); + return res; +} diff --git a/client/deps/mbedtls.cmake b/client/deps/mbedtls.cmake index 49c141b68..f33d5ac51 100644 --- a/client/deps/mbedtls.cmake +++ b/client/deps/mbedtls.cmake @@ -44,7 +44,7 @@ add_library(pm3rrg_rdv4_mbedtls STATIC ../../common/mbedtls/x509.c ../../common/mbedtls/x509_crl.c ../../common/mbedtls/x509_crt.c - ../../common/mbedtls/net_sockets.c + ../../common/mbedtls/net_sockets.c ) target_include_directories(pm3rrg_rdv4_mbedtls PRIVATE ../../common) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index f305c33e5..d4bc1da45 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1135,7 +1135,7 @@ int read_iclass_csn(bool loop, bool verbose, bool shallow_mod) { res = PM3_EMALLOC; } } - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); DropField(); return res; diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index be84822bf..f47466761 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -781,17 +781,20 @@ int legic_print_type(uint32_t tagtype, uint8_t spaces) { } int legic_get_type(legic_card_select_t *card) { - if (card == NULL) + if (card == NULL) { return PM3_EINVARG; + } clearCommandBuffer(); SendCommandNG(CMD_HF_LEGIC_INFO, NULL, 0); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_LEGIC_INFO, &resp, 1500) == false) + if (WaitForResponseTimeout(CMD_HF_LEGIC_INFO, &resp, 1500) == false) { return PM3_ETIMEOUT; + } - if (resp.status != PM3_SUCCESS) + if (resp.status != PM3_SUCCESS) { return PM3_ESOFT; + } memcpy(card, resp.data.asBytes, sizeof(legic_card_select_t)); return PM3_SUCCESS; @@ -1527,7 +1530,7 @@ int readLegicUid(bool loop, bool verbose) { PrintAndLogEx(SUCCESS, " MSN: " _GREEN_("%s"), sprint_hex(card.uid + 1, sizeof(card.uid) - 1)); legic_print_type(card.cardsize, 0); - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlf.c b/client/src/cmdlf.c index 20940ae6d..b36c1c97b 100644 --- a/client/src/cmdlf.c +++ b/client/src/cmdlf.c @@ -462,7 +462,7 @@ int CmdLFCommandRead(const char *Cmd) { return PM3_ETIMEOUT; } - } while (cm && kbd_enter_pressed() == false); + } while (cm && (kbd_enter_pressed() == false)); return ret; } @@ -859,7 +859,7 @@ int CmdLFRead(const char *Cmd) { int ret = PM3_SUCCESS; do { ret = lf_read_internal(realtime, verbose, samples); - } while (cm && kbd_enter_pressed() == false); + } while (cm && (kbd_enter_pressed() == false)); if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Got " _YELLOW_("%zu") " samples", g_GraphTraceLen); @@ -985,7 +985,7 @@ int CmdLFSniff(const char *Cmd) { int ret = PM3_SUCCESS; do { ret = lf_sniff(realtime, verbose, samples); - } while (cm && kbd_enter_pressed() == false); + } while (cm && (kbd_enter_pressed() == false)); return ret; } diff --git a/client/src/cmdlfawid.c b/client/src/cmdlfawid.c index 9e330844a..0f28d823d 100644 --- a/client/src/cmdlfawid.c +++ b/client/src/cmdlfawid.c @@ -349,7 +349,7 @@ static int CmdAWIDReader(const char *Cmd) { do { lf_read(false, 12000); demodAWID(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfem410x.c b/client/src/cmdlfem410x.c index 1b4f31d95..c178eb440 100644 --- a/client/src/cmdlfem410x.c +++ b/client/src/cmdlfem410x.c @@ -445,7 +445,7 @@ static int CmdEM410xReader(const char *Cmd) { if (break_first && gs_em410xid != 0) { break; } - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfguard.c b/client/src/cmdlfguard.c index 9275a5e32..8ff8c13d1 100644 --- a/client/src/cmdlfguard.c +++ b/client/src/cmdlfguard.c @@ -243,7 +243,7 @@ static int CmdGuardReader(const char *Cmd) { do { lf_read(false, 10000); demodGuard(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c index 1db507df4..0e932fccb 100644 --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -215,7 +215,7 @@ static int CmdHIDReader(const char *Cmd) { do { lf_read(false, 16000); demodHID(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 63c25ff99..e75cd0804 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -912,7 +912,7 @@ static int CmdLFHitagReader(const char *Cmd) { if (ht2_get_uid(&uid)) { PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%08X"), uid); } - } while (cm && kbd_enter_pressed() == false); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index 985aad1f4..f6ec34e17 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -575,10 +575,11 @@ static int DesfireExchangeNative(bool activate_field, DesfireContext_t *ctx, uin size_t sentdatalen = 0; while (cdatalen >= sentdatalen) { - if ((cdatalen - sentdatalen) > DESFIRE_TX_FRAME_MAX_LEN) + if ((cdatalen - sentdatalen) > DESFIRE_TX_FRAME_MAX_LEN) { len = DESFIRE_TX_FRAME_MAX_LEN; - else + } else { len = cdatalen - sentdatalen; + } size_t sendindx = sentdatalen; size_t sendlen = len; @@ -657,8 +658,9 @@ static int DesfireExchangeNative(bool activate_field, DesfireContext_t *ctx, uin } pos += buflen; - if (rcode != MFDES_ADDITIONAL_FRAME) + if (rcode != MFDES_ADDITIONAL_FRAME) { break; + } } if (resplen) { @@ -969,12 +971,14 @@ int DesfireSelectAIDHexNoFieldOn(DesfireContext_t *ctx, uint32_t aid) { ctx->secureChannel = DACNone; int res = DesfireExchangeEx(false, ctx, MFDES_SELECT_APPLICATION, data, 3, &respcode, resp, &resplen, true, 0); if (res == PM3_SUCCESS) { - if (resplen != 0) + if (resplen != 0) { return PM3_ECARDEXCHANGE; + } // select operation fail - if (respcode != MFDES_S_OPERATION_OK) + if (respcode != MFDES_S_OPERATION_OK) { return PM3_EAPDU_FAIL; + } DesfireClearSession(ctx); ctx->appSelected = (aid != 0x000000); diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index 2481803fa..2904aaab8 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -17,14 +17,12 @@ //----------------------------------------------------------------------------- #include "proxmark3.h" - #include #include #include #include #include // basename #include - #include "pm3line.h" #include "usart_defs.h" #include "util_posix.h" @@ -43,7 +41,6 @@ #include #endif - static int mainret = PM3_SUCCESS; #ifndef LIBPM3 @@ -209,7 +206,6 @@ static void showBanner(void) { PrintAndLogEx(NORMAL, " [ " _YELLOW_("%s!")" :coffee: ]", get_quote()); // PrintAndLogEx(NORMAL, " [ https://patreon.com/iceman1001/ ]"); -// PrintAndLogEx(NORMAL, ""); // PrintAndLogEx(NORMAL, " Monero"); // PrintAndLogEx(NORMAL, " 43mNJLpgBVaTvyZmX9ajcohpvVkaRy1kbZPm8tqAb7itZgfuYecgkRF36rXrKFUkwEGeZedPsASRxgv4HPBHvJwyJdyvQuP"); PrintAndLogEx(NORMAL, ""); diff --git a/common/commonutil.h b/common/commonutil.h index 00949b851..bb4697ffa 100644 --- a/common/commonutil.h +++ b/common/commonutil.h @@ -75,6 +75,7 @@ typedef struct { int calculate_hours_between_dates(const Date_t s, Date_t *e); +void add_minutes(Date_t *d, int minutes_to_add); void add_hours(Date_t *d, int hours_to_add); void add_days(Date_t *d, int days_to_add); uint8_t days_in_month(int year, int month); diff --git a/common/hitag2/hitag2_crypto.h b/common/hitag2/hitag2_crypto.h index 1dae77353..f5d4e7102 100644 --- a/common/hitag2/hitag2_crypto.h +++ b/common/hitag2/hitag2_crypto.h @@ -29,7 +29,7 @@ typedef struct { enum { TAG_STATE_RESET = 0x01, // Just powered up, awaiting GetSnr TAG_STATE_ACTIVATING = 0x02, // In activation phase (password mode), sent UID, awaiting reader password - TAG_STATE_ACTIVATED = 0x03, // Activation complete, awaiting read/write commands +// TAG_STATE_ACTIVATED = 0x03, // Activation complete, awaiting read/write commands TAG_STATE_WRITING = 0x04, // In write command, awaiting sector contents to be written } state; uint16_t active_sector; diff --git a/include/iclass_cmd.h b/include/iclass_cmd.h index 8210490a8..259c8cc2f 100644 --- a/include/iclass_cmd.h +++ b/include/iclass_cmd.h @@ -24,7 +24,8 @@ //----------------------------------------------------------------------------- // iCLASS / PICOPASS //----------------------------------------------------------------------------- -#define PICOPASS_BLOCK_SIZE 8 +#define PICOPASS_BLOCK_SIZE ( 8 ) +#define PICOPASS_MAX_BYTES ( 4096 ) // # 32k bits = 4096 bytes // iCLASS reader flags #define FLAG_ICLASS_READER_INIT 0x01 @@ -197,5 +198,12 @@ typedef struct { } header; } PACKED iclass_card_select_resp_t; +typedef struct { + union { + picopass_hdr_t hdr; + picopass_ns_hdr_t ns_hdr; + } header; + uint8_t data[PICOPASS_MAX_BYTES]; +} PACKED iclass_tag_t; #endif // _ICLASS_H_ From fa59b9cb6b7520dfae9ac0000e8020eb0ae6c8fa Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 8 Jul 2025 21:51:48 +0200 Subject: [PATCH 068/149] fix mqtt receive command default behaviour --- client/src/cmdmqtt.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/client/src/cmdmqtt.c b/client/src/cmdmqtt.c index 62397a91d..902ddb768 100644 --- a/client/src/cmdmqtt.c +++ b/client/src/cmdmqtt.c @@ -31,22 +31,20 @@ static int CmdHelp(const char *Cmd); static void mqtt_publish_callback(void **unused, struct mqtt_response_publish *published) { - /* note that published->topic_name is NOT null-terminated (here we'll change it to a c-string) */ + // note that published->topic_name is NOT null-terminated (here we'll change it to a c-string) char *topic_name = (char *) calloc(published->topic_name_size + 1, 1); memcpy(topic_name, published->topic_name, published->topic_name_size); - PrintAndLogEx(INFO, "rec.. %zu", published->application_message_size); - const char *msg = published->application_message; char *ps = strstr(msg, "Created\": \"proxmark3"); if (ps) { int res = saveFileTXT("ice_mqtt", ".json", msg, published->application_message_size, spDefault); if (res == PM3_SUCCESS) { - PrintAndLogEx(INFO, "Got a json file, save OK"); + PrintAndLogEx(INFO, "Got a json file ( %s )", _GREEN_("ok")); } } else { - PrintAndLogEx(SUCCESS, "[" _GREEN_("%s")"] " _YELLOW_("%s"), topic_name, msg); + PrintAndLogEx(SUCCESS, _GREEN_("%s") " - ( %zu ) " _YELLOW_("%s"), topic_name, published->application_message_size, msg); } free(topic_name); } @@ -102,7 +100,7 @@ static void mqtt_reconnect_client(struct mqtt_client* client, void **reconnect_s } */ -static int mqtt_receive(const char *addr, const char *port, const char *topic) { +static int mqtt_receive(const char *addr, const char *port, const char *topic, const char *fn) { // open the non-blocking TCP socket (connecting to the broker) int sockfd = open_nb_socket(addr, port); if (sockfd == -1) { @@ -324,10 +322,10 @@ static int CmdMqttReceive(const char *Cmd) { arg_str0(NULL, "addr", "", "MQTT server address"), arg_str0("p", "port", "", "MQTT server port"), arg_str0(NULL, "topic", "", "MQTT topic"), - arg_str0("f", "file", "", "file to send"), + arg_str0("f", "file", "", "file name to use for received files"), arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, false); + CLIExecWithReturn(ctx, Cmd, argtable, true); int alen = 0; char addr[256] = {0x00}; @@ -363,7 +361,7 @@ static int CmdMqttReceive(const char *Cmd) { strcpy(topic, "proxdump"); } - return mqtt_receive(addr, port, topic); + return mqtt_receive(addr, port, topic, filename); } static command_t CommandTable[] = { From 931e93a11beb4a97fdd7a06fe76cf5256970c31e Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 9 Jul 2025 00:20:43 +0200 Subject: [PATCH 069/149] Win32 socket vs POSIX sockets is not always happy together. Seperated the two to mimic the same behavior we have for /uart/. The code compiles but the socket doesnt work on Proxspace yet. More fixes to be done --- client/deps/mqtt/posix_sockets.h | 67 ++++++++--------------- client/deps/mqtt/win32_sockets.h | 91 ++++++++++++++++++++++++++++++++ client/src/cmdmqtt.c | 7 +++ 3 files changed, 121 insertions(+), 44 deletions(-) create mode 100644 client/deps/mqtt/win32_sockets.h diff --git a/client/deps/mqtt/posix_sockets.h b/client/deps/mqtt/posix_sockets.h index ab13f863d..d44e1c374 100644 --- a/client/deps/mqtt/posix_sockets.h +++ b/client/deps/mqtt/posix_sockets.h @@ -1,23 +1,21 @@ #if !defined(__POSIX_SOCKET_TEMPLATE_H__) #define __POSIX_SOCKET_TEMPLATE_H__ -#include -#include -#if !defined(WIN32) -#include -#include -#else -#include -#endif -#if defined(__VMS) -#include -#endif -#include -#include +#ifndef _WIN32 -/* - A template for opening a non-blocking POSIX socket. -*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// A template for opening a non-blocking POSIX socket. + +void close_nb_socket(int sockfd); int open_nb_socket(const char *addr, const char *port); int open_nb_socket(const char *addr, const char *port) { @@ -27,21 +25,23 @@ int open_nb_socket(const char *addr, const char *port) { hints.ai_family = AF_UNSPEC; /* IPv4 or IPv6 */ hints.ai_socktype = SOCK_STREAM; /* Must be TCP */ - int sockfd = -1; - int rv; + struct addrinfo *p, *servinfo; /* get address information */ - rv = getaddrinfo(addr, port, &hints, &servinfo); + int rv = getaddrinfo(addr, port, &hints, &servinfo); if (rv != 0) { fprintf(stderr, "Failed to open socket (getaddrinfo): %s\n", gai_strerror(rv)); return -1; } /* open the first possible socket */ + int sockfd = -1; for (p = servinfo; p != NULL; p = p->ai_next) { sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol); - if (sockfd == -1) continue; + if (sockfd == -1) { + continue; + } /* connect to server */ rv = connect(sockfd, p->ai_addr, p->ai_addrlen); @@ -53,42 +53,21 @@ int open_nb_socket(const char *addr, const char *port) { break; } - /* free servinfo */ + // free servinfo freeaddrinfo(servinfo); - /* make non-blocking */ -#if !defined(WIN32) + // make non-blocking if (sockfd != -1) { fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK); } -#else - if (sockfd != INVALID_SOCKET) { - int iMode = 1; - ioctlsocket(sockfd, FIONBIO, &iMode); - } -#endif -#if defined(__VMS) - /* - OpenVMS only partially implements fcntl. It works on file descriptors - but silently fails on socket descriptors. So we need to fall back on - to the older ioctl system to set non-blocking IO - */ - int on = 1; - if (sockfd != -1) { - ioctl(sockfd, FIONBIO, &on); - } -#endif - - /* return the new socket fd */ return sockfd; } -void close_nb_socket(int sockfd); - void close_nb_socket(int sockfd) { if (sockfd != -1) { close(sockfd); } } #endif +#endif \ No newline at end of file diff --git a/client/deps/mqtt/win32_sockets.h b/client/deps/mqtt/win32_sockets.h new file mode 100644 index 000000000..126c7560d --- /dev/null +++ b/client/deps/mqtt/win32_sockets.h @@ -0,0 +1,91 @@ +#if !defined(__WIN32_SOCKET_TEMPLATE_H__) + +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +void close_nb_socket(int sockfd); +int open_nb_socket(const char *addr, const char *port); + +int open_nb_socket(const char *addr, const char *port) { + + WSADATA wsaData; + int res = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (res != 0) { + fprintf(stderr, "error: WSAStartup failed with error: %i", res); + return -1; + } + + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; // IPv4 or IPv6 + hints.ai_socktype = SOCK_STREAM; // Must be TCP + hints.ai_protocol = IPPROTO_TCP; // + + struct addrinfo *p, *servinfo; + // get address information + int rv = getaddrinfo(addr, port, &hints, &servinfo); + if (rv != 0) { + fprintf(stderr, "error: getaddrinfo: %s", gai_strerror(rv)); + WSACleanup(); + return -1; + } + + /* open the first possible socket */ + SOCKET hSocket = INVALID_SOCKET; + for (p = servinfo; p != NULL; p = p->ai_next) { + hSocket = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + + if (hSocket == INVALID_SOCKET) { + continue; + } + + // connect to server + if (connect(hSocket, p->ai_addr, (int)p->ai_addrlen) != INVALID_SOCKET) { + break; + } + + closesocket(hSocket); + hSocket = INVALID_SOCKET; + + } + + // free servinfo + freeaddrinfo(servinfo); + + if (p == NULL) { // No address succeeded + fprintf(stderr, "error: Could not connect"); + WSACleanup(); + return -1; + } + + // make non-blocking + if (hSocket != INVALID_SOCKET) { + uint32_t mode; // FIONBIO returns size on 32b + ioctlsocket(hSocket, FIONBIO, (u_long *)&mode); + } + + int flag = 1; + res = setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); + if (res != 0) { + closesocket(hSocket); + WSACleanup(); + return -1; + } + + return hSocket; +} + +void close_nb_socket(int sockfd) { + if (sockfd != -1) { + close(sockfd); + } +} +#endif + +#endif \ No newline at end of file diff --git a/client/src/cmdmqtt.c b/client/src/cmdmqtt.c index 902ddb768..ba7d93685 100644 --- a/client/src/cmdmqtt.c +++ b/client/src/cmdmqtt.c @@ -21,7 +21,12 @@ #include "cliparser.h" #include "mqtt.h" // MQTT support //#include "mbedtls_sockets.h" // MQTT networkings examples + +#ifndef _WIN32 #include "posix_sockets.h" // MQTT networkings examples +#else +#include "win32_sockets.h" // MQTT networkings examples +#endif #include "util_posix.h" // time #include "fileutils.h" @@ -62,7 +67,9 @@ static int mqtt_exit(int status, int sockfd, pthread_t *client_daemon) { if (client_daemon != NULL) { pthread_cancel(*client_daemon); +#ifndef _WIN32 pthread_join(*client_daemon, NULL); // Wait for the thread to finish +#endif } return status; } From 815d445382e034d242ec42bd8e906bb0dbaa5c5e Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 9 Jul 2025 08:21:21 +0200 Subject: [PATCH 070/149] fix mqtt receive on proxspace --- client/deps/mqtt/win32_sockets.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/deps/mqtt/win32_sockets.h b/client/deps/mqtt/win32_sockets.h index 126c7560d..c7726ed20 100644 --- a/client/deps/mqtt/win32_sockets.h +++ b/client/deps/mqtt/win32_sockets.h @@ -45,7 +45,7 @@ int open_nb_socket(const char *addr, const char *port) { continue; } - // connect to server + // connect to server if (connect(hSocket, p->ai_addr, (int)p->ai_addrlen) != INVALID_SOCKET) { break; } @@ -66,7 +66,7 @@ int open_nb_socket(const char *addr, const char *port) { // make non-blocking if (hSocket != INVALID_SOCKET) { - uint32_t mode; // FIONBIO returns size on 32b + uint32_t mode = 1; // FIONBIO returns size on 32b ioctlsocket(hSocket, FIONBIO, (u_long *)&mode); } @@ -76,7 +76,7 @@ int open_nb_socket(const char *addr, const char *port) { closesocket(hSocket); WSACleanup(); return -1; - } + } return hSocket; } @@ -88,4 +88,4 @@ void close_nb_socket(int sockfd) { } #endif -#endif \ No newline at end of file +#endif From 36f5a0c69be590f01f14588489c9bf30d0b14968 Mon Sep 17 00:00:00 2001 From: ry4000 <154689120+ry4000@users.noreply.github.com> Date: Wed, 9 Jul 2025 20:09:40 +1000 Subject: [PATCH 071/149] R&Y: Updated `aid_desfire.json` Signed-off-by: ry4000 <154689120+ry4000@users.noreply.github.com> --- client/resources/aid_desfire.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index 0a6c78822..88101ba67 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -767,14 +767,6 @@ "Description": "car2go - Member Card // Multi Functional Badge / Private Application #1", "Type": "carsharing" }, - { - "AID": "000005", - "Vendor": "Transports Metropolitans de Barcelona (TMB)", - "Country": "ES", - "Name": "T-mobilitat (BCN)", - "Description": "BCN T-mobilitat", - "Type": "transport" - }, { "AID": "000001", "Vendor": "Invalid / Reserved", @@ -783,6 +775,14 @@ "Description": "Used by ATL Breeze, PHL FREEDOM, and YVR Compass", "Type": "transport" }, + { + "AID": "000005", + "Vendor": "Transports Metropolitans de Barcelona (TMB)", + "Country": "ES", + "Name": "T-mobilitat (BCN)", + "Description": "BCN T-mobilitat", + "Type": "transport" + }, { "AID": "0000F0", "Vendor": "Metropolitan Transportation Authority (MTA) / Bayerische Motoren Werke (BMW) AG", @@ -793,7 +793,7 @@ }, { "AID": "002000", - "Vendor": "Metrolinx", + "Vendor": "Metrolinx via Accenture", "Country": "CA", "Name": "PRESTO Card (YYZ/YHM/YOW)", "Description": "FIDs 00,0F: Backup Data; 08-0E,10-14: Standard Data", @@ -1081,7 +1081,7 @@ }, { "AID": "415431", - "Vendor": "Athens Urban Transport Organization (OASA)", + "Vendor": "Athens Urban Transport Organisation (OASA)", "Country": "GR", "Name": "ATH.ENA CARD (ATH)", "Description": "ATH ATH.ENA CARD", @@ -1153,7 +1153,7 @@ }, { "AID": "4F5931", - "Vendor": "Transport for London (TfL)", + "Vendor": "Transport for London (TfL) via Cubic Transportation Systems", "Country": "UK", "Name": "Oyster Card (LHR)", "Description": "FIDs: 00-07: Standard Data", @@ -1169,7 +1169,7 @@ }, { "AID": "534531", - "Vendor": "Transport for New South Wales (TfNSW)", + "Vendor": "Transport for New South Wales (TfNSW) via Pearl Consortium", "Country": "AU", "Name": "Opal Card (SYD)", "Description": "FIDs 00-06: Standard Data; 07: Card Balance/Number and Trip History", @@ -1177,7 +1177,7 @@ }, { "AID": "554000", - "Vendor": "Auckland Transport", + "Vendor": "Auckland Transport via Thales Group", "Country": "NZ", "Name": "AT HOP Card (AKL)", "Description": "FIDs: 00: Backup Data; 08/09/0A", From c39e18a014a563238231c350a52a9137be9841d6 Mon Sep 17 00:00:00 2001 From: Def Date: Wed, 9 Jul 2025 14:08:40 +0300 Subject: [PATCH 072/149] fixes to mqtt (win sockets) --- client/deps/mqtt/win32_sockets.h | 25 +++++++++++++------------ client/src/cmdmqtt.c | 8 +++----- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/client/deps/mqtt/win32_sockets.h b/client/deps/mqtt/win32_sockets.h index c7726ed20..20a774a63 100644 --- a/client/deps/mqtt/win32_sockets.h +++ b/client/deps/mqtt/win32_sockets.h @@ -1,4 +1,5 @@ #if !defined(__WIN32_SOCKET_TEMPLATE_H__) +#define __WIN32_SOCKET_TEMPLATE_H__ #include #include @@ -9,16 +10,16 @@ #include #include -void close_nb_socket(int sockfd); -int open_nb_socket(const char *addr, const char *port); +void close_nb_socket(mqtt_pal_socket_handle sockfd); +mqtt_pal_socket_handle open_nb_socket(const char *addr, const char *port); -int open_nb_socket(const char *addr, const char *port) { +mqtt_pal_socket_handle open_nb_socket(const char *addr, const char *port) { WSADATA wsaData; int res = WSAStartup(MAKEWORD(2, 2), &wsaData); if (res != 0) { fprintf(stderr, "error: WSAStartup failed with error: %i", res); - return -1; + return INVALID_SOCKET; } struct addrinfo hints; @@ -33,7 +34,7 @@ int open_nb_socket(const char *addr, const char *port) { if (rv != 0) { fprintf(stderr, "error: getaddrinfo: %s", gai_strerror(rv)); WSACleanup(); - return -1; + return INVALID_SOCKET; } /* open the first possible socket */ @@ -61,13 +62,13 @@ int open_nb_socket(const char *addr, const char *port) { if (p == NULL) { // No address succeeded fprintf(stderr, "error: Could not connect"); WSACleanup(); - return -1; + return INVALID_SOCKET; } // make non-blocking if (hSocket != INVALID_SOCKET) { - uint32_t mode = 1; // FIONBIO returns size on 32b - ioctlsocket(hSocket, FIONBIO, (u_long *)&mode); + u_long mode = 1; // FIONBIO returns size on 32b + ioctlsocket(hSocket, FIONBIO, &mode); } int flag = 1; @@ -75,15 +76,15 @@ int open_nb_socket(const char *addr, const char *port) { if (res != 0) { closesocket(hSocket); WSACleanup(); - return -1; + return INVALID_SOCKET; } return hSocket; } -void close_nb_socket(int sockfd) { - if (sockfd != -1) { - close(sockfd); +void close_nb_socket(mqtt_pal_socket_handle sockfd) { + if (sockfd != INVALID_SOCKET) { + closesocket(sockfd); } } #endif diff --git a/client/src/cmdmqtt.c b/client/src/cmdmqtt.c index ba7d93685..ace6c62c1 100644 --- a/client/src/cmdmqtt.c +++ b/client/src/cmdmqtt.c @@ -56,20 +56,18 @@ static void mqtt_publish_callback(void **unused, struct mqtt_response_publish *p static void *mqtt_client_refresher(void *client) { while (1) { + pthread_testcancel(); // check if we cancelled mqtt_sync((struct mqtt_client *) client); msleep(100); } return NULL; } -static int mqtt_exit(int status, int sockfd, pthread_t *client_daemon) { +static int mqtt_exit(int status, mqtt_pal_socket_handle sockfd, pthread_t *client_daemon) { close_nb_socket(sockfd); - if (client_daemon != NULL) { pthread_cancel(*client_daemon); -#ifndef _WIN32 pthread_join(*client_daemon, NULL); // Wait for the thread to finish -#endif } return status; } @@ -109,7 +107,7 @@ static void mqtt_reconnect_client(struct mqtt_client* client, void **reconnect_s static int mqtt_receive(const char *addr, const char *port, const char *topic, const char *fn) { // open the non-blocking TCP socket (connecting to the broker) - int sockfd = open_nb_socket(addr, port); + mqtt_pal_socket_handle sockfd = open_nb_socket(addr, port); if (sockfd == -1) { PrintAndLogEx(FAILED, "Failed to open socket"); return mqtt_exit(PM3_EFAILED, sockfd, NULL); From 2597c7576ed46cb655c1b2f15f87a646bdb7af26 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 10 Jul 2025 11:52:39 +0200 Subject: [PATCH 073/149] style --- client/Makefile | 2 + client/deps/mqtt/posix_sockets.h | 4 +- client/deps/mqtt/win32_sockets.h | 2 +- client/dictionaries/mfc_default_keys.dic | 2 +- client/luascripts/kybercrystals.lua | 30 +++++++-------- client/luascripts/lf_awid_bulkclone.lua | 14 +++---- client/luascripts/lf_electra.lua | 2 +- client/luascripts/lf_hid_bulkclone_v2.lua | 6 +-- client/luascripts/lf_ioprox_bulkclone.lua | 2 +- client/luascripts/lf_t55xx_fix.lua | 4 +- client/luascripts/paxton_clone.lua | 14 +++---- client/src/cmdhf14b.c | 2 +- client/src/cmdhfcryptorf.c | 3 +- client/src/cmdhffelica.c | 2 +- client/src/cmdhffudan.c | 2 +- client/src/cmdhflto.c | 2 +- client/src/cmdhftexkom.c | 2 +- client/src/cmdhftopaz.c | 4 +- client/src/cmdhfxerox.c | 2 +- client/src/cmdlfdestron.c | 2 +- client/src/cmdlffdxb.c | 2 +- client/src/cmdlfgallagher.c | 2 +- client/src/cmdlfhitaghts.c | 2 +- client/src/cmdlfidteck.c | 2 +- client/src/cmdlfindala.c | 2 +- client/src/cmdlfio.c | 2 +- client/src/cmdlfjablotron.c | 2 +- client/src/cmdlfkeri.c | 2 +- client/src/cmdlfmotorola.c | 2 +- client/src/cmdlfnedap.c | 2 +- client/src/cmdlfnexwatch.c | 2 +- client/src/cmdlfnoralsy.c | 2 +- client/src/cmdlfpac.c | 2 +- client/src/cmdlfparadox.c | 2 +- client/src/cmdlfpcf7931.c | 2 +- client/src/cmdlfpresco.c | 2 +- client/src/cmdlfpyramid.c | 2 +- client/src/cmdlfsecurakey.c | 2 +- client/src/cmdlfti.c | 2 +- client/src/cmdlfviking.c | 2 +- client/src/cmdlfvisa2000.c | 2 +- client/src/cmdlfzx8211.c | 2 +- client/src/cmdmqtt.c | 7 +++- client/src/cmdsmartcard.c | 2 +- client/src/fileutils.c | 6 +++ client/src/fileutils.h | 1 + client/src/uart/uart_common.c | 19 +++++---- client/src/util.c | 1 + common/crc16.c | 47 +++++++++++++++-------- doc/commands.json | 4 +- 50 files changed, 135 insertions(+), 99 deletions(-) diff --git a/client/Makefile b/client/Makefile index 2c0f7ef04..de90a5f5c 100644 --- a/client/Makefile +++ b/client/Makefile @@ -452,6 +452,7 @@ endif ifeq ($(SWIG_LUA_FOUND),1) PM3CFLAGS += -DHAVE_LUA_SWIG endif + ifeq ($(SWIG_PYTHON_FOUND),1) PM3CFLAGS += -DHAVE_PYTHON_SWIG endif @@ -594,6 +595,7 @@ endif ifeq ($(SWIG_LUA_FOUND),1) $(info Lua SWIG: wrapper found) endif + ifeq ($(SWIG_PYTHON_FOUND),1) $(info Python SWIG: wrapper found) endif diff --git a/client/deps/mqtt/posix_sockets.h b/client/deps/mqtt/posix_sockets.h index d44e1c374..42954f640 100644 --- a/client/deps/mqtt/posix_sockets.h +++ b/client/deps/mqtt/posix_sockets.h @@ -53,7 +53,7 @@ int open_nb_socket(const char *addr, const char *port) { break; } - // free servinfo + // free servinfo freeaddrinfo(servinfo); // make non-blocking @@ -70,4 +70,4 @@ void close_nb_socket(int sockfd) { } } #endif -#endif \ No newline at end of file +#endif diff --git a/client/deps/mqtt/win32_sockets.h b/client/deps/mqtt/win32_sockets.h index 20a774a63..4775bb851 100644 --- a/client/deps/mqtt/win32_sockets.h +++ b/client/deps/mqtt/win32_sockets.h @@ -29,7 +29,7 @@ mqtt_pal_socket_handle open_nb_socket(const char *addr, const char *port) { hints.ai_protocol = IPPROTO_TCP; // struct addrinfo *p, *servinfo; - // get address information + // get address information int rv = getaddrinfo(addr, port, &hints, &servinfo); if (rv != 0) { fprintf(stderr, "error: getaddrinfo: %s", gai_strerror(rv)); diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 465431dd8..eeeaecc23 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2542,7 +2542,7 @@ FAB943906E9C # R.A.T.T transport card key A/B AA034F342A55 456776908C48 - +# # BusFacil - Brazilian public transport card for some cities fae9b14365a9 c567dd4a6004 diff --git a/client/luascripts/kybercrystals.lua b/client/luascripts/kybercrystals.lua index 70306842b..1b707768b 100644 --- a/client/luascripts/kybercrystals.lua +++ b/client/luascripts/kybercrystals.lua @@ -2,7 +2,7 @@ Simple script to program DIY kyber crystals works on real kyber crystals and EM4305 2.12x12mm chips simply run the program and select a profile via a number - + issues if you are getting errors when trying to read or write a chip run the cmd "lf tune" with no chip on the device, then move the chip over the coils till you see the lowest voltage. try different angles and in the center and or the edge of the antenna ring. @@ -10,7 +10,7 @@ if thats still not working run "lf tune" again and put the chip in the best position like before the total voltage may be too high to reduce it slowly lower tin foil over the antenna and watch the voltage. the foil should be a bit bigger than the coil exact size does not matter. - + data pulled from here https://docs.google.com/spreadsheets/d/13P_GE6tNYpGvoVUTEQvA3SQzMqpZ-SoiWaTNoJoTV9Q/edit?usp=sharing --]] @@ -663,12 +663,12 @@ function get_profile_data(profile_name) [9] = "010D0000" } } - + -- When called without arguments, return the whole table if not profile_name then return profiles end - + -- Otherwise return the specific profile or wipe chip return profiles[profile_name] or profiles["wipe chip"] end @@ -676,7 +676,7 @@ end function get_profile_names() -- Get the complete profiles table from get_profile_data local all_profiles = get_profile_data() - + local names = {} for name, _ in pairs(all_profiles) do table.insert(names, name) @@ -687,16 +687,16 @@ end function select_profile() local profile_names = get_profile_names() - + print("\nAvailable profiles:") for i, name in ipairs(profile_names) do print(string.format("%d. %s", i, name)) end - + while true do io.write("\nSelect profile (1-" .. #profile_names .. "): ") local choice = tonumber(io.read()) - + if choice and choice >= 1 and choice <= #profile_names then return profile_names[choice] else @@ -707,40 +707,40 @@ end function main() print("\n[=== kyber crystal programmer ===]") - + -- Get profile from command line argument or prompt user local profile_name = args and args[1] if not profile_name then --print("\nNo profile specified as argument.") profile_name = select_profile() end - + local data_to_write = get_profile_data(profile_name) print("\n[+] Using profile: " .. profile_name) - + -- Display what will be written print("\n[+] Data to be written:") for addr, data in pairs(data_to_write) do print(string.format("Address %d: %s", addr, data)) end - + -- Step 1: Wipe the tag print("\n[+] Wiping tag...") send_command("lf em 4x05 wipe --4305") - + -- Step 2: Write data print("\n[+] Writing data...") for addr, data in pairs(data_to_write) do send_command("lf em 4x05 write -a " .. addr .. " -d " .. data) utils.Sleep(0.5) end - + -- Step 3: Read back and display data for verification print("\n[+] Verifying writes by reading back data...") for addr, expected_data in pairs(data_to_write) do local output = send_command("lf em 4x05 read -a " .. addr) end - + print("\n[+] Read complete. Review output above.") end diff --git a/client/luascripts/lf_awid_bulkclone.lua b/client/luascripts/lf_awid_bulkclone.lua index 63394aa99..3fa145ec2 100644 --- a/client/luascripts/lf_awid_bulkclone.lua +++ b/client/luascripts/lf_awid_bulkclone.lua @@ -10,7 +10,7 @@ For more info, check the comments in the code ]] example = [[ -- - script run lf_awid_bulkclone.lua -f 1 -b 1000 + script run lf_awid_bulkclone.lua -f 1 -b 1000 ]] usage = [[ script run lf_awid_bulkclone.lua -f facility -b base_id_num @@ -91,7 +91,7 @@ local function main(args) end end if o == 'b' then - if isempty(a) then + if isempty(a) then print('You did not supply a starting card number, using 59615') cn = 59615 else @@ -105,18 +105,18 @@ local function main(args) print("Session Start: " .. sessionStart) print("Facility Code,Card Number") - + while true do print(string.format("Preparing to Write: Facility Code %d, Card Number %d", fc, cn)) - + local command = string.format("lf awid clone --fmt 26 --fc %d --cn %d", fc, cn) core.console(command) - + print(string.format("%d,%d", fc, cn)) - + print("Press Enter to continue with the next card number or type 'q' and press Enter to quit.") local user_input = io.read() - + if user_input:lower() == 'q' then break else diff --git a/client/luascripts/lf_electra.lua b/client/luascripts/lf_electra.lua index 7502522e8..2c130ba7a 100644 --- a/client/luascripts/lf_electra.lua +++ b/client/luascripts/lf_electra.lua @@ -145,7 +145,7 @@ local function readfile() local f = io.open(ID_STATUS, "r") for line in f:lines() do id = line:match"^(%x+)" - if id then break end + if id then break end end f:close() if not id then diff --git a/client/luascripts/lf_hid_bulkclone_v2.lua b/client/luascripts/lf_hid_bulkclone_v2.lua index 31615d19d..50762e8f1 100644 --- a/client/luascripts/lf_hid_bulkclone_v2.lua +++ b/client/luascripts/lf_hid_bulkclone_v2.lua @@ -14,7 +14,7 @@ example = [[ script run lf_hid_bulkclone_v2.lua -f 1 -b 1000 ]] usage = [[ -script run lf_hid_bulkclone_v2.lua -f facility -b base_id_num +script run lf_hid_bulkclone_v2.lua -f facility -b base_id_num ]] arguments = [[ -h : this help @@ -67,7 +67,7 @@ local function exitMsg(msg) end local function main(args) - + print( string.rep('--',20) ) print( string.rep('--',20) ) print() @@ -107,7 +107,7 @@ local function main(args) print("Press Enter to write the next card, type 'r' and press Enter to retry, or type 'q' and press Enter to quit.") local user_input = io.read() - + if user_input:lower() == 'q' then print("Timestamp: ", timestamp) print("Successful Writes:") diff --git a/client/luascripts/lf_ioprox_bulkclone.lua b/client/luascripts/lf_ioprox_bulkclone.lua index 37e9c4ce9..31fb91e76 100644 --- a/client/luascripts/lf_ioprox_bulkclone.lua +++ b/client/luascripts/lf_ioprox_bulkclone.lua @@ -69,7 +69,7 @@ local function exitMsg(msg) end local function main(args) - + print( string.rep('--',20) ) print( string.rep('--',20) ) print() diff --git a/client/luascripts/lf_t55xx_fix.lua b/client/luascripts/lf_t55xx_fix.lua index cfdffc8f0..b793cf629 100644 --- a/client/luascripts/lf_t55xx_fix.lua +++ b/client/luascripts/lf_t55xx_fix.lua @@ -18,7 +18,7 @@ desc = [[ is found, it uses the wipe command to erase the T5577. Then the reanimation procedure is applied. If the password is not found or doesn't exist the script only performs the reanimation procedure. The script revives 99% of blocked tags. -]] + ]] usage = [[ script run lf_t55xx_fix ]] @@ -87,7 +87,7 @@ local function reanimate_t5577(password) p:console('lf t55 wipe -p ' .. password) print("T5577 wiped using a password: " ..ac.green.. password ..ac.reset) else - print(ac.yellow.."No valid password found, proceeding with reanimation."..ac.reset) + print(ac.yellow.." No valid password found, proceeding with reanimation."..ac.reset) end p:console('lf t55 write -b 0 -d 000880E8 -p 00000000') diff --git a/client/luascripts/paxton_clone.lua b/client/luascripts/paxton_clone.lua index b8aaf2a9d..c4fb206be 100644 --- a/client/luascripts/paxton_clone.lua +++ b/client/luascripts/paxton_clone.lua @@ -15,10 +15,10 @@ command('clear') author = ' Author: jareckib - 30.01.2025' tutorial = ' Based on Equipter tutorial - Downgrade Paxton to EM4102' version = ' version v1.20' -desc = [[ - The script automates the copying of Paxton fobs read - write. - It also allows manual input of data for blocks 4-7. - The third option is reading data stored in the log file and create new fob. +desc = [[ + The script automates the copying of Paxton fobs read - write. + It also allows manual input of data for blocks 4-7. + The third option is reading data stored in the log file and create new fob. Additionally, the script calculates the ID for downgrading Paxton to EM4102. ]] @@ -61,7 +61,7 @@ local function reset_log_file() file:write("") file:close() end - + local function read_log_file(logfile) local file = io.open(logfile, "r") if not file then @@ -331,7 +331,7 @@ local function main(args) print(ac.cyan .. ' 1' .. ac.reset .. ' - Read Paxton blocks 4-7 to make a copy') print(ac.cyan .. ' 2' .. ac.reset .. ' - Manually input data for Paxton blocks 4-7') print(ac.cyan .. " 3" .. ac.reset .. " - Search in Paxton_log by name and use the data") - print(dash) + print(dash) while true do io.write(' Your choice '..ac.cyan..'(1/2/3): ' .. ac.reset) input_option = io.read() @@ -428,7 +428,7 @@ local function main(args) was_option_3 = true local retries = 3 while retries > 0 do - io.write(' Enter the name to search ('..retries..' attempts) : '..ac.yellow) + io.write(' Enter the name to search ('..retries..' attempts) : '..ac.yellow) local user_input = io.read() io.write(ac.reset..'') if user_input == nil or user_input:match("^%s*$") then diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index f61d7d1fb..7d05c2e75 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -3073,7 +3073,7 @@ plot: } } - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); if (verbose && found == false) { PrintAndLogEx(FAILED, "no ISO 14443-B tag found"); diff --git a/client/src/cmdhfcryptorf.c b/client/src/cmdhfcryptorf.c index af703e511..1a00881e1 100644 --- a/client/src/cmdhfcryptorf.c +++ b/client/src/cmdhfcryptorf.c @@ -232,8 +232,7 @@ int readHFCryptoRF(bool loop, bool verbose) { PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex_inrow(card.uid, card.uidlen)); set_last_known_card(card); } - } while (loop && kbd_enter_pressed() == false); - + } while (loop && (kbd_enter_pressed() == false)); DropField(); return res; } diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 278b301c4..3b2a53b49 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -348,7 +348,7 @@ int read_felica_uid(bool loop, bool verbose) { res = PM3_SUCCESS; } - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); DropField(); return res; diff --git a/client/src/cmdhffudan.c b/client/src/cmdhffudan.c index 52ed3bc5f..817749353 100644 --- a/client/src/cmdhffudan.c +++ b/client/src/cmdhffudan.c @@ -199,7 +199,7 @@ int read_fudan_uid(bool loop, bool verbose) { PrintAndLogEx(NORMAL, ""); } - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); return PM3_SUCCESS; diff --git a/client/src/cmdhflto.c b/client/src/cmdhflto.c index 51ede17ca..1eadb3d67 100644 --- a/client/src/cmdhflto.c +++ b/client/src/cmdhflto.c @@ -438,7 +438,7 @@ int reader_lto(bool loop, bool verbose) { PrintAndLogEx(INFO, "UID......... " _GREEN_("%s"), sprint_hex_inrow(serial, sizeof(serial))); } - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); lto_switch_off_field(); return ret; diff --git a/client/src/cmdhftexkom.c b/client/src/cmdhftexkom.c index 6993d70cf..8a6cee6f2 100644 --- a/client/src/cmdhftexkom.c +++ b/client/src/cmdhftexkom.c @@ -623,7 +623,7 @@ int read_texkom_uid(bool loop, bool verbose) { } } - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdhftopaz.c b/client/src/cmdhftopaz.c index 05069a35b..ca3efdaa3 100644 --- a/client/src/cmdhftopaz.c +++ b/client/src/cmdhftopaz.c @@ -109,7 +109,7 @@ static int topaz_select(uint8_t *atqa, uint8_t atqa_len, uint8_t *rid_response, } // read all of the static memory of a selected Topaz tag. -static int topaz_rall(uint8_t *uid, uint8_t *response) { +static int topaz_rall(const uint8_t *uid, uint8_t *response) { uint16_t resp_len = 124; uint8_t rall_cmd[] = {TOPAZ_RALL, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -1180,7 +1180,7 @@ int readTopazUid(bool loop, bool verbose) { topaz_tag.HR01[0] = rid_response[0]; topaz_tag.HR01[1] = rid_response[1]; - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); topaz_switch_off_field(); return res; diff --git a/client/src/cmdhfxerox.c b/client/src/cmdhfxerox.c index 0bec9a1d9..713b7f616 100644 --- a/client/src/cmdhfxerox.c +++ b/client/src/cmdhfxerox.c @@ -593,7 +593,7 @@ int read_xerox_uid(bool loop, bool verbose) { return PM3_ESOFT; } - } while (loop && kbd_enter_pressed() == false); + } while (loop && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfdestron.c b/client/src/cmdlfdestron.c index c8cd6c596..a9c6b1bd2 100644 --- a/client/src/cmdlfdestron.c +++ b/client/src/cmdlfdestron.c @@ -129,7 +129,7 @@ static int CmdDestronReader(const char *Cmd) { do { lf_read(false, 16000); demodDestron(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlffdxb.c b/client/src/cmdlffdxb.c index c3034f98f..853c10dde 100644 --- a/client/src/cmdlffdxb.c +++ b/client/src/cmdlffdxb.c @@ -692,7 +692,7 @@ static int CmdFdxBReader(const char *Cmd) { lf_read(false, 10000); ret = demodFDXB(!cm); // be verbose only if not in continuous mode - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); if (old_div != curr_div) { diff --git a/client/src/cmdlfgallagher.c b/client/src/cmdlfgallagher.c index b4955290d..98e6bf524 100644 --- a/client/src/cmdlfgallagher.c +++ b/client/src/cmdlfgallagher.c @@ -132,7 +132,7 @@ static int CmdGallagherReader(const char *Cmd) { do { lf_read(false, 4096 * 2 + 20); demodGallagher(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfhitaghts.c b/client/src/cmdlfhitaghts.c index f474e1bd1..8af7dc998 100644 --- a/client/src/cmdlfhitaghts.c +++ b/client/src/cmdlfhitaghts.c @@ -968,7 +968,7 @@ static int CmdLFHitagSReader(const char *Cmd) { if (hts_get_uid(&uid)) { PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%08X"), uid); } - } while (cm && kbd_enter_pressed() == false); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfidteck.c b/client/src/cmdlfidteck.c index fa498ea74..b6976fe4d 100644 --- a/client/src/cmdlfidteck.c +++ b/client/src/cmdlfidteck.c @@ -315,7 +315,7 @@ static int CmdIdteckReader(const char *Cmd) { do { lf_read(false, 5000); demodIdteck(NULL, !cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfindala.c b/client/src/cmdlfindala.c index 87ad4d833..53cd2647a 100644 --- a/client/src/cmdlfindala.c +++ b/client/src/cmdlfindala.c @@ -632,7 +632,7 @@ static int CmdIndalaReader(const char *Cmd) { do { lf_read(false, 30000); demodIndalaEx(clk, invert, max_err, !cm); - } while (cm && !kbd_enter_pressed()); + } while (cm & (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfio.c b/client/src/cmdlfio.c index 865b2b74a..a56b6b3a0 100644 --- a/client/src/cmdlfio.c +++ b/client/src/cmdlfio.c @@ -202,7 +202,7 @@ static int CmdIOProxReader(const char *Cmd) { do { lf_read(false, 12000); demodIOProx(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfjablotron.c b/client/src/cmdlfjablotron.c index 290181ae7..d0f9305d8 100644 --- a/client/src/cmdlfjablotron.c +++ b/client/src/cmdlfjablotron.c @@ -152,7 +152,7 @@ static int CmdJablotronReader(const char *Cmd) { do { lf_read(false, 16000); demodJablotron(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfkeri.c b/client/src/cmdlfkeri.c index fc599a646..4fc5cac72 100644 --- a/client/src/cmdlfkeri.c +++ b/client/src/cmdlfkeri.c @@ -221,7 +221,7 @@ static int CmdKeriReader(const char *Cmd) { do { lf_read(false, 10000); demodKeri(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfmotorola.c b/client/src/cmdlfmotorola.c index 99caaa7a3..04cf7fe20 100644 --- a/client/src/cmdlfmotorola.c +++ b/client/src/cmdlfmotorola.c @@ -179,7 +179,7 @@ static int CmdMotorolaReader(const char *Cmd) { // 64 * 32 * 2 * n-ish lf_read(false, 5000); res = demodMotorola(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); // reset back to 125 kHz sc.divisor = LF_DIVISOR_125; diff --git a/client/src/cmdlfnedap.c b/client/src/cmdlfnedap.c index ac6bea204..ac50d4851 100644 --- a/client/src/cmdlfnedap.c +++ b/client/src/cmdlfnedap.c @@ -296,7 +296,7 @@ static int CmdLFNedapReader(const char *Cmd) { do { lf_read(false, 16000); demodNedap(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfnexwatch.c b/client/src/cmdlfnexwatch.c index b1a162817..505ca6d7e 100644 --- a/client/src/cmdlfnexwatch.c +++ b/client/src/cmdlfnexwatch.c @@ -287,7 +287,7 @@ static int CmdNexWatchReader(const char *Cmd) { do { lf_read(false, 20000); demodNexWatch(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfnoralsy.c b/client/src/cmdlfnoralsy.c index 2a0fbd2b2..b10ee7f97 100644 --- a/client/src/cmdlfnoralsy.c +++ b/client/src/cmdlfnoralsy.c @@ -150,7 +150,7 @@ static int CmdNoralsyReader(const char *Cmd) { do { lf_read(false, 8000); demodNoralsy(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfpac.c b/client/src/cmdlfpac.c index 8d9dbebf4..8d00c40df 100644 --- a/client/src/cmdlfpac.c +++ b/client/src/cmdlfpac.c @@ -214,7 +214,7 @@ static int CmdPacReader(const char *Cmd) { do { lf_read(false, 4096 * 2 + 20); demodPac(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfparadox.c b/client/src/cmdlfparadox.c index dae518311..71aa19ebe 100644 --- a/client/src/cmdlfparadox.c +++ b/client/src/cmdlfparadox.c @@ -285,7 +285,7 @@ static int CmdParadoxReader(const char *Cmd) { do { lf_read(false, 10000); demodParadox(!cm, old); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfpcf7931.c b/client/src/cmdlfpcf7931.c index ae19b174f..df18617dc 100644 --- a/client/src/cmdlfpcf7931.c +++ b/client/src/cmdlfpcf7931.c @@ -84,7 +84,7 @@ static int CmdLFPCF7931Reader(const char *Cmd) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfpresco.c b/client/src/cmdlfpresco.c index a3f1c89e2..969a054ce 100644 --- a/client/src/cmdlfpresco.c +++ b/client/src/cmdlfpresco.c @@ -160,7 +160,7 @@ static int CmdPrescoReader(const char *Cmd) { do { lf_read(false, 12000); demodPresco(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfpyramid.c b/client/src/cmdlfpyramid.c index 9e24c319b..e763c68e3 100644 --- a/client/src/cmdlfpyramid.c +++ b/client/src/cmdlfpyramid.c @@ -231,7 +231,7 @@ static int CmdPyramidReader(const char *Cmd) { do { lf_read(false, 15000); demodPyramid(true); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfsecurakey.c b/client/src/cmdlfsecurakey.c index f4e381e42..30e997421 100644 --- a/client/src/cmdlfsecurakey.c +++ b/client/src/cmdlfsecurakey.c @@ -161,7 +161,7 @@ static int CmdSecurakeyReader(const char *Cmd) { do { lf_read(false, 8000); demodSecurakey(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfti.c b/client/src/cmdlfti.c index 4664a344d..17d843a7c 100644 --- a/client/src/cmdlfti.c +++ b/client/src/cmdlfti.c @@ -325,7 +325,7 @@ static int CmdTIReader(const char *Cmd) { do { clearCommandBuffer(); SendCommandNG(CMD_LF_TI_READ, NULL, 0); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index 6807e828c..7c206af4b 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -102,7 +102,7 @@ static int CmdVikingReader(const char *Cmd) { do { lf_read(false, 10000); demodViking(true); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfvisa2000.c b/client/src/cmdlfvisa2000.c index 7de1e9334..092f38e30 100644 --- a/client/src/cmdlfvisa2000.c +++ b/client/src/cmdlfvisa2000.c @@ -185,7 +185,7 @@ static int CmdVisa2kReader(const char *Cmd) { do { lf_read(false, 20000); demodVisa2k(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfzx8211.c b/client/src/cmdlfzx8211.c index 03fcd6053..793cc3d54 100644 --- a/client/src/cmdlfzx8211.c +++ b/client/src/cmdlfzx8211.c @@ -138,7 +138,7 @@ static int CmdzxReader(const char *Cmd) { do { lf_Zx_read(); demodzx(!cm); - } while (cm && !kbd_enter_pressed()); + } while (cm && (kbd_enter_pressed() == false)); return PM3_SUCCESS; } diff --git a/client/src/cmdmqtt.c b/client/src/cmdmqtt.c index ace6c62c1..e805f473d 100644 --- a/client/src/cmdmqtt.c +++ b/client/src/cmdmqtt.c @@ -30,12 +30,17 @@ #include "util_posix.h" // time #include "fileutils.h" - #define MQTT_BUFFER_SIZE ( 1 << 16 ) static int CmdHelp(const char *Cmd); static void mqtt_publish_callback(void **unused, struct mqtt_response_publish *published) { + + if (published == NULL) { + return; + } + + // note that published->topic_name is NOT null-terminated (here we'll change it to a c-string) char *topic_name = (char *) calloc(published->topic_name_size + 1, 1); memcpy(topic_name, published->topic_name, published->topic_name_size); diff --git a/client/src/cmdsmartcard.c b/client/src/cmdsmartcard.c index 46b467f94..6e1c67ba5 100644 --- a/client/src/cmdsmartcard.c +++ b/client/src/cmdsmartcard.c @@ -1469,7 +1469,7 @@ static int CmdPCSC(const char *Cmd) { msleep(300); } - } while (!kbd_enter_pressed()); + } while (kbd_enter_pressed() == false); mbedtls_net_close(&netCtx); mbedtls_net_free(&netCtx); diff --git a/client/src/fileutils.c b/client/src/fileutils.c index 805235e42..4ab0b7595 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -89,6 +89,8 @@ DumpFileType_t get_filetype(const char *filename) { o = FLIPPER; } else if (str_endswith(s, "picopass")) { o = FLIPPER; + } else if (str_endswith(s, "xml")) { + o = TAGINFO; } else { // mfd, trc, trace is binary o = BIN; @@ -3323,6 +3325,10 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl } break; } + case TAGINFO: { + //res = loadFileXML_safe(fn, ".xml", pdump, dumplen); + break; + } } return res; } diff --git a/client/src/fileutils.h b/client/src/fileutils.h index bbb548fb9..a2d31c196 100644 --- a/client/src/fileutils.h +++ b/client/src/fileutils.h @@ -83,6 +83,7 @@ typedef enum { DICTIONARY, MCT, FLIPPER, + TAGINFO, } DumpFileType_t; typedef enum { diff --git a/client/src/uart/uart_common.c b/client/src/uart/uart_common.c index e10897326..f797d763c 100644 --- a/client/src/uart/uart_common.c +++ b/client/src/uart/uart_common.c @@ -39,31 +39,36 @@ #endif bool uart_bind(void *socket, const char *bindAddrStr, const char *bindPortStr, bool isBindingIPv6) { - if (bindAddrStr == NULL && bindPortStr == NULL) + if (bindAddrStr == NULL && bindPortStr == NULL) { return true; // no need to bind + } struct sockaddr_storage bindSockaddr; memset(&bindSockaddr, 0, sizeof(bindSockaddr)); int bindPort = 0; // 0: port unspecified - if (bindPortStr != NULL) + if (bindPortStr != NULL) { bindPort = atoi(bindPortStr); + } - if (!isBindingIPv6) { + if (isBindingIPv6 == false) { struct sockaddr_in *bindSockaddr4 = (struct sockaddr_in *)&bindSockaddr; bindSockaddr4->sin_family = AF_INET; bindSockaddr4->sin_port = htons(bindPort); - if (bindAddrStr == NULL) + if (bindAddrStr == NULL) { bindSockaddr4->sin_addr.s_addr = INADDR_ANY; - else + } else { bindSockaddr4->sin_addr.s_addr = inet_addr(bindAddrStr); + } + } else { struct sockaddr_in6 *bindSockaddr6 = (struct sockaddr_in6 *)&bindSockaddr; bindSockaddr6->sin6_family = AF_INET6; bindSockaddr6->sin6_port = htons(bindPort); - if (bindAddrStr == NULL) + if (bindAddrStr == NULL) { bindSockaddr6->sin6_addr = in6addr_any; - else + } else { inet_pton(AF_INET6, bindAddrStr, &(bindSockaddr6->sin6_addr)); + } } #ifdef _WIN32 int res = bind(*(SOCKET *)socket, (struct sockaddr *)&bindSockaddr, sizeof(bindSockaddr)); diff --git a/client/src/util.c b/client/src/util.c index ff71b74e1..133f639ee 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -73,6 +73,7 @@ int kbd_enter_pressed(void) { c = getchar(); ret |= c == '\n'; } while (c != EOF); + //blocking flags &= ~O_NONBLOCK; if (fcntl(STDIN_FILENO, F_SETFL, flags) < 0) { diff --git a/common/crc16.c b/common/crc16.c index e55b20bdc..ecf8c1ed7 100644 --- a/common/crc16.c +++ b/common/crc16.c @@ -27,12 +27,14 @@ static CrcType_t current_crc_type = CRC_NONE; void init_table(CrcType_t crctype) { // same crc algo, and initialised already - if (crctype == current_crc_type && crc_table_init) + if (crctype == current_crc_type && crc_table_init) { return; + } // not the same crc algo. reset table. - if (crctype != current_crc_type) + if (crctype != current_crc_type) { reset_table(); + } current_crc_type = crctype; @@ -68,23 +70,29 @@ void init_table(CrcType_t crctype) { void generate_table(uint16_t polynomial, bool refin) { for (uint16_t i = 0; i < 256; i++) { + uint16_t c, crc = 0; - if (refin) + + if (refin) { c = reflect8(i) << 8; - else + } else { c = i << 8; + } for (uint16_t j = 0; j < 8; j++) { - if ((crc ^ c) & 0x8000) + if ((crc ^ c) & 0x8000) { crc = (crc << 1) ^ polynomial; - else + } else { crc = crc << 1; + } c = c << 1; } - if (refin) + + if (refin) { crc = reflect16(crc); + } crc_table[i] = crc; } @@ -102,21 +110,25 @@ uint16_t crc16_fast(uint8_t const *d, size_t n, uint16_t initval, bool refin, bo // fast lookup table algorithm without augmented zero bytes, e.g. used in pkzip. // only usable with polynom orders of 8, 16, 24 or 32. - if (n == 0) + if (n == 0) { return (~initval); + } uint16_t crc = initval; - if (refin) + if (refin) { crc = reflect16(crc); + } - if (!refin) + if (refin == false) { while (n--) crc = (crc << 8) ^ crc_table[((crc >> 8) ^ *d++) & 0xFF ]; - else + } else { while (n--) crc = (crc >> 8) ^ crc_table[(crc & 0xFF) ^ *d++]; + } - if (refout ^ refin) + if (refout ^ refin) { crc = reflect16(crc); + } return crc; } @@ -143,8 +155,9 @@ uint16_t update_crc16(uint16_t crc, uint8_t c) { // two ways. msb or lsb loop. uint16_t Crc16(uint8_t const *d, size_t bitlength, uint16_t remainder, uint16_t polynomial, bool refin, bool refout) { - if (bitlength == 0) + if (bitlength == 0) { return (~remainder); + } uint8_t offset = 8 - (bitlength % 8); // front padding with 0s won't change the CRC result @@ -153,7 +166,9 @@ uint16_t Crc16(uint8_t const *d, size_t bitlength, uint16_t remainder, uint16_t uint8_t c = prebits | d[i] >> offset; prebits = d[i] << (8 - offset); - if (refin) c = reflect8(c); + if (refin) { + c = reflect8(c); + } // xor in at msb remainder ^= (c << 8); @@ -167,8 +182,10 @@ uint16_t Crc16(uint8_t const *d, size_t bitlength, uint16_t remainder, uint16_t } } } - if (refout) + + if (refout) { remainder = reflect16(remainder); + } return remainder; } diff --git a/doc/commands.json b/doc/commands.json index 0d4ab0c28..2c1c2b02a 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -12363,7 +12363,7 @@ "--addr MQTT server address", "-p, --port MQTT server port", "--topic MQTT topic", - "-f, --file file to send" + "-f, --file file name to use for received files" ], "usage": "mqtt receive [-h] [--addr ] [-p ] [--topic ] [-f ]" }, @@ -13413,6 +13413,6 @@ "metadata": { "commands_extracted": 770, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-07-08T19:08:23" + "extracted_on": "2025-07-10T05:07:28" } } From 2d3ad38853a5010037489fb1becebcf0e05e9cf3 Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 10 Jul 2025 13:22:21 +0300 Subject: [PATCH 074/149] Update CHANGELOG.md Signed-off-by: Alexander --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5875edeec..5d8a9132c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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] +- Fixed `mqtt` segfault and gdb warning under windows (proper thread stopping and socket handling). (@virtyvoid) - Added `mqtt` - the pm3 client can now send and receive MQTT messages or json files. (@iceman1001) - Changed `hf iclass wrbl` - replay behavior to use privilege escalation if the macs field is not passed empty(@antiklesys) - Changed `hf iclass restore` - it now supports privilege escalation to restore card content using replay (@antiklesys) From c216fbeeaf194bd6cdbbc3836a832b9de7a57b39 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 10 Jul 2025 14:10:53 +0200 Subject: [PATCH 075/149] preferences and mqtt commands now handles mqtt server/port/topic settings. if mqtt command is called w/o any mqtt and preference is set it takes prio --- CHANGELOG.md | 2 + client/src/cmdmqtt.c | 30 +++++- client/src/preferences.c | 200 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 226 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d8a9132c..4fce6d72a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ 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] +- Changed `mqtt` commnands - now honors preference settings (@iceman1001) +- Changed `prefs` - now handles MQTT settings too (@iceman1001) - Fixed `mqtt` segfault and gdb warning under windows (proper thread stopping and socket handling). (@virtyvoid) - Added `mqtt` - the pm3 client can now send and receive MQTT messages or json files. (@iceman1001) - Changed `hf iclass wrbl` - replay behavior to use privilege escalation if the macs field is not passed empty(@antiklesys) diff --git a/client/src/cmdmqtt.c b/client/src/cmdmqtt.c index e805f473d..759b2f827 100644 --- a/client/src/cmdmqtt.c +++ b/client/src/cmdmqtt.c @@ -166,7 +166,7 @@ static int mqtt_receive(const char *addr, const char *port, const char *topic, c // subscribe to a topic with a max QoS level of 0 mqtt_subscribe(&client, topic, 0); - PrintAndLogEx(INFO, _CYAN_("%s") " listening at " _CYAN_("%s:%s") " for " _YELLOW_("%s") " messages", cid, addr, port, topic); + PrintAndLogEx(INFO, _CYAN_("%s") " listening at " _CYAN_("%s:%s/%s"), cid, addr, port, topic); PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); while (kbd_enter_pressed() == false) { @@ -299,13 +299,27 @@ static int CmdMqttSend(const char *Cmd) { } if (alen == 0) { + if (strlen(g_session.mqtt_server)) { + strcpy(addr, g_session.mqtt_server); + } else { strcpy(addr, "proxdump.com"); } + } + if (plen == 0) { + if (strlen(g_session.mqtt_port)) { + strcpy(port, g_session.mqtt_port); + } else { strcpy(port, "1883"); } + } + if (tlen == 0) { + if (strlen(g_session.mqtt_topic)) { + strcpy(topic, g_session.mqtt_topic); + } else { strcpy(topic, "proxdump"); + } } if (fnlen) { @@ -362,13 +376,27 @@ static int CmdMqttReceive(const char *Cmd) { } if (alen == 0) { + if (strlen(g_session.mqtt_server)) { + strcpy(addr, g_session.mqtt_server); + } else { strcpy(addr, "proxdump.com"); } + } + if (plen == 0) { + if (strlen(g_session.mqtt_port)) { + strcpy(port, g_session.mqtt_port); + } else { strcpy(port, "1883"); } + } + if (tlen == 0) { + if (strlen(g_session.mqtt_topic)) { + strcpy(topic, g_session.mqtt_topic); + } else { strcpy(topic, "proxdump"); + } } return mqtt_receive(addr, port, topic, filename); diff --git a/client/src/preferences.c b/client/src/preferences.c index 37941ed65..f654a8533 100644 --- a/client/src/preferences.c +++ b/client/src/preferences.c @@ -48,6 +48,54 @@ static char *prefGetFilename(void) { return str_dup(preferencesFilename); } +static bool setDefaultMqttServer(const char *srv) { + + if ((srv == NULL) && (g_session.mqtt_server != NULL)) { + free(g_session.mqtt_server); + g_session.mqtt_server = NULL; + } + + if (srv == NULL) { + return false; + } + + g_session.mqtt_server = (char *)realloc(g_session.mqtt_server, strlen(srv) + 1); + strcpy(g_session.mqtt_server, srv); + return true; +} + +static bool setDefaultMqttPort(const char *port) { + + if ((port == NULL) && (g_session.mqtt_port != NULL)) { + free(g_session.mqtt_port); + g_session.mqtt_port = NULL; + } + + if (port == NULL) { + return false; + } + + g_session.mqtt_port = (char *)realloc(g_session.mqtt_port, strlen(port) + 1); + strcpy(g_session.mqtt_port, port); + return true; +} + +static bool setDefaultMqttTopic(const char *topic) { + + if ((topic == NULL) && (g_session.mqtt_topic != NULL)) { + free(g_session.mqtt_topic); + g_session.mqtt_topic = NULL; + } + + if (topic == NULL) { + return false; + } + + g_session.mqtt_topic = (char *)realloc(g_session.mqtt_topic, strlen(topic) + 1); + strcpy(g_session.mqtt_topic, topic); + return true; +} + int preferences_load(void) { // Set all defaults @@ -73,6 +121,10 @@ int preferences_load(void) { setDefaultPath(spDump, ""); setDefaultPath(spTrace, ""); + setDefaultMqttServer(""); + setDefaultMqttPort(""); + setDefaultMqttTopic(""); + // default save path if (get_my_user_directory() != NULL) { // should return path to .proxmark3 folder setDefaultPath(spDefault, get_my_user_directory()); @@ -277,7 +329,13 @@ void preferences_save_callback(json_t *root) { */ JsonSaveInt(root, "client.exe.delay", g_session.client_exe_delay); JsonSaveInt(root, "client.timeout", g_session.timeout); + + // MQTT + JsonSaveStr(root, "mqtt.server", g_session.mqtt_server); + JsonSaveStr(root, "mqtt.port", g_session.mqtt_port); + JsonSaveStr(root, "mqtt.topic", g_session.mqtt_topic); } + void preferences_load_callback(json_t *root) { json_error_t up_error = {0}; int b1; @@ -381,6 +439,17 @@ void preferences_load_callback(json_t *root) { // client command timeout if (json_unpack_ex(root, &up_error, 0, "{s:i}", "client.timeout", &i1) == 0) g_session.timeout = i1; + + // MQTT server + if (json_unpack_ex(root, &up_error, 0, "{s:s}", "mqtt.server", &s1) == 0) + setDefaultMqttServer(s1); + + if (json_unpack_ex(root, &up_error, 0, "{s:s}", "mqtt.port", &s1) == 0) + setDefaultMqttPort(s1); + + if (json_unpack_ex(root, &up_error, 0, "{s:s}", "mqtt.topic", &s1) == 0) + setDefaultMqttTopic(s1); + } // Help Functions @@ -400,7 +469,6 @@ static const char *pref_show_status_msg(prefShowOpt_t opt) { case prefShowUnknown: default: return ""; - } } @@ -520,6 +588,7 @@ static void showSavePathState(savePaths_t path_index, prefShowOpt_t opt) { case spItemCount: default: strcpy(s, _RED_("unknown")" save path......."); + break; } if (path_index < spItemCount) { @@ -602,6 +671,30 @@ static void showClientTimeoutState(void) { PrintAndLogEx(INFO, " communication timeout... " _GREEN_("%u") " ms", g_session.timeout); } +static void showMqttServer(prefShowOpt_t opt) { + if ((g_session.mqtt_server == NULL) || (strcmp(g_session.mqtt_server, "") == 0)) { + PrintAndLogEx(INFO, " MQTT server.............%s "_WHITE_("not set"), pref_show_status_msg(opt)); + } else { + PrintAndLogEx(INFO, " MQTT server.............%s "_GREEN_("%s"), pref_show_status_msg(opt), g_session.mqtt_server); + } +} + +static void showMqttPort(prefShowOpt_t opt) { + if ((g_session.mqtt_port == NULL) || (strcmp(g_session.mqtt_port, "") == 0)) { + PrintAndLogEx(INFO, " MQTT port...............%s "_WHITE_("not set"), pref_show_status_msg(opt)); + } else { + PrintAndLogEx(INFO, " MQTT port...............%s "_GREEN_("%s"), pref_show_status_msg(opt), g_session.mqtt_port); + } +} + +static void showMqttTopic(prefShowOpt_t opt) { + if ((g_session.mqtt_topic == NULL) || (strcmp(g_session.mqtt_topic, "") == 0)) { + PrintAndLogEx(INFO, " MQTT topic..............%s "_WHITE_("not set"), pref_show_status_msg(opt)); + } else { + PrintAndLogEx(INFO, " MQTT topic..............%s "_GREEN_("%s"), pref_show_status_msg(opt), g_session.mqtt_topic); + } +} + static int setCmdEmoji(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "prefs set emoji ", @@ -937,7 +1030,7 @@ static int setCmdExeDelay(const char *Cmd) { return PM3_SUCCESS; } -static int setClientTimeout(const char *Cmd) { +static int setCmdClientTimeout(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "prefs set client.timeout", "Set persistent preference of client communication timeout", @@ -1188,6 +1281,80 @@ static int setCmdBarMode(const char *Cmd) { return PM3_SUCCESS; } +static int setCmdMqtt(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs set mqtt", + "Set persistent preference MQTT Server in the client", + "prefs set mqtt -s test.mosquito.com\n" + "prefs set mqtt -s test.mosquito.com -p 1883 -t proxdump\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("s", "srv", "", "default MQTT Server"), + arg_str0("p", "port", "", "default MQTT Port"), + arg_str0("t", "topic", "", "default MQTT Topic"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int deflen = 0; + char def_server[128] = {0}; + memset(def_server, 0, sizeof(def_server)); + int res = CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)def_server, sizeof(def_server), &deflen); + + int plen = 0; + char def_port[10] = {0}; + memset(def_port, 0, sizeof(def_port)); + res |= CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)def_port, sizeof(def_port), &plen); + + int tlen = 0; + char def_topic[10] = {0}; + memset(def_topic, 0, sizeof(def_topic)); + res |= CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)def_topic, sizeof(def_topic), &tlen); + CLIParserFree(ctx); + + // sanity checks + if (res) { + PrintAndLogEx(FAILED, "Error parsing input strings"); + return PM3_EINVARG; + } + + if (deflen) { + if (strcmp(def_server, g_session.mqtt_server) != 0) { + showMqttServer(prefShowOLD); + setDefaultMqttServer(def_server); + showMqttServer(prefShowNEW); + preferences_save(); + } else { + showMqttServer(prefShowNone); + } + } + + if (plen) { + if (strcmp(def_port, g_session.mqtt_port) != 0) { + showMqttPort(prefShowOLD); + setDefaultMqttPort(def_port); + showMqttPort(prefShowNEW); + preferences_save(); + } else { + showMqttPort(prefShowNone); + } + } + + if (tlen) { + if (strcmp(def_topic, g_session.mqtt_topic) != 0) { + showMqttTopic(prefShowOLD); + setDefaultMqttTopic(def_topic); + showMqttTopic(prefShowNEW); + preferences_save(); + } else { + showMqttTopic(prefShowNone); + } + } + + return PM3_SUCCESS; +} + static int getCmdEmoji(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "prefs get emoji", @@ -1334,7 +1501,7 @@ static int getCmdExeDelay(const char *Cmd) { return PM3_SUCCESS; } -static int getClientTimeout(const char *Cmd) { +static int getCmdClientTimeout(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "prefs get client.timeout", "Get preference of delay time before execution of a command in the client", @@ -1350,11 +1517,29 @@ static int getClientTimeout(const char *Cmd) { return PM3_SUCCESS; } +static int getCmdMqtt(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs get mqtt", + "Get preference of MQTT settings in the client", + "prefs get mqtt" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + showMqttServer(prefShowNone); + showMqttPort(prefShowNone); + showMqttTopic(prefShowNone); + return PM3_SUCCESS; +} + static command_t CommandTableGet[] = { {"barmode", getCmdBarMode, AlwaysAvailable, "Get bar mode preference"}, {"client.debug", getCmdDebug, AlwaysAvailable, "Get client debug level preference"}, {"client.delay", getCmdExeDelay, AlwaysAvailable, "Get client execution delay preference"}, - {"client.timeout", getClientTimeout, AlwaysAvailable, "Get client execution delay preference"}, + {"client.timeout", getCmdClientTimeout, AlwaysAvailable, "Get client execution delay preference"}, {"color", getCmdColor, AlwaysAvailable, "Get color support preference"}, {"savepaths", getCmdSavePaths, AlwaysAvailable, "Get file folder "}, // {"devicedebug", getCmdDeviceDebug, AlwaysAvailable, "Get device debug level"}, @@ -1362,6 +1547,7 @@ static command_t CommandTableGet[] = { {"hints", getCmdHint, AlwaysAvailable, "Get hint display preference"}, {"output", getCmdOutput, AlwaysAvailable, "Get dump output style preference"}, {"plotsliders", getCmdPlotSlider, AlwaysAvailable, "Get plot slider display preference"}, + {"mqtt", getCmdMqtt, AlwaysAvailable, "Get MQTT preference"}, {NULL, NULL, NULL, NULL} }; @@ -1370,7 +1556,7 @@ static command_t CommandTableSet[] = { {"barmode", setCmdBarMode, AlwaysAvailable, "Set bar mode"}, {"client.debug", setCmdDebug, AlwaysAvailable, "Set client debug level"}, {"client.delay", setCmdExeDelay, AlwaysAvailable, "Set client execution delay"}, - {"client.timeout", setClientTimeout, AlwaysAvailable, "Set client communication timeout"}, + {"client.timeout", setCmdClientTimeout, AlwaysAvailable, "Set client communication timeout"}, {"color", setCmdColor, AlwaysAvailable, "Set color support"}, {"emoji", setCmdEmoji, AlwaysAvailable, "Set emoji display"}, @@ -1379,6 +1565,7 @@ static command_t CommandTableSet[] = { // {"devicedebug", setCmdDeviceDebug, AlwaysAvailable, "Set device debug level"}, {"output", setCmdOutput, AlwaysAvailable, "Set dump output style"}, {"plotsliders", setCmdPlotSliders, AlwaysAvailable, "Set plot slider display"}, + {"mqtt", setCmdMqtt, AlwaysAvailable, "Set MQTT default values"}, {NULL, NULL, NULL, NULL} }; @@ -1443,6 +1630,9 @@ static int CmdPrefShow(const char *Cmd) { showClientExeDelayState(); showOutputState(prefShowNone); showClientTimeoutState(); + showMqttServer(prefShowNone); + showMqttPort(prefShowNone); + showMqttTopic(prefShowNone); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; From c25dbf8f2115ff56e06fa9df490e7e8151dd758a Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 10 Jul 2025 16:02:18 +0200 Subject: [PATCH 076/149] forget the struct changes --- client/src/ui.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/src/ui.h b/client/src/ui.h index 95ed70390..b79798862 100644 --- a/client/src/ui.h +++ b/client/src/ui.h @@ -63,6 +63,9 @@ typedef struct { char *history_path; pm3_device_t *current_device; uint32_t timeout; + char *mqtt_server; + char *mqtt_port; + char *mqtt_topic; } session_arg_t; extern session_arg_t g_session; From 66fc610a66819556a98c12fca356b24cb084fda5 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 11 Jul 2025 13:26:04 +0200 Subject: [PATCH 077/149] hf mf info: add detection for unknown backdoor keys and for some backdoor variants --- CHANGELOG.md | 1 + client/src/cmdhfmf.c | 75 ++++++++++++++++++++++++++++------ client/src/mifare/mifarehost.c | 39 ++++++++++++++++++ client/src/mifare/mifarehost.h | 1 + 4 files changed, 103 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fce6d72a..a5e218e7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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] +- Changed `hf mf info` - add detection for unknown backdoor keys and for some backdoor variants (@doegox) - Changed `mqtt` commnands - now honors preference settings (@iceman1001) - Changed `prefs` - now handles MQTT settings too (@iceman1001) - Fixed `mqtt` segfault and gdb warning under windows (proper thread stopping and socket handling). (@virtyvoid) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 63229caf9..dd07c9ab0 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -10342,7 +10342,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { PrintAndLogEx(SUCCESS, "Sector 0 key A... " _GREEN_("%012" PRIX64), e_sector[0].Key[MF_KEY_A]); num_to_bytes(e_sector[0].Key[MF_KEY_A], MIFARE_KEY_SIZE, fkey); - if (mf_read_block(0, MF_KEY_A, key, blockdata) == PM3_SUCCESS) { + if (mf_read_block(0, MF_KEY_A, fkey, blockdata) == PM3_SUCCESS) { fKeyType = MF_KEY_A; } } @@ -10352,7 +10352,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { if (fKeyType == 0xFF) { num_to_bytes(e_sector[0].Key[MF_KEY_B], MIFARE_KEY_SIZE, fkey); - if (mf_read_block(0, MF_KEY_B, key, blockdata) == PM3_SUCCESS) { + if (mf_read_block(0, MF_KEY_B, fkey, blockdata) == PM3_SUCCESS) { fKeyType = MF_KEY_B; } } @@ -10361,33 +10361,56 @@ static int CmdHF14AMfInfo(const char *Cmd) { if (e_sector[1].foundKey[MF_KEY_A]) { PrintAndLogEx(SUCCESS, "Sector 1 key A... " _GREEN_("%012" PRIX64), e_sector[1].Key[MF_KEY_A]); } + + if (e_sector[1].foundKey[MF_KEY_B]) { + PrintAndLogEx(SUCCESS, "Sector 1 key B... " _GREEN_("%012" PRIX64), e_sector[1].Key[MF_KEY_B]); + } } uint8_t k08s[MIFARE_KEY_SIZE] = {0xA3, 0x96, 0xEF, 0xA4, 0xE2, 0x4F}; uint8_t k08[MIFARE_KEY_SIZE] = {0xA3, 0x16, 0x67, 0xA8, 0xCE, 0xC1}; - uint8_t k32[MIFARE_KEY_SIZE] = {0x51, 0x8B, 0x33, 0x54, 0xE7, 0x60}; + uint8_t k32n[MIFARE_KEY_SIZE] = {0x51, 0x8B, 0x33, 0x54, 0xE7, 0x60}; + uint8_t k32n2[MIFARE_KEY_SIZE] = {0x73, 0xB9, 0x83, 0x6C, 0xF1, 0x68}; if (mf_read_block(0, 4, k08s, blockdata) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k08s, sizeof(k08s))); fKeyType = MF_KEY_BD; memcpy(fkey, k08s, sizeof(fkey)); - } else if (mf_read_block(0, 4, k08, blockdata) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k08, sizeof(k08))); fKeyType = MF_KEY_BD; memcpy(fkey, k08, sizeof(fkey)); - } else if (mf_read_block(0, 4, k32, blockdata) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k32, sizeof(k32))); + } else if (mf_read_block(0, 4, k32n, blockdata) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k32n, sizeof(k32n))); fKeyType = MF_KEY_BD; - memcpy(fkey, k32, sizeof(fkey)); + memcpy(fkey, k32n, sizeof(fkey)); + } else if (mf_read_block(0, 4, k32n2, blockdata) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k32n2, sizeof(k32n2))); + fKeyType = MF_KEY_BD; + memcpy(fkey, k32n2, sizeof(fkey)); + } + + if ((fKeyType == MF_KEY_A) || (fKeyType == MF_KEY_B)) { + // we've a key but not a backdoor key + uint8_t blockdata2[MFBLOCK_SIZE] = {0}; + if (mf_read_block(0, fKeyType + 4, key, blockdata2) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Backdoor key..... " _GREEN_("same as keyA/keyB")); + } else if (detect_classic_auth(MF_KEY_BD)) { + PrintAndLogEx(SUCCESS, "Backdoor key..... " _RED_("detected but unknown!")); + PrintAndLogEx(HINT, "Hint: Try........ " + _YELLOW_("hf mf nested --blk 0 -%s -k %s --tblk 0 --tc 4"), + (fKeyType == MF_KEY_A) ? "a" : "b", sprint_hex_inrow(fkey, sizeof(fkey))); + fKeyType = MF_KEY_BD; + } } if (fKeyType != 0xFF) { - PrintAndLogEx(SUCCESS, "Block 0.... %s | " NOLF, sprint_hex_inrow(blockdata, MFBLOCK_SIZE)); + PrintAndLogEx(SUCCESS, "Block 0.......... %s | " NOLF, sprint_hex_inrow(blockdata, MFBLOCK_SIZE)); PrintAndLogEx(NORMAL, "%s", sprint_ascii(blockdata + 8, 8)); } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Fingerprint")); + bool expect_static_enc_nonce = false; if (fKeyType != 0xFF) { // cards with known backdoor @@ -10397,15 +10420,23 @@ static int CmdHF14AMfInfo(const char *Cmd) { } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08s, sizeof(fkey)) == 0 && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 && (blockdata[8] == 0x03 || blockdata[8] == 0x04) && blockdata[15] == 0x90) { - PrintAndLogEx(SUCCESS, "Fudan FM11RF08S"); + PrintAndLogEx(SUCCESS, "Fudan FM11RF08S %02X%02X", blockdata[8], blockdata[15]); + expect_static_enc_nonce = true; + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08s, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 + && (blockdata[8] == 0x03 || blockdata[8] == 0x04) && blockdata[15] == 0x91) { + PrintAndLogEx(SUCCESS, "Fudan FM11RF08S %02X%02X without static enc nonce", blockdata[8], blockdata[15]); + expect_static_enc_nonce = false; } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08s, sizeof(fkey)) == 0 && card.sak == 0x08 && memcmp(blockdata + 5, "\x00\x03\x00\x10", 4) == 0 && blockdata[15] == 0x90) { - PrintAndLogEx(SUCCESS, "Fudan FM11RF08S-7B"); + PrintAndLogEx(SUCCESS, "Fudan FM11RF08S-7B %02X%02X", blockdata[8], blockdata[15]); + expect_static_enc_nonce = true; } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 && blockdata[15] == 0x98) { - PrintAndLogEx(SUCCESS, "Fudan FM11RF08S **98"); + PrintAndLogEx(SUCCESS, "Fudan FM11RF08S %02X%02X", blockdata[8], blockdata[15]); + expect_static_enc_nonce = true; } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 && (blockdata[8] >= 0x01 && blockdata[8] <= 0x03) && blockdata[15] == 0x1D) { @@ -10414,9 +10445,12 @@ static int CmdHF14AMfInfo(const char *Cmd) { && card.sak == 0x08 && memcmp(blockdata + 5, "\x00\x01\x00\x10", 4) == 0 && blockdata[15] == 0x1D) { PrintAndLogEx(SUCCESS, "Fudan FM11RF08-7B"); - } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k32, sizeof(fkey)) == 0 + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k32n, sizeof(fkey)) == 0 && card.sak == 0x18 && memcmp(blockdata + 5, "\x18\x02\x00\x46\x44\x53\x37\x30\x56\x30\x31", 11) == 0) { - PrintAndLogEx(SUCCESS, "Fudan FM11RF32"); + PrintAndLogEx(SUCCESS, "Fudan FM11RF32N"); + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k32n2, sizeof(fkey)) == 0 + && card.sak == 0x18 && memcmp(blockdata + 5, "\x18\x02\x00\x46\x44\x53\x37\x30\x56\x30\x31", 11) == 0) { + PrintAndLogEx(SUCCESS, "Fudan FM11RF32N (variant)"); } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 && card.sak == 0x20 && memcmp(blockdata + 8, "\x62\x63\x64\x65\x66\x67\x68\x69", 8) == 0) { PrintAndLogEx(SUCCESS, "Fudan FM11RF32 (SAK=20)"); @@ -10499,6 +10533,9 @@ static int CmdHF14AMfInfo(const char *Cmd) { PrintAndLogEx(FAILED, "Prng........ " _RED_("fail")); } + bool tested_static_nonce = false; + int result_static_nonce = 0; + // detect static encrypted nonce if (keylen == MIFARE_KEY_SIZE) { res = detect_classic_static_encrypted_nonce(blockn, keytype, key); @@ -10512,6 +10549,8 @@ static int CmdHF14AMfInfo(const char *Cmd) { PrintAndLogEx(SUCCESS, "Static enc nonce... " _RED_("yes")); fKeyType = 0xFF; // dont detect twice } + result_static_nonce = res; + tested_static_nonce = true; } if (fKeyType != 0xFF) { @@ -10523,6 +10562,16 @@ static int CmdHF14AMfInfo(const char *Cmd) { } else if (res == NONCE_STATIC_ENC) { PrintAndLogEx(SUCCESS, "Static enc nonce... " _RED_("yes")); } + result_static_nonce = res; + tested_static_nonce = true; + } + if (tested_static_nonce) { + if ((result_static_nonce == NONCE_STATIC_ENC) && (!expect_static_enc_nonce)) { + PrintAndLogEx(WARNING, "Static enc nonce detected on a card not supposed to support it, please report... "); + } + if ((result_static_nonce != NONCE_STATIC_ENC) && (expect_static_enc_nonce)) { + PrintAndLogEx(WARNING, "Static enc nonce not detected on a card supposed to support it, please report... "); + } } if (do_nack_test) { diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index d3e6a902b..0b2ae1b23 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -1418,6 +1418,45 @@ int detect_classic_prng(void) { uint32_t nonce = bytes_to_num(respA.data.asBytes, respA.oldarg[0]); return validate_prng_nonce(nonce); } + + +/* Detect supported Auth, +* function performs a partial AUTH, where it tries to authenticate against block0, but only collects tag nonce. +* @returns +* TRUE if tag replies with a nonce +* FALSE is tag does not reply with a nonce +*/ +int detect_classic_auth(uint8_t key_type) { + + PacketResponseNG resp, respA; + uint8_t cmd[] = {MIFARE_AUTH_KEYA + key_type, 0x00}; + uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(cmd), 0, cmd, sizeof(cmd)); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + // if select tag failed. + if (resp.oldarg[0] == 0) { + PrintAndLogEx(ERR, "error: selecting tag failed, can't detect nonce\n"); + return PM3_ERFTRANS; + } + if (WaitForResponseTimeout(CMD_ACK, &respA, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + // check respA + if (respA.oldarg[0] != 4) { + PrintAndLogEx(ERR, "PRNG data error: Wrong length: %"PRIu64, respA.oldarg[0]); + return false; + } + return true; +} + /* Detect Mifare Classic NACK bug returns: diff --git a/client/src/mifare/mifarehost.h b/client/src/mifare/mifarehost.h index 9e6ba67c3..02c0422f0 100644 --- a/client/src/mifare/mifarehost.h +++ b/client/src/mifare/mifarehost.h @@ -110,6 +110,7 @@ int mf_chinese_gen_4_set_block(uint8_t blockNo, uint8_t *block, uint8_t *key); int try_decrypt_word(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); int detect_classic_prng(void); +int detect_classic_auth(uint8_t key_type); int detect_classic_nackbug(bool verbose); uint16_t detect_mf_magic(bool is_mfc, uint8_t key_type, uint64_t key); int detect_classic_static_nonce(void); From c8219a603096d4dfdeee6606b3c4f945897c998c Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 11 Jul 2025 13:43:50 +0200 Subject: [PATCH 078/149] detect_classic_auth: silent error --- client/src/mifare/mifarehost.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index 0b2ae1b23..d8f3a04ed 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -1449,12 +1449,8 @@ int detect_classic_auth(uint8_t key_type) { return PM3_ETIMEOUT; } - // check respA - if (respA.oldarg[0] != 4) { - PrintAndLogEx(ERR, "PRNG data error: Wrong length: %"PRIu64, respA.oldarg[0]); - return false; - } - return true; + // check respA for a nonce + return respA.oldarg[0] == 4; } /* Detect Mifare Classic NACK bug From 7cf206030deb35f5d448e6abd05b2b12185f41fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ho=C5=99=C4=8Dica?= Date: Sat, 12 Jul 2025 10:31:07 +0200 Subject: [PATCH 079/149] Add recognition of Swissbit iShield Key Mifare as MFD EV3 --- CHANGELOG.md | 1 + client/src/cmdhfmfdes.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5e218e7e..3a67c11a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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] +- Changed `hf mfdes info` - add recognition of Swissbit iShield Key Mifare - Changed `hf mf info` - add detection for unknown backdoor keys and for some backdoor variants (@doegox) - Changed `mqtt` commnands - now honors preference settings (@iceman1001) - Changed `prefs` - now handles MQTT settings too (@iceman1001) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 341ced612..14e716c8c 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -238,7 +238,7 @@ static char *getProtocolStr(uint8_t id, bool hw) { static char *getVersionStr(uint8_t type, uint8_t major, uint8_t minor) { - static char buf[40] = {0x00}; + static char buf[60] = {0x00}; char *retStr = buf; if (type == 0x01 && major == 0x00) @@ -255,6 +255,8 @@ static char *getVersionStr(uint8_t type, uint8_t major, uint8_t minor) { snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire EV2") " )", major, minor); else if (type == 0x01 && major == 0x33 && minor == 0x00) snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire EV3") " )", major, minor); + else if (type == 0x81 && major == 0x43 && minor == 0x01) + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire EV3C implementation on P71D600") " )", major, minor); // Swisskey iShield Key else if (type == 0x01 && major == 0x30 && minor == 0x00) snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire Light") " )", major, minor); else if (type == 0x02 && major == 0x11 && minor == 0x00) @@ -329,6 +331,9 @@ nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { if (type == 0x01 && major == 0x33 && minor == 0x00) return DESFIRE_EV3; + if (type == 0x81 && major == 0x43 && minor == 0x01) + return DESFIRE_EV3; + // Duox if (type == 0x01 && major == 0xA0 && minor == 0x00) return DUOX; From 134d76f60d517b69efaa4b7b43a91ec08a065547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ho=C5=99=C4=8Dica?= Date: Sat, 12 Jul 2025 10:48:31 +0200 Subject: [PATCH 080/149] Add missing author name --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a67c11a6..f00237fb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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] -- Changed `hf mfdes info` - add recognition of Swissbit iShield Key Mifare +- Changed `hf mfdes info` - add recognition of Swissbit iShield Key Mifare (@ah01) - Changed `hf mf info` - add detection for unknown backdoor keys and for some backdoor variants (@doegox) - Changed `mqtt` commnands - now honors preference settings (@iceman1001) - Changed `prefs` - now handles MQTT settings too (@iceman1001) From 58690ed24e04b918dc6eb704e93e54d662c8a614 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 13 Jul 2025 11:02:59 +0200 Subject: [PATCH 081/149] text --- client/src/cmdhfmfdes.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 14e716c8c..eddba910f 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -1032,7 +1032,7 @@ static int AuthCheckDesfire(DesfireContext_t *dctx, DesfireSetKeyNoClear(dctx, keyno, T_DES, deskeyList[curkey]); res = DesfireAuthenticate(dctx, secureChannel, false); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "AID 0x%06X, Found DES Key %02u : " _GREEN_("%s"), curaid, keyno, sprint_hex(deskeyList[curkey], 8)); + PrintAndLogEx(SUCCESS, "AID 0x%06X, Found DES Key %02u... " _GREEN_("%s"), curaid, keyno, sprint_hex(deskeyList[curkey], 8)); foundKeys[0][keyno][0] = 0x01; *result = true; memcpy(&foundKeys[0][keyno][1], deskeyList[curkey], 8); @@ -2181,25 +2181,32 @@ static int CmdHF14ADesBruteApps(const char *Cmd) { reverse_array(startAid, 3); reverse_array(endAid, 3); + uint32_t idStart = DesfireAIDByteToUint(startAid); uint32_t idEnd = DesfireAIDByteToUint(endAid); + if (idStart > idEnd) { PrintAndLogEx(ERR, "Start should be lower than end. start: %06x end: %06x", idStart, idEnd); return PM3_EINVARG; } - PrintAndLogEx(INFO, "Bruteforce from %06x to %06x", idStart, idEnd); - PrintAndLogEx(INFO, "Enumerating through all AIDs manually, this will take a while!"); - for (uint32_t id = idStart; id <= idEnd && id >= idStart; id += idIncrement) { - if (kbd_enter_pressed()) break; - int progress = ((id - idStart) * 100) / ((idEnd - idStart)); - PrintAndLogEx(INPLACE, "Progress: %d %%, current AID: %06X", progress, id); + PrintAndLogEx(INFO, "Bruteforce from " _YELLOW_("%06x") " to " _YELLOW_("%06x"), idStart, idEnd); + PrintAndLogEx(INFO, "Enumerating through all AIDs manually, this will take a while!"); + + for (uint32_t id = idStart; id <= idEnd && id >= idStart; id += idIncrement) { + + if (kbd_enter_pressed()) { + break; + } + + float progress = ((id - idStart) / (idEnd - idStart)); + PrintAndLogEx(INPLACE, "Progress " _YELLOW_("%0.1f") " %% current AID: %06X", progress, id); res = DesfireSelectAIDHexNoFieldOn(&dctx, id); if (res == PM3_SUCCESS) { printf("\33[2K\r"); // clear current line before printing - PrintAndLogEx(SUCCESS, "Got new APPID %06X", id); + PrintAndLogEx(SUCCESS, "Got new APPID " _GREEN_("%06X"), id); } } From 0dc80263e8792feb0f72cf0676c32030f949e174 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 13 Jul 2025 11:03:20 +0200 Subject: [PATCH 082/149] add aes256 --- client/src/crypto/libpcrypto.c | 39 ++++++++++++++++++++++++++++++++++ client/src/crypto/libpcrypto.h | 4 ++++ 2 files changed, 43 insertions(+) diff --git a/client/src/crypto/libpcrypto.c b/client/src/crypto/libpcrypto.c index 41f7574c5..9d506d8a8 100644 --- a/client/src/crypto/libpcrypto.c +++ b/client/src/crypto/libpcrypto.c @@ -163,6 +163,45 @@ int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int l return PM3_SUCCESS; } +// NIST Special Publication 800-38A — Recommendation for block cipher modes of operation: methods and techniques, 2001. +int aes256_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length) { + uint8_t iiv[16] = {0}; + if (iv) { + memcpy(iiv, iv, sizeof(iiv)); + } + + mbedtls_aes_context aes; + mbedtls_aes_init(&aes); + if (mbedtls_aes_setkey_enc(&aes, key, 256)) { + return 1; + } + if (mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, length, iiv, input, output)) { + return 2; + } + mbedtls_aes_free(&aes); + return PM3_SUCCESS; +} + + +int aes256_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length) { + uint8_t iiv[16] = {0}; + if (iv) { + memcpy(iiv, iv, 16); + } + + mbedtls_aes_context aes; + mbedtls_aes_init(&aes); + if (mbedtls_aes_setkey_dec(&aes, key, 256)) { + return 1; + } + if (mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, length, iiv, input, output)) { + return 2; + } + mbedtls_aes_free(&aes); + return PM3_SUCCESS; +} + + // NIST Special Publication 800-38B — Recommendation for block cipher modes of operation: The CMAC mode for authentication. // https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CMAC.pdf int aes_cmac(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length) { diff --git a/client/src/crypto/libpcrypto.h b/client/src/crypto/libpcrypto.h index 5d10b10ee..7b086bf33 100644 --- a/client/src/crypto/libpcrypto.h +++ b/client/src/crypto/libpcrypto.h @@ -38,6 +38,10 @@ void des3_decrypt(void *out, const void *in, const void *key, uint8_t keycount); int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); + +int aes256_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); +int aes256_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); + int aes_cmac(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length); int aes_cmac8(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length); From ca6dbc52511e8333a1b4008c7caf69a43b40b832 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 13 Jul 2025 11:06:01 +0200 Subject: [PATCH 083/149] data crypto - now also can du AES256 --- CHANGELOG.md | 1 + client/src/cmddata.c | 80 +++++++++++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f00237fb0..cefd2bd57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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] +- Changed `data crypto` - now also handles AES-256 (@iceman1001) - Changed `hf mfdes info` - add recognition of Swissbit iShield Key Mifare (@ah01) - Changed `hf mf info` - add detection for unknown backdoor keys and for some backdoor variants (@doegox) - Changed `mqtt` commnands - now honors preference settings (@iceman1001) diff --git a/client/src/cmddata.c b/client/src/cmddata.c index a8e285b7f..aaf6cd891 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -3527,8 +3527,8 @@ static int CmdAtrLookup(const char *Cmd) { static int CmdCryptography(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "data crypto", - "Encrypt data, right here, right now. Or decrypt.", - "Supply data, key, IV (needed for des MAC or aes), and cryptography action.\n" + "This command lets you encrypt or decrypt data using DES/3DES/AES.\n" + "Supply data, key, IV (needed for des MAC or aes), and cryptography action.\n", "To calculate a MAC for FMCOS, supply challenge as IV, data as data, and session/line protection key as key.\n" "To calculate a MAC for FeliCa, supply first RC as IV, BLE+data as data and session key as key.\n" "data crypto -d 04D6850E06AABB80 -k FFFFFFFFFFFFFFFF --iv 9EA0401A00000000 --des -> Calculate a MAC for FMCOS chip. The result should be ED3A0133\n" @@ -3544,76 +3544,97 @@ static int CmdCryptography(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); + uint8_t dati[250] = {0}; uint8_t dato[250] = {0}; int datilen = 0; CLIGetHexWithReturn(ctx, 1, dati, &datilen); - uint8_t key[25] = {0}; + + uint8_t key[33] = {0}; int keylen = 0; CLIGetHexWithReturn(ctx, 2, key, &keylen); - int type = 0; - if (arg_get_lit(ctx, 3)) type ^= 8; - if (arg_get_lit(ctx, 4)) type ^= 4; - if (arg_get_lit(ctx, 5)) type ^= 2; + + uint8_t type = 0; + if (arg_get_lit(ctx, 3)) { + type ^= 0x08; + } + + if (arg_get_lit(ctx, 4)) { + type ^= 0x04; + } + + if (arg_get_lit(ctx, 5)) { + type ^= 0x02; + } + uint8_t iv[250] = {0}; int ivlen = 0; CLIGetHexWithReturn(ctx, 6, iv, &ivlen); CLIParserFree(ctx); // Do data length check - if ((type & 0x4) >> 2) { // Use AES(0) or DES(1)? + if ((type & 0x04) == 0x04) { // Use AES(0) or DES(1)? if (datilen % 8 != 0) { PrintAndLogEx(ERR, " length must be a multiple of 8. Got %d", datilen); return PM3_EINVARG; } - if (keylen != 8 && keylen != 16 && keylen != 24) { - PrintAndLogEx(ERR, " must be 8, 16 or 24 bytes. Got %d", keylen); + if (keylen != 8 && keylen != 16 && keylen != 24 && keylen != 32) { + PrintAndLogEx(ERR, " must be 8, 16, 24, 32 bytes. Got %d", keylen); return PM3_EINVARG; } } else { - if (datilen % 16 != 0 && ((type & 0x2) >> 1 == 0)) { + if (datilen % 16 != 0 && ((type & 0x02) == 0)) { PrintAndLogEx(ERR, " length must be a multiple of 16. Got %d", datilen); return PM3_EINVARG; } - if (keylen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes. Got %d", keylen); + if (keylen != 16 && keylen != 32) { + PrintAndLogEx(ERR, " must be 16 or 32 bytes. Got %d", keylen); return PM3_EINVARG; } } // Encrypt(0) or decrypt(1)? - if ((type & 0x8) >> 3) { + if ((type & 0x08) == 0x08) { - if ((type & 0x4) >> 2) { // AES or DES? + if ((type & 0x04) == 0x04) { // AES or DES? if (keylen > 8) { - PrintAndLogEx(INFO, "Called 3DES decrypt"); des3_decrypt(dato, dati, key, keylen / 8); + PrintAndLogEx(INFO, "3DES decrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } else { - PrintAndLogEx(INFO, "Called DES decrypt"); + if (ivlen == 0) { // If there's an IV, use CBC des_decrypt_ecb(dato, dati, datilen, key); + PrintAndLogEx(INFO, "DES ECB decrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } else { des_decrypt_cbc(dato, dati, datilen, key, iv); + PrintAndLogEx(INFO, "DES CBC decrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } } + } else { - PrintAndLogEx(INFO, "Called AES decrypt"); + if (keylen == 32) { + aes256_decode(iv, key, dati, dato, datilen); + PrintAndLogEx(INFO, "AES-256 decrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); + } else { aes_decode(iv, key, dati, dato, datilen); + PrintAndLogEx(INFO, "AES-128 decrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); + } } } else { - if (type & 0x4) { // AES or DES? - if (type & 0x02) { // If we will calculate a MAC + + if ((type & 0x04) == 0x04) { // AES or DES? + if ((type & 0x02) == 0x02) { // If we will calculate a MAC /*PrintAndLogEx(INFO, "Called FeliCa MAC"); // For DES all I know useful is the felica and fudan MAC algorithm.This is just des-cbc, but felica needs it in its way. for (int i = 0; i < datilen; i+=8){ // For all 8 byte sequences @@ -3637,37 +3658,42 @@ static int CmdCryptography(const char *Cmd) { } else { if (keylen > 8) { - PrintAndLogEx(INFO, "Called 3DES encrypt keysize: %i", keylen / 8); des3_encrypt(dato, dati, key, keylen / 8); + PrintAndLogEx(INFO, "3DES encrypt keysize ( %d )... " _YELLOW_("%s"), (keylen / 8), sprint_hex_inrow(dato, datilen)); } else { - PrintAndLogEx(INFO, "Called DES encrypt"); - if (ivlen == 0) { // If there's an IV, use ECB des_encrypt_ecb(dato, dati, datilen, key); + PrintAndLogEx(INFO, "DES ECB encrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } else { des_encrypt_cbc(dato, dati, datilen, key, iv); char pad[250]; memset(pad, ' ', 4 + 8 + (datilen - 8) * 3); pad[8 + (datilen - 8) * 3] = 0; // Make a padding to insert FMCOS macing algorithm guide PrintAndLogEx(INFO, "%sVV VV VV VV FMCOS MAC", pad); + PrintAndLogEx(INFO, "DES CBC encrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } } } } else { - if (type & 0x02) { - PrintAndLogEx(INFO, "Called AES CMAC"); + if ((type & 0x02) == 0x02) { // If we will calculate a MAC aes_cmac8(iv, key, dati, dato, datilen); + PrintAndLogEx(INFO, "AES CMAC... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } else { - PrintAndLogEx(INFO, "Called AES encrypt"); + if (keylen == 32) { + aes256_encode(iv, key, dati, dato, datilen); + PrintAndLogEx(INFO, "AES-256 encrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); + } else { aes_encode(iv, key, dati, dato, datilen); + PrintAndLogEx(INFO, "AES-128 encrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); + } } } } - PrintAndLogEx(SUCCESS, "Result: %s", sprint_hex(dato, datilen)); + return PM3_SUCCESS; } From ef372a5ef0c380ab452ab5d8554c312394d323e8 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 13 Jul 2025 11:29:29 +0200 Subject: [PATCH 084/149] text --- client/src/cmddata.h | 1 - client/src/cmdhfmf.c | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/cmddata.h b/client/src/cmddata.h index 5b19b0f4e..5f70516ff 100644 --- a/client/src/cmddata.h +++ b/client/src/cmddata.h @@ -67,7 +67,6 @@ int CmdLtrim(const char *Cmd); int CmdNorm(const char *Cmd); // used by cmd lf data (!) int CmdPlot(const char *Cmd); // used by cmd lf cotag int CmdSave(const char *Cmd); // used by cmd auto -int CmdTuneSamples(const char *Cmd); // used by cmd lf hw int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose); // used by cmd lf em4x, lf fdxb, lf guard, lf jablotron, lf nedap, lf t55xx int ASKDemod(int clk, int invert, int maxErr, size_t maxlen, bool amplify, bool verbose, bool emSearch, uint8_t askType); // used by cmd lf em4x, lf t55xx, lf viking diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index dd07c9ab0..356817877 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -1128,9 +1128,9 @@ static int mf_load_keys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *userkey } *pkeyBlock = p; // Copy default keys to list - for (int i = 0; i < ARRAYLEN(g_mifare_default_keys); i++) { + for (uint32_t i = 0; i < ARRAYLEN(g_mifare_default_keys); i++) { num_to_bytes(g_mifare_default_keys[i], MIFARE_KEY_SIZE, (uint8_t *)(*pkeyBlock + (*pkeycnt + i) * MIFARE_KEY_SIZE)); - PrintAndLogEx(DEBUG, _YELLOW_("%2d") " - %s", *pkeycnt + i, sprint_hex(*pkeyBlock + (*pkeycnt + i) * MIFARE_KEY_SIZE, MIFARE_KEY_SIZE)); + PrintAndLogEx(DEBUG, _YELLOW_("%2u") " - %s", *pkeycnt + i, sprint_hex(*pkeyBlock + (*pkeycnt + i) * MIFARE_KEY_SIZE, MIFARE_KEY_SIZE)); } *pkeycnt += ARRAYLEN(g_mifare_default_keys); PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%zu") " hardcoded keys", ARRAYLEN(g_mifare_default_keys)); @@ -10403,6 +10403,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { } } + if (fKeyType != 0xFF) { PrintAndLogEx(SUCCESS, "Block 0.......... %s | " NOLF, sprint_hex_inrow(blockdata, MFBLOCK_SIZE)); PrintAndLogEx(NORMAL, "%s", sprint_ascii(blockdata + 8, 8)); From 8f9bb379adff58a692921a3888299fbe0ddacdae Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sun, 13 Jul 2025 11:57:14 +0200 Subject: [PATCH 085/149] fix client Makefile if no Makefie.platform is provided --- client/Makefile | 2 ++ tools/fpga_compress/Makefile | 2 ++ 2 files changed, 4 insertions(+) diff --git a/client/Makefile b/client/Makefile index de90a5f5c..777343d07 100644 --- a/client/Makefile +++ b/client/Makefile @@ -17,6 +17,8 @@ ifeq ($(PLTNAME),) -include ../Makefile.platform -include ../.Makefile.options.cache +# Default platform if no platform specified + PLATFORM?=PM3RDV4 ifneq ($(PLATFORM), $(CACHED_PLATFORM)) $(error platform definitions have been changed, please "make clean" at the root of the project) endif diff --git a/tools/fpga_compress/Makefile b/tools/fpga_compress/Makefile index 92af2739e..3e34619e8 100644 --- a/tools/fpga_compress/Makefile +++ b/tools/fpga_compress/Makefile @@ -1,6 +1,8 @@ ifeq ($(PLTNAME),) -include ../../Makefile.platform -include ../../.Makefile.options.cache +# Default platform if no platform specified + PLATFORM?=PM3RDV4 ifneq ($(PLATFORM), $(CACHED_PLATFORM)) $(error platform definitions have been changed, please "make clean" at the root of the project) endif From 53595e0a3a4f7c2880d34f2fa944b7da564d8d92 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sun, 13 Jul 2025 12:21:30 +0200 Subject: [PATCH 086/149] cmdmqtt: avoid using pthread_cancel, for compatibility with termux --- client/src/cmdmqtt.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/src/cmdmqtt.c b/client/src/cmdmqtt.c index 759b2f827..72b0914c7 100644 --- a/client/src/cmdmqtt.c +++ b/client/src/cmdmqtt.c @@ -59,20 +59,21 @@ static void mqtt_publish_callback(void **unused, struct mqtt_response_publish *p free(topic_name); } +static volatile int mqtt_client_should_exit = 0; + static void *mqtt_client_refresher(void *client) { - while (1) { - pthread_testcancel(); // check if we cancelled + while (!mqtt_client_should_exit) { mqtt_sync((struct mqtt_client *) client); msleep(100); } return NULL; } - static int mqtt_exit(int status, mqtt_pal_socket_handle sockfd, pthread_t *client_daemon) { close_nb_socket(sockfd); if (client_daemon != NULL) { - pthread_cancel(*client_daemon); + mqtt_client_should_exit = 1; pthread_join(*client_daemon, NULL); // Wait for the thread to finish + mqtt_client_should_exit = 0; } return status; } From 8de424410ff13075c608e8c351ebd23ae29020a3 Mon Sep 17 00:00:00 2001 From: James White Date: Sun, 13 Jul 2025 13:10:51 +0100 Subject: [PATCH 087/149] Detect operating system for logfile handling on Linux environments --- client/luascripts/paxton_clone.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/client/luascripts/paxton_clone.lua b/client/luascripts/paxton_clone.lua index b8aaf2a9d..5e9076c9f 100644 --- a/client/luascripts/paxton_clone.lua +++ b/client/luascripts/paxton_clone.lua @@ -4,7 +4,16 @@ local ac = require('ansicolors') local os = require('os') local dash = string.rep('--', 32) local dir = os.getenv('HOME') .. '/.proxmark3/logs/' -local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+")) +local logfilecmd + +--Determine platform for logfile handling (Windows vs Unix/Linux) +if package.config:sub(1,1) == "\\" then + logfilecmd = 'dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:' +else + logfilecmd = 'find "' .. dir .. '" -type f -printf "%T@ %p\\n" | sort -nr | cut -d" " -f2-' +end + +local logfile = (io.popen(logfilecmd):read("*a"):match("%C+")) local log_file_path = dir .. "Paxton_log.txt" local nam = "" local pm3 = require('pm3') From 64111a43eac707239484d2e4a06510cd35a0fb1a Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 13 Jul 2025 14:24:13 +0200 Subject: [PATCH 088/149] text --- client/src/cmdhfmf.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 356817877..ff556827d 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -10336,6 +10336,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { uint8_t blockdata[MFBLOCK_SIZE] = {0}; res = mf_check_keys_fast(sectorsCnt, true, true, 1, keycnt, keyBlock, e_sector, false, verbose); + // Identify Backdoor keyed cards if (res == PM3_SUCCESS || res == PM3_EPARTIAL) { if (e_sector[0].foundKey[MF_KEY_A]) { @@ -10393,12 +10394,13 @@ static int CmdHF14AMfInfo(const char *Cmd) { // we've a key but not a backdoor key uint8_t blockdata2[MFBLOCK_SIZE] = {0}; if (mf_read_block(0, fKeyType + 4, key, blockdata2) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Backdoor key..... " _GREEN_("same as keyA/keyB")); + PrintAndLogEx(SUCCESS, "Backdoor key..... " _GREEN_("same as key A/B")); } else if (detect_classic_auth(MF_KEY_BD)) { PrintAndLogEx(SUCCESS, "Backdoor key..... " _RED_("detected but unknown!")); - PrintAndLogEx(HINT, "Hint: Try........ " - _YELLOW_("hf mf nested --blk 0 -%s -k %s --tblk 0 --tc 4"), - (fKeyType == MF_KEY_A) ? "a" : "b", sprint_hex_inrow(fkey, sizeof(fkey))); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mf nested --blk 0 -%s -k %s --tblk 0 --tc 4") "`" + , (fKeyType == MF_KEY_A) ? "a" : "b" + , sprint_hex_inrow(fkey, MIFARE_KEY_SIZE) + ); fKeyType = MF_KEY_BD; } } From ed84b1fcf4be2f43ef074748291d83793262835c Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 13 Jul 2025 15:55:44 +0200 Subject: [PATCH 089/149] style --- client/src/cmddata.c | 8 +++---- client/src/cmdhfmf.c | 6 ++--- client/src/cmdmqtt.c | 20 ++++++++--------- client/src/mifare/mifaredefault.h | 18 +++++++++------ client/src/pm3line_vocabulary.h | 2 ++ doc/commands.json | 37 ++++++++++++++++++++++++++----- doc/commands.md | 2 ++ 7 files changed, 64 insertions(+), 29 deletions(-) diff --git a/client/src/cmddata.c b/client/src/cmddata.c index aaf6cd891..df05e9084 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -3626,7 +3626,7 @@ static int CmdCryptography(const char *Cmd) { aes256_decode(iv, key, dati, dato, datilen); PrintAndLogEx(INFO, "AES-256 decrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } else { - aes_decode(iv, key, dati, dato, datilen); + aes_decode(iv, key, dati, dato, datilen); PrintAndLogEx(INFO, "AES-128 decrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } } @@ -3659,7 +3659,7 @@ static int CmdCryptography(const char *Cmd) { if (keylen > 8) { des3_encrypt(dato, dati, key, keylen / 8); - PrintAndLogEx(INFO, "3DES encrypt keysize ( %d )... " _YELLOW_("%s"), (keylen / 8), sprint_hex_inrow(dato, datilen)); + PrintAndLogEx(INFO, "3DES encrypt keysize ( %d )... " _YELLOW_("%s"), (keylen / 8), sprint_hex_inrow(dato, datilen)); } else { if (ivlen == 0) { @@ -3687,13 +3687,13 @@ static int CmdCryptography(const char *Cmd) { aes256_encode(iv, key, dati, dato, datilen); PrintAndLogEx(INFO, "AES-256 encrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } else { - aes_encode(iv, key, dati, dato, datilen); + aes_encode(iv, key, dati, dato, datilen); PrintAndLogEx(INFO, "AES-128 encrypt... " _YELLOW_("%s"), sprint_hex_inrow(dato, datilen)); } } } } - + return PM3_SUCCESS; } diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index ff556827d..3463ffc63 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -10398,9 +10398,9 @@ static int CmdHF14AMfInfo(const char *Cmd) { } else if (detect_classic_auth(MF_KEY_BD)) { PrintAndLogEx(SUCCESS, "Backdoor key..... " _RED_("detected but unknown!")); PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mf nested --blk 0 -%s -k %s --tblk 0 --tc 4") "`" - , (fKeyType == MF_KEY_A) ? "a" : "b" - , sprint_hex_inrow(fkey, MIFARE_KEY_SIZE) - ); + , (fKeyType == MF_KEY_A) ? "a" : "b" + , sprint_hex_inrow(fkey, MIFARE_KEY_SIZE) + ); fKeyType = MF_KEY_BD; } } diff --git a/client/src/cmdmqtt.c b/client/src/cmdmqtt.c index 72b0914c7..1a0ea15ae 100644 --- a/client/src/cmdmqtt.c +++ b/client/src/cmdmqtt.c @@ -303,23 +303,23 @@ static int CmdMqttSend(const char *Cmd) { if (strlen(g_session.mqtt_server)) { strcpy(addr, g_session.mqtt_server); } else { - strcpy(addr, "proxdump.com"); - } + strcpy(addr, "proxdump.com"); + } } if (plen == 0) { if (strlen(g_session.mqtt_port)) { strcpy(port, g_session.mqtt_port); } else { - strcpy(port, "1883"); - } + strcpy(port, "1883"); + } } if (tlen == 0) { if (strlen(g_session.mqtt_topic)) { strcpy(topic, g_session.mqtt_topic); } else { - strcpy(topic, "proxdump"); + strcpy(topic, "proxdump"); } } @@ -380,23 +380,23 @@ static int CmdMqttReceive(const char *Cmd) { if (strlen(g_session.mqtt_server)) { strcpy(addr, g_session.mqtt_server); } else { - strcpy(addr, "proxdump.com"); - } + strcpy(addr, "proxdump.com"); + } } if (plen == 0) { if (strlen(g_session.mqtt_port)) { strcpy(port, g_session.mqtt_port); } else { - strcpy(port, "1883"); - } + strcpy(port, "1883"); + } } if (tlen == 0) { if (strlen(g_session.mqtt_topic)) { strcpy(topic, g_session.mqtt_topic); } else { - strcpy(topic, "proxdump"); + strcpy(topic, "proxdump"); } } diff --git a/client/src/mifare/mifaredefault.h b/client/src/mifare/mifaredefault.h index bcdc4a84b..e43c42a4b 100644 --- a/client/src/mifare/mifaredefault.h +++ b/client/src/mifare/mifaredefault.h @@ -121,20 +121,24 @@ static const uint64_t g_mifare_default_keys[] = { 0x96a301bce267, }; -static const uint8_t g_mifare_default_key[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -static const uint8_t g_mifare_mad_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}; -static const uint8_t g_mifare_mad_key_b[] = {0x89, 0xEC, 0xA9, 0x7F, 0x8C, 0x2A}; +static const uint8_t g_mifare_default_key[MIFARE_KEY_SIZE] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +static const uint8_t g_mifare_mad_key[MIFARE_KEY_SIZE] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}; +static const uint8_t g_mifare_mad_key_b[MIFARE_KEY_SIZE] = {0x89, 0xEC, 0xA9, 0x7F, 0x8C, 0x2A}; // 16 key B D01AFEEB890A -static const uint8_t g_mifare_signature_key_a[] = {0x5C, 0x8F, 0xF9, 0x99, 0x0D, 0xA2}; -static const uint8_t g_mifare_signature_key_b[] = {0x4b, 0x79, 0x1b, 0xea, 0x7b, 0xcc}; +static const uint8_t g_mifare_signature_key_a[MIFARE_KEY_SIZE] = {0x5C, 0x8F, 0xF9, 0x99, 0x0D, 0xA2}; +static const uint8_t g_mifare_signature_key_b[MIFARE_KEY_SIZE] = {0x4b, 0x79, 0x1b, 0xea, 0x7b, 0xcc}; // Manufacture MFC / QL88 (S17 / B) -static const uint8_t g_mifare_ql88_signature_key_b[] = {0x70, 0x7B, 0x11, 0xFC, 0x14, 0x81}; +static const uint8_t g_mifare_ql88_signature_key_b[MIFARE_KEY_SIZE] = {0x70, 0x7B, 0x11, 0xFC, 0x14, 0x81}; -static const uint8_t g_mifare_ndef_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}; +static const uint8_t g_mifare_ndef_key[MIFARE_KEY_SIZE] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}; static const uint8_t g_mifarep_mad_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7}; static const uint8_t g_mifarep_ndef_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}; +static const uint8_t g_mifare_k08s[MIFARE_KEY_SIZE] = {0xA3, 0x96, 0xEF, 0xA4, 0xE2, 0x4F}; +static const uint8_t g_mifare_k08[MIFARE_KEY_SIZE] = {0xA3, 0x16, 0x67, 0xA8, 0xCE, 0xC1}; +static const uint8_t g_mifare_k32n[MIFARE_KEY_SIZE] = {0x51, 0x8B, 0x33, 0x54, 0xE7, 0x60}; +static const uint8_t g_mifare_k32n2[MIFARE_KEY_SIZE] = {0x73, 0xB9, 0x83, 0x6C, 0xF1, 0x68}; extern const char *g_mifare_plus_default_keys[]; extern size_t g_mifare_plus_default_keys_len; diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index 7cae2a36a..0d2672fad 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -51,6 +51,7 @@ const static vocabulary_t vocabulary[] = { { 1, "prefs get hints" }, { 1, "prefs get output" }, { 1, "prefs get plotsliders" }, + { 1, "prefs get mqtt" }, { 1, "prefs set help" }, { 1, "prefs set barmode" }, { 1, "prefs set client.debug" }, @@ -62,6 +63,7 @@ const static vocabulary_t vocabulary[] = { { 1, "prefs set savepaths" }, { 1, "prefs set output" }, { 1, "prefs set plotsliders" }, + { 1, "prefs set mqtt" }, { 1, "analyse help" }, { 1, "analyse lrc" }, { 1, "analyse crc" }, diff --git a/doc/commands.json b/doc/commands.json index 2c1c2b02a..feddfcfa9 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -278,9 +278,8 @@ }, "data crypto": { "command": "data crypto", - "description": "Encrypt data, right here, right now. Or decrypt.", + "description": "This command lets you encrypt or decrypt data using DES/3DES/AES. Supply data, key, IV (needed for des MAC or aes), and cryptography action.", "notes": [ - "Supply data, key, IV (needed for des MAC or aes), and cryptography action.", "To calculate a MAC for FMCOS, supply challenge as IV, data as data, and session/line protection key as key.", "To calculate a MAC for FeliCa, supply first RC as IV, BLE+data as data and session key as key.", "data crypto -d 04D6850E06AABB80 -k FFFFFFFFFFFFFFFF --iv 9EA0401A00000000 --des -> Calculate a MAC for FMCOS chip. The result should be ED3A0133" @@ -12797,6 +12796,18 @@ ], "usage": "prefs get hints [-h]" }, + "prefs get mqtt": { + "command": "prefs get mqtt", + "description": "Get preference of MQTT settings in the client", + "notes": [ + "prefs get mqtt" + ], + "offline": true, + "options": [ + "-h, --help This help" + ], + "usage": "prefs get mqtt [-h]" + }, "prefs get output": { "command": "prefs get output", "description": "Get preference of dump output style", @@ -12922,7 +12933,7 @@ }, "prefs set help": { "command": "prefs set help", - "description": "help This help barmode Set bar mode client.debug Set client debug level client.delay Set client execution delay client.timeout Set client communication timeout color Set color support emoji Set emoji display hints Set hint display savepaths ... to be adjusted next ... output Set dump output style plotsliders Set plot slider display --------------------------------------------------------------------------------------- prefs set barmode available offline: yes Set persistent preference of HF/LF tune command styled output in the client", + "description": "help This help barmode Set bar mode client.debug Set client debug level client.delay Set client execution delay client.timeout Set client communication timeout color Set color support emoji Set emoji display hints Set hint display savepaths ... to be adjusted next ... output Set dump output style plotsliders Set plot slider display mqtt Set MQTT default values --------------------------------------------------------------------------------------- prefs set barmode available offline: yes Set persistent preference of HF/LF tune command styled output in the client", "notes": [ "prefs set barmode --mix" ], @@ -12949,6 +12960,22 @@ ], "usage": "prefs set hints [-h] [--off] [--on]" }, + "prefs set mqtt": { + "command": "prefs set mqtt", + "description": "Set persistent preference MQTT Server in the client", + "notes": [ + "prefs set mqtt -s test.mosquito.com", + "prefs set mqtt -s test.mosquito.com -p 1883 -t proxdump" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-s, --srv default MQTT Server", + "-p, --port default MQTT Port", + "-t, --topic default MQTT Topic" + ], + "usage": "prefs set mqtt [-h] [-s ] [-p ] [-t ]" + }, "prefs set output": { "command": "prefs set output", "description": "Set dump output style to condense consecutive repeated data", @@ -13411,8 +13438,8 @@ } }, "metadata": { - "commands_extracted": 770, + "commands_extracted": 772, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-07-10T05:07:28" + "extracted_on": "2025-07-13T13:53:32" } } diff --git a/doc/commands.md b/doc/commands.md index 96bb744d9..05cec3ad5 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -46,6 +46,7 @@ Check column "offline" for their availability. |`prefs get hints `|Y |`Get hint display preference` |`prefs get output `|Y |`Get dump output style preference` |`prefs get plotsliders `|Y |`Get plot slider display preference` +|`prefs get mqtt `|Y |`Get MQTT preference` ### prefs set @@ -65,6 +66,7 @@ Check column "offline" for their availability. |`prefs set savepaths `|Y |`... to be adjusted next ... ` |`prefs set output `|Y |`Set dump output style` |`prefs set plotsliders `|Y |`Set plot slider display` +|`prefs set mqtt `|Y |`Set MQTT default values` ### analyse From d933e329c44ae59f9caa8a5062b94a6a338a4eb8 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 15 Jul 2025 10:44:19 +0200 Subject: [PATCH 090/149] hf mf info: fix backdoor detection when using non-default key --- client/src/cmdhfmf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 3463ffc63..025698692 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -10393,13 +10393,14 @@ static int CmdHF14AMfInfo(const char *Cmd) { if ((fKeyType == MF_KEY_A) || (fKeyType == MF_KEY_B)) { // we've a key but not a backdoor key uint8_t blockdata2[MFBLOCK_SIZE] = {0}; - if (mf_read_block(0, fKeyType + 4, key, blockdata2) == PM3_SUCCESS) { + if (mf_read_block(0, fKeyType + 4, fkey, blockdata2) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Backdoor key..... " _GREEN_("same as key A/B")); } else if (detect_classic_auth(MF_KEY_BD)) { PrintAndLogEx(SUCCESS, "Backdoor key..... " _RED_("detected but unknown!")); - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mf nested --blk 0 -%s -k %s --tblk 0 --tc 4") "`" + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mf nested --blk 0 -%s -k %s --tblk 0 --tc %i") "`" , (fKeyType == MF_KEY_A) ? "a" : "b" , sprint_hex_inrow(fkey, MIFARE_KEY_SIZE) + , fKeyType + 4 ); fKeyType = MF_KEY_BD; } From c32a55eabc80affa64857ccfa818564f0708592b Mon Sep 17 00:00:00 2001 From: 47LeCoste <82815207+grugnoymeme@users.noreply.github.com> Date: Wed, 16 Jul 2025 20:13:36 +0200 Subject: [PATCH 091/149] Update ModemManager-Must-Be-Discarded.md better mask than disable th ModemManager bc some services could force the activation also if it was "only" disabled.. If u mask it, u're gonna stay safe... I also added a comment to my .zshrc: # services masked: ModemManager so in case i'll need it in the future i can unmask it and i'll remember suddenly the status of it. Signed-off-by: 47LeCoste <82815207+grugnoymeme@users.noreply.github.com> --- .../ModemManager-Must-Be-Discarded.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md b/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md index 5c9149826..458e45a02 100644 --- a/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md +++ b/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md @@ -47,12 +47,13 @@ On Archlinux: sudo pacman -R modemmanager ``` -# Solution 2: disable ModemManager +# Solution 2: mask ModemManager ^[Top](#top) ```sh sudo systemctl stop ModemManager sudo systemctl disable ModemManager +sudo systemctl mask ModemManager ``` # Solution 3: use filtering udev rules From 77bae8a9cb1cf9bd13f57435366334bd69a268dc Mon Sep 17 00:00:00 2001 From: 47LeCoste <82815207+grugnoymeme@users.noreply.github.com> Date: Wed, 16 Jul 2025 20:16:46 +0200 Subject: [PATCH 092/149] Update ModemManager-Must-Be-Discarded.md Signed-off-by: 47LeCoste <82815207+grugnoymeme@users.noreply.github.com> --- .../ModemManager-Must-Be-Discarded.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md b/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md index 458e45a02..2a6611659 100644 --- a/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md +++ b/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md @@ -55,6 +55,14 @@ sudo systemctl stop ModemManager sudo systemctl disable ModemManager sudo systemctl mask ModemManager ``` +After doing this check if it has been masked with: +`systemctl status ModemManager` +If you'll get something like this, you've masked ModemManager and you will be ready to install and setup your pm3. +``` +○ ModemManager.service + Loaded: masked (Reason: Unit ModemManager.service is masked.) + Active: inactive (dead) +``` # Solution 3: use filtering udev rules ^[Top](#top) From b2939332c2ab1c2079ba0c850a400a5cf044f8ce Mon Sep 17 00:00:00 2001 From: 47LeCoste <82815207+grugnoymeme@users.noreply.github.com> Date: Wed, 16 Jul 2025 23:45:47 +0200 Subject: [PATCH 093/149] Update CHANGELOG.md Signed-off-by: 47LeCoste <82815207+grugnoymeme@users.noreply.github.com> --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cefd2bd57..60721b79c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `mem load` - now handles UL-C and UL-AES dictionary files (@iceman1001) - Changed `hf mfu sim` - now support UL-C simulation (@iceman1001) - Added `!` - run system commands from inside the client. Potentially dangerous if running client as SUDO, SU, ROOT (@iceman1001) +- Improved To avoid conflicts with ModemManager on Linux, is recommended to masking the service (@grugnoymeme) ## [Daddy Iceman.4.20469][2025-06-16] - Fixed edge case in fm11rf08s key recovery tools (@doegox) From 95759a13ba7bf84ba0b54727e7a6ac43026ec912 Mon Sep 17 00:00:00 2001 From: q0jt <89930816+q0jt@users.noreply.github.com> Date: Sat, 19 Jul 2025 15:42:00 +0900 Subject: [PATCH 094/149] hf felica litedump: skip blocks that require Read After Authentication --- client/src/cmdhffelica.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 3b2a53b49..619f09da2 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -1881,9 +1881,15 @@ static uint16_t PrintFliteBlock(uint16_t tracepos, uint8_t *trace, uint16_t trac uint8_t status1 = trace[1]; uint8_t status2 = trace[2]; + bool error = (status1 != 0x00 && (status2 == 0xB1 || status2 == 0xB2)); + char line[110] = {0}; for (int j = 0; j < 16; j++) { - snprintf(line + (j * 4), sizeof(line) - 1 - (j * 4), "%02x ", trace[j + 3]); + if (error) { + snprintf(line + (j * 4), sizeof(line) - 1 - (j * 4), "?? "); + } else { + snprintf(line + (j * 4), sizeof(line) - 1 - (j * 4), "%02x ", trace[j + 3]); + } } PrintAndLogEx(NORMAL, "block number %02x, status: %02x %02x", blocknum, status1, status2); @@ -1931,13 +1937,17 @@ static uint16_t PrintFliteBlock(uint16_t tracepos, uint8_t *trace, uint16_t trac PrintAndLogEx(NORMAL, "S_PAD13: %s", line); break; case 0x0E: { - uint32_t regA = trace[3] | trace[4] << 8 | trace[5] << 16 | trace[ 6] << 24; + uint32_t regA = trace[3] | trace[4] << 8 | trace[5] << 16 | trace[6] << 24; uint32_t regB = trace[7] | trace[8] << 8 | trace[9] << 16 | trace[10] << 24; line[0] = 0; for (int j = 0; j < 8; j++) snprintf(line + (j * 2), sizeof(line) - 1 - (j * 2), "%02x", trace[j + 11]); - PrintAndLogEx(NORMAL, "REG: regA: %d regB: %d regC: %s ", regA, regB, line); + if (error) { + PrintAndLogEx(NORMAL, "REG: regA: ???????? regB: ???????? regC: ???????????????? "); + } else { + PrintAndLogEx(NORMAL, "REG: regA: %d regB: %d regC: %s ", regA, regB, line); + } } break; case 0x80: From e1598cd620a7c08471c7574cb9356da6c62fc8c1 Mon Sep 17 00:00:00 2001 From: Mistial Developer Date: Sat, 19 Jul 2025 08:39:47 -0400 Subject: [PATCH 095/149] DESFire: fix value file operations and improve MAC mode compatibility - Add auto-detection fallback for MAC mode in value operations When MAC mode fails with length errors, automatically retry with plain mode for better compatibility across different card types - Fix MAC transmission behavior for value operations Remove CREDIT, LIMITED_CREDIT, and DEBIT from EV1D40TransmitMAC array to match real card behavior and prevent authentication issues - Change default algorithm from DES to 2TDEA Real DESFire cards seem to use 2TDEA by default, improving out-of-the-box compatibility with factory cards - Update help text for value commands to follow client patterns Standardize "Crypt algo (deft: 2TDEA)" format for consistency - Add online test suite for DESFire value operations New pm3_online_tests.sh script validates value file creation, credit/debit operations in both plain and MAC modes with real cards --- client/src/cmdhfmfdes.c | 6 +- client/src/mifare/desfirecore.c | 14 +++ client/src/mifare/desfiresecurechan.c | 3 - tools/pm3_online_tests.sh | 148 ++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 6 deletions(-) create mode 100755 tools/pm3_online_tests.sh diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index eddba910f..d2160104c 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -463,7 +463,7 @@ static void swap24(uint8_t *data) { // default parameters static uint8_t defaultKeyNum = 0; -static DesfireCryptoAlgorithm defaultAlgoId = T_DES; +static DesfireCryptoAlgorithm defaultAlgoId = T_3DES; // Real DESFire cards seem to use 2TDEA by default static uint8_t defaultKey[DESFIRE_MAX_KEY_SIZE] = {0}; static int defaultKdfAlgo = MFDES_KDF_ALGO_NONE; static int defaultKdfInputLen = 0; @@ -4034,7 +4034,7 @@ static int CmdHF14ADesCreateValueFile(const char *Cmd) { arg_lit0("a", "apdu", "Show APDU requests and responses"), arg_lit0("v", "verbose", "Verbose output"), arg_int0("n", "keyno", "", "Key number"), - arg_str0("t", "algo", "", "Crypt algo"), + arg_str0("t", "algo", "", "Crypt algo (deft: 2TDEA)"), arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), arg_str0(NULL, "kdf", "", "Key Derivation Function (KDF)"), arg_str0("i", "kdfi", "", "KDF input (1-31 hex bytes)"), @@ -4474,7 +4474,7 @@ static int CmdHF14ADesValueOperations(const char *Cmd) { arg_lit0("a", "apdu", "Show APDU requests and responses"), arg_lit0("v", "verbose", "Verbose output"), arg_int0("n", "keyno", "", "Key number"), - arg_str0("t", "algo", "", "Crypt algo"), + arg_str0("t", "algo", "", "Crypt algo (deft: 2TDEA)"), arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), arg_str0(NULL, "kdf", "", "Key Derivation Function (KDF)"), arg_str0("i", "kdfi", "", "KDF input (1-31 hex bytes)"), diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index f6ec34e17..97fcbc184 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -2240,6 +2240,20 @@ int DesfireValueFileOperations(DesfireContext_t *dctx, uint8_t fid, uint8_t oper int res = DesfireCommand(dctx, operation, data, datalen, resp, &resplen, -1); + // Auto-detection fallback: if MAC mode fails with length error, retry with plain mode + if ((res == 0x7E || res == -20) && dctx->commMode == DCMMACed) { + PrintAndLogEx(INFO, "MAC mode failed with length error, retrying with plain mode"); + DesfireCommunicationMode original_mode = dctx->commMode; + dctx->commMode = DCMPlain; + + memset(resp, 0, sizeof(resp)); + resplen = 0; + res = DesfireCommand(dctx, operation, data, datalen, resp, &resplen, -1); + + // Restore original mode for future commands + dctx->commMode = original_mode; + } + if (resplen == 4 && value) { *value = MemLeToUint4byte(resp); } diff --git a/client/src/mifare/desfiresecurechan.c b/client/src/mifare/desfiresecurechan.c index 2b439b1b4..d9506d48b 100644 --- a/client/src/mifare/desfiresecurechan.c +++ b/client/src/mifare/desfiresecurechan.c @@ -219,9 +219,6 @@ static uint8_t DesfireGetCmdHeaderLen(uint8_t cmd) { static const uint8_t EV1D40TransmitMAC[] = { MFDES_WRITE_DATA, - MFDES_CREDIT, - MFDES_LIMITED_CREDIT, - MFDES_DEBIT, MFDES_WRITE_RECORD, MFDES_UPDATE_RECORD, MFDES_COMMIT_READER_ID, diff --git a/tools/pm3_online_tests.sh b/tools/pm3_online_tests.sh new file mode 100755 index 000000000..998122f62 --- /dev/null +++ b/tools/pm3_online_tests.sh @@ -0,0 +1,148 @@ +#!/usr/bin/env bash + +# Online tests that require actual PM3 device connection +# This is used to make sure that the language for the functions is english instead of the system default language. +LANG=C + +PM3PATH="$(dirname "$0")/.." +cd "$PM3PATH" || exit 1 + +TESTALL=false +TESTDESFIREVALUE=false + +# https://medium.com/@Drew_Stokes/bash-argument-parsing-54f3b81a6a8f +PARAMS="" +while (( "$#" )); do + case "$1" in + -h|--help) + echo """ +Usage: $0 [--pm3bin /path/to/pm3] [desfire_value] + --pm3bin ...: Specify path to pm3 binary to test + desfire_value: Test DESFire value operations with card + You must specify a test target - no default 'all' for online tests +""" + exit 0 + ;; + --pm3bin) + if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then + PM3BIN=$2 + shift 2 + else + echo "Error: Argument for $1 is missing" >&2 + exit 1 + fi + ;; + desfire_value) + TESTALL=false + TESTDESFIREVALUE=true + shift + ;; + -*|--*=) # unsupported flags + echo "Error: Unsupported flag $1" >&2 + exit 1 + ;; + *) # preserve positional arguments + PARAMS="$PARAMS $1" + shift + ;; + esac +done +# set positional arguments in their proper place +eval set -- "$PARAMS" + +C_RED='\033[0;31m' +C_GREEN='\033[0;32m' +C_YELLOW='\033[0;33m' +C_BLUE='\033[0;34m' +C_NC='\033[0m' # No Color +C_OK='\xe2\x9c\x94\xef\xb8\x8f' +C_FAIL='\xe2\x9d\x8c' + +# Check if file exists +function CheckFileExist() { + printf "%-40s" "$1 " + if [ -f "$2" ]; then + echo -e "[ ${C_GREEN}OK${C_NC} ] ${C_OK}" + return 0 + fi + if ls "$2" 1> /dev/null 2>&1; then + echo -e "[ ${C_GREEN}OK${C_NC} ] ${C_OK}" + return 0 + fi + echo -e "[ ${C_RED}FAIL${C_NC} ] ${C_FAIL}" + return 1 +} + +# Execute command and check result +function CheckExecute() { + printf "%-40s" "$1 " + + start=$(date +%s) + TIMEINFO="" + RES=$(eval "$2") + end=$(date +%s) + delta=$(expr $end - $start) + if [ $delta -gt 2 ]; then + TIMEINFO=" ($delta s)" + fi + if echo "$RES" | grep -E -q "$3"; then + echo -e "[ ${C_GREEN}OK${C_NC} ] ${C_OK} $TIMEINFO" + return 0 + fi + echo -e "[ ${C_RED}FAIL${C_NC} ] ${C_FAIL} $TIMEINFO" + echo "Execution trace:" + echo "$RES" + return 1 +} + +echo -e "${C_BLUE}Iceman Proxmark3 online test tool${C_NC}" +echo "" +echo "work directory: $(pwd)" + +if command -v git >/dev/null && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo -n "git branch: " + git describe --all + echo -n "git sha: " + git rev-parse HEAD + echo "" +fi + +# Check that user specified a test +if [ "$TESTDESFIREVALUE" = false ]; then + echo "Error: You must specify a test target. Use -h for help." + exit 1 +fi + +while true; do + # DESFire value tests + if $TESTDESFIREVALUE; then + echo -e "\n${C_BLUE}Testing DESFire card value operations${C_NC} ${PM3BIN:=./pm3}" + echo " PLACE A FACTORY DESFIRE CARD ON THE READER NOW" + if ! CheckFileExist "pm3 exists" "$PM3BIN"; then break; fi + + echo " Formatting card to clean state..." + if ! CheckExecute "format card" "$PM3BIN -c 'hf mfdes formatpicc'" "done"; then break; fi + + echo " Running value operation tests..." + if ! CheckExecute "card auth test" "$PM3BIN -c 'hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none'" "authenticated.*succes"; then break; fi + if ! CheckExecute "card app creation" "$PM3BIN -c 'hf mfdes createapp --aid 123456 --ks1 0F --ks2 0E --numkeys 1'" "successfully created"; then break; fi + if ! CheckExecute "card value file creation" "$PM3BIN -c 'hf mfdes createvaluefile --aid 123456 --fid 02 --lower 00000000 --upper 000003E8 --value 00000064'" "created successfully"; then break; fi + if ! CheckExecute "card value get plain" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m plain'" "Value.*100"; then break; fi + if ! CheckExecute "card value get mac" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m mac'" "Value.*100"; then break; fi + if ! CheckExecute "card value credit plain" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op credit -d 00000032 -m plain'" "Value.*changed"; then break; fi + if ! CheckExecute "card value get after credit" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m plain'" "Value.*150"; then break; fi + if ! CheckExecute "card value credit mac" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op credit -d 0000000A -m mac'" "Value.*changed"; then break; fi + if ! CheckExecute "card value debit plain" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op debit -d 00000014 -m plain'" "Value.*changed"; then break; fi + if ! CheckExecute "card value debit mac" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op debit -d 00000014 -m mac'" "Value.*changed"; then break; fi + if ! CheckExecute "card value final check" "$PM3BIN -c 'hf mfdes value --aid 123456 --fid 02 --op get -m mac'" "Value.*120"; then break; fi + if ! CheckExecute "card cleanup" "$PM3BIN -c 'hf mfdes selectapp --aid 000000; hf mfdes auth -n 0 -t 2tdea -k 00000000000000000000000000000000 --kdf none; hf mfdes deleteapp --aid 123456'" "application.*deleted"; then break; fi + echo " card value operation tests completed successfully!" + fi + + echo -e "\n------------------------------------------------------------" + echo -e "Tests [ ${C_GREEN}OK${C_NC} ] ${C_OK}\n" + exit 0 +done +echo -e "\n------------------------------------------------------------" +echo -e "\nTests [ ${C_RED}FAIL${C_NC} ] ${C_FAIL}\n" +exit 1 \ No newline at end of file From b3b24855d02727ea1f25affce016e1e3aedce684 Mon Sep 17 00:00:00 2001 From: Mistial Developer Date: Sat, 19 Jul 2025 09:52:30 -0400 Subject: [PATCH 096/149] DESFire: add detailed documentation for value file operations - Introduced a new "How to work with value files" section - Updated command examples with links to the new section - Clarified current/deprecated value operation commands - Expanded examples with practical use cases, security modes, and error handling scenarios --- doc/desfire.md | 191 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 3 deletions(-) diff --git a/doc/desfire.md b/doc/desfire.md index 50bddc9d5..65f1af748 100644 --- a/doc/desfire.md +++ b/doc/desfire.md @@ -23,6 +23,7 @@ - [How to create files](#how-to-create-files) - [How to delete files](#how-to-delete-files) - [How to read/write files](#how-to-readwrite-files) + - [How to work with value files](#how-to-work-with-value-files) - [How to work with transaction mac](#how-to-work-with-transaction-mac) - [How to switch DESFire Light to LRP mode](#how-to-switch-desfire-light-to-lrp-mode) @@ -254,7 +255,7 @@ Create standard file with mac access mode and specified access settings. access `hf mfdes createfile --aid 123456 --fid 01 --isofid 0001 --size 000010 --amode mac --rrights free --wrights free --rwrights free --chrights key0` -`hf mfdes createvaluefile --aid 123456 --fid 01 --isofid 0001 --lower 00000010 --upper 00010000 --value 00000100` - create value file +`hf mfdes createvaluefile --aid 123456 --fid 01 --isofid 0001 --lower 00000010 --upper 00010000 --value 00000100` - create value file (see [How to work with value files](#how-to-work-with-value-files) for detailed examples) `hf mfdes createrecordfile --aid 123456 --fid 01 --isofid 0001 --size 000010 --maxrecord 000010` - create linear record file @@ -294,9 +295,11 @@ Here it is needed to specify the type of the file because there is no `hf mfdes `hf mfdes write --aid 123456 --fid 01 --type data -d 01020304 --commit` - write backup data file and commit -`hf mfdes write --aid 123456 --fid 01 --type value -d 00000001` increment value file +`hf mfdes write --aid 123456 --fid 01 --type value -d 00000001` increment value file (deprecated, use `hf mfdes value` command) -`hf mfdes write --aid 123456 --fid 01 --type value -d 00000001 --debit` decrement value file +`hf mfdes write --aid 123456 --fid 01 --type value -d 00000001 --debit` decrement value file (deprecated, use `hf mfdes value` command) + +For modern value file operations, see [How to work with value files](#how-to-work-with-value-files) `hf mfdes write --aid 123456 --fid 01 --type record -d 01020304` write data to a record file @@ -314,6 +317,188 @@ For more detailed samples look at the next howto. `hf mfdes write --aid 123456 --fid 01 -d 01020304 --readerid 010203` write data to the file with CommitReaderID command before and CommitTransaction after write +### How to work with value files +^[Top](#top) + +Value files are specialized files designed for storing and manipulating monetary values or counters. They provide atomic operations for incrementing (credit) and decrementing (debit) values with built-in limits and security features. + +**Key Features:** +- 32-bit value storage (represented internally as unsigned) +- Lower and upper limits to prevent underflow/overflow +- Atomic operations with automatic transaction commit +- Transaction logging support +- Secure communication modes (plain, MAC, encrypted) + +**Value File Structure:** +- Current value: 32-bit value +- Lower limit: minimum allowed value (prevents underflow) +- Upper limit: maximum allowed value (prevents overflow) + +**Access Rights:** +Value files use four access right categories: +- **Read**: Required to get the current value (`hf mfdes value --op get`) +- **Write**: Required for debit operations (`hf mfdes value --op debit`) +- **Read/Write**: Required for credit operations (`hf mfdes value --op credit`) +- **Change**: Required to modify file settings or delete the file + +Access rights can be set to: +- `key0` through `keyE`: Requires authentication with the specified key +- `free`: No authentication required +- `deny`: Operation is forbidden + +*Create value file:* + +Creating a Bitcoin wallet on your DESFire card: +``` +pm3 --> hf mfdes createapp --aid 425443 --ks1 0B --ks2 0E +[+] Desfire application 425443 successfully created + +pm3 --> hf mfdes createvaluefile --aid 425443 --fid 01 --lower 00000000 --upper 01406F40 --value 00000032 +[=] ---- Create file settings ---- +[+] File type : Value +[+] File number : 0x01 (1) +[+] File comm mode : Plain +[+] Additional access: No +[+] Access rights : EEEE +[+] read......... free +[+] write........ free +[+] read/write... free +[+] change....... free +[=] Lower limit... 0 / 0x00000000 +[=] Upper limit... 21000000 / 0x01406F40 +[=] Value............ 50 / 0x00000032 +[=] Limited credit... 0 - disabled +[=] GetValue access... Not Free +[+] Value file 01 in the app 425443 created successfully +``` +This creates a DESFire Bitcoin wallet with: +- Application ID 0x425443 (ASCII "BTC") +- File ID 0x01 for the wallet +- Lower limit: 0 BTC (no overdrafts in crypto) +- Upper limit: 21,000,000 BTC (respecting Satoshi's vision) +- Initial value: 50 BTC (the original block reward) + +Creating the infamous Pizza Day wallet: +``` +pm3 --> hf mfdes createvaluefile --aid 425443 --fid 02 --lower 00000000 --upper 01406F40 --value 00002710 +[=] ---- Create file settings ---- +[+] File type : Value +[+] File number : 0x02 (2) +[+] File comm mode : Plain +[+] Additional access: No +[+] Access rights : EEEE +[+] read......... free +[+] write........ free +[+] read/write... free +[+] change....... free +[=] Lower limit... 0 / 0x00000000 +[=] Upper limit... 21000000 / 0x01406F40 +[=] Value............ 10000 / 0x00002710 +[=] Limited credit... 0 - disabled +[=] GetValue access... Not Free +[+] Value file 02 in the app 425443 created successfully +``` +This creates a wallet pre-loaded with 10,000 BTC (historical exchange rate: 2 pizzas) + +*Value file operations:* + +Check your Bitcoin balance: +``` +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 50 (0x00000032) + +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get -m mac +[+] Value: 50 (0x00000032) +``` + +Loading Bitcoin IOUs onto your card: +``` +pm3 --> hf mfdes value --aid 425443 --fid 01 --op credit -d 00000019 +[+] Value changed successfully + +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 75 (0x0000004b) +``` +Card now holds 75 BTC in IOUs ($9,000,000 in debt obligations) + +Buying coffee with Bitcoin IOUs: +``` +pm3 --> hf mfdes value --aid 425443 --fid 01 --op debit -d 00000001 +[+] Value changed successfully # You now owe the coffee shop $120,000 + +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 74 (0x0000004a) # Remaining debt capacity +``` + +The legendary Pizza Day recreation: +``` +pm3 --> hf mfdes value --aid 425443 --fid 02 --op debit -d 00002710 +[+] Value changed successfully # You now owe Papa John's $1.2 billion + +pm3 --> hf mfdes value --aid 425443 --fid 02 --op get +[+] Value: 0 (0x00000000) # Card empty, bankruptcy imminent +``` + +*Communication modes:* + +Value files support different communication modes for security: + +Plain mode (no encryption): +``` +pm3 --> hf mfdes value --aid 123456 --fid 02 --op get -m plain +[+] Value: 125 (0x0000007d) +``` + +MAC mode (message authentication): +``` +pm3 --> hf mfdes value --aid 123456 --fid 02 --op credit -d 00000032 -m mac +[+] Value changed successfully +``` + +Encrypted mode (full encryption): +``` +pm3 --> hf mfdes value --aid 123456 --fid 02 --op debit -d 00000014 -m encrypted +[+] Value changed successfully +``` + +*Error handling and compatibility:* + +The Proxmark3 implementation includes automatic fallback for compatibility: +- If MAC mode fails with a length error (-20), it automatically retries in plain mode +- This ensures compatibility across different DESFire card generations +- Original communication mode is restored after fallback + +*Transaction behavior:* + +Value operations are atomic with automatic commit: +- The `hf mfdes value` command automatically issues CommitTransaction after credit/debit operations +- Get operations do not require a commit +- Operations either complete fully (including commit) or fail completely +- No manual transaction management required when using the `hf mfdes value` command +- Transaction MAC files can log all value operations for audit trails + +*Practical examples:* + +Daily Bitcoin IOU catastrophes: +``` +# Check morning IOU balance +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 50 (0x00000032) # $6 million in IOUs + +# Friend sends you more IOUs via NFC bump +pm3 --> hf mfdes value --aid 425443 --fid 01 --op credit -d 000000C8 +[+] Value changed successfully # +200 BTC IOUs ($24M more debt) + +# Buy a Tesla (tap payment) +pm3 --> hf mfdes value --aid 425443 --fid 01 --op debit -d 00000001 +[+] Value changed successfully + +# Check remaining IOU capacity +pm3 --> hf mfdes value --aid 425443 --fid 01 --op get +[+] Value: 273 (0x00000111) # $32.76M in transferable debt +``` + + ### How to work with transaction mac ^[Top](#top) From 12e68d7a2888a4b45752579fbe50aa7e989d24e8 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sat, 19 Jul 2025 16:52:27 +0200 Subject: [PATCH 097/149] Change readline hack logic for async dbg msg to be ready for readline 8.3 --- CHANGELOG.md | 3 ++- client/src/ui.c | 14 ++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60721b79c..d7281d870 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ 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] +- Changed readline hack logic for async dbg msg to be ready for readline 8.3 (@doegox) +- Improved To avoid conflicts with ModemManager on Linux, is recommended to masking the service (@grugnoymeme) - Changed `data crypto` - now also handles AES-256 (@iceman1001) - Changed `hf mfdes info` - add recognition of Swissbit iShield Key Mifare (@ah01) - Changed `hf mf info` - add detection for unknown backdoor keys and for some backdoor variants (@doegox) @@ -18,7 +20,6 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `mem load` - now handles UL-C and UL-AES dictionary files (@iceman1001) - Changed `hf mfu sim` - now support UL-C simulation (@iceman1001) - Added `!` - run system commands from inside the client. Potentially dangerous if running client as SUDO, SU, ROOT (@iceman1001) -- Improved To avoid conflicts with ModemManager on Linux, is recommended to masking the service (@grugnoymeme) ## [Daddy Iceman.4.20469][2025-06-16] - Fixed edge case in fm11rf08s key recovery tools (@doegox) diff --git a/client/src/ui.c b/client/src/ui.c index 4aed59a45..1901ac9ef 100644 --- a/client/src/ui.c +++ b/client/src/ui.c @@ -424,18 +424,13 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) { #ifdef RL_STATE_READCMD // We are using GNU readline. libedit (OSX) doesn't support this flag. int need_hack = (rl_readline_state & RL_STATE_READCMD) > 0; - char *saved_line; - int saved_point; + char *saved_line = NULL; if (need_hack) { - saved_point = rl_point; saved_line = rl_copy_text(0, rl_end); - rl_save_prompt(); - rl_replace_line("", 0); - rl_redisplay(); + rl_clear_visible_line(); } #endif - va_start(argptr, fmt); vsnprintf(buffer, sizeof(buffer), fmt, argptr); va_end(argptr); @@ -453,14 +448,13 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) { if (linefeed) { fprintf(stream, "\n"); } + fflush(stream); } #ifdef RL_STATE_READCMD - // We are using GNU readline. libedit (OSX) doesn't support this flag. if (need_hack) { - rl_restore_prompt(); + rl_on_new_line(); rl_replace_line(saved_line, 0); - rl_point = saved_point; rl_redisplay(); free(saved_line); } From 008693635fbb80ab1757b37d18e1744456de2a49 Mon Sep 17 00:00:00 2001 From: Mistial Developer Date: Tue, 22 Jul 2025 02:06:19 -0400 Subject: [PATCH 098/149] Add unofficial desfire bible --- doc/unofficial_desfire_bible.md | 1008 +++++++++++++++++++++++++++++++ 1 file changed, 1008 insertions(+) create mode 100644 doc/unofficial_desfire_bible.md diff --git a/doc/unofficial_desfire_bible.md b/doc/unofficial_desfire_bible.md new file mode 100644 index 000000000..104f64fcc --- /dev/null +++ b/doc/unofficial_desfire_bible.md @@ -0,0 +1,1008 @@ +# The Unofficial DESFire Bible +## A Comprehensive Technical Reference with Citations + +### Table of Contents +1. [Introduction](#introduction) +2. [DESFire Evolution Timeline](#desfire-evolution-timeline) +3. [Version Comparison Table](#version-comparison-table) +4. [Memory Architecture](#memory-architecture) +5. [Security Features by Version](#security-features-by-version) +6. [Complete Command Reference](#complete-command-reference) +7. [Authentication Deep Dive](#authentication-deep-dive) +8. [File Types and Operations](#file-types-and-operations) +9. [Cryptographic Implementation](#cryptographic-implementation) +10. [Communication Modes](#communication-modes) +11. [Error Codes Reference](#error-codes-reference) +12. [Implementation Examples](#implementation-examples) +13. [Bibliography](#bibliography) + +--- + +## Introduction + +MIFARE DESFire is a family of contactless smart card ICs (Integrated Circuits) compliant with ISO/IEC 14443-4 Type A. This comprehensive reference documents all DESFire versions from Classic (D40) through EV3, including the cost-optimized Light variant. Every technical detail includes inline citations to ensure accuracy and traceability. + +### Document Scope +This bible covers: +- All DESFire versions: Classic/EV0, EV1, EV2, EV3, and Light +- Complete command sets with hex codes and parameters +- Authentication protocols and cryptographic implementations +- Memory organization and file structures +- Security features and attack mitigations +- Real-world implementation examples + +--- + +## DESFire Evolution Timeline + +### DESFire Classic/EV0 (D40) - Original Release +- **Release**: Early 2000s +- **Memory**: Fixed 4KB EEPROM [Source: MF3D_H_X3_SDS.pdf] +- **Applications**: Maximum 28 applications [Source: MF3D_H_X3_SDS.pdf] +- **Files per App**: Up to 16 files [Source: AN11004.pdf] +- **Encryption**: DES and 3DES only [Source: MF3D_H_X3_SDS.pdf] +- **Communication Speed**: 106 kbps [Source: AN11004.pdf] +- **Key Features**: + - Basic file types: Standard, Backup, Value, Cyclic Record + - Simple authentication protocol + - No advanced security features + +### DESFire EV1 - First Evolution (2006) +- **Memory Options**: 2KB, 4KB, 8KB EEPROM [Source: AN11004.pdf] +- **Applications**: Still limited to 28 [Source: MF3D_H_X3_SDS.pdf] +- **Files per App**: Increased to 32 [Source: AN11004.pdf] +- **New Cryptography**: Added AES-128 support [Source: AN11004.pdf] +- **Communication Speed**: Up to 848 kbps [Source: AN11004.pdf] +- **New Features** [Source: AN11004.pdf]: + - ISO/IEC 7816-4 APDU wrapping support + - Random UID option for privacy + - GetCardUID command + - ISO file identifiers (2 bytes) + - Transaction backup mechanism + - Improved key management + +### DESFire EV2 - Second Generation (2016) +- **Memory Options**: 2KB, 4KB, 8KB EEPROM [Source: AN12696.pdf] +- **Applications**: Unlimited (removed 28 app limit) [Source: MF3D_H_X3_SDS.pdf] +- **Communication Improvements**: 128-byte frame size (2x EV1) [Source: AN12696.pdf] +- **Major New Features**: + - **Virtual Card Architecture (VCA)** [Source: AN12696.pdf]: Privacy-preserving multiple card emulation + - **Transaction MAC (TMAC)** [Source: AN12696.pdf]: Offline transaction verification + - **Proximity Check** [Source: AN12696.pdf]: Protection against relay attacks + - **Delegated Application Management (DAM)** [Source: AN12696.pdf]: Secure cloud provisioning + - **Multiple Key Sets** [Source: AN12696.pdf]: Key rolling mechanism + - **Originality Check** [Source: AN12696.pdf]: Verify genuine NXP silicon + +### DESFire EV3 - Latest Generation (2020) +- **Memory Options**: 2KB, 4KB, 8KB, 16KB EEPROM [Source: MF3D_H_X3_SDS.pdf] +- **Performance**: 1.6x faster than EV1 [Source: AN12753.pdf] +- **Communication**: 256-byte frame size (2x EV2) [Source: AN12753.pdf] +- **Security Certification**: Common Criteria EAL5+ [Source: plt-05618-a.0-mifare-desfire-ev3-application-note.pdf] +- **New Features**: + - **Transaction Timer** [Source: AN12753.pdf]: Prevents delayed attack scenarios + - **Secure Dynamic Messaging (SDM)** [Source: AN12753.pdf]: Dynamic URL generation + - **Secure Unique NFC (SUN)** [Source: AN12753.pdf]: Unique tap verification + - **Pre-configured DAM Keys** [Source: AN12753.pdf]: Simplified cloud setup + - **Improved MACing** [Source: AN12753.pdf]: Enhanced integrity protection + +### DESFire Light - Cost-Optimized Variant +- **Memory Options**: 0.5KB (640B) or 2KB [Source: [0011955][v1.0] st_pegasus_desfire_lite_v10.pdf] +- **Applications**: Single application only [Source: [0011955][v1.0] st_pegasus_desfire_lite_v10.pdf] +- **Files**: Up to 32 files [Source: [0011955][v1.0] st_pegasus_desfire_lite_v10.pdf] +- **Cryptography**: AES-128 only (no DES/3DES) [Source: [0011955][v1.0] st_pegasus_desfire_lite_v10.pdf] +- **Limitations**: + - No backup files support + - Simplified command set + - No multi-application features + - Reduced security options + +--- + +## Version Comparison Table + +| Feature | Classic/EV0 | EV1 | EV2 | EV3 | Light | +|---------|-------------|-----|-----|-----|-------| +| **Memory Options** | 4KB | 2/4/8KB | 2/4/8KB | 2/4/8/16KB | 0.5/2KB | +| **Max Applications** | 28 [^1] | 28 [^1] | Unlimited [^2] | Unlimited [^2] | 1 [^3] | +| **Files per App** | 16 [^4] | 32 [^4] | 32 [^5] | 32 [^5] | 32 [^3] | +| **Frame Size** | 64B | 64B | 128B [^5] | 256B [^6] | 64B | +| **DES/3DES** | ✓ | ✓ | ✓ | ✓ | ✗ | +| **AES-128** | ✗ | ✓ [^4] | ✓ | ✓ | ✓ [^3] | +| **Random UID** | ✗ | ✓ [^4] | ✓ | ✓ | ✗ | +| **VCA** | ✗ | ✗ | ✓ [^5] | ✓ | ✗ | +| **Proximity Check** | ✗ | ✗ | ✓ [^5] | ✓ | ✗ | +| **Transaction MAC** | ✗ | ✗ | ✓ [^5] | ✓ | Limited | +| **Transaction Timer** | ✗ | ✗ | ✗ | ✓ [^6] | ✗ | +| **SDM/SUN** | ✗ | ✗ | ✗ | ✓ [^6] | ✗ | +| **Speed** | 106 kbps | 848 kbps [^4] | 848 kbps | 1.6x EV1 [^6] | 106 kbps | +| **CC Certification** | ✗ | EAL4+ | EAL5+ | EAL5+ [^7] | EAL4+ | + +[^1]: [Source: MF3D_H_X3_SDS.pdf] +[^2]: [Source: MF3D_H_X3_SDS.pdf] +[^3]: [Source: [0011955][v1.0] st_pegasus_desfire_lite_v10.pdf] +[^4]: [Source: AN11004.pdf] +[^5]: [Source: AN12696.pdf] +[^6]: [Source: AN12753.pdf] +[^7]: [Source: plt-05618-a.0-mifare-desfire-ev3-application-note.pdf] + +--- + +## Memory Architecture + +### Memory Layout Structure + +All DESFire cards follow a hierarchical structure: + +``` +PICC (Card) Level +├── Master Application (AID 0x000000) +│ ├── PICC Master Key +│ └── Card Configuration +└── Applications (AID 0x000001 - 0xFFFFFF) + ├── Application Master Key + ├── Application Keys (0-13) + └── Files (0-31) + ├── Standard Data Files + ├── Backup Files + ├── Value Files + ├── Linear Record Files + └── Cyclic Record Files +``` + +### Application Identifier (AID) +- **Size**: 3 bytes (24 bits) [Source: AN11004.pdf] +- **Range**: 0x000000 to 0xFFFFFF +- **Reserved**: 0x000000 (Master Application) +- **User Range**: 0x000001 to 0xFFFFFF + +### File Types and Structures + +#### 1. Standard Data File +- **Purpose**: Store raw data [Source: AN11004.pdf] +- **Size**: 1 to 8191 bytes (EV1), 1 to 32 bytes (Light) [Source: various] +- **Operations**: Read, Write +- **Structure**: Simple byte array + +#### 2. Backup File +- **Purpose**: Transactional data with commit/abort [Source: AN11004.pdf] +- **Size**: Same as Standard File +- **Operations**: Read, Write, Commit, Abort +- **Note**: Not supported on DESFire Light [Source: [0011955][v1.0] st_pegasus_desfire_lite_v10.pdf] + +#### 3. Value File +- **Purpose**: Store 32-bit signed integer [Source: AN11004.pdf] +- **Operations**: Read, Credit, Debit, Limited Credit +- **Limits**: Configurable lower and upper bounds +- **Structure**: + ``` + Value: 4 bytes (signed int32) + ``` + +#### 4. Linear Record File +- **Purpose**: Append-only records [Source: AN11004.pdf] +- **Record Size**: 1 to 8191 bytes +- **Max Records**: Configurable +- **Operations**: Read, Write (append), Clear + +#### 5. Cyclic Record File +- **Purpose**: Circular buffer of records [Source: AN11004.pdf] +- **Behavior**: Oldest record overwritten when full +- **Operations**: Read, Write (newest), Clear + +### Memory Access Rights + +Each file has configurable access rights [Source: AN11004.pdf]: +- **Read Access**: Key 0-13, 0xE (free), 0xF (deny) +- **Write Access**: Key 0-13, 0xE (free), 0xF (deny) +- **Read&Write Access**: Key 0-13, 0xE (free), 0xF (deny) +- **Change Access Rights**: Key 0-13, 0xF (deny) + +Communication settings per file: +- **0x00**: Plain communication +- **0x01**: MACed communication +- **0x03**: Fully enciphered communication + +--- + +## Security Features by Version + +### DESFire Classic/EV0 Security +- **Encryption**: DES/3DES only [Source: MF3D_H_X3_SDS.pdf] +- **Authentication**: Simple challenge-response +- **Protection**: Basic anti-collision, no advanced features + +### DESFire EV1 Security Enhancements +- **AES-128 Support**: Added alongside DES/3DES [Source: AN11004.pdf] +- **Random UID**: Configurable for privacy [Source: AN11004.pdf] +- **Diversified Keys**: Support for key derivation +- **Anti-tearing**: Transaction backup mechanism + +### DESFire EV2 Security Additions +- **Proximity Check** [Source: AN12696.pdf]: + - Prevents relay attacks + - Time-based distance bounding + - Configurable timing parameters + +- **Virtual Card Architecture (VCA)** [Source: AN12696.pdf]: + - Multiple virtual cards in one + - Install/Select/Delete virtual cards + - Privacy through UID randomization + +- **Transaction MAC (TMAC)** [Source: AN12696.pdf, MF2DLHX0.pdf]: + - Offline transaction verification + - Reader-specific MACs with CommitReaderID command (0xC8) + - Counter-based freshness (TMC - Transaction MAC Counter) + - Special file type 0x05 with unique access rights: + - Read: Normal access control + - Write: Always 0xF (disabled) + - ReadWrite: CommitReaderID key (0x0-0xE enabled, 0xF disabled) + - Change: Normal access control + - TMV (Transaction MAC Value) calculated on CommitTransaction + +- **Secure Messaging v2** [Source: AN12696.pdf]: + - Improved IV generation + - Command counter protection + - Enhanced session key derivation + +### DESFire EV3 Security Features +- **Transaction Timer** [Source: AN12753.pdf]: + - Maximum time window for operations + - Prevents delayed attack scenarios + - Configurable per application + +- **Secure Dynamic Messaging (SDM)** [Source: AN12753.pdf]: + - Dynamic NDEF message generation + - Encrypted file data in URLs + - PICCData and MACed responses + +- **Common Criteria EAL5+** [Source: plt-05618-a.0-mifare-desfire-ev3-application-note.pdf]: + - Highest security certification + - Formally verified implementation + - Hardware security evaluation + +--- + +## Complete Command Reference + +### Authentication Commands + +#### 0x0A - Authenticate (Legacy DES/3DES) +- **Parameters**: KeyNo (1 byte) [Source: protocols.h, line 334] +- **Response**: Encrypted RndB (8 bytes) + status +- **Versions**: All except Light +- **Flow**: See Authentication Deep Dive section + +#### 0x1A - Authenticate ISO (3DES) +- **Parameters**: KeyNo (1 byte) [Source: protocols.h, line 335] +- **Response**: Encrypted RndB (8 bytes) + status +- **Versions**: EV1, EV2, EV3 +- **Note**: ISO/IEC 7816-4 compliant + +#### 0xAA - Authenticate AES +- **Parameters**: KeyNo (1 byte) [Source: protocols.h, line 336] +- **Response**: Encrypted RndB (16 bytes) + status +- **Versions**: EV1, EV2, EV3, Light +- **Note**: Uses AES-128 in CBC mode + +#### 0x71 - AuthenticateEV2First +- **Parameters**: KeyNo (1 byte) + Capabilities [Source: protocols.h, line 337] +- **Response**: Transaction identifier + encrypted data +- **Versions**: EV2, EV3 +- **Purpose**: Initial EV2 authentication with capability exchange + +#### 0x77 - AuthenticateEV2NonFirst +- **Parameters**: KeyNo (1 byte) [Source: protocols.h, line 338] +- **Response**: Encrypted authentication data +- **Versions**: EV2, EV3 +- **Purpose**: Subsequent EV2 authentication + +#### 0x70 - FreeMem +- **Parameters**: None [Source: protocols.h, line 339] +- **Response**: Free memory (3 bytes) +- **Versions**: All +- **Authentication**: Not required + +### Application Management Commands + +#### 0xCA - CreateApplication +- **Parameters**: [Source: protocols.h, line 344] + - AID (3 bytes) + - KeySettings (1 byte) + - NumOfKeys (1 byte): Lower nibble = key count, Upper nibble = crypto method +- **Versions**: All +- **Example**: `CA 01 00 00 0F 81` creates AID 0x000001 with 1 AES key + +#### 0xDA - DeleteApplication +- **Parameters**: AID (3 bytes) [Source: protocols.h, line 345] +- **Versions**: All +- **Authentication**: PICC Master Key required + +#### 0x5A - SelectApplication +- **Parameters**: AID (3 bytes) [Source: protocols.h, line 347] +- **Versions**: All +- **Note**: AID 0x000000 selects master application + +#### 0x6A - GetApplicationIDs +- **Parameters**: None [Source: protocols.h, line 346] +- **Response**: List of AIDs (3 bytes each) +- **Versions**: All + +#### 0x45 - GetKeySettings +- **Parameters**: None [Source: protocols.h, line 350] +- **Response**: KeySettings (1 byte) + NumOfKeys (1 byte) +- **Versions**: All + +#### 0x64 - GetKeyVersion +- **Parameters**: KeyNo (1 byte) [Source: protocols.h, line 355] +- **Response**: Key version (1 byte) +- **Versions**: All + +### File Management Commands + +#### 0xCD - CreateStdDataFile +- **Parameters**: [Source: protocols.h, line 357] + - FileNo (1 byte) + - FileOption/CommSettings (1 byte) + - AccessRights (2 bytes) + - FileSize (3 bytes, LSB first) +- **Versions**: All + +#### 0xCB - CreateBackupFile +- **Parameters**: Same as CreateStdDataFile [Source: protocols.h, line 358] +- **Versions**: All except Light +- **Note**: Supports transaction mechanism + +#### 0xCC - CreateValueFile +- **Parameters**: [Source: protocols.h, line 359] + - FileNo (1 byte) + - CommSettings (1 byte) + - AccessRights (2 bytes) + - LowerLimit (4 bytes) + - UpperLimit (4 bytes) + - Value (4 bytes) + - LimitedCreditEnable (1 byte) +- **Versions**: All + +#### 0xC1 - CreateLinearRecordFile +- **Parameters**: [Source: protocols.h, line 360] + - FileNo (1 byte) + - CommSettings (1 byte) + - AccessRights (2 bytes) + - RecordSize (3 bytes) + - MaxNumberOfRecords (3 bytes) +- **Versions**: All + +#### 0xC0 - CreateCyclicRecordFile +- **Parameters**: Same as CreateLinearRecordFile [Source: protocols.h, line 361] +- **Versions**: All + +#### 0xDF - DeleteFile +- **Parameters**: FileNo (1 byte) [Source: protocols.h, line 362] +- **Versions**: All + +#### 0x6F - GetFileIDs +- **Parameters**: None [Source: protocols.h, line 363] +- **Response**: List of FileIDs (1 byte each) +- **Versions**: All + +#### 0xF5 - GetFileSettings +- **Parameters**: FileNo (1 byte) [Source: protocols.h, line 364] +- **Response**: File type + settings structure +- **Versions**: All + +### Data Manipulation Commands + +#### 0xBD - ReadData +- **Parameters**: [Source: protocols.h, line 367] + - FileNo (1 byte) + - Offset (3 bytes, LSB first) + - Length (3 bytes, LSB first) +- **Response**: Data + status +- **Versions**: All + +#### 0x3D - WriteData +- **Parameters**: [Source: protocols.h, line 368] + - FileNo (1 byte) + - Offset (3 bytes) + - Length (3 bytes) + - Data (variable) +- **Versions**: All + +#### 0x6C - GetValue +- **Parameters**: FileNo (1 byte) [Source: protocols.h, line 369] +- **Response**: Value (4 bytes) +- **Versions**: All + +#### 0x0C - Credit +- **Parameters**: [Source: protocols.h, line 370] + - FileNo (1 byte) + - Amount (4 bytes) +- **Versions**: All + +#### 0xDC - Debit +- **Parameters**: Same as Credit [Source: protocols.h, line 371] +- **Versions**: All + +#### 0x1C - LimitedCredit +- **Parameters**: Same as Credit [Source: protocols.h, line 372] +- **Versions**: All +- **Note**: Only if LimitedCreditEnabled + +#### 0x3B - WriteRecord +- **Parameters**: [Source: protocols.h, line 373] + - FileNo (1 byte) + - Offset (3 bytes) + - Length (3 bytes) + - Data (variable) +- **Versions**: All + +#### 0xBB - ReadRecords +- **Parameters**: [Source: protocols.h, line 374] + - FileNo (1 byte) + - Offset (3 bytes): Record number + - Length (3 bytes): Number of records +- **Versions**: All + +#### 0xEB - ClearRecordFile +- **Parameters**: FileNo (1 byte) [Source: protocols.h, line 375] +- **Versions**: All + +#### 0xC7 - CommitTransaction +- **Parameters**: Option byte (optional, 1 byte) [Source: MF2DLHX0.pdf, AN12343.pdf] +- **Versions**: All +- **Purpose**: Commit all pending changes +- **Note**: With option 0x01, returns TMC and TMV for TMAC verification + +#### 0xC8 - CommitReaderID +- **Parameters**: ReaderID (16 bytes) [Source: MF2DLHX0.pdf, Section 10.3] +- **Versions**: EV2, EV3, Light +- **Purpose**: Set reader-specific identifier for Transaction MAC generation +- **Authentication**: Depends on TMAC file ReadWrite access rights: + - 0x0-0x4: Authentication with specified key required + - 0xE: Free access allowed + - 0xF: CommitReaderID disabled +- **Communication**: Requires MACed or Encrypted mode +- **Response**: + - When authenticated: EncTMRI (16 bytes) = E_TM(SesTMENCKey, TMRIPrev) + - When not authenticated: No data, only status code +- **Notes**: + - EncTMRI uses AES CBC with zero IV for encryption + - TMRIPrev tracks previous transaction's ReaderID for chain verification + - TMRIPrev only updated on CommitTransaction if authenticated + - Used with TMAC file type (0x05) for offline transaction verification + +#### 0xA7 - AbortTransaction +- **Parameters**: None [Source: protocols.h, line 377] +- **Versions**: All +- **Purpose**: Rollback pending changes + +### Configuration Commands + +#### 0x5F - ChangeFileSettings +- **Parameters**: [Source: protocols.h, line 365] + - FileNo (1 byte) + - CommSettings (1 byte) + - AccessRights (2 bytes) +- **Versions**: All + +#### 0x54 - ChangeKeySettings +- **Parameters**: KeySettings (1 byte) [Source: protocols.h, line 351] +- **Versions**: All + +#### 0xC4 - ChangeKey +- **Parameters**: [Source: protocols.h, line 352] + - KeyNo (1 byte) + - New key data (encrypted) +- **Versions**: All + +### Information Commands + +#### 0x60 - GetVersion +- **Parameters**: None [Source: protocols.h, line 349] +- **Response**: Version info structure (28 bytes) +- **Versions**: All + +#### 0x51 - GetCardUID +- **Parameters**: None [Source: protocols.h, line 389] +- **Response**: UID (7 bytes) +- **Versions**: EV1+ +- **Authentication**: Required + +#### 0x61 - GetFileCounters +- **Parameters**: FileNo (1 byte) [Source: protocols.h, line 390] +- **Response**: Counters for SDM +- **Versions**: EV2+ + +#### 0x6E - GetFreeMemory +- **Parameters**: None [Source: AN11004.pdf] +- **Response**: Free memory (3 bytes) +- **Versions**: All + +### ISO Wrapped Commands + +#### 0xAD - ISOReadBinary +- **Parameters**: ISO 7816-4 wrapped ReadData [Source: protocols.h, line 378] +- **Versions**: EV1+ + +#### 0xAB - ISOAppendRecord +- **Parameters**: ISO 7816-4 wrapped WriteRecord [Source: protocols.h, line 380] +- **Versions**: EV1+ + +#### 0xA2 - ISOReadRecords +- **Parameters**: ISO 7816-4 wrapped ReadRecords [Source: protocols.h, line 379] +- **Versions**: EV1+ + +#### 0xA0 - ISOSelectFile +- **Parameters**: ISO 7816-4 file selection [Source: protocols.h, line 382] +- **Versions**: EV1+ + +#### 0x3A - ISOUpdateBinary +- **Parameters**: ISO 7816-4 wrapped WriteData [Source: protocols.h, line 383] +- **Versions**: EV1+ + +### Special Commands + +#### 0xAF - Additional Frame +- **Purpose**: Continue previous command [Source: protocols.h, line 342] +- **Parameters**: Additional data +- **Versions**: All + +#### 0x00 - ISO Wrapping +- **Purpose**: ISO 7816-4 command wrapping [Source: protocols.h, line 341] +- **Versions**: EV1+ + +### Transaction/Security Commands (EV2/EV3) + +#### 0xC9 - InitializeKeySet +- **Parameters**: KeySetNo + KeySetSettings [Source: protocols.h, line 385] +- **Versions**: EV2+ + +#### 0xCE - FinalizeKeySet +- **Parameters**: KeySetNo + KeyVersion [Source: protocols.h, line 386] +- **Versions**: EV2+ + +#### 0xCF - RollKeySet +- **Parameters**: KeySetNo [Source: protocols.h, line 387] +- **Versions**: EV2+ + +#### 0xF6 - GetDelegatedInfo +- **Parameters**: DAMSlotNo [Source: protocols.h, line 391] +- **Versions**: EV2+ + +#### 0xFA - TransactionMAC +- **Parameters**: Transaction data [Source: various sources] +- **Versions**: EV2+ +- **Purpose**: Generate offline verification MAC + +### Status Codes + +#### Success Codes +- **0x00**: OPERATION_OK [Source: protocols.h, line 393] +- **0x0C**: NO_CHANGES [Source: protocols.h, line 394] + +#### Error Codes +- **0x0E**: OUT_OF_MEMORY [Source: protocols.h, line 395] +- **0x1C**: ILLEGAL_COMMAND_CODE [Source: protocols.h, line 396] +- **0x1E**: INTEGRITY_ERROR [Source: protocols.h, line 397] +- **0x40**: NO_SUCH_KEY [Source: protocols.h, line 398] +- **0x7E**: LENGTH_ERROR [Source: protocols.h, line 399] +- **0x9D**: PERMISSION_DENIED [Source: protocols.h, line 400] +- **0x9E**: PARAMETER_ERROR [Source: protocols.h, line 401] +- **0xA0**: APPLICATION_NOT_FOUND [Source: protocols.h, line 402] +- **0xA1**: APPL_INTEGRITY_ERROR [Source: protocols.h, line 403] +- **0xAE**: AUTHENTICATION_ERROR [Source: protocols.h, line 404] +- **0xAF**: ADDITIONAL_FRAME [Source: protocols.h, line 405] +- **0xBE**: BOUNDARY_ERROR [Source: protocols.h, line 406] +- **0xC1**: COMMAND_ABORTED [Source: protocols.h, line 408] +- **0xCA**: PICC_INTEGRITY_ERROR [Source: protocols.h, line 407] +- **0xCD**: PICC_DISABLED_ERROR [Source: protocols.h, line 409] +- **0xCE**: COUNT_ERROR [Source: protocols.h, line 410] +- **0xDE**: DUPLICATE_ERROR [Source: protocols.h, line 411] +- **0xEE**: EEPROM_ERROR [Source: protocols.h, line 412] +- **0xF0**: FILE_NOT_FOUND [Source: protocols.h, line 413] +- **0xF1**: FILE_INTEGRITY_ERROR [Source: protocols.h, line 414] + +--- + +## Authentication Deep Dive + +### DES/3DES Authentication Protocol + +#### Phase 1: Initial Authentication Request +``` +PCD → PICC: 90 0A 00 00 01 [KeyNo] 00 + └─ Authenticate command (0x0A) +``` +[Source: DESFire DES authentication D40-DES authentification.pdf, line 7] + +#### Phase 2: PICC Responds with Encrypted RndB +``` +PICC → PCD: [Ek(RndB)] 91 AF + └─ 8 bytes encrypted RndB +``` +[Source: DESFire DES authentication D40-DES authentification.pdf, line 9] + +#### Phase 3: PCD Prepares Response +1. Decrypt RndB using key +2. Generate RndA (8 bytes) +3. Rotate RndB left by 1 byte +4. Concatenate: RndA || RndB_rotated +5. Encrypt with CBC mode, IV from previous response + +[Source: DESFire DES authentication D40-DES authentification.pdf, lines 23-39] + +#### Phase 4: Send Encrypted Challenge +``` +PCD → PICC: 90 AF 00 00 10 [Ek(RndA || RndB_rot)] 00 +``` +[Source: DESFire DES authentication D40-DES authentification.pdf, line 41] + +#### Phase 5: Verify PICC Response +``` +PICC → PCD: [Ek(RndA_rot)] 91 00 +``` +PCD decrypts and verifies rotated RndA matches +[Source: DESFire DES authentication D40-DES authentification.pdf, lines 43-56] + +### AES Authentication Protocol + +Similar flow but with 16-byte blocks: +1. Uses command 0xAA instead of 0x0A +2. RndA and RndB are 16 bytes each +3. AES-128 in CBC mode +4. Session key derivation differs + +[Source: DESFire.py, lines 79-144] + +### EV2 Authentication Protocol + +#### EV2First Authentication +1. **Capability Exchange**: + ``` + PCD → PICC: 71 [KeyNo] [Len] [PCDcap2] + PICC → PCD: [TI] [PDcap2] [PCDcap2] AF + ``` + [Source: desfire_ev3_authentication.pdf, lines 18-25] + +2. **Complete Authentication**: + - Similar challenge-response + - Generates Transaction Identifier (TI) + - Establishes secure channel + +#### EV2NonFirst Authentication +``` +PCD → PICC: 77 [KeyNo] +``` +Requires previous EV2First in same session +[Source: desfire_ev3_authentication.pdf, lines 27-30] + +### Session Key Generation + +#### DES Session Key (8 bytes) +``` +SessionKey = RndA[0:4] || RndB[0:4] +``` +[Source: DESFire DES authentication D40-DES authentification.pdf, lines 66-71] + +#### 2K3DES Session Key (16 bytes) +``` +SessionKey = RndA[0:4] || RndB[0:4] || RndA[4:8] || RndB[4:8] +``` +[Source: DESFire.py, lines 135-136] + +#### 3K3DES Session Key (24 bytes) +``` +SessionKey = RndA[0:4] || RndB[0:4] || + RndA[6:10] || RndB[6:10] || + RndA[12:16] || RndB[12:16] +``` +[Source: DESFire.py, lines 138-141] + +#### AES Session Key (16 bytes) +``` +SessionKey = RndA[0:4] || RndB[0:4] || RndA[12:16] || RndB[12:16] +``` +[Source: DESFire.py, lines 143-144] + +### CMAC Calculation + +#### Subkey Generation +```python +# Generate L by encrypting zero block +L = AES_Encrypt(Key, 0x00000000000000000000000000000000) + +# Generate K1 +K1 = L << 1 +if MSB(L) == 1: + K1 = K1 XOR Rb # Rb = 0x87 for AES + +# Generate K2 +K2 = K1 << 1 +if MSB(K1) == 1: + K2 = K2 XOR Rb +``` +[Source: mifare_desfire_crypto.c, lines 95-123] + +#### CMAC Calculation +1. Pad message if needed (0x80 0x00...) +2. XOR last block with K1 (complete) or K2 (incomplete) +3. CBC encrypt all blocks +4. Final block is CMAC + +[Source: mifare_desfire_crypto.c, lines 126-151] + +--- + +## File Types and Operations + +### Standard Data File Operations + +#### CreateStdDataFile +``` +Command: CD [FileNo] [CommSettings] [AccessRights] [FileSize] +Example: CD 01 00 00 00 00 10 00 00 // File 01, plain, free access, 16 bytes +``` +[Source: protocols.h, line 357] + +#### ReadData +``` +Command: BD [FileNo] [Offset-3B] [Length-3B] +Example: BD 01 00 00 00 10 00 00 // Read 16 bytes from offset 0 +``` +[Source: protocols.h, line 367] + +#### WriteData +``` +Command: 3D [FileNo] [Offset-3B] [Length-3B] [Data] +Example: 3D 01 00 00 00 04 00 00 DE AD BE EF // Write 4 bytes +``` +[Source: protocols.h, line 368] + +### Value File Operations + +#### CreateValueFile +``` +Command: CC [FileNo] [CommSettings] [AccessRights] [LowerLimit-4B] [UpperLimit-4B] [Value-4B] [LimitedCreditEnable] +Example: CC 02 00 00 00 00 00 00 00 E8 03 00 00 00 00 00 00 01 + // Value file 02, limits 0-1000, initial 0, limited credit enabled +``` +[Source: protocols.h, line 359] + +#### Credit Operation +``` +Command: 0C [FileNo] [Amount-4B] +Example: 0C 02 64 00 00 00 // Credit 100 to file 02 +``` +[Source: protocols.h, line 370] + +#### Debit Operation +``` +Command: DC [FileNo] [Amount-4B] +Example: DC 02 0A 00 00 00 // Debit 10 from file 02 +``` +[Source: protocols.h, line 371] + +### Record File Operations + +#### CreateLinearRecordFile +``` +Command: C1 [FileNo] [CommSettings] [AccessRights] [RecordSize-3B] [MaxRecords-3B] +Example: C1 03 00 00 00 20 00 00 0A 00 00 + // Linear record file 03, 32-byte records, max 10 records +``` +[Source: protocols.h, line 360] + +#### WriteRecord +``` +Command: 3B [FileNo] [Offset-3B] [Length-3B] [Data] +Example: 3B 03 00 00 00 20 00 00 [32 bytes of data] +``` +[Source: protocols.h, line 373] + +#### ReadRecords +``` +Command: BB [FileNo] [RecordNo-3B] [NumRecords-3B] +Example: BB 03 00 00 00 05 00 00 // Read 5 records starting from record 0 +``` +[Source: protocols.h, line 374] + +### Transaction Mechanism + +For Backup and Value files: +1. Perform operations (Write, Credit, Debit) +2. Changes are pending until: + - **CommitTransaction (0xC7)**: Apply changes + - **AbortTransaction (0xA7)**: Discard changes + +[Source: protocols.h, lines 376-377] + +--- + +## Cryptographic Implementation + +### Key Diversification (AN10922) + +#### Algorithm Steps +1. **Prepare Diversification Input**: + ``` + M = [Constant] || [UID] || [AID] || [SystemIdentifier] + ``` + Constants: + - 0x01: AES-128 + - 0x21: 2K3DES + - 0x31: 3K3DES + [Source: mifare_key_deriver.c, lines 10-17] + +2. **Calculate Diversified Key**: + ``` + DiversifiedKey = CMAC(MasterKey, M) + ``` + [Source: mifare_key_deriver.c, lines 101-177] + +### Secure Messaging + +#### MACed Communication Mode (0x01) +- Commands sent in plain +- Response includes 8-byte CMAC +- CMAC covers: Response Data + Status Code +[Source: various implementation files] + +#### Full Enciphered Mode (0x03) +- Command data encrypted after authentication +- Response data encrypted +- Both include CMAC for integrity +- Uses session keys and IVs + +### IV Generation + +#### EV1 IV Handling +- Initial IV: All zeros +- Subsequent: Last block of previous crypto operation + +#### EV2/EV3 IV Generation +``` +IV = EncryptedFlag || TI || .pdfCtr || ZeroPadding +``` +- TI: Transaction Identifier (4 bytes) +- .pdfCtr: Command Counter (2 bytes) +[Source: hf_desfire.c and crypto implementations] + +--- + +## Communication Modes + +### Plain Communication (0x00) +- No encryption or MACing +- Suitable for public data +- Fastest performance +- No authentication required for read + +### MACed Communication (0x01) +- Data transmitted in plain +- 8-byte CMAC appended to responses +- Integrity protection +- Requires authentication + +### Fully Enciphered Communication (0x03) +- All data encrypted +- CMAC for integrity +- Maximum security +- Requires authentication +- Performance impact + +--- + +## Error Codes Reference + +### Common Error Scenarios + +#### 0x9D - PERMISSION_DENIED +- Attempting operation without required authentication +- Wrong key authenticated for operation +- Access rights don't permit operation + +#### 0xAE - AUTHENTICATION_ERROR +- Authentication protocol failure +- Wrong key or key version +- Corrupted authentication data + +#### 0x7E - LENGTH_ERROR +- Command parameters wrong length +- Data exceeds file size +- Frame size exceeded + +#### 0xA0 - APPLICATION_NOT_FOUND +- Invalid AID selected +- Application was deleted +- Card not properly initialized + +--- + +## Implementation Examples + +### Example 1: Creating an Application with AES Keys +```python +# Create application 0x000001 with 5 AES keys +aid = [0x01, 0x00, 0x00] +key_settings = 0x0F # All keys changeable, free directory +num_keys = 0x85 # 5 keys, AES encryption (bit 7 set) + +command = [0xCA] + aid + [key_settings, num_keys] +response = send_command(command) +``` + +### Example 2: Secure File Write with MACing +```python +# Authenticate first +authenticate_aes(key_no=0x01, key=master_key) + +# Create MACed file +create_std_file(file_no=0x01, + comm_settings=0x01, # MACed + access_rights=0x0000, # Free access + file_size=32) + +# Write data (will be MACed automatically) +write_data(file_no=0x01, offset=0, data=b"Secure data here") +``` + +### Example 3: Value File Transaction +```python +# Create value file with limits +create_value_file(file_no=0x02, + lower_limit=0, + upper_limit=10000, + initial_value=1000, + limited_credit=True) + +# Perform operations +credit(file_no=0x02, amount=500) # Balance: 1500 +debit(file_no=0x02, amount=200) # Balance: 1300 + +# Commit all changes +commit_transaction() +``` + +--- + +## Bibliography + +### Primary Sources (Datasheets) +1. **AN11004**: MIFARE DESFire EV1 Features and Hints +2. **AN12696**: MIFARE DESFire EV2 Features and Hints +3. **AN12753**: MIFARE DESFire EV3 Features and Hints +4. **MF3D_H_X3_SDS**: MIFARE DESFire EV3 Secure Data Sheet +5. **PLT-05618**: MIFARE DESFire EV3 Application Note +6. **[0011955][v1.0]**: ST Pegasus DESFire Light v1.0 Specification +7. **AN-315**: Understanding Protege MIFARE DESFire Credentials + +### Implementation Sources +1. **protocols.h**: Proxmark3 DESFire protocol definitions +2. **hf_desfire.c**: Proxmark3 DESFire implementation +3. **DESFire.py**: Python DESFire implementation +4. **DESFire_DEF.py**: Python DESFire constants +5. **mifare_desfire.c**: libfreefare C implementation +6. **mifare_desfire_crypto.c**: libfreefare crypto implementation +7. **DesfireEv3.java**: Android DESFire EV3 implementation + +### Documentation Sources +1. **desfire_ev3_authentication.pdf**: EV3 authentication details +2. **desfire_ev3_file_operations.pdf**: EV3 file operation examples +3. **DESFire DES authentication D40-DES authentification.pdf**: Legacy auth flow +4. **DESFire TDES decryption SEND mode.pdf**: TDES implementation details +5. **auth1d_d40.pdf**: D40 authentication documentation + +### Additional References +1. ISO/IEC 14443-4: Proximity cards protocol +2. ISO/IEC 7816-4: Smart card APDU specification +3. Common Criteria EAL5+ certification documents +4. NIST SP 800-38B: CMAC specification +5. AN10922: NXP Key Diversification + +--- + +*End of The Unofficial DESFire Bible* + +*Compiled from official documentation and implementation sources* +*All information includes inline citations for verification* +*Last updated: Based on DESFire EV3 specifications* \ No newline at end of file From f943a385583794e821f1e2710d488e210415ef79 Mon Sep 17 00:00:00 2001 From: Iceman Date: Tue, 22 Jul 2025 10:45:16 +0200 Subject: [PATCH 099/149] Update README.md Signed-off-by: Iceman --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5efcfc5d7..9418c837a 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,8 @@ The Proxmark3 is the swiss-army tool of RFID, allowing for interactions with the |[Developing standalone mode](/armsrc/Standalone/readme.md)|[Wiki about standalone mode](https://github.com/RfidResearchGroup/proxmark3/wiki/Standalone-mode)|[Notes on Magic UID cards](/doc/magic_cards_notes.md)| |[Notes on Color usage](/doc/colors_notes.md)|[Makefile vs CMake](/doc/md/Development/Makefile-vs-CMake.md)|[Notes on Cloner guns](/doc/cloner_notes.md)| |[Notes on cliparser usage](/doc/cliparser.md)|[Notes on clocks](/doc/clocks.md)|[Notes on MIFARE DESFire](/doc/desfire.md)| -|[Notes on CIPURSE](/doc/cipurse.md)|[Notes on NDEF type4a](/doc/ndef_type4a.md)|[Notes on downgrade attacks](/doc/hid_downgrade.md)| +|[Notes on CIPURSE](/doc/cipurse.md)|[Notes on NDEF type4a](/doc/ndef_type4a.md)|[Unofficial MIFARE DESFire bible](/doc/unofficial_desfire_bible.md)| +[Notes on downgrade attacks](/doc/hid_downgrade.md)||| # How to build? From 47b78279825a85f8014a88438fb65c632935aa9d Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 25 Jul 2025 20:24:53 +0200 Subject: [PATCH 100/149] hf mf sim: Missing line in trace when reader attempts to auth to sectors out of range --- armsrc/mifaresim.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index 21014be12..456c10358 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -793,7 +793,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t if (cardAUTHSC >= cardMaxSEC) { cardAUTHKEY = AUTHKEYNONE; // not authenticated cardSTATE_TO_IDLE(); - if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] Out of range sector %d(0x%02x)", cardAUTHSC, cardAUTHSC); + if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] Out of range sector %d(0x%02x) >= %d(0x%02x)", cardAUTHSC, cardAUTHSC, cardMaxSEC, cardMaxSEC); + LogTrace(uart->output, uart->len, uart->startTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->endTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->parity, true); break; } From 48724e44b42822f2fcfdfefa879d1f14fbfaf57e Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 25 Jul 2025 20:54:01 +0200 Subject: [PATCH 101/149] hf mf sim: add --allowover option, needed for RF08S originality check --- armsrc/mifaresim.c | 2 +- client/src/cmdhfmf.c | 9 +++++++-- include/pm3_cmd.h | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index 456c10358..a276ce28c 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -790,7 +790,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] KEY %c: %012" PRIx64, (cardAUTHKEY == 0) ? 'A' : 'B', emlGetKey(cardAUTHSC, cardAUTHKEY)); // sector out of range - do not respond - if (cardAUTHSC >= cardMaxSEC) { + if ((cardAUTHSC >= cardMaxSEC) && (flags & FLAG_MF_ALLOW_OOB_AUTH) == 0) { cardAUTHKEY = AUTHKEYNONE; // not authenticated cardSTATE_TO_IDLE(); if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] Out of range sector %d(0x%02x) >= %d(0x%02x)", cardAUTHSC, cardAUTHSC, cardMaxSEC, cardMaxSEC); diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 025698692..cf48dd5be 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -4671,6 +4671,7 @@ static int CmdHF14AMfSim(const char *Cmd) { arg_lit0("e", "emukeys", "Fill simulator keys from found keys. Requires -x or -y. Implies -i. Simulation will restart automatically."), // If access bits show that key B is Readable, any subsequent memory access should be refused. arg_lit0(NULL, "allowkeyb", "Allow key B even if readable"), + arg_lit0(NULL, "allowover", "Allow auth attempts out of range for selected mifare type"), arg_lit0("v", "verbose", "Verbose output"), arg_lit0(NULL, "cve", "Trigger CVE 2021_0430"), arg_param_end @@ -4725,9 +4726,13 @@ static int CmdHF14AMfSim(const char *Cmd) { flags |= FLAG_MF_USE_READ_KEYB; } - bool verbose = arg_get_lit(ctx, 14); + if (arg_get_lit(ctx, 14)) { + flags |= FLAG_MF_ALLOW_OOB_AUTH; + } - if (arg_get_lit(ctx, 15)) { + bool verbose = arg_get_lit(ctx, 15); + + if (arg_get_lit(ctx, 16)) { flags |= FLAG_CVE21_0430; } CLIParserFree(ctx); diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index cbce45a24..e5d8d2474 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -872,7 +872,7 @@ typedef struct { #define FLAG_NR_AR_ATTACK 0x0400 // support nested authentication attack #define FLAG_NESTED_AUTH_ATTACK 0x0800 - +#define FLAG_MF_ALLOW_OOB_AUTH 0x1000 #define MODE_SIM_CSN 0 #define MODE_EXIT_AFTER_MAC 1 From 06a9a67aaeb5187d545c177430bbc420a4f99715 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 25 Jul 2025 21:00:26 +0200 Subject: [PATCH 102/149] typo --- client/src/cmdhfmf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index cf48dd5be..bc7f46cb4 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -4671,7 +4671,7 @@ static int CmdHF14AMfSim(const char *Cmd) { arg_lit0("e", "emukeys", "Fill simulator keys from found keys. Requires -x or -y. Implies -i. Simulation will restart automatically."), // If access bits show that key B is Readable, any subsequent memory access should be refused. arg_lit0(NULL, "allowkeyb", "Allow key B even if readable"), - arg_lit0(NULL, "allowover", "Allow auth attempts out of range for selected mifare type"), + arg_lit0(NULL, "allowover", "Allow auth attempts out of range for selected MIFARE Classic type"), arg_lit0("v", "verbose", "Verbose output"), arg_lit0(NULL, "cve", "Trigger CVE 2021_0430"), arg_param_end From e7cbf9ff633d760998919d572e2148f4e5b2b954 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 25 Jul 2025 22:28:33 +0200 Subject: [PATCH 103/149] update commands.json --- doc/commands.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/commands.json b/doc/commands.json index feddfcfa9..af21007c9 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -5509,10 +5509,11 @@ "-y Performs the nested 'reader attack'. This requires preloading nt & nt_enc in emulator memory. Implies -x.", "-e, --emukeys Fill simulator keys from found keys. Requires -x or -y. Implies -i. Simulation will restart automatically.", "--allowkeyb Allow key B even if readable", + "--allowover Allow auth attempts out of range for selected MIFARE Classic type", "-v, --verbose Verbose output", "--cve Trigger CVE 2021_0430" ], - "usage": "hf mf sim [-hixyev] [-u ] [--mini] [--1k] [--2k] [--4k] [--atqa ] [--sak ] [-n ] [--allowkeyb] [--cve]" + "usage": "hf mf sim [-hixyev] [-u ] [--mini] [--1k] [--2k] [--4k] [--atqa ] [--sak ] [-n ] [--allowkeyb] [--allowover] [--cve]" }, "hf mf staticnested": { "command": "hf mf staticnested", @@ -6037,7 +6038,7 @@ "-a, --apdu Show APDU requests and responses", "-v, --verbose Verbose output", "-n, --keyno Key number", - "-t, --algo Crypt algo", + "-t, --algo Crypt algo (deft: 2TDEA)", "-k, --key Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)", "--kdf Key Derivation Function (KDF)", "-i, --kdfi KDF input (1-31 hex bytes)", @@ -6699,7 +6700,7 @@ "-a, --apdu Show APDU requests and responses", "-v, --verbose Verbose output", "-n, --keyno Key number", - "-t, --algo Crypt algo", + "-t, --algo Crypt algo (deft: 2TDEA)", "-k, --key Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)", "--kdf Key Derivation Function (KDF)", "-i, --kdfi KDF input (1-31 hex bytes)", @@ -13440,6 +13441,6 @@ "metadata": { "commands_extracted": 772, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-07-13T13:53:32" + "extracted_on": "2025-07-25T20:28:03" } } From 90d766a8112204182c84aeb653fc7ba2cfbdbab7 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 25 Jul 2025 22:29:58 +0200 Subject: [PATCH 104/149] Fix FM11RF08 0391 description --- client/src/cmdhfmf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index bc7f46cb4..04e024974 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -10434,7 +10434,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08s, sizeof(fkey)) == 0 && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 && (blockdata[8] == 0x03 || blockdata[8] == 0x04) && blockdata[15] == 0x91) { - PrintAndLogEx(SUCCESS, "Fudan FM11RF08S %02X%02X without static enc nonce", blockdata[8], blockdata[15]); + PrintAndLogEx(SUCCESS, "Fudan FM11RF08 %02X%02X with advanced verification method", blockdata[8], blockdata[15]); expect_static_enc_nonce = false; } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08s, sizeof(fkey)) == 0 && card.sak == 0x08 && memcmp(blockdata + 5, "\x00\x03\x00\x10", 4) == 0 From 83c54bb174c981a48c5da952f5ec6beb487604be Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Fri, 25 Jul 2025 23:47:00 -0400 Subject: [PATCH 105/149] initial working for single node --- client/src/cmdhffelica.c | 74 ++++++++++++++++++++++++++++++++++++++-- client/src/cmdhffelica.h | 1 + include/iso18.h | 5 +++ 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 619f09da2..a498d492c 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -531,6 +531,28 @@ int send_rd_plain(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, } } +/** + * Sends a dump_service frame to the pm3 and prints response. + * @param flags to use for pm3 communication. + * @param datalen frame length. + * @param data frame to be send. + * @param verbose display additional output. + * @param dump_sv_resp frame in which the response will be saved. + * @param is_area true if the service is an area, false if it is a service. + * @return success if response was received. + */ +int send_dump_sv_plain(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_service_dump_response_t *dump_sv_resp, bool is_area) { + clear_and_send_command(flags, datalen, data, verbose); + PacketResponseNG resp; + if (waitCmdFelica(false, &resp, verbose) == false) { + PrintAndLogEx(ERR, "No response from card"); + return PM3_ERFTRANS; + } else { + memcpy(dump_sv_resp, (felica_service_dump_response_t *)resp.data.asBytes, sizeof(felica_service_dump_response_t)); + return PM3_SUCCESS; + } +} + /** * Checks if last known card can be added to data and adds it if possible. * @param custom_IDm @@ -1743,18 +1765,64 @@ static int CmdHFFelicaRequestService(const char *Cmd) { return PM3_SUCCESS; } -static int CmdHFFelicaNotImplementedYet(const char *Cmd) { +static int CmdHFFelicaDumpServiceArea(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf felica scsvcode", - "Feature not implemented yet. Feel free to contribute!", + "Dump all existing Area Code and Service Code.\n", "hf felica scsvcode" ); void *argtable[] = { arg_param_begin, arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, false); + + CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); + + + uint8_t data[PM3_CMD_DATA_SIZE]; + memset(data, 0, sizeof(data)); + data[0] = 0x0C; // Static length + data[1] = 0x0A; // Command ID + + uint16_t datalen = 12; // Length (1), Command ID (1), IDm (8), Cursor Node (2) + if (check_last_idm(data, datalen) == false) { + return PM3_EINVARG; + } + + uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW); + + // for each cursor stuff RFU + data[10] = 0x00; // Cursor Node + data[11] = 0x00; // Cursor Node + + AddCrc(data, datalen); + + felica_service_dump_response_t dump_sv_resp; + bool is_area = false; + + if ((send_dump_sv_plain(flags, datalen + 2, data, 0, &dump_sv_resp, is_area) == PM3_SUCCESS)) { + if (dump_sv_resp.frame_response.cmd_code[0] != 0x0B) { + PrintAndLogEx(ERR, "Return command wrong. \nExpected 0x0B, got 0x%02X", dump_sv_resp.frame_response.cmd_code[0]); + return PM3_ERFTRANS; + } else { + if (dump_sv_resp.frame_response.length[0] == 0x0C) { + // service code read + PrintAndLogEx(SUCCESS, "Service Code Read 0x%02X%02X", dump_sv_resp.payload[0], dump_sv_resp.payload[1]); + } else if (dump_sv_resp.frame_response.length[0] == 0x0E) { + // service code read with area (this current parsing is wrong.) + PrintAndLogEx(SUCCESS, "Service Code Read 0x%02X%02X Area 0x%02X%02X", dump_sv_resp.payload[0], dump_sv_resp.payload[1], dump_sv_resp.payload[2], dump_sv_resp.payload[3]); + } else { + PrintAndLogEx(ERR, "Something went wrong, check your card"); + return PM3_ERFTRANS; + + } + } + } else { + PrintAndLogEx(ERR, "Something went wrong, check your card"); + return PM3_ERFTRANS; + } + return PM3_SUCCESS; } diff --git a/client/src/cmdhffelica.h b/client/src/cmdhffelica.h index 9cb524814..6ab42aa36 100644 --- a/client/src/cmdhffelica.h +++ b/client/src/cmdhffelica.h @@ -26,5 +26,6 @@ int CmdHFFelica(const char *Cmd); int read_felica_uid(bool loop, bool verbose); int send_request_service(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose); int send_rd_plain(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_read_without_encryption_response_t *rd_noCry_resp); +int send_dump_sv_plain(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_service_dump_response_t *dump_sv_resp, bool is_area); #endif diff --git a/include/iso18.h b/include/iso18.h index 377f864d0..5446c74eb 100644 --- a/include/iso18.h +++ b/include/iso18.h @@ -124,4 +124,9 @@ typedef struct { uint8_t PMi[8]; } PACKED felica_auth2_response_t; +typedef struct { + felica_frame_response_t frame_response; + uint8_t payload[4]; +} PACKED felica_service_dump_response_t; + #endif // _ISO18_H_ From c74d04bfe79f4d908c909267187e322b8d3c2fef Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Sat, 26 Jul 2025 00:10:37 -0400 Subject: [PATCH 106/149] enumerate nodes --- client/src/cmdhffelica.c | 132 +++++++++++++++++++++++++-------------- 1 file changed, 84 insertions(+), 48 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index a498d492c..4b6811813 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -1765,64 +1765,100 @@ static int CmdHFFelicaRequestService(const char *Cmd) { return PM3_SUCCESS; } +/** + * Command parser for rqservice. + * @param Cmd input data of the user. + * @return client result code. + */ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { + /* ── CLI boilerplate (unchanged) ─────────────────────────────── */ CLIParserContext *ctx; CLIParserInit(&ctx, "hf felica scsvcode", "Dump all existing Area Code and Service Code.\n", - "hf felica scsvcode" - ); - void *argtable[] = { - arg_param_begin, - arg_param_end - }; - + "hf felica scsvcode"); + void *argtable[] = { arg_param_begin, arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); + /* ── build static part of Search-Service frame ──────────────── */ + uint8_t data[PM3_CMD_DATA_SIZE] = {0}; + data[0] = 0x0C; /* LEN */ + data[1] = 0x0A; /* CMD = 0x0A */ + uint16_t datalen = 12; /* LEN + CMD + IDm + cursor */ - uint8_t data[PM3_CMD_DATA_SIZE]; - memset(data, 0, sizeof(data)); - data[0] = 0x0C; // Static length - data[1] = 0x0A; // Command ID - - uint16_t datalen = 12; // Length (1), Command ID (1), IDm (8), Cursor Node (2) - if (check_last_idm(data, datalen) == false) { + if(!check_last_idm(data, datalen)) return PM3_EINVARG; + + PrintAndLogEx(INFO, "Dumping Service Code and Area Code...\n"); + + uint8_t flags = FELICA_APPEND_CRC | FELICA_RAW; + + /* ── traversal state ────────────────────────────────────────── */ + uint16_t cursor = 0x0000; + uint16_t area_end_stack[8] = {0xFFFF}; /* root “end” = 0xFFFF */ + int depth = 0; /* current stack depth */ + + felica_service_dump_response_t resp; + + while(true){ + + /* insert cursor LE */ + data[10] = cursor & 0xFF; + data[11] = cursor >> 8; + AddCrc(data, datalen); + + if(send_dump_sv_plain(flags, datalen + 2, data, 0, + &resp, false) != PM3_SUCCESS){ + PrintAndLogEx(ERR, "No response at cursor 0x%04X", cursor); + return PM3_ERFTRANS; + } + if(resp.frame_response.cmd_code[0] != 0x0B){ + PrintAndLogEx(ERR, "Bad response cmd 0x%02X @ 0x%04X", + resp.frame_response.cmd_code[0], cursor); + return PM3_ERFTRANS; + } + + uint8_t len = resp.frame_response.length[0]; + uint16_t node_code = resp.payload[0] | (resp.payload[1] << 8); + + if(node_code == 0xFFFF) break; /* end-marker */ + + /* pop finished areas */ + while(depth && node_code > area_end_stack[depth]) depth--; + + /* ---------- build pretty prefix & print node -------------- */ + /* 1. Is this the last child in its parent? */ + bool last_in_parent = (node_code == area_end_stack[depth]); + + /* 2. Compose prefix string */ + char prefix[64] = ""; + for(int i = 1; i < depth; i++) { + bool more_siblings = (cursor < area_end_stack[i]); + strcat(prefix, more_siblings ? "│ " : " "); + } + strcat(prefix, last_in_parent ? "└── " : "├── "); + + /* 3. Print the line */ + if(len == 0x0E) { /* AREA */ + uint16_t end_code = resp.payload[2] | (resp.payload[3] << 8); + PrintAndLogEx(INFO, "%sAREA_%04X", prefix, node_code >> 6); + + if(depth < 7) { /* push end */ + depth++; + area_end_stack[depth] = end_code; + } + } else if (len == 0x0C) { /* SERVICE */ + PrintAndLogEx(INFO, "%ssvc_%04X", prefix, node_code); + } else{ + PrintAndLogEx(ERR, "Unexpected length 0x%02X @ 0x%04X", + len, cursor); + return PM3_ERFTRANS; + } + cursor++; + if(cursor == 0) break; /* overflow safety */ } - uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW); - - // for each cursor stuff RFU - data[10] = 0x00; // Cursor Node - data[11] = 0x00; // Cursor Node - - AddCrc(data, datalen); - - felica_service_dump_response_t dump_sv_resp; - bool is_area = false; - - if ((send_dump_sv_plain(flags, datalen + 2, data, 0, &dump_sv_resp, is_area) == PM3_SUCCESS)) { - if (dump_sv_resp.frame_response.cmd_code[0] != 0x0B) { - PrintAndLogEx(ERR, "Return command wrong. \nExpected 0x0B, got 0x%02X", dump_sv_resp.frame_response.cmd_code[0]); - return PM3_ERFTRANS; - } else { - if (dump_sv_resp.frame_response.length[0] == 0x0C) { - // service code read - PrintAndLogEx(SUCCESS, "Service Code Read 0x%02X%02X", dump_sv_resp.payload[0], dump_sv_resp.payload[1]); - } else if (dump_sv_resp.frame_response.length[0] == 0x0E) { - // service code read with area (this current parsing is wrong.) - PrintAndLogEx(SUCCESS, "Service Code Read 0x%02X%02X Area 0x%02X%02X", dump_sv_resp.payload[0], dump_sv_resp.payload[1], dump_sv_resp.payload[2], dump_sv_resp.payload[3]); - } else { - PrintAndLogEx(ERR, "Something went wrong, check your card"); - return PM3_ERFTRANS; - - } - } - } else { - PrintAndLogEx(ERR, "Something went wrong, check your card"); - return PM3_ERFTRANS; - } - + PrintAndLogEx(SUCCESS, "Service code and area dump complete."); return PM3_SUCCESS; } @@ -2292,7 +2328,7 @@ static command_t CommandTable[] = { //{"dump", CmdHFFelicaDump, IfPm3Felica, "Wait for and try dumping FeliCa"}, {"rqservice", CmdHFFelicaRequestService, IfPm3Felica, "verify the existence of Area and Service, and to acquire Key Version."}, {"rqresponse", CmdHFFelicaRequestResponse, IfPm3Felica, "verify the existence of a card and its Mode."}, - {"scsvcode", CmdHFFelicaNotImplementedYet, IfPm3Felica, "acquire Area Code and Service Code."}, + {"scsvcode", CmdHFFelicaDumpServiceArea, IfPm3Felica, "acquire Area Code and Service Code."}, {"rqsyscode", CmdHFFelicaRequestSystemCode, IfPm3Felica, "acquire System Code registered to the card."}, {"auth1", CmdHFFelicaAuthentication1, IfPm3Felica, "authenticate a card. Start mutual authentication with Auth1"}, {"auth2", CmdHFFelicaAuthentication2, IfPm3Felica, "allow a card to authenticate a Reader/Writer. Complete mutual authentication"}, From 3b88dd45a34e39eb94dbe97b76eb446f140d0ca6 Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Sat, 26 Jul 2025 00:49:57 -0400 Subject: [PATCH 107/149] beautify output format --- client/src/cmdhffelica.c | 51 ++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 4b6811813..3ae1ce4fa 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -1789,8 +1789,8 @@ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { if(!check_last_idm(data, datalen)) return PM3_EINVARG; - PrintAndLogEx(INFO, "Dumping Service Code and Area Code...\n"); - + PrintAndLogEx(INFO, "┌───────────────────────────────────────────────"); + uint8_t flags = FELICA_APPEND_CRC | FELICA_RAW; /* ── traversal state ────────────────────────────────────────── */ @@ -1809,12 +1809,14 @@ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { if(send_dump_sv_plain(flags, datalen + 2, data, 0, &resp, false) != PM3_SUCCESS){ - PrintAndLogEx(ERR, "No response at cursor 0x%04X", cursor); + PrintAndLogEx(FAILED, "No response at cursor 0x%04X", cursor); return PM3_ERFTRANS; } if(resp.frame_response.cmd_code[0] != 0x0B){ - PrintAndLogEx(ERR, "Bad response cmd 0x%02X @ 0x%04X", + PrintAndLogEx(FAILED, "Bad response cmd 0x%02X @ 0x%04X.", resp.frame_response.cmd_code[0], cursor); + PrintAndLogEx(INFO, "This is a normal signal issue. Please try again."); + PrintAndLogEx(INFO, "If the issue persists, move the card around and check signal strength. FeliCa can be hard to keep in field."); return PM3_ERFTRANS; } @@ -1826,31 +1828,29 @@ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { /* pop finished areas */ while(depth && node_code > area_end_stack[depth]) depth--; - /* ---------- build pretty prefix & print node -------------- */ - /* 1. Is this the last child in its parent? */ - bool last_in_parent = (node_code == area_end_stack[depth]); - - /* 2. Compose prefix string */ + + /* ----- compose nice prefix ------------------------------------ */ char prefix[64] = ""; - for(int i = 1; i < depth; i++) { + for (int i = 1; i < depth; i++) { bool more_siblings = (cursor < area_end_stack[i]); strcat(prefix, more_siblings ? "│ " : " "); } - strcat(prefix, last_in_parent ? "└── " : "├── "); + /* decide glyph for this line (areas always use └──) */ + const char *line_glyph = "├── "; + strcat(prefix, line_glyph); - /* 3. Print the line */ - if(len == 0x0E) { /* AREA */ + /* ----- print --------------------------------------------------- */ + if (len == 0x0E) { /* AREA node */ uint16_t end_code = resp.payload[2] | (resp.payload[3] << 8); PrintAndLogEx(INFO, "%sAREA_%04X", prefix, node_code >> 6); - if(depth < 7) { /* push end */ - depth++; - area_end_stack[depth] = end_code; + if (depth < 7) { + area_end_stack[++depth] = end_code; } } else if (len == 0x0C) { /* SERVICE */ PrintAndLogEx(INFO, "%ssvc_%04X", prefix, node_code); } else{ - PrintAndLogEx(ERR, "Unexpected length 0x%02X @ 0x%04X", + PrintAndLogEx(FAILED, "Unexpected length 0x%02X @ 0x%04X", len, cursor); return PM3_ERFTRANS; } @@ -1858,6 +1858,23 @@ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { if(cursor == 0) break; /* overflow safety */ } + /* draw closing bar └─┴─... based on final depth */ + char bar[128]; /* large enough for depth≤7 */ + size_t pos = 0; + + /* leading corner */ + pos += snprintf(bar + pos, sizeof(bar) - pos, "└"); + + /* one segment per level-1 */ + for(int i = 0; i < depth - 1 && pos < sizeof(bar); i++) + pos += snprintf(bar + pos, sizeof(bar) - pos, "───┴"); + + /* tail */ + snprintf(bar + pos, sizeof(bar) - pos, "───────────────────────"); + + PrintAndLogEx(INFO, "%s", bar); + + PrintAndLogEx(SUCCESS, "Service code and area dump complete."); return PM3_SUCCESS; } From 4090828ebcb12ced172bb0fe1cf7f707cc537920 Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Sat, 26 Jul 2025 00:59:37 -0400 Subject: [PATCH 108/149] hints for endian issue --- client/src/cmdhffelica.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 3ae1ce4fa..b06ea860c 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -1789,6 +1789,8 @@ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { if(!check_last_idm(data, datalen)) return PM3_EINVARG; + PrintAndLogEx(HINT, "Area and service code are printed in big endian."); + PrintAndLogEx(HINT, "Don't forget to convert to little endian when using hf felica rdbl."); PrintAndLogEx(INFO, "┌───────────────────────────────────────────────"); uint8_t flags = FELICA_APPEND_CRC | FELICA_RAW; From 8014aa03618673aa37fc3018f4a0b13ed1f46e9c Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Sat, 26 Jul 2025 01:08:12 -0400 Subject: [PATCH 109/149] Update CHANGELOG.md per Github bot's suggestion --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7281d870..f71c9634f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `mem load` - now handles UL-C and UL-AES dictionary files (@iceman1001) - Changed `hf mfu sim` - now support UL-C simulation (@iceman1001) - Added `!` - run system commands from inside the client. Potentially dangerous if running client as SUDO, SU, ROOT (@iceman1001) +- Implemented `hf felica scsvcode` - now dumps all service and area codes. (@zinongli) ## [Daddy Iceman.4.20469][2025-06-16] - Fixed edge case in fm11rf08s key recovery tools (@doegox) From e94921c1afa95f65fd88717a350004f174f5ad4a Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Sat, 26 Jul 2025 01:12:18 -0400 Subject: [PATCH 110/149] format --- client/src/cmdhffelica.c | 68 ++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index b06ea860c..9b28ee222 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -47,10 +47,10 @@ static void print_status_flag1_interpretation(void) { PrintAndLogEx(INFO, "----+--------------------------------------------------------------------------------------------------------------------"); PrintAndLogEx(INFO, " 00 | Indicates the successful completion of a command."); PrintAndLogEx(INFO, " FF | If an error occurs during the processing of a command that includes no list in the command packet, \n" - " | or if an error occurs independently of any list, the card returns a response by setting FFh to Status Flag1."); + " | or if an error occurs independently of any list, the card returns a response by setting FFh to Status Flag1."); PrintAndLogEx(INFO, " XX | If an error occurs while processing a command that includes Service Code List or Block List \n" - " | in the command packet, the card returns a response by setting a number in the list to Status Flag1,\n" - " | indicating the location of the error."); + " | in the command packet, the card returns a response by setting a number in the list to Status Flag1,\n" + " | indicating the location of the error."); PrintAndLogEx(INFO, "----+--------------------------------------------------------------------------------------------------------------------"); } @@ -59,28 +59,28 @@ static void print_status_flag2_interpration(void) { PrintAndLogEx(INFO, "----+--------------------------------------------------------------------------------------------------------------------"); PrintAndLogEx(INFO, " 00 | Indicates the successful completion of a command."); PrintAndLogEx(INFO, " 01 | The calculated result is either less than zero when the purse data is decremented, or exceeds 4\n" - " | Bytes when the purse data is incremented."); + " | Bytes when the purse data is incremented."); PrintAndLogEx(INFO, " 02 | The specified data exceeds the value of cashback data at cashback of purse."); PrintAndLogEx(INFO, " 70 | Memory error (fatal error)."); PrintAndLogEx(INFO, " 71 | The number of memory rewrites exceeds the upper limit (this is only a warning; data writing is performed as normal).\n" - " | The maximum number of rewrites can differ, depending on the product being used.\n" - " | In addition, Status Flag1 is either 00h or FFh depending on the product being used."); + " | The maximum number of rewrites can differ, depending on the product being used.\n" + " | In addition, Status Flag1 is either 00h or FFh depending on the product being used."); PrintAndLogEx(INFO, " A1 | Illegal Number of Service| Number of Service or Number of Node specified by the command \n" - " | falls outside the range of the prescribed value."); + " | falls outside the range of the prescribed value."); PrintAndLogEx(INFO, " A2 | Illegal command packet (specified Number of Block) : Number of Block specified by the \n" - " | command falls outside the range of the prescribed values for the product."); + " | command falls outside the range of the prescribed values for the product."); PrintAndLogEx(INFO, " A3 | Illegal Block List (specified order of Service) : Service Code List Order specified by \n" - " | Block List Element falls outside the Number of Service specified by the command \n" - " | (or the Number of Service specified at the times of mutual authentication)."); + " | Block List Element falls outside the Number of Service specified by the command \n" + " | (or the Number of Service specified at the times of mutual authentication)."); PrintAndLogEx(INFO, " A4 | Illegal Service type : Area Attribute specified by the command or Service Attribute of Service Code is incorrect."); PrintAndLogEx(INFO, " A5 | Access is not allowed : Area or Service specified by the command cannot be accessed.\n" - " | The parameter specified by the command does not satisfy the conditions for success."); + " | The parameter specified by the command does not satisfy the conditions for success."); PrintAndLogEx(INFO, " A6 | Illegal Service Code List : Target to be accessed, identified by Service Code List Order, specified by Block\n" - " | List Element does not exist. Or, Node specified by Node Code List does not exist."); + " | List Element does not exist. Or, Node specified by Node Code List does not exist."); PrintAndLogEx(INFO, " A7 | Illegal Block List (Access Mode) : Access Mode specified by Block List Element is incorrect."); PrintAndLogEx(INFO, " A8 | Illegal Block Number Block Number (access to the specified data is inhibited) :\n" - " | specified by Block List Element exceeds the number of Blocks assigned to Service."); + " | specified by Block List Element exceeds the number of Blocks assigned to Service."); PrintAndLogEx(INFO, " A9 | Data write failure : This is the error that occurs in issuance commands."); PrintAndLogEx(INFO, " AA | Key-change failure : Key change failed."); PrintAndLogEx(INFO, " AB | Illegal Package Parity or illegal Package MAC : This is the error that occurs in issuance commands."); @@ -88,7 +88,7 @@ static void print_status_flag2_interpration(void) { PrintAndLogEx(INFO, " AD | Service exists already : This is the error that occurs in issuance commands."); PrintAndLogEx(INFO, " AE | Illegal System Code : This is the error that occurs in issuance commands."); PrintAndLogEx(INFO, " AF | Too many simultaneous cyclic write operations : Number of simultaneous write Blocks\n" - " | specified by the command to Cyclic Service exceeds the number of Blocks assigned to Service."); + " | specified by the command to Cyclic Service exceeds the number of Blocks assigned to Service."); PrintAndLogEx(INFO, " C0 | Illegal Package Identifier : This is the error that occurs in issuance commands."); PrintAndLogEx(INFO, " C1 | Discrepancy of parameters inside and outside Package : This is the error that occurs in issuance commands."); PrintAndLogEx(INFO, " C2 | Command is disabled already : This is the error that occurs in issuance commands."); @@ -112,16 +112,16 @@ static void print_number_of_service_constraints(void) { static void print_number_of_block_constraints(void) { PrintAndLogEx(INFO, " - Number of Block: shall be less than or equal to the maximum number of Blocks that can be read simultaneously.\n" - " The maximum number of Blocks that can be read simultaneously can differ, depending on the product being used.\n" - " Use as default 01"); + " The maximum number of Blocks that can be read simultaneously can differ, depending on the product being used.\n" + " Use as default 01"); } static void print_service_code_list_constraints(void) { PrintAndLogEx(INFO, " - Service Code List: For Service Code List, only Service Code existing in the product shall be specified:"); PrintAndLogEx(INFO, " - Even when Service Code exists in the product, Service Code not referenced from Block List shall not \n" - " be specified to Service Code List."); + " be specified to Service Code List."); PrintAndLogEx(INFO, " - For existence or nonexistence of Service in a product, please check using the Request Service \n" - " (or Request Service v2) command."); + " (or Request Service v2) command."); } /* @@ -1786,13 +1786,13 @@ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { data[1] = 0x0A; /* CMD = 0x0A */ uint16_t datalen = 12; /* LEN + CMD + IDm + cursor */ - if(!check_last_idm(data, datalen)) + if (!check_last_idm(data, datalen)) return PM3_EINVARG; - + PrintAndLogEx(HINT, "Area and service code are printed in big endian."); PrintAndLogEx(HINT, "Don't forget to convert to little endian when using hf felica rdbl."); PrintAndLogEx(INFO, "┌───────────────────────────────────────────────"); - + uint8_t flags = FELICA_APPEND_CRC | FELICA_RAW; /* ── traversal state ────────────────────────────────────────── */ @@ -1802,19 +1802,19 @@ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { felica_service_dump_response_t resp; - while(true){ + while (true) { /* insert cursor LE */ data[10] = cursor & 0xFF; data[11] = cursor >> 8; AddCrc(data, datalen); - if(send_dump_sv_plain(flags, datalen + 2, data, 0, - &resp, false) != PM3_SUCCESS){ + if (send_dump_sv_plain(flags, datalen + 2, data, 0, + &resp, false) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "No response at cursor 0x%04X", cursor); return PM3_ERFTRANS; } - if(resp.frame_response.cmd_code[0] != 0x0B){ + if (resp.frame_response.cmd_code[0] != 0x0B) { PrintAndLogEx(FAILED, "Bad response cmd 0x%02X @ 0x%04X.", resp.frame_response.cmd_code[0], cursor); PrintAndLogEx(INFO, "This is a normal signal issue. Please try again."); @@ -1825,12 +1825,12 @@ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { uint8_t len = resp.frame_response.length[0]; uint16_t node_code = resp.payload[0] | (resp.payload[1] << 8); - if(node_code == 0xFFFF) break; /* end-marker */ + if (node_code == 0xFFFF) break; /* end-marker */ /* pop finished areas */ - while(depth && node_code > area_end_stack[depth]) depth--; + while (depth && node_code > area_end_stack[depth]) depth--; + - /* ----- compose nice prefix ------------------------------------ */ char prefix[64] = ""; for (int i = 1; i < depth; i++) { @@ -1846,18 +1846,18 @@ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { uint16_t end_code = resp.payload[2] | (resp.payload[3] << 8); PrintAndLogEx(INFO, "%sAREA_%04X", prefix, node_code >> 6); - if (depth < 7) { - area_end_stack[++depth] = end_code; + if (depth < 7) { + area_end_stack[++depth] = end_code; } } else if (len == 0x0C) { /* SERVICE */ PrintAndLogEx(INFO, "%ssvc_%04X", prefix, node_code); - } else{ + } else { PrintAndLogEx(FAILED, "Unexpected length 0x%02X @ 0x%04X", - len, cursor); + len, cursor); return PM3_ERFTRANS; } cursor++; - if(cursor == 0) break; /* overflow safety */ + if (cursor == 0) break; /* overflow safety */ } /* draw closing bar └─┴─... based on final depth */ @@ -1868,7 +1868,7 @@ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { pos += snprintf(bar + pos, sizeof(bar) - pos, "└"); /* one segment per level-1 */ - for(int i = 0; i < depth - 1 && pos < sizeof(bar); i++) + for (int i = 0; i < depth - 1 && pos < sizeof(bar); i++) pos += snprintf(bar + pos, sizeof(bar) - pos, "───┴"); /* tail */ From 9dd6e4a85f8c30ad133c651691afa38385e6395e Mon Sep 17 00:00:00 2001 From: Andy Shieh Date: Sun, 27 Jul 2025 21:31:42 +1000 Subject: [PATCH 111/149] Fix getIndalaBits checksum logic Signed-off-by: Andy Shieh --- client/src/cmdlfindala.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/client/src/cmdlfindala.c b/client/src/cmdlfindala.c index 53cd2647a..01de546c0 100644 --- a/client/src/cmdlfindala.c +++ b/client/src/cmdlfindala.c @@ -1163,21 +1163,14 @@ int getIndalaBits(uint8_t fc, uint16_t cn, uint8_t *bits) { chk += ((cn >> 2) & 1); //y14 == 89 - 30 = 59 chk += (cn & 1); //y16 == 71 - 30 = 41 - if ((chk & 1) == 0) { - bits[62] = 0; - bits[63] = 1; - } else { + if ((chk % 2) == 0) { // If the sum is even, checksum is '10' (binary) = 2. bits[62] = 1; bits[63] = 0; + } else { // If the sum is odd, checksum is '01' (binary) = 1. + bits[62] = 0; + bits[63] = 1; } - // add parity - // bits[34] = 1; // p1 64 - 30 = 34 - // bits[38] = 1; // p2 68 - 30 = 38 - - // 92 = 62 - // 93 = 63 - bits[34] = 0; // parity for odd bits bits[38] = 0; // parity for even bits uint8_t p1 = 1; From 9ce90315163b5bf2e49f7b5cbcf7e53ed971b4d1 Mon Sep 17 00:00:00 2001 From: Andy Shieh Date: Sun, 27 Jul 2025 21:39:09 +1000 Subject: [PATCH 112/149] Update CHANGELOG.md Signed-off-by: Andy Shieh --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f71c9634f..4e6c7c13c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `hf mfu sim` - now support UL-C simulation (@iceman1001) - Added `!` - run system commands from inside the client. Potentially dangerous if running client as SUDO, SU, ROOT (@iceman1001) - Implemented `hf felica scsvcode` - now dumps all service and area codes. (@zinongli) +- Fixed `lf indala cone` - now writing the right bits when using `--fc` and `--cn` ## [Daddy Iceman.4.20469][2025-06-16] - Fixed edge case in fm11rf08s key recovery tools (@doegox) From ab6e203560e7b4c4fb73038187f0b5474db6ae28 Mon Sep 17 00:00:00 2001 From: Benjamin DELPY Date: Sun, 27 Jul 2025 14:29:27 +0200 Subject: [PATCH 113/149] Update intertic.py to support [FRA] Caen Signed-off-by: Benjamin DELPY --- client/pyscripts/intertic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/pyscripts/intertic.py b/client/pyscripts/intertic.py index 8f2690dd7..6ccfaee2b 100644 --- a/client/pyscripts/intertic.py +++ b/client/pyscripts/intertic.py @@ -338,6 +338,7 @@ FRA_OrganizationalAuthority_Contract_Provider = { }, 0x912: { 3: InterticHelper('Le Havre', 'Lia / Transdev', Describe_Usage_1_1), + 29: InterticHelper('Caen', 'Twisto / RATP', Describe_Usage_2), 35: InterticHelper('Cherbourg-en-Cotentin', 'Cap Cotentin / Transdev'), }, 0x913: { From 7e7fe1837ee7b24e707ba247e8f72b7a7110ba9b Mon Sep 17 00:00:00 2001 From: Andy Shieh Date: Mon, 28 Jul 2025 22:41:18 +1000 Subject: [PATCH 114/149] [Fix] added original comments back; use same bitwise operation for even check Signed-off-by: Andy Shieh --- client/src/cmdlfindala.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/src/cmdlfindala.c b/client/src/cmdlfindala.c index 01de546c0..c42f0ddf1 100644 --- a/client/src/cmdlfindala.c +++ b/client/src/cmdlfindala.c @@ -1163,14 +1163,21 @@ int getIndalaBits(uint8_t fc, uint16_t cn, uint8_t *bits) { chk += ((cn >> 2) & 1); //y14 == 89 - 30 = 59 chk += (cn & 1); //y16 == 71 - 30 = 41 - if ((chk % 2) == 0) { // If the sum is even, checksum is '10' (binary) = 2. + if ((chk & 1) == 0) { // If the sum is even, checksum is '10' (binary) = 2. bits[62] = 1; bits[63] = 0; } else { // If the sum is odd, checksum is '01' (binary) = 1. bits[62] = 0; bits[63] = 1; } + + // add parity + // bits[34] = 1; // p1 64 - 30 = 34 + // bits[38] = 1; // p2 68 - 30 = 38 + // 92 = 62 + // 93 = 63 + bits[34] = 0; // parity for odd bits bits[38] = 0; // parity for even bits uint8_t p1 = 1; From 0dacfb996f79e79ae5aa6ff45291280f5a685068 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 28 Jul 2025 15:44:10 +0200 Subject: [PATCH 115/149] addition --- client/resources/mad.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/resources/mad.json b/client/resources/mad.json index 66a592e44..6b8465f55 100644 --- a/client/resources/mad.json +++ b/client/resources/mad.json @@ -4130,6 +4130,13 @@ "service_provider": "HID Corporation", "system_integrator": "HID Corporation" }, + { + "application": "Access Control (SIO Elite)", + "company": "HID Global", + "mad": "0x3D05", + "service_provider": "HID Corporation", + "system_integrator": "HID Corporation" + }, { "application": "City transport, prepaid ticket, cardholder, servicespass", "company": "Ridango AS", From 79d1e102af41a7ff7a94560194f414354c08c0a9 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 28 Jul 2025 15:45:04 +0200 Subject: [PATCH 116/149] style --- client/luascripts/paxton_clone.lua | 14 +++++++------- client/src/cmdlfindala.c | 4 ++-- client/src/mifare/desfirecore.c | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/client/luascripts/paxton_clone.lua b/client/luascripts/paxton_clone.lua index c1159c1b7..5e9076c9f 100644 --- a/client/luascripts/paxton_clone.lua +++ b/client/luascripts/paxton_clone.lua @@ -24,10 +24,10 @@ command('clear') author = ' Author: jareckib - 30.01.2025' tutorial = ' Based on Equipter tutorial - Downgrade Paxton to EM4102' version = ' version v1.20' -desc = [[ - The script automates the copying of Paxton fobs read - write. - It also allows manual input of data for blocks 4-7. - The third option is reading data stored in the log file and create new fob. +desc = [[ + The script automates the copying of Paxton fobs read - write. + It also allows manual input of data for blocks 4-7. + The third option is reading data stored in the log file and create new fob. Additionally, the script calculates the ID for downgrading Paxton to EM4102. ]] @@ -70,7 +70,7 @@ local function reset_log_file() file:write("") file:close() end - + local function read_log_file(logfile) local file = io.open(logfile, "r") if not file then @@ -340,7 +340,7 @@ local function main(args) print(ac.cyan .. ' 1' .. ac.reset .. ' - Read Paxton blocks 4-7 to make a copy') print(ac.cyan .. ' 2' .. ac.reset .. ' - Manually input data for Paxton blocks 4-7') print(ac.cyan .. " 3" .. ac.reset .. " - Search in Paxton_log by name and use the data") - print(dash) + print(dash) while true do io.write(' Your choice '..ac.cyan..'(1/2/3): ' .. ac.reset) input_option = io.read() @@ -437,7 +437,7 @@ local function main(args) was_option_3 = true local retries = 3 while retries > 0 do - io.write(' Enter the name to search ('..retries..' attempts) : '..ac.yellow) + io.write(' Enter the name to search ('..retries..' attempts) : '..ac.yellow) local user_input = io.read() io.write(ac.reset..'') if user_input == nil or user_input:match("^%s*$") then diff --git a/client/src/cmdlfindala.c b/client/src/cmdlfindala.c index c42f0ddf1..4efdd3531 100644 --- a/client/src/cmdlfindala.c +++ b/client/src/cmdlfindala.c @@ -1170,14 +1170,14 @@ int getIndalaBits(uint8_t fc, uint16_t cn, uint8_t *bits) { bits[62] = 0; bits[63] = 1; } - + // add parity // bits[34] = 1; // p1 64 - 30 = 34 // bits[38] = 1; // p2 68 - 30 = 38 // 92 = 62 // 93 = 63 - + bits[34] = 0; // parity for odd bits bits[38] = 0; // parity for even bits uint8_t p1 = 1; diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index 97fcbc184..e1ce6c93a 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -2245,11 +2245,11 @@ int DesfireValueFileOperations(DesfireContext_t *dctx, uint8_t fid, uint8_t oper PrintAndLogEx(INFO, "MAC mode failed with length error, retrying with plain mode"); DesfireCommunicationMode original_mode = dctx->commMode; dctx->commMode = DCMPlain; - + memset(resp, 0, sizeof(resp)); resplen = 0; res = DesfireCommand(dctx, operation, data, datalen, resp, &resplen, -1); - + // Restore original mode for future commands dctx->commMode = original_mode; } From c9978051171271636663ab6b6943d3be53ec19c1 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 28 Jul 2025 15:45:33 +0200 Subject: [PATCH 117/149] style --- client/src/cmdhffelica.c | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 9b28ee222..bbe9feabb 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -47,10 +47,10 @@ static void print_status_flag1_interpretation(void) { PrintAndLogEx(INFO, "----+--------------------------------------------------------------------------------------------------------------------"); PrintAndLogEx(INFO, " 00 | Indicates the successful completion of a command."); PrintAndLogEx(INFO, " FF | If an error occurs during the processing of a command that includes no list in the command packet, \n" - " | or if an error occurs independently of any list, the card returns a response by setting FFh to Status Flag1."); + " | or if an error occurs independently of any list, the card returns a response by setting FFh to Status Flag1."); PrintAndLogEx(INFO, " XX | If an error occurs while processing a command that includes Service Code List or Block List \n" - " | in the command packet, the card returns a response by setting a number in the list to Status Flag1,\n" - " | indicating the location of the error."); + " | in the command packet, the card returns a response by setting a number in the list to Status Flag1,\n" + " | indicating the location of the error."); PrintAndLogEx(INFO, "----+--------------------------------------------------------------------------------------------------------------------"); } @@ -59,28 +59,28 @@ static void print_status_flag2_interpration(void) { PrintAndLogEx(INFO, "----+--------------------------------------------------------------------------------------------------------------------"); PrintAndLogEx(INFO, " 00 | Indicates the successful completion of a command."); PrintAndLogEx(INFO, " 01 | The calculated result is either less than zero when the purse data is decremented, or exceeds 4\n" - " | Bytes when the purse data is incremented."); + " | Bytes when the purse data is incremented."); PrintAndLogEx(INFO, " 02 | The specified data exceeds the value of cashback data at cashback of purse."); PrintAndLogEx(INFO, " 70 | Memory error (fatal error)."); PrintAndLogEx(INFO, " 71 | The number of memory rewrites exceeds the upper limit (this is only a warning; data writing is performed as normal).\n" - " | The maximum number of rewrites can differ, depending on the product being used.\n" - " | In addition, Status Flag1 is either 00h or FFh depending on the product being used."); + " | The maximum number of rewrites can differ, depending on the product being used.\n" + " | In addition, Status Flag1 is either 00h or FFh depending on the product being used."); PrintAndLogEx(INFO, " A1 | Illegal Number of Service| Number of Service or Number of Node specified by the command \n" - " | falls outside the range of the prescribed value."); + " | falls outside the range of the prescribed value."); PrintAndLogEx(INFO, " A2 | Illegal command packet (specified Number of Block) : Number of Block specified by the \n" - " | command falls outside the range of the prescribed values for the product."); + " | command falls outside the range of the prescribed values for the product."); PrintAndLogEx(INFO, " A3 | Illegal Block List (specified order of Service) : Service Code List Order specified by \n" - " | Block List Element falls outside the Number of Service specified by the command \n" - " | (or the Number of Service specified at the times of mutual authentication)."); + " | Block List Element falls outside the Number of Service specified by the command \n" + " | (or the Number of Service specified at the times of mutual authentication)."); PrintAndLogEx(INFO, " A4 | Illegal Service type : Area Attribute specified by the command or Service Attribute of Service Code is incorrect."); PrintAndLogEx(INFO, " A5 | Access is not allowed : Area or Service specified by the command cannot be accessed.\n" - " | The parameter specified by the command does not satisfy the conditions for success."); + " | The parameter specified by the command does not satisfy the conditions for success."); PrintAndLogEx(INFO, " A6 | Illegal Service Code List : Target to be accessed, identified by Service Code List Order, specified by Block\n" - " | List Element does not exist. Or, Node specified by Node Code List does not exist."); + " | List Element does not exist. Or, Node specified by Node Code List does not exist."); PrintAndLogEx(INFO, " A7 | Illegal Block List (Access Mode) : Access Mode specified by Block List Element is incorrect."); PrintAndLogEx(INFO, " A8 | Illegal Block Number Block Number (access to the specified data is inhibited) :\n" - " | specified by Block List Element exceeds the number of Blocks assigned to Service."); + " | specified by Block List Element exceeds the number of Blocks assigned to Service."); PrintAndLogEx(INFO, " A9 | Data write failure : This is the error that occurs in issuance commands."); PrintAndLogEx(INFO, " AA | Key-change failure : Key change failed."); PrintAndLogEx(INFO, " AB | Illegal Package Parity or illegal Package MAC : This is the error that occurs in issuance commands."); @@ -88,7 +88,7 @@ static void print_status_flag2_interpration(void) { PrintAndLogEx(INFO, " AD | Service exists already : This is the error that occurs in issuance commands."); PrintAndLogEx(INFO, " AE | Illegal System Code : This is the error that occurs in issuance commands."); PrintAndLogEx(INFO, " AF | Too many simultaneous cyclic write operations : Number of simultaneous write Blocks\n" - " | specified by the command to Cyclic Service exceeds the number of Blocks assigned to Service."); + " | specified by the command to Cyclic Service exceeds the number of Blocks assigned to Service."); PrintAndLogEx(INFO, " C0 | Illegal Package Identifier : This is the error that occurs in issuance commands."); PrintAndLogEx(INFO, " C1 | Discrepancy of parameters inside and outside Package : This is the error that occurs in issuance commands."); PrintAndLogEx(INFO, " C2 | Command is disabled already : This is the error that occurs in issuance commands."); @@ -112,16 +112,16 @@ static void print_number_of_service_constraints(void) { static void print_number_of_block_constraints(void) { PrintAndLogEx(INFO, " - Number of Block: shall be less than or equal to the maximum number of Blocks that can be read simultaneously.\n" - " The maximum number of Blocks that can be read simultaneously can differ, depending on the product being used.\n" - " Use as default 01"); + " The maximum number of Blocks that can be read simultaneously can differ, depending on the product being used.\n" + " Use as default 01"); } static void print_service_code_list_constraints(void) { PrintAndLogEx(INFO, " - Service Code List: For Service Code List, only Service Code existing in the product shall be specified:"); PrintAndLogEx(INFO, " - Even when Service Code exists in the product, Service Code not referenced from Block List shall not \n" - " be specified to Service Code List."); + " be specified to Service Code List."); PrintAndLogEx(INFO, " - For existence or nonexistence of Service in a product, please check using the Request Service \n" - " (or Request Service v2) command."); + " (or Request Service v2) command."); } /* @@ -1771,7 +1771,7 @@ static int CmdHFFelicaRequestService(const char *Cmd) { * @return client result code. */ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { - /* ── CLI boilerplate (unchanged) ─────────────────────────────── */ + /* -- CLI boilerplate (unchanged) ------------------------------- */ CLIParserContext *ctx; CLIParserInit(&ctx, "hf felica scsvcode", "Dump all existing Area Code and Service Code.\n", @@ -1780,7 +1780,7 @@ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); - /* ── build static part of Search-Service frame ──────────────── */ + /* -- build static part of Search-Service frame ---------------- */ uint8_t data[PM3_CMD_DATA_SIZE] = {0}; data[0] = 0x0C; /* LEN */ data[1] = 0x0A; /* CMD = 0x0A */ @@ -1795,7 +1795,7 @@ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { uint8_t flags = FELICA_APPEND_CRC | FELICA_RAW; - /* ── traversal state ────────────────────────────────────────── */ + /* -- traversal state ------------------------------------------ */ uint16_t cursor = 0x0000; uint16_t area_end_stack[8] = {0xFFFF}; /* root “end” = 0xFFFF */ int depth = 0; /* current stack depth */ @@ -1861,7 +1861,7 @@ static int CmdHFFelicaDumpServiceArea(const char *Cmd) { } /* draw closing bar └─┴─... based on final depth */ - char bar[128]; /* large enough for depth≤7 */ + char bar[128]; /* large enough for depth ≤ 7 */ size_t pos = 0; /* leading corner */ From ee2f5595ee8e09623829f2fb28c329291d661297 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 28 Jul 2025 15:46:09 +0200 Subject: [PATCH 118/149] added a `lf t55xx view` command to view t55xx dump files --- CHANGELOG.md | 4 +- client/src/cmdlft55xx.c | 69 +++++++++++++++++++++++++++++++++ client/src/pm3line_vocabulary.h | 1 + doc/commands.json | 22 +++++++++-- doc/commands.md | 1 + 5 files changed, 92 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e6c7c13c..adde4b514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ 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 `lf t55xx view` - now viewing of T55XX dump files is possible (@iceman1001) +- Fixed `lf indala cone` - now writing the right bits when using `--fc` and `--cn` - Changed readline hack logic for async dbg msg to be ready for readline 8.3 (@doegox) - Improved To avoid conflicts with ModemManager on Linux, is recommended to masking the service (@grugnoymeme) - Changed `data crypto` - now also handles AES-256 (@iceman1001) @@ -21,7 +23,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `hf mfu sim` - now support UL-C simulation (@iceman1001) - Added `!` - run system commands from inside the client. Potentially dangerous if running client as SUDO, SU, ROOT (@iceman1001) - Implemented `hf felica scsvcode` - now dumps all service and area codes. (@zinongli) -- Fixed `lf indala cone` - now writing the right bits when using `--fc` and `--cn` + ## [Daddy Iceman.4.20469][2025-06-16] - Fixed edge case in fm11rf08s key recovery tools (@doegox) diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index 96184c87f..49e50902f 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -4694,6 +4694,74 @@ static int CmdT55xxSniff(const char *Cmd) { return PM3_SUCCESS; } +static int CmdT55xxView(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf t55xx view", + "Print a T55xx dump file (bin/eml/json)\n", + "lf t55xx view -f lf-t55xx-00000000-11111111-22222222-33333333-dump.bin" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "Specify a filename for dump file"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fnlen = 0; + char filename[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + // bool verbose = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + if (fnlen == 0) { + PrintAndLogEx(ERR, "Must specify a filename"); + return PM3_EINVARG; + } + + // read dump file + uint32_t *dump = NULL; + size_t bytes_read = 0; + int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, (T55x7_BLOCK_COUNT * 4)); + if (res != PM3_SUCCESS) { + return res; + } + + if (bytes_read != (T55x7_BLOCK_COUNT * 4)) { + free(dump); + PrintAndLogEx(FAILED, "wrong length of dump file. Expected 48 bytes, got %zu", bytes_read); + return PM3_EFILE; + } + + + PrintAndLogEx(INFO, ""); + PrintAndLogEx(SUCCESS, " " _CYAN_("Page 0")); + PrintAndLogEx(SUCCESS, "----+----------+-------"); + PrintAndLogEx(SUCCESS, "blk | hex data | ascii"); + PrintAndLogEx(SUCCESS, "----+----------+-------"); + + uint32_t *pd = dump; + uint8_t tmp[4] = {0}; + for (uint8_t i = 0; i < 8; ++i) { + Uint4byteToMemLe(tmp, *pd); + PrintAndLogEx(SUCCESS, " %02d | %s | %s", i, sprint_hex_inrow(tmp, sizeof(tmp)), sprint_ascii(tmp, 4)); + pd++; + } + PrintAndLogEx(INFO, ""); + PrintAndLogEx(SUCCESS, " " _CYAN_("Page 1")); + PrintAndLogEx(SUCCESS, "----+----------+-------"); + PrintAndLogEx(SUCCESS, "blk | hex data | ascii"); + PrintAndLogEx(SUCCESS, "----+----------+-------"); + for (uint8_t i = 0; i < 4; i++) { + Uint4byteToMemLe(tmp, *pd); + PrintAndLogEx(SUCCESS, " %02d | %s | %s", i, sprint_hex_inrow(tmp, sizeof(tmp)), sprint_ascii(tmp, 4)); + pd++; + } + PrintAndLogEx(NORMAL, ""); + free(dump); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"-----------", CmdHelp, AlwaysAvailable, "---------------------------- " _CYAN_("notice") " -----------------------------"}, {"", CmdHelp, AlwaysAvailable, "Remember to run `" _YELLOW_("lf t55xx detect") "` first whenever a new card"}, @@ -4714,6 +4782,7 @@ static command_t CommandTable[] = { {"restore", CmdT55xxRestore, IfPm3Lf, "Restore T55xx card Page 0 / Page 1 blocks"}, {"trace", CmdT55xxReadTrace, AlwaysAvailable, "Show T55x7 traceability data (page 1/ blk 0-1)"}, {"wakeup", CmdT55xxWakeUp, IfPm3Lf, "Send AOR wakeup command"}, + {"view", CmdT55xxView, AlwaysAvailable, "Display content from tag dump file"}, {"write", CmdT55xxWriteBlock, IfPm3Lf, "Write T55xx block data"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("recovery") " ---------------------"}, {"bruteforce", CmdT55xxBruteForce, IfPm3Lf, "Simple bruteforce attack to find password"}, diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index 0d2672fad..1a071244e 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -796,6 +796,7 @@ const static vocabulary_t vocabulary[] = { { 0, "lf t55xx restore" }, { 1, "lf t55xx trace" }, { 0, "lf t55xx wakeup" }, + { 1, "lf t55xx view" }, { 0, "lf t55xx write" }, { 0, "lf t55xx bruteforce" }, { 0, "lf t55xx chk" }, diff --git a/doc/commands.json b/doc/commands.json index af21007c9..13c1b6227 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -2854,7 +2854,7 @@ }, "hf felica scsvcode": { "command": "hf felica scsvcode", - "description": "Feature not implemented yet. Feel free to contribute!", + "description": "Dump all existing Area Code and Service Code.", "notes": [ "hf felica scsvcode" ], @@ -11648,7 +11648,7 @@ }, "lf t55xx help": { "command": "lf t55xx help", - "description": "----------- ---------------------------- notice ----------------------------- Remember to run `lf t55xx detect` first whenever a new card is placed on the Proxmark3 or the config block changed. help This help ----------- --------------------- operations --------------------- config Set/Get T55XX configuration (modulation, inverted, offset, rate) detect Try detecting the tag modulation from reading the configuration block info Show T55x7 configuration data (page 0/ blk 0) trace Show T55x7 traceability data (page 1/ blk 0-1) ----------- --------------------- recovery --------------------- sniff Attempt to recover T55xx commands from sample buffer --------------------------------------------------------------------------------------- lf t55xx clonehelp available offline: no Display a list of available commands for cloning specific techs on T5xx tags", + "description": "----------- ---------------------------- notice ----------------------------- Remember to run `lf t55xx detect` first whenever a new card is placed on the Proxmark3 or the config block changed. help This help ----------- --------------------- operations --------------------- config Set/Get T55XX configuration (modulation, inverted, offset, rate) detect Try detecting the tag modulation from reading the configuration block info Show T55x7 configuration data (page 0/ blk 0) trace Show T55x7 traceability data (page 1/ blk 0-1) view Display content from tag dump file ----------- --------------------- recovery --------------------- sniff Attempt to recover T55xx commands from sample buffer --------------------------------------------------------------------------------------- lf t55xx clonehelp available offline: no Display a list of available commands for cloning specific techs on T5xx tags", "notes": [ "lf t55xx clonehelp" ], @@ -11843,6 +11843,20 @@ ], "usage": "lf t55xx trace [-h1] [--r0] [--r1] [--r2] [--r3]" }, + "lf t55xx view": { + "command": "lf t55xx view", + "description": "Print a T55xx dump file (bin/eml/json)", + "notes": [ + "lf t55xx view -f lf-t55xx-00000000-11111111-22222222-33333333-dump.bin" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-f, --file Specify a filename for dump file", + "-v, --verbose verbose output" + ], + "usage": "lf t55xx view [-hv] -f " + }, "lf t55xx wakeup": { "command": "lf t55xx wakeup", "description": "This commands sends the Answer-On-Request command and leaves the readerfield ON afterwards", @@ -13439,8 +13453,8 @@ } }, "metadata": { - "commands_extracted": 772, + "commands_extracted": 773, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-07-25T20:28:03" + "extracted_on": "2025-07-28T13:42:15" } } diff --git a/doc/commands.md b/doc/commands.md index 05cec3ad5..0671c769d 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -1344,6 +1344,7 @@ Check column "offline" for their availability. |`lf t55xx restore `|N |`Restore T55xx card Page 0 / Page 1 blocks` |`lf t55xx trace `|Y |`Show T55x7 traceability data (page 1/ blk 0-1)` |`lf t55xx wakeup `|N |`Send AOR wakeup command` +|`lf t55xx view `|Y |`Display content from tag dump file` |`lf t55xx write `|N |`Write T55xx block data` |`lf t55xx bruteforce `|N |`Simple bruteforce attack to find password` |`lf t55xx chk `|N |`Check passwords` From 4f77c18ab4afddb4db240a927f46070daf0602f5 Mon Sep 17 00:00:00 2001 From: Flole Date: Tue, 29 Jul 2025 15:08:46 +0200 Subject: [PATCH 119/149] Allow writing key B even when no key A was found Likely a copy/paste bug Signed-off-by: Flole --- client/src/fileutils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/fileutils.c b/client/src/fileutils.c index 4ab0b7595..1cbe50fba 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -977,7 +977,7 @@ int createMfcKeyDump(const char *preferredName, uint8_t sectorsCnt, const sector } for (int i = 0; i < sectorsCnt; i++) { - if (e_sector[i].foundKey[0]) + if (e_sector[i].foundKey[1]) num_to_bytes(e_sector[i].Key[1], sizeof(tmp), tmp); else memcpy(tmp, empty, sizeof(tmp)); From 5a627381af8a673d6ac2697fdf8bf1ea8b8ce978 Mon Sep 17 00:00:00 2001 From: q0jt <89930816+q0jt@users.noreply.github.com> Date: Thu, 31 Jul 2025 07:44:13 +0900 Subject: [PATCH 120/149] hf felica: add FeliCa Lite-S authentication --- CHANGELOG.md | 1 + client/src/cmdhffelica.c | 426 +++++++++++++++++++++++++++++++++++++++ include/iso18.h | 22 ++ 3 files changed, 449 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index adde4b514..b0e15e239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `hf mfu sim` - now support UL-C simulation (@iceman1001) - Added `!` - run system commands from inside the client. Potentially dangerous if running client as SUDO, SU, ROOT (@iceman1001) - Implemented `hf felica scsvcode` - now dumps all service and area codes. (@zinongli) +- Added `hf felica liteauth` - now support FeliCa Lite-S authentication(@q0jt) ## [Daddy Iceman.4.20469][2025-06-16] diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index bbe9feabb..65e157d69 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -29,9 +29,13 @@ #include "ui.h" #include "iso18.h" // felica_card_select_t struct #include "des.h" +#include "platform_util.h" #include "cliparser.h" // cliparser #include "util_posix.h" // msleep +#define FELICA_BLK_SIZE 16 +#define FELICA_BLK_HALF (FELICA_BLK_SIZE/2) + #define AddCrc(data, len) compute_crc(CRC_FELICA, (data), (len), (data)+(len)+1, (data)+(len)) static int CmdHelp(const char *Cmd); @@ -2247,6 +2251,427 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { return PM3_SUCCESS; } +static void reverse_byte_array(const uint8_t* in, const int length, uint8_t* out) { + for (int i = 0; i < length; i++) { + out[i] = in[(length - 1) - i]; + } +} + +static void reverse_block16(const uint8_t* in, uint8_t *out) { + reverse_byte_array(in, 8, out); + reverse_byte_array(in + 8, 8, out + 8); +} + +static int felica_make_block_list(uint16_t *out, const uint8_t *blk_numbers, const size_t length) { + if (length > 4) { + PrintAndLogEx(ERR, "felica_make_block_list: exceeds max size"); + return PM3_EINVARG; + } + + uint16_t tmp[4]; + memset(tmp, 0, sizeof(tmp)); + + for (size_t i = 0; i < length; i++) { + tmp[i] = (uint16_t)(blk_numbers[i] << 8) | 0x80; + } + memcpy(out, tmp, length * sizeof(uint16_t)); + + return PM3_SUCCESS; +} + +static int read_without_encryption( + const uint8_t *idm, + const uint8_t num, + const uint8_t *blk_numbers, + uint8_t *out, + uint16_t *n) { + + felica_read_request_haeder_t request = { + .command_code = 0x06, + .number_of_service = 1, + .service_code_list = 0x00B, + .number_of_block = num, + }; + memcpy(request.IDm, idm, sizeof(request.IDm)); + + uint16_t svc[num]; + int ret = felica_make_block_list(svc, blk_numbers, num); + if (ret) { + return PM3_EINVARG; + } + + size_t hdr_size = sizeof(request); + uint16_t size = hdr_size + sizeof(svc) + 1; + *n = size; + + memcpy(out, &(uint8_t){ size }, sizeof(uint8_t)); + memcpy(out + 1, &request, hdr_size); + memcpy(out + hdr_size + 1, &svc, sizeof(svc)); + + return PM3_SUCCESS; +} + +static bool check_write_req_data(const felica_write_request_haeder_t *hdr, const uint8_t datalen) { + if (!hdr || !datalen) + return false; + + uint8_t num = *(hdr->number_of_block); + if (num != 1 && num != 2) + return false; + + // Check Block data size + if (num * 16 != datalen) + return false; + + return true; +} + +static int write_without_encryption( + const uint8_t *idm, + uint8_t num, + uint8_t *blk_numbers, + const uint8_t *data, + size_t datalen, + uint8_t *out, + uint16_t *n) { + + felica_write_request_haeder_t hdr = { + .command_code = 0x08, + .number_of_service = 1, + .service_code_list = 0x009, + .number_of_block = num, + }; + memcpy(hdr.IDm, idm, sizeof(hdr.IDm)); + + uint8_t dl = (uint8_t)(datalen); + + if (!check_write_req_data(&hdr, dl)) { + PrintAndLogEx(FAILED, "invalid request"); + return PM3_EINVARG; + } + + uint16_t blk[num]; + int ret = felica_make_block_list(blk, blk_numbers, num); + if (ret) { + return PM3_EINVARG; + } + + + size_t hdr_size = sizeof(hdr); + size_t offset = hdr_size + (num * 2) + 1; + + uint8_t size = hdr_size + sizeof(blk) + dl + 1; + *n = size; + + memcpy(out, &(uint8_t){ size }, sizeof(uint8_t)); + memcpy(out + 1, &hdr, hdr_size); + memcpy(out + hdr_size + 1, &blk, sizeof(blk)); + memcpy(out + offset, data, dl); + + return PM3_SUCCESS; +} + +static int parse_multiple_block_data(const uint8_t *data, const size_t datalen, uint8_t *out, uint8_t *outlen) { + if (datalen < 3) { + PrintAndLogEx(ERR, "\ndata size must be at least 3 bytes"); + return PM3_EINVARG; + } + + felica_status_response_t res; + memcpy(&res, data, sizeof(res)); + + uint8_t empty[8] = {0}; + + if (!memcmp(res.frame_response.IDm, empty, sizeof(empty))) { + PrintAndLogEx(ERR, "internal error"); + return PM3_ERFTRANS; + } + + + if (res.status_flags.status_flag1[0] != 0x00 || res.status_flags.status_flag2[0] != 0x00) { + PrintAndLogEx(ERR, "error status"); + return PM3_ERFTRANS; + } + + size_t res_size = sizeof(res); + + uint8_t num = 0; + memcpy(&num, data + res_size, sizeof(uint8_t)); + res_size++; + + memcpy(out, data + res_size, num * FELICA_BLK_SIZE); + + *outlen = num * FELICA_BLK_SIZE; + + return PM3_SUCCESS; +} + +static int felica_auth_context_init( + mbedtls_des3_context* ctx, + const uint8_t* rc, + const size_t rclen, + const uint8_t* key, + const size_t keylen, + felica_auth_context_t *auth_ctx) { + + int ret = PM3_SUCCESS; + + uint8_t rev_rc[16], rev_key[16]; + uint8_t encrypted_sk[16], rev_sk[16]; + uint8_t iv[8] = {0}; + + if (!ctx || !auth_ctx || rclen != 16 || keylen != 16) { + PrintAndLogEx(ERR, "\nfelica_auth_context_init: invalid parameters"); + return PM3_EINVARG; + } + + reverse_block16(rc, rev_rc); + memcpy(auth_ctx->random_challenge, rev_rc, sizeof(auth_ctx->random_challenge)); + + reverse_block16(key, rev_key); + + if (mbedtls_des3_set2key_enc(ctx, rev_key) != 0) { + ret = PM3_ECRYPTO; + goto cleanup; + } + + if (mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, 16, iv, rev_rc, encrypted_sk) != 0) { + ret = PM3_ECRYPTO; + goto cleanup; + } + + reverse_block16(encrypted_sk, rev_sk); + + memcpy(auth_ctx->session_key, rev_sk, sizeof(auth_ctx->session_key)); + +cleanup: + mbedtls_platform_zeroize(rev_rc, sizeof(rev_rc)); + mbedtls_platform_zeroize(rev_key, sizeof(rev_key)); + mbedtls_platform_zeroize(iv, sizeof(iv)); + mbedtls_platform_zeroize(encrypted_sk, sizeof(encrypted_sk)); + mbedtls_platform_zeroize(rev_sk, sizeof(rev_sk)); + + return ret; +} + +static int felica_generate_mac( + mbedtls_des3_context* ctx, + const felica_auth_context_t *auth_ctx, + const uint8_t* initialize_block, + const uint8_t* block_data, + const size_t length, + uint8_t* mac) { + + int ret = PM3_SUCCESS; + + uint8_t rev_sk[FELICA_BLK_SIZE]; + uint8_t iv[8], rev_block[8], out[8]; + + if (!ctx || !auth_ctx || !initialize_block || !block_data || !mac) { + return PM3_EINVARG; + } + + if (length % FELICA_BLK_HALF != 0) { + return PM3_EINVARG; + } + + reverse_block16(auth_ctx->session_key, rev_sk); + + memcpy(iv, auth_ctx->random_challenge, sizeof(iv)); + + reverse_byte_array(initialize_block, sizeof(rev_block), rev_block); + + if (mbedtls_des3_set2key_enc(ctx, rev_sk) != 0) { + ret = PM3_ECRYPTO; + goto cleanup; + } + + for (int i = 0; i <= length; i += 8) { + if (mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, sizeof(rev_block), iv, rev_block, out) != 0) { + ret = PM3_ECRYPTO; + goto cleanup; + } + memcpy(iv, out, sizeof(iv)); + reverse_byte_array(block_data + i, 8, rev_block); + } + + reverse_byte_array(out, FELICA_BLK_HALF, mac); + +cleanup: + mbedtls_platform_zeroize(rev_sk, sizeof(rev_sk)); + mbedtls_platform_zeroize(iv, sizeof(iv)); + mbedtls_platform_zeroize(out, sizeof(out)); + mbedtls_platform_zeroize(rev_block, sizeof(rev_block)); + + return ret; +} + +/** + * Command parser for liteauth. + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaAuthenticationLite(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica liteauth", + "Authenticate", + "hf felica liteauth -i 11100910C11BC407\n" + "hf felica liteauth -k 46656c69436130313233343536616263\n" + "hf felica liteauth -c 701185c59f8d30afeab8e4b3a61f5cc4 -k 46656c69436130313233343536616263" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("k", "key", "", "set card key, 16 bytes"), + arg_str0("c", "", "", "set random challenge, 16 bytes"), + arg_str0("i", "", "", "set custom IDm"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t key[FELICA_BLK_SIZE]; + memset(key, 0, sizeof(key)); + int keylen = 0; + int ret = CLIParamHexToBuf(arg_get_str(ctx, 1), key, sizeof(key), &keylen); + if (ret) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t rc[FELICA_BLK_SIZE]; + memset(rc, 0, sizeof(rc)); + int rclen = 0; + ret = CLIParamHexToBuf(arg_get_str(ctx, 2), rc, sizeof(rc), &rclen); + if (ret) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t idm[8]; + memset(idm, 0, sizeof(idm)); + int ilen = 0; + ret = CLIParamHexToBuf(arg_get_str(ctx, 3), idm, sizeof(idm), &ilen); + if (ret) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + CLIParserFree(ctx); + + if (!ilen) { + if (last_known_card.IDm[0] != 0 && last_known_card.IDm[1] != 0) { + memcpy(idm, last_known_card.IDm, sizeof(idm)); + } else { + PrintAndLogEx(WARNING, "No last known card! Use `" _YELLOW_("hf felica reader") "` first or set a custom IDm"); + return PM3_EINVARG; + } + } + + PrintAndLogEx(INFO, "Card Key: %s", sprint_hex(key, sizeof(key))); + PrintAndLogEx(INFO, "Random Challenge(RC): %s", sprint_hex(rc, sizeof(rc))); + + PrintAndLogEx(SUCCESS, "FeliCa lite - auth started"); + + uint8_t data[PM3_CMD_DATA_SIZE]; + memset(data, 0, sizeof(data)); + + uint8_t blk_numbers[1] = {0x80}; // RC + + uint16_t datalen = 0; + + ret = write_without_encryption(idm, (uint8_t)sizeof(blk_numbers), blk_numbers, rc, sizeof(rc), data, &datalen); + if (ret) { + return PM3_ERFTRANS; + } + + AddCrc(data, datalen); + datalen += 2; + + uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW | FELICA_NO_DISCONNECT); + + felica_status_response_t sres; + bool error = (send_wr_plain(flags, datalen, data, false, &sres) != PM3_SUCCESS); + if (error && sres.status_flags.status_flag1[0] != 0x00 && sres.status_flags.status_flag2[0] != 0x00) { + PrintAndLogEx(ERR, "\nError RC Write"); + return PM3_ERFTRANS; + } + + mbedtls_des3_context des3_ctx; + mbedtls_des3_init(&des3_ctx); + + felica_auth_context_t auth_ctx; + + ret = felica_auth_context_init(&des3_ctx, rc, sizeof(rc), key, sizeof(key), &auth_ctx); + if (ret) { + return PM3_ERFTRANS; + } + + PrintAndLogEx(INFO, "Session Key(SK): %s", sprint_hex(auth_ctx.session_key, sizeof(auth_ctx.session_key))); + + memset(data, 0, sizeof(data)); + + uint8_t blk_numbers2[2] = {0x82, 0x91}; + + ret = read_without_encryption(idm, (uint8_t)sizeof(blk_numbers2), blk_numbers2, data, &datalen); + if (ret) { + return PM3_ERFTRANS; + } + + AddCrc(data, datalen); + datalen += 2; + + flags &= ~FELICA_NO_DISCONNECT; + + clear_and_send_command(flags, datalen, data, false); + PacketResponseNG pres; + if (waitCmdFelica(false, &pres, false) == false) { + PrintAndLogEx(ERR, "\nGot no response from card"); + return PM3_ERFTRANS; + } + + uint8_t pd[64]; + memset(pd, 0, sizeof(pd)); + + uint8_t pd_size = 0; + ret = parse_multiple_block_data(pres.data.asBytes, sizeof(pres.data.asBytes), pd, &pd_size); + if (ret) { + return PM3_ERFTRANS; + } + + uint8_t id_blk[FELICA_BLK_SIZE]; + memcpy(id_blk, pd, FELICA_BLK_SIZE); + + uint8_t mac_blk[FELICA_BLK_SIZE]; + memcpy(mac_blk, pd + FELICA_BLK_SIZE, FELICA_BLK_SIZE); + + uint8_t initialize_blk[8]; + memset(initialize_blk, 0xFF, sizeof(initialize_blk)); + + initialize_blk[0] = 0x82; // ID + initialize_blk[1] = 0x00; + initialize_blk[2] = 0x91; // MAC_A + initialize_blk[3] = 0x00; + + uint8_t mac[FELICA_BLK_HALF]; + + ret = felica_generate_mac(&des3_ctx, &auth_ctx, initialize_blk, id_blk, sizeof(id_blk), mac); + if (ret) { + return PM3_ERFTRANS; + } + + PrintAndLogEx(SUCCESS, "MAC_A: %s", sprint_hex(mac, sizeof(mac))); + + mbedtls_des3_free(&des3_ctx); + + if (memcmp(mac_blk, mac, FELICA_BLK_HALF) != 0) { + PrintAndLogEx(ERR, "\nAuthenticate Failed"); + return PM3_ERFTRANS; + } + + PrintAndLogEx(SUCCESS, "Authenticate Success"); + + return PM3_SUCCESS; +} + static int CmdHFFelicaCmdRaw(const char *Cmd) { CLIParserContext *ctx; @@ -2365,6 +2790,7 @@ static command_t CommandTable[] = { {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("FeliCa Light") " -----------------------"}, {"litesim", CmdHFFelicaSimLite, IfPm3Felica, "Emulating ISO/18092 FeliCa Lite tag"}, {"litedump", CmdHFFelicaDumpLite, IfPm3Felica, "Wait for and try dumping FelicaLite"}, + {"liteauth", CmdHFFelicaAuthenticationLite, IfPm3Felica, "authenticate a card."}, // {"sim", CmdHFFelicaSim, IfPm3Felica, " -- Simulate ISO 18092/FeliCa tag"} {NULL, NULL, NULL, NULL} }; diff --git a/include/iso18.h b/include/iso18.h index 5446c74eb..da37819da 100644 --- a/include/iso18.h +++ b/include/iso18.h @@ -129,4 +129,26 @@ typedef struct { uint8_t payload[4]; } PACKED felica_service_dump_response_t; +typedef struct { + uint8_t command_code[1]; + uint8_t IDm[8]; + uint8_t number_of_service[1]; + uint8_t service_code_list[2]; + uint8_t number_of_block[1]; +} PACKED felica_write_request_haeder_t; + +typedef struct { + uint8_t command_code[1]; + uint8_t IDm[8]; + uint8_t number_of_service[1]; + uint8_t service_code_list[2]; + uint8_t number_of_block[1]; +} PACKED felica_read_request_haeder_t; + + +typedef struct { + uint8_t random_challenge[16]; + uint8_t session_key[16]; +} PACKED felica_auth_context_t; + #endif // _ISO18_H_ From d8240404416dd6c7c1498c602af490270f1b0359 Mon Sep 17 00:00:00 2001 From: q0jt <89930816+q0jt@users.noreply.github.com> Date: Thu, 31 Jul 2025 07:59:56 +0900 Subject: [PATCH 121/149] bug fix --- client/src/cmdhffelica.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 65e157d69..4428db06a 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -2288,9 +2288,9 @@ static int read_without_encryption( felica_read_request_haeder_t request = { .command_code = 0x06, - .number_of_service = 1, - .service_code_list = 0x00B, - .number_of_block = num, + .number_of_service = { 1 }, + .service_code_list = { 0x00B }, + .number_of_block = { num }, }; memcpy(request.IDm, idm, sizeof(request.IDm)); @@ -2337,9 +2337,9 @@ static int write_without_encryption( felica_write_request_haeder_t hdr = { .command_code = 0x08, - .number_of_service = 1, - .service_code_list = 0x009, - .number_of_block = num, + .number_of_service = { 1 }, + .service_code_list = { 0x009 }, + .number_of_block = { num }, }; memcpy(hdr.IDm, idm, sizeof(hdr.IDm)); From ddaa6de5cf4759de86993c8cbd14935e96071e6e Mon Sep 17 00:00:00 2001 From: q0jt <89930816+q0jt@users.noreply.github.com> Date: Thu, 31 Jul 2025 08:06:29 +0900 Subject: [PATCH 122/149] bug fix --- client/src/cmdhffelica.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 4428db06a..e10610db1 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -2287,7 +2287,7 @@ static int read_without_encryption( uint16_t *n) { felica_read_request_haeder_t request = { - .command_code = 0x06, + .command_code = { 0x06 }, .number_of_service = { 1 }, .service_code_list = { 0x00B }, .number_of_block = { num }, @@ -2336,7 +2336,7 @@ static int write_without_encryption( uint16_t *n) { felica_write_request_haeder_t hdr = { - .command_code = 0x08, + .command_code = { 0x08 }, .number_of_service = { 1 }, .service_code_list = { 0x009 }, .number_of_block = { num }, From 13053e14d1101bc68f79dae730e253a53980ce72 Mon Sep 17 00:00:00 2001 From: q0jt <89930816+q0jt@users.noreply.github.com> Date: Thu, 31 Jul 2025 08:22:31 +0900 Subject: [PATCH 123/149] resolve maybe-uninitialized --- client/src/cmdhffelica.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index e10610db1..a58480c38 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -2589,8 +2589,11 @@ static int CmdHFFelicaAuthenticationLite(const char *Cmd) { uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW | FELICA_NO_DISCONNECT); felica_status_response_t sres; - bool error = (send_wr_plain(flags, datalen, data, false, &sres) != PM3_SUCCESS); - if (error && sres.status_flags.status_flag1[0] != 0x00 && sres.status_flags.status_flag2[0] != 0x00) { + if (send_wr_plain(flags, datalen, data, false, &sres) != PM3_SUCCESS) { + return PM3_ERFTRANS; + } + + if (sres.status_flags.status_flag1[0] != 0x00 && sres.status_flags.status_flag2[0] != 0x00) { PrintAndLogEx(ERR, "\nError RC Write"); return PM3_ERFTRANS; } From a8d74d6d60dea082d03510b8d27bf9757d60954e Mon Sep 17 00:00:00 2001 From: q0jt <89930816+q0jt@users.noreply.github.com> Date: Thu, 31 Jul 2025 18:20:05 +0900 Subject: [PATCH 124/149] refactor --- client/src/cmdhffelica.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index a58480c38..0b4b34530 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -2251,17 +2251,6 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { return PM3_SUCCESS; } -static void reverse_byte_array(const uint8_t* in, const int length, uint8_t* out) { - for (int i = 0; i < length; i++) { - out[i] = in[(length - 1) - i]; - } -} - -static void reverse_block16(const uint8_t* in, uint8_t *out) { - reverse_byte_array(in, 8, out); - reverse_byte_array(in + 8, 8, out + 8); -} - static int felica_make_block_list(uint16_t *out, const uint8_t *blk_numbers, const size_t length) { if (length > 4) { PrintAndLogEx(ERR, "felica_make_block_list: exceeds max size"); @@ -2345,7 +2334,7 @@ static int write_without_encryption( uint8_t dl = (uint8_t)(datalen); - if (!check_write_req_data(&hdr, dl)) { + if (check_write_req_data(&hdr, dl) == false) { PrintAndLogEx(FAILED, "invalid request"); return PM3_EINVARG; } @@ -2425,10 +2414,10 @@ static int felica_auth_context_init( return PM3_EINVARG; } - reverse_block16(rc, rev_rc); + SwapEndian64ex(rc, sizeof(rev_rc), 8, rev_rc); memcpy(auth_ctx->random_challenge, rev_rc, sizeof(auth_ctx->random_challenge)); - reverse_block16(key, rev_key); + SwapEndian64ex(key, sizeof(rev_key), 8, rev_key); if (mbedtls_des3_set2key_enc(ctx, rev_key) != 0) { ret = PM3_ECRYPTO; @@ -2440,7 +2429,7 @@ static int felica_auth_context_init( goto cleanup; } - reverse_block16(encrypted_sk, rev_sk); + SwapEndian64ex(encrypted_sk, sizeof(encrypted_sk), 8, rev_sk); memcpy(auth_ctx->session_key, rev_sk, sizeof(auth_ctx->session_key)); @@ -2475,11 +2464,11 @@ static int felica_generate_mac( return PM3_EINVARG; } - reverse_block16(auth_ctx->session_key, rev_sk); + SwapEndian64ex(auth_ctx->session_key, sizeof(auth_ctx->session_key), 8, rev_sk); memcpy(iv, auth_ctx->random_challenge, sizeof(iv)); - reverse_byte_array(initialize_block, sizeof(rev_block), rev_block); + SwapEndian64ex(initialize_block, sizeof(rev_block), 8, rev_block); if (mbedtls_des3_set2key_enc(ctx, rev_sk) != 0) { ret = PM3_ECRYPTO; @@ -2492,10 +2481,10 @@ static int felica_generate_mac( goto cleanup; } memcpy(iv, out, sizeof(iv)); - reverse_byte_array(block_data + i, 8, rev_block); + SwapEndian64ex(block_data + i, 8, 8, rev_block); } - reverse_byte_array(out, FELICA_BLK_HALF, mac); + SwapEndian64ex(out, FELICA_BLK_HALF, 8, mac); cleanup: mbedtls_platform_zeroize(rev_sk, sizeof(rev_sk)); @@ -2592,7 +2581,7 @@ static int CmdHFFelicaAuthenticationLite(const char *Cmd) { if (send_wr_plain(flags, datalen, data, false, &sres) != PM3_SUCCESS) { return PM3_ERFTRANS; } - + if (sres.status_flags.status_flag1[0] != 0x00 && sres.status_flags.status_flag2[0] != 0x00) { PrintAndLogEx(ERR, "\nError RC Write"); return PM3_ERFTRANS; From 948dce92305466ee4a6ceccd8fbec35748c2af8c Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Thu, 31 Jul 2025 14:59:02 +0200 Subject: [PATCH 125/149] new pk --- client/src/crypto/originality.c | 6 ++++++ tools/recover_pk.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/client/src/crypto/originality.c b/client/src/crypto/originality.c index 6b6e687a0..e0dfe1f9c 100644 --- a/client/src/crypto/originality.c +++ b/client/src/crypto/originality.c @@ -59,6 +59,12 @@ const ecdsa_publickey_ng_t manufacturer_public_keys[] = { "04DC34DAA903F2726A6225B11C692AF6AB4396575CA12810CBBCE3F781A097B3833B50AB364A70D9C2B641A728A599AE74" }, + // ref: AN12998 NTAG 22x DNA (StatusDetect) - Features and hints + { + PK_MFUL, MBEDTLS_ECP_DP_SECP192R1, 49, "NXP NTAG 22x DNA", + "0485D5B9353B4FAA77581BA2AE96630C5876D6E8603308ABE9A81A0B506F52D02D04FEE6F2D365B3DEE7B9FAD9133E2976" + }, + { PK_MFP, MBEDTLS_ECP_DP_SECP224R1, 57, "MIFARE Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E" diff --git a/tools/recover_pk.py b/tools/recover_pk.py index 1e6bc33f7..06cac1ce7 100755 --- a/tools/recover_pk.py +++ b/tools/recover_pk.py @@ -183,6 +183,12 @@ def selftests(): "20593261", "F762CDD59EEDC075F4DDBA7ECD529FEEE5135C65A84D12EF0A250A321B2012F5"], 'pk': "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"}, + {'name': "NTAG223DNA, NTAG224DNA", + # uses prime192v1, None, + # TODO more samples + 'samples': ["043302218D3D00", "6A5D5E034F4FC823CACAB56C1A77A409B8DB345F89BD3FD59ED1F9C0093518609BE62D0A20764D2011E47EFA187F29AA"], # noqa: E501 + 'pk': "0485D5B9353B4FAA77581BA2AE96630C5876D6E8603308ABE9A81A0B506F52D02D04FEE6F2D365B3DEE7B9FAD9133E2976"}, # noqa: E501 + # TruST25 (ST25TA) - KeyID 0x01? # curve=secp128r1, hash=sha256 - from block 224 in ST25TA NDEF file {'name': "ST25TA02KB TruST25 (ST) / KeyID 0x01?", From ac0f74710490eb5a28535d7bc9f331d438d3068f Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Sat, 2 Aug 2025 20:05:42 -0400 Subject: [PATCH 126/149] add `hf felica dump` for unauth readable blocks --- client/src/cmdhffelica.c | 160 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 158 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 0b4b34530..5822f9ff8 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -36,6 +36,14 @@ #define FELICA_BLK_SIZE 16 #define FELICA_BLK_HALF (FELICA_BLK_SIZE/2) +#define FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ (0b000001) +#define FELICA_SERVICE_ATTRIBUTE_READ_ONLY (0b000010) +#define FELICA_SERVICE_ATTRIBUTE_RANDOM_ACCESS (0b001000) +#define FELICA_SERVICE_ATTRIBUTE_CYCLIC (0b001100) +#define FELICA_SERVICE_ATTRIBUTE_PURSE (0b010000) +#define FELICA_SERVICE_ATTRIBUTE_PURSE_SUBFIELD (0b000110) + + #define AddCrc(data, len) compute_crc(CRC_FELICA, (data), (len), (data)+(len)+1, (data)+(len)) static int CmdHelp(const char *Cmd); @@ -254,6 +262,7 @@ static const char *felica_model_name(uint8_t rom_type, uint8_t ic_type) { return "FeliCa Standard RC-S919"; case 0x0B: case 0x31: + case 0x36: return "Suica card (FeliCa Standard RC-S ?)"; default: break; @@ -477,7 +486,7 @@ static void print_rd_plain_response(felica_read_without_encryption_response_t *r temp = sprint_hex(rd_noCry_resp->block_element_number, sizeof(rd_noCry_resp->block_element_number)); strncpy(bl_element_number, temp, sizeof(bl_element_number) - 1); - PrintAndLogEx(INFO, " %s | %s ", bl_element_number, bl_data); + PrintAndLogEx(INFO, " %s | %s ", bl_element_number, bl_data); } else { PrintAndLogEx(SUCCESS, "IDm... %s", sprint_hex_inrow(rd_noCry_resp->frame_response.IDm, sizeof(rd_noCry_resp->frame_response.IDm))); PrintAndLogEx(SUCCESS, " Status flag 1... %s", sprint_hex(rd_noCry_resp->status_flags.status_flag1, sizeof(rd_noCry_resp->status_flags.status_flag1))); @@ -1660,6 +1669,153 @@ static int CmdHFFelicaRequestSystemCode(const char *Cmd) { return PM3_SUCCESS; } +/** + * Command parser for rqservice. + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaDump(const char *Cmd) { + /* -- CLI boilerplate (unchanged) ------------------------------- */ + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica dump", + "Dump all existing Area Code and Service Code.\n" + "Only works on services that do not require authentication yet.\n", + "hf felica dump"); + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "no-auth", "read public services"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + // bool no_auth = arg_get_lit(ctx, 1); + + uint8_t data_service_dump[PM3_CMD_DATA_SIZE] = {0}; + data_service_dump[0] = 0x0C; + data_service_dump[1] = 0x0A; + uint16_t service_datalen = 12; + if (!check_last_idm(data_service_dump, service_datalen)) + return PM3_EINVARG; + + uint8_t data_block_dump[PM3_CMD_DATA_SIZE] = {0}; + data_block_dump[0] = 0x10; // Static length + data_block_dump[1] = 0x06; // unauth read block command + data_block_dump[10] = 1; // read one service at a time + data_block_dump[13] = 1; // read one block at a time + data_block_dump[14] = 0x80; // block list element first byte + uint16_t block_datalen = 16; // Length (1), Command ID (1), IDm (8), Number of Service (1), Service Code List(2), Number of Block(1), Block List(3) + if (!check_last_idm(data_block_dump, block_datalen)) { + return PM3_EINVARG; + } + + uint8_t flags = FELICA_APPEND_CRC | FELICA_RAW; + + uint16_t cursor = 0x0000; + + felica_service_dump_response_t resp; + + while (true) { + + data_service_dump[10] = cursor & 0xFF; + data_service_dump[11] = cursor >> 8; + AddCrc(data_service_dump, service_datalen); + + if (send_dump_sv_plain(flags, service_datalen + 2, data_service_dump, 0, + &resp, false) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "No response at cursor 0x%04X", cursor); + return PM3_ERFTRANS; + } + if (resp.frame_response.cmd_code[0] != 0x0B) { + PrintAndLogEx(FAILED, "Bad response cmd 0x%02X @ 0x%04X.", + resp.frame_response.cmd_code[0], cursor); + PrintAndLogEx(INFO, "This is a normal signal issue. Please try again."); + PrintAndLogEx(INFO, "If the issue persists, move the card around and check signal strength. FeliCa can be hard to keep in field."); + return PM3_ERFTRANS; + } + uint8_t len = resp.frame_response.length[0]; + uint16_t node_code = resp.payload[0] | (resp.payload[1] << 8); + if (node_code == 0xFFFF) break; + char attrib_str[64] = ""; + switch (len) { + case 0x0E: + break; + case 0x0C: { + uint8_t attribute = node_code & 0x3F; + bool is_public = (attribute & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) != 0; + strcat(attrib_str, is_public ? "| Public " : "| Private "); + + bool is_purse = (attribute & FELICA_SERVICE_ATTRIBUTE_PURSE) != 0; + // Subfield bitwise attributes are applicable depending on is PURSE or not + + if(is_purse) { + strcat(attrib_str, "| Purse |"); + switch((attribute & FELICA_SERVICE_ATTRIBUTE_PURSE_SUBFIELD) >> 1) { + case 0: + strcat(attrib_str, " Direct |"); + break; + case 1: + strcat(attrib_str, " Cashback |"); + break; + case 2: + strcat(attrib_str, " Decrement |"); + break; + case 3: + strcat(attrib_str, " Read Only |"); + break; + default: + strcat(attrib_str, " Unknown |"); + break; + } + } else { + bool is_random = (attribute & FELICA_SERVICE_ATTRIBUTE_RANDOM_ACCESS) != 0; + strcat(attrib_str, is_random ? "| Random |" : "| Cyclic |"); + bool is_readonly = (attribute & FELICA_SERVICE_ATTRIBUTE_READ_ONLY) != 0; + strcat(attrib_str, is_readonly ? " Read Only |" : " Read/Write |"); + } + + PrintAndLogEx(INFO, "Service %04X %s", node_code, attrib_str); + + if (is_public) { + // dump blocks here + PrintAndLogEx(INFO, " block | data "); + PrintAndLogEx(INFO, "-------+----------------------------------------"); + + data_block_dump[11] = resp.payload[0]; // convert service code to little endian + data_block_dump[12] = resp.payload[1]; + + uint16_t last_blockno = 0xFF; + for (uint16_t i = 0x00; i < last_blockno; i++) { + data_block_dump[15] = i; + AddCrc(data_block_dump, block_datalen); + felica_read_without_encryption_response_t rd_noCry_resp; + if ((send_rd_plain(flags, block_datalen + 2, data_block_dump, 0, &rd_noCry_resp) == PM3_SUCCESS)) { + if (rd_noCry_resp.status_flags.status_flag1[0] == 0 && rd_noCry_resp.status_flags.status_flag2[0] == 0) { + print_rd_plain_response(&rd_noCry_resp); + } else { + break; // no more blocks to read + } + } else { + break; + } + } + } + break; + } + default: + PrintAndLogEx(FAILED, "Unexpected length 0x%02X @ 0x%04X", + len, cursor); + return PM3_ERFTRANS; + } + cursor++; + if (cursor == 0) break; + } + + PrintAndLogEx(SUCCESS, "Unauth service dump complete."); + return PM3_SUCCESS; +} + + /** * Command parser for rqservice. * @param Cmd input data of the user. @@ -2761,7 +2917,7 @@ static command_t CommandTable[] = { {"sniff", CmdHFFelicaSniff, IfPm3Felica, "Sniff ISO 18092/FeliCa traffic"}, {"wrbl", CmdHFFelicaWritePlain, IfPm3Felica, "write block data to an authentication-not-required Service."}, {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("FeliCa Standard") " -----------------------"}, - //{"dump", CmdHFFelicaDump, IfPm3Felica, "Wait for and try dumping FeliCa"}, + {"dump", CmdHFFelicaDump, IfPm3Felica, "Wait for and try dumping FeliCa"}, {"rqservice", CmdHFFelicaRequestService, IfPm3Felica, "verify the existence of Area and Service, and to acquire Key Version."}, {"rqresponse", CmdHFFelicaRequestResponse, IfPm3Felica, "verify the existence of a card and its Mode."}, {"scsvcode", CmdHFFelicaDumpServiceArea, IfPm3Felica, "acquire Area Code and Service Code."}, From 72d475f484a2ebd8680e7445e1f03bbbb6d066b6 Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Sat, 2 Aug 2025 20:06:49 -0400 Subject: [PATCH 127/149] clean up --- client/src/cmdhffelica.c | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 5822f9ff8..b2f0bb936 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -1675,7 +1675,6 @@ static int CmdHFFelicaRequestSystemCode(const char *Cmd) { * @return client result code. */ static int CmdHFFelicaDump(const char *Cmd) { - /* -- CLI boilerplate (unchanged) ------------------------------- */ CLIParserContext *ctx; CLIParserInit(&ctx, "hf felica dump", "Dump all existing Area Code and Service Code.\n" From 60982bb53f4971e24e0a16ce01d7b7a77beec844 Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Sat, 2 Aug 2025 20:10:29 -0400 Subject: [PATCH 128/149] change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0e15e239..3fa42b7b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `!` - run system commands from inside the client. Potentially dangerous if running client as SUDO, SU, ROOT (@iceman1001) - Implemented `hf felica scsvcode` - now dumps all service and area codes. (@zinongli) - Added `hf felica liteauth` - now support FeliCa Lite-S authentication(@q0jt) +- Added `he felica dump` - partial support for dumping all blocks from unauth readable services (@zinongli) ## [Daddy Iceman.4.20469][2025-06-16] From 52c9d90d64d387cbfc315112d0cd652e2d71bc54 Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Sat, 2 Aug 2025 20:26:26 -0400 Subject: [PATCH 129/149] fixed it? --- client/src/cmdhffelica.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index b2f0bb936..72a1b1d26 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -1700,8 +1700,8 @@ static int CmdHFFelicaDump(const char *Cmd) { uint8_t data_block_dump[PM3_CMD_DATA_SIZE] = {0}; data_block_dump[0] = 0x10; // Static length data_block_dump[1] = 0x06; // unauth read block command - data_block_dump[10] = 1; // read one service at a time - data_block_dump[13] = 1; // read one block at a time + data_block_dump[10] = 0x01; // read one service at a time + data_block_dump[13] = 0x01; // read one block at a time data_block_dump[14] = 0x80; // block list element first byte uint16_t block_datalen = 16; // Length (1), Command ID (1), IDm (8), Number of Service (1), Service Code List(2), Number of Block(1), Block List(3) if (!check_last_idm(data_block_dump, block_datalen)) { From 595517bfedd3eb0329a352bfbd6ea28735fd9637 Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Sat, 2 Aug 2025 23:06:01 -0400 Subject: [PATCH 130/149] Update cmdhf14b.c --- client/src/cmdhf14b.c | 50 ++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index 7d05c2e75..d64d200a7 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -2708,25 +2708,32 @@ static int CmdHF14BCalypsoRead(const char *Cmd) { CLIParserFree(ctx); transport_14b_apdu_t cmds[] = { - {"01.Select ICC file", "\x94\xa4\x08\x00\x04\x3f\x00\x00\x02", 9}, - {"02.ICC", "\x94\xb2\x01\x04\x1d", 5}, - {"03.Select EnvHol file", "\x94\xa4\x08\x00\x04\x20\x00\x20\x01", 9}, - {"04.EnvHol1", "\x94\xb2\x01\x04\x1d", 5}, - {"05.Select EvLog file", "\x94\xa4\x08\x00\x04\x20\x00\x20\x10", 9}, - {"06.EvLog1", "\x94\xb2\x01\x04\x1d", 5}, - {"07.EvLog2", "\x94\xb2\x02\x04\x1d", 5}, - {"08.EvLog3", "\x94\xb2\x03\x04\x1d", 5}, - {"09.Select ConList file", "\x94\xa4\x08\x00\x04\x20\x00\x20\x50", 9}, - {"10.ConList", "\x94\xb2\x01\x04\x1d", 5}, - {"11.Select Contra file", "\x94\xa4\x08\x00\x04\x20\x00\x20\x20", 9}, - {"12.Contra1", "\x94\xb2\x01\x04\x1d", 5}, - {"13.Contra2", "\x94\xb2\x02\x04\x1d", 5}, - {"14.Contra3", "\x94\xb2\x03\x04\x1d", 5}, - {"15.Contra4", "\x94\xb2\x04\x04\x1d", 5}, - {"16.Select Counter file", "\x94\xa4\x08\x00\x04\x20\x00\x20\x69", 9}, - {"17.Counter", "\x94\xb2\x01\x04\x1d", 5}, - {"18.Select SpecEv file", "\x94\xa4\x08\x00\x04\x20\x00\x20\x40", 9}, - {"19.SpecEv1", "\x94\xb2\x01\x04\x1d", 5}, + {"01.Select ICC ", "\x94\xa4\x08\x00\x04\x3f\x00\x00\x02", 9}, + {"02.ICC ", "\x94\xb2\x01\x04\x1d", 5}, + {"03.Select EnvHol ", "\x94\xa4\x08\x00\x04\x20\x00\x20\x01", 9}, + {"04.EnvHol1 ", "\x94\xb2\x01\x04\x1d", 5}, + {"05.Select EvLog ", "\x94\xa4\x08\x00\x04\x20\x00\x20\x10", 9}, + {"06.EvLog1 ", "\x94\xb2\x01\x04\x1d", 5}, + {"07.EvLog2 ", "\x94\xb2\x02\x04\x1d", 5}, + {"08.EvLog3 ", "\x94\xb2\x03\x04\x1d", 5}, + {"09.Select ConList ", "\x94\xa4\x08\x00\x04\x20\x00\x20\x50", 9}, + {"10.ConList ", "\x94\xb2\x01\x04\x1d", 5}, + {"11.Select Contra ", "\x94\xa4\x08\x00\x04\x20\x00\x20\x20", 9}, + {"12.Contra1 ", "\x94\xb2\x01\x04\x1d", 5}, + {"13.Contra2 ", "\x94\xb2\x02\x04\x1d", 5}, + {"14.Contra3 ", "\x94\xb2\x03\x04\x1d", 5}, + {"15.Contra4 ", "\x94\xb2\x04\x04\x1d", 5}, + {"16.Select Counter ", "\x94\xa4\x08\x00\x04\x20\x00\x20\x69", 9}, + {"17.Counter ", "\x94\xb2\x01\x04\x1d", 5}, + {"18.Select SpecEv ", "\x94\xa4\x08\x00\x04\x20\x00\x20\x40", 9}, + {"19.SpecEv1 ", "\x94\xb2\x01\x04\x1d", 5}, + {"20.Select Purse ", "\x00\xa4\x00\x00\x02\x10\x15", 7}, + {"21.Purse1 ", "\x00\xb2\x01\x04\x1d", 5}, + {"22.Purse2 ", "\x00\xb2\x02\x04\x1d", 5}, + {"23.Purse3 ", "\x00\xb2\x03\x04\x1d", 5}, + {"24.Select Top Up ", "\x00\xa4\x00\x00\x02\x10\x14", 7}, + {"25.Topup1 ", "\x00\xb2\x01\x04\x1d", 5}, + {"26.Select 1TIC.ICA", "\x00\xa4\x04\x00\x08\x31\x54\x49\x43\x2e\x49\x43\x41", 13}, }; /* @@ -2780,9 +2787,8 @@ static int CmdHF14BCalypsoRead(const char *Cmd) { uint16_t sw = get_sw(response, resplen); if (sw != ISO7816_OK) { - PrintAndLogEx(ERR, "Sending command failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); - switch_off_field_14b(); - return PM3_ESOFT; + PrintAndLogEx(INFO, "%s - command failed (%04x - %s).", cmds[i].desc, sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + continue; } PrintAndLogEx(INFO, "%s - %s", cmds[i].desc, sprint_hex(response, resplen)); From 9448a41e7d4d74a5ece737b409352a0464ab7ddd Mon Sep 17 00:00:00 2001 From: zinongli <131403964+zinongli@users.noreply.github.com> Date: Sat, 2 Aug 2025 23:11:03 -0400 Subject: [PATCH 131/149] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fa42b7b6..d4ca1a385 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Implemented `hf felica scsvcode` - now dumps all service and area codes. (@zinongli) - Added `hf felica liteauth` - now support FeliCa Lite-S authentication(@q0jt) - Added `he felica dump` - partial support for dumping all blocks from unauth readable services (@zinongli) +- Changed `hf 14b calypso` - now don't break the file id loop when one file can't be selected or read. Add new file ids to iterate through (@zinongli) ## [Daddy Iceman.4.20469][2025-06-16] From dccfb391aadd7e96c49416f054a6944309ee241b Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sun, 3 Aug 2025 08:33:55 +0200 Subject: [PATCH 132/149] Fingerprint FM11RF08S 0590, thanks Jackson Ouzts --- client/src/cmdhfmf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 04e024974..e35b6e18f 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -10428,7 +10428,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { PrintAndLogEx(SUCCESS, "Fudan based card"); } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08s, sizeof(fkey)) == 0 && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 - && (blockdata[8] == 0x03 || blockdata[8] == 0x04) && blockdata[15] == 0x90) { + && (blockdata[8] == 0x03 || blockdata[8] == 0x04 || blockdata[8] == 0x05) && blockdata[15] == 0x90) { PrintAndLogEx(SUCCESS, "Fudan FM11RF08S %02X%02X", blockdata[8], blockdata[15]); expect_static_enc_nonce = true; } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08s, sizeof(fkey)) == 0 From 989dad18ccfd97b894ba5541f5ad578518cfefc7 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sun, 3 Aug 2025 22:05:34 +0200 Subject: [PATCH 133/149] fm11rf08s_recovery.py: load def keys as lowercase --- client/pyscripts/fm11rf08s_recovery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index 9c7b0797e..8b7eaffd8 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -216,7 +216,7 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, with open(dict_path, 'r', encoding='utf-8') as file: for line in file: if line[0] != '#' and len(line) >= 12: - DEFAULT_KEYS.add(line[:12]) + DEFAULT_KEYS.add(line[:12].lower()) show(f"Loaded {dict_def}") except FileNotFoundError: show(f"Warning, {dict_def} not found.") From 09c4446f7895a24d2b3960ac2453ff3a962c9b38 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Mon, 4 Aug 2025 16:26:37 +0200 Subject: [PATCH 134/149] recover_pk: add sample --- tools/recover_pk.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/recover_pk.py b/tools/recover_pk.py index 06cac1ce7..7795604d8 100755 --- a/tools/recover_pk.py +++ b/tools/recover_pk.py @@ -173,10 +173,10 @@ def selftests(): 'samples': ["045E4CC2451390", "C9BBDA1B99EB6634CDFD8E3251AC5C4742EA5FA507B8A8A8B39B19AB7340D173331589C54C56C49F0CCA6DDBAC1E492A", # noqa: E501 "043F88C2451390", "5C2055A7373F119C3FDD9843020B06AA0E6DE18C16496C425C4AD971A50F05FA1A67B9E39CA60C355EEEEBF8214A84A5"], # noqa: E501 'pk': "0453BF8C49B7BD9FE3207A91513B9C1D238ECAB07186B772104AB535F7D3AE63CF7C7F3DD0D169DA3E99E43C6399621A86"}, # noqa: E501 - {'name': "MIFARE Ultralight AES (alt key)", + {'name': "MIFARE Ultralight AES (Vingcard alt key)", # uses prime192v1, None, - # TODO more samples - 'samples': ["04A31232241C90", "057595DCC601CA7E21341F1F978FA134F0204D87A33749C56DDB4ABD6F1F26194341DB10093B34C42F524A30DCC5CE54"], # noqa: E501 + 'samples': ["04A31232241C90", "057595DCC601CA7E21341F1F978FA134F0204D87A33749C56DDB4ABD6F1F26194341DB10093B34C42F524A30DCC5CE54", # noqa: E501 + "041BC1D2F31B90", "FB5CF8F1B3CC39984BCA54A50FCF47ACFDC8C969010C1F4599554AF9A8E4F2B8371524855E45AD7EE71179A660D27667"], # noqa: E501 'pk': "04DC34DAA903F2726A6225B11C692AF6AB4396575CA12810CBBCE3F781A097B3833B50AB364A70D9C2B641A728A599AE74"}, # noqa: E501 {'name': "MIFARE Classic / QL88", 'samples': ["30933C61", "AEA4DD0B800FAC63D4DE08EE91F4650ED825FD6B4D7DEEE98DBC9BAE10BE003E", @@ -215,7 +215,7 @@ def selftests(): succeeded = True for t in tests: - print("Testing %-40s" % (t['name']+":"), end="") + print("Testing %-41s" % (t['name']+":"), end="") curvenames = guess_curvename(t['samples'][1]) recovered = set() @@ -229,16 +229,16 @@ def selftests(): c, h, pk = recovered.pop() pk = binascii.hexlify(pk).decode('utf8') if pk.lower() == t['pk'].lower(): - print("%15s/%-8s ( %s )" % (c, h, color('ok', fg='green'))) + print("%14s/%-8s ( %s )" % (c, h, color('ok', fg='green'))) else: succeeded = False - print("%15s/%-8s ( %s ) got %s" % (c, h, color('fail', fg='red'), pk.lower())) + print("%14s/%-8s ( %s ) got %s" % (c, h, color('fail', fg='red'), pk.lower())) elif len(t['samples'])//2 == 1: recovereds = [(c, h) for c, h, pk in list(recovered) if t['pk'].lower() == binascii.hexlify(pk).decode('utf8').lower()] if len(recovereds) == 1: c, h = recovereds[0] - print("%15s/%-8s ( %s ) partial" % (c, h, color('ok', fg='green'))) + print("%14s/%-8s ( %s ) partial" % (c, h, color('ok', fg='green'))) else: succeeded = False print(" ( %s ), got" % color('fail', fg='red')) From a0df90af183233ea7f63ff0d32789ff905550517 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 4 Aug 2025 19:53:08 +0200 Subject: [PATCH 135/149] style --- client/src/cmdhffelica.c | 158 ++++++++++++++++---------------- client/src/pm3line_vocabulary.h | 2 + doc/commands.json | 34 ++++++- doc/commands.md | 2 + 4 files changed, 115 insertions(+), 81 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 72a1b1d26..c84c8c519 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -1734,80 +1734,80 @@ static int CmdHFFelicaDump(const char *Cmd) { } uint8_t len = resp.frame_response.length[0]; uint16_t node_code = resp.payload[0] | (resp.payload[1] << 8); - if (node_code == 0xFFFF) break; + if (node_code == 0xFFFF) break; char attrib_str[64] = ""; switch (len) { - case 0x0E: - break; - case 0x0C: { - uint8_t attribute = node_code & 0x3F; - bool is_public = (attribute & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) != 0; - strcat(attrib_str, is_public ? "| Public " : "| Private "); + case 0x0E: + break; + case 0x0C: { + uint8_t attribute = node_code & 0x3F; + bool is_public = (attribute & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) != 0; + strcat(attrib_str, is_public ? "| Public " : "| Private "); - bool is_purse = (attribute & FELICA_SERVICE_ATTRIBUTE_PURSE) != 0; - // Subfield bitwise attributes are applicable depending on is PURSE or not + bool is_purse = (attribute & FELICA_SERVICE_ATTRIBUTE_PURSE) != 0; + // Subfield bitwise attributes are applicable depending on is PURSE or not - if(is_purse) { - strcat(attrib_str, "| Purse |"); - switch((attribute & FELICA_SERVICE_ATTRIBUTE_PURSE_SUBFIELD) >> 1) { - case 0: - strcat(attrib_str, " Direct |"); - break; - case 1: - strcat(attrib_str, " Cashback |"); - break; - case 2: - strcat(attrib_str, " Decrement |"); - break; - case 3: - strcat(attrib_str, " Read Only |"); - break; - default: - strcat(attrib_str, " Unknown |"); - break; + if (is_purse) { + strcat(attrib_str, "| Purse |"); + switch ((attribute & FELICA_SERVICE_ATTRIBUTE_PURSE_SUBFIELD) >> 1) { + case 0: + strcat(attrib_str, " Direct |"); + break; + case 1: + strcat(attrib_str, " Cashback |"); + break; + case 2: + strcat(attrib_str, " Decrement |"); + break; + case 3: + strcat(attrib_str, " Read Only |"); + break; + default: + strcat(attrib_str, " Unknown |"); + break; + } + } else { + bool is_random = (attribute & FELICA_SERVICE_ATTRIBUTE_RANDOM_ACCESS) != 0; + strcat(attrib_str, is_random ? "| Random |" : "| Cyclic |"); + bool is_readonly = (attribute & FELICA_SERVICE_ATTRIBUTE_READ_ONLY) != 0; + strcat(attrib_str, is_readonly ? " Read Only |" : " Read/Write |"); } - } else { - bool is_random = (attribute & FELICA_SERVICE_ATTRIBUTE_RANDOM_ACCESS) != 0; - strcat(attrib_str, is_random ? "| Random |" : "| Cyclic |"); - bool is_readonly = (attribute & FELICA_SERVICE_ATTRIBUTE_READ_ONLY) != 0; - strcat(attrib_str, is_readonly ? " Read Only |" : " Read/Write |"); - } - PrintAndLogEx(INFO, "Service %04X %s", node_code, attrib_str); + PrintAndLogEx(INFO, "Service %04X %s", node_code, attrib_str); - if (is_public) { - // dump blocks here - PrintAndLogEx(INFO, " block | data "); - PrintAndLogEx(INFO, "-------+----------------------------------------"); + if (is_public) { + // dump blocks here + PrintAndLogEx(INFO, " block | data "); + PrintAndLogEx(INFO, "-------+----------------------------------------"); - data_block_dump[11] = resp.payload[0]; // convert service code to little endian - data_block_dump[12] = resp.payload[1]; + data_block_dump[11] = resp.payload[0]; // convert service code to little endian + data_block_dump[12] = resp.payload[1]; - uint16_t last_blockno = 0xFF; - for (uint16_t i = 0x00; i < last_blockno; i++) { - data_block_dump[15] = i; - AddCrc(data_block_dump, block_datalen); - felica_read_without_encryption_response_t rd_noCry_resp; - if ((send_rd_plain(flags, block_datalen + 2, data_block_dump, 0, &rd_noCry_resp) == PM3_SUCCESS)) { - if (rd_noCry_resp.status_flags.status_flag1[0] == 0 && rd_noCry_resp.status_flags.status_flag2[0] == 0) { - print_rd_plain_response(&rd_noCry_resp); + uint16_t last_blockno = 0xFF; + for (uint16_t i = 0x00; i < last_blockno; i++) { + data_block_dump[15] = i; + AddCrc(data_block_dump, block_datalen); + felica_read_without_encryption_response_t rd_noCry_resp; + if ((send_rd_plain(flags, block_datalen + 2, data_block_dump, 0, &rd_noCry_resp) == PM3_SUCCESS)) { + if (rd_noCry_resp.status_flags.status_flag1[0] == 0 && rd_noCry_resp.status_flags.status_flag2[0] == 0) { + print_rd_plain_response(&rd_noCry_resp); + } else { + break; // no more blocks to read + } } else { - break; // no more blocks to read + break; } - } else { - break; } } + break; } - break; - } - default: - PrintAndLogEx(FAILED, "Unexpected length 0x%02X @ 0x%04X", - len, cursor); - return PM3_ERFTRANS; + default: + PrintAndLogEx(FAILED, "Unexpected length 0x%02X @ 0x%04X", + len, cursor); + return PM3_ERFTRANS; } cursor++; - if (cursor == 0) break; + if (cursor == 0) break; } PrintAndLogEx(SUCCESS, "Unauth service dump complete."); @@ -2448,7 +2448,7 @@ static int read_without_encryption( uint16_t size = hdr_size + sizeof(svc) + 1; *n = size; - memcpy(out, &(uint8_t){ size }, sizeof(uint8_t)); + memcpy(out, &(uint8_t) { size }, sizeof(uint8_t)); memcpy(out + 1, &request, hdr_size); memcpy(out + hdr_size + 1, &svc, sizeof(svc)); @@ -2462,7 +2462,7 @@ static bool check_write_req_data(const felica_write_request_haeder_t *hdr, const uint8_t num = *(hdr->number_of_block); if (num != 1 && num != 2) return false; - + // Check Block data size if (num * 16 != datalen) return false; @@ -2470,11 +2470,11 @@ static bool check_write_req_data(const felica_write_request_haeder_t *hdr, const return true; } -static int write_without_encryption( - const uint8_t *idm, - uint8_t num, - uint8_t *blk_numbers, - const uint8_t *data, +static int write_without_encryption( + const uint8_t *idm, + uint8_t num, + uint8_t *blk_numbers, + const uint8_t *data, size_t datalen, uint8_t *out, uint16_t *n) { @@ -2499,7 +2499,7 @@ static int write_without_encryption( if (ret) { return PM3_EINVARG; } - + size_t hdr_size = sizeof(hdr); size_t offset = hdr_size + (num * 2) + 1; @@ -2507,7 +2507,7 @@ static int write_without_encryption( uint8_t size = hdr_size + sizeof(blk) + dl + 1; *n = size; - memcpy(out, &(uint8_t){ size }, sizeof(uint8_t)); + memcpy(out, &(uint8_t) { size }, sizeof(uint8_t)); memcpy(out + 1, &hdr, hdr_size); memcpy(out + hdr_size + 1, &blk, sizeof(blk)); memcpy(out + offset, data, dl); @@ -2530,7 +2530,7 @@ static int parse_multiple_block_data(const uint8_t *data, const size_t datalen, PrintAndLogEx(ERR, "internal error"); return PM3_ERFTRANS; } - + if (res.status_flags.status_flag1[0] != 0x00 || res.status_flags.status_flag2[0] != 0x00) { PrintAndLogEx(ERR, "error status"); @@ -2551,13 +2551,13 @@ static int parse_multiple_block_data(const uint8_t *data, const size_t datalen, } static int felica_auth_context_init( - mbedtls_des3_context* ctx, - const uint8_t* rc, + mbedtls_des3_context *ctx, + const uint8_t *rc, const size_t rclen, - const uint8_t* key, + const uint8_t *key, const size_t keylen, felica_auth_context_t *auth_ctx) { - + int ret = PM3_SUCCESS; uint8_t rev_rc[16], rev_key[16]; @@ -2578,7 +2578,7 @@ static int felica_auth_context_init( ret = PM3_ECRYPTO; goto cleanup; } - + if (mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, 16, iv, rev_rc, encrypted_sk) != 0) { ret = PM3_ECRYPTO; goto cleanup; @@ -2599,12 +2599,12 @@ cleanup: } static int felica_generate_mac( - mbedtls_des3_context* ctx, + mbedtls_des3_context *ctx, const felica_auth_context_t *auth_ctx, - const uint8_t* initialize_block, - const uint8_t* block_data, + const uint8_t *initialize_block, + const uint8_t *block_data, const size_t length, - uint8_t* mac) { + uint8_t *mac) { int ret = PM3_SUCCESS; @@ -2734,7 +2734,7 @@ static int CmdHFFelicaAuthenticationLite(const char *Cmd) { felica_status_response_t sres; if (send_wr_plain(flags, datalen, data, false, &sres) != PM3_SUCCESS) { - return PM3_ERFTRANS; + return PM3_ERFTRANS; } if (sres.status_flags.status_flag1[0] != 0x00 && sres.status_flags.status_flag2[0] != 0x00) { @@ -2811,7 +2811,7 @@ static int CmdHFFelicaAuthenticationLite(const char *Cmd) { if (memcmp(mac_blk, mac, FELICA_BLK_HALF) != 0) { PrintAndLogEx(ERR, "\nAuthenticate Failed"); - return PM3_ERFTRANS; + return PM3_ERFTRANS; } PrintAndLogEx(SUCCESS, "Authenticate Success"); diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index 1a071244e..248a4a667 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -242,6 +242,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf felica reader" }, { 0, "hf felica sniff" }, { 0, "hf felica wrbl" }, + { 0, "hf felica dump" }, { 0, "hf felica rqservice" }, { 0, "hf felica rqresponse" }, { 0, "hf felica scsvcode" }, @@ -252,6 +253,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf felica resetmode" }, { 0, "hf felica litesim" }, { 0, "hf felica litedump" }, + { 0, "hf felica liteauth" }, { 1, "hf fido help" }, { 1, "hf fido list" }, { 0, "hf fido info" }, diff --git a/doc/commands.json b/doc/commands.json index 13c1b6227..ea1ffa469 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -2659,6 +2659,19 @@ ], "usage": "hf felica auth2 [-hv] [-i ] [-c ] [-k ]" }, + "hf felica dump": { + "command": "hf felica dump", + "description": "Dump all existing Area Code and Service Code. Only works on services that do not require authentication yet.", + "notes": [ + "hf felica dump" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--no-auth read public services" + ], + "usage": "hf felica dump [-h] [--no-auth]" + }, "hf felica help": { "command": "hf felica help", "description": "----------- ----------------------- General ----------------------- help This help list List ISO 18092/FeliCa history ----------- ----------------------- Operations ----------------------- ----------- ----------------------- FeliCa Standard ----------------------- ----------- ----------------------- FeliCa Light ----------------------- --------------------------------------------------------------------------------------- hf felica list available offline: yes Alias of `trace list -t felica` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", @@ -2692,6 +2705,23 @@ ], "usage": "hf felica info [-h]" }, + "hf felica liteauth": { + "command": "hf felica liteauth", + "description": "Authenticate", + "notes": [ + "hf felica liteauth -i 11100910C11BC407", + "hf felica liteauth -k 46656c69436130313233343536616263", + "hf felica liteauth -c 701185c59f8d30afeab8e4b3a61f5cc4 -k 46656c69436130313233343536616263" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-k, --key set card key, 16 bytes", + "-c, set random challenge, 16 bytes", + "-i, set custom IDm" + ], + "usage": "hf felica liteauth [-h] [-k ] [-c ] [-i ]" + }, "hf felica litedump": { "command": "hf felica litedump", "description": "Dump ISO/18092 FeliCa Lite tag. It will timeout after 200sec", @@ -13453,8 +13483,8 @@ } }, "metadata": { - "commands_extracted": 773, + "commands_extracted": 775, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-07-28T13:42:15" + "extracted_on": "2025-08-04T17:50:35" } } diff --git a/doc/commands.md b/doc/commands.md index 0671c769d..fc80c4404 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -333,6 +333,7 @@ Check column "offline" for their availability. |`hf felica reader `|N |`Act like an ISO18092/FeliCa reader` |`hf felica sniff `|N |`Sniff ISO 18092/FeliCa traffic` |`hf felica wrbl `|N |`write block data to an authentication-not-required Service.` +|`hf felica dump `|N |`Wait for and try dumping FeliCa` |`hf felica rqservice `|N |`verify the existence of Area and Service, and to acquire Key Version.` |`hf felica rqresponse `|N |`verify the existence of a card and its Mode.` |`hf felica scsvcode `|N |`acquire Area Code and Service Code.` @@ -343,6 +344,7 @@ Check column "offline" for their availability. |`hf felica resetmode `|N |`reset Mode to Mode 0.` |`hf felica litesim `|N |`Emulating ISO/18092 FeliCa Lite tag` |`hf felica litedump `|N |`Wait for and try dumping FelicaLite` +|`hf felica liteauth `|N |`authenticate a card.` ### hf fido From 30689f7d3cc457df3230133c760871f2472ce82e Mon Sep 17 00:00:00 2001 From: q0jt <89930816+q0jt@users.noreply.github.com> Date: Wed, 6 Aug 2025 08:32:34 +0900 Subject: [PATCH 136/149] hf felica liteauth: add external authentication --- client/src/cmdhffelica.c | 371 +++++++++++++++++++++++++++++---------- 1 file changed, 274 insertions(+), 97 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index c84c8c519..dd0b5789e 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -33,9 +33,16 @@ #include "cliparser.h" // cliparser #include "util_posix.h" // msleep + #define FELICA_BLK_SIZE 16 #define FELICA_BLK_HALF (FELICA_BLK_SIZE/2) +#define FELICA_BLK_NUMBER_RC 0x80 +#define FELICA_BLK_NUMBER_ID 0x82 +#define FELICA_BLK_NUMBER_WCNT 0x90 +#define FELICA_BLK_NUMBER_MACA 0x91 +#define FELICA_BLK_NUMBER_STATE 0x92 + #define FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ (0b000001) #define FELICA_SERVICE_ATTRIBUTE_READ_ONLY (0b000010) #define FELICA_SERVICE_ATTRIBUTE_RANDOM_ACCESS (0b001000) @@ -2289,7 +2296,7 @@ static uint16_t PrintFliteBlock(uint16_t tracepos, uint8_t *trace, uint16_t trac } break; case 0x90: { - PrintAndLogEx(NORMAL, "Write count, RO: %02x %02x %02x ", trace[3], trace[4], trace[5]); + PrintAndLogEx(NORMAL, "Write counter, RO: %02x %02x %02x ", trace[3], trace[4], trace[5]); } break; case 0x91: { @@ -2545,7 +2552,32 @@ static int parse_multiple_block_data(const uint8_t *data, const size_t datalen, memcpy(out, data + res_size, num * FELICA_BLK_SIZE); - *outlen = num * FELICA_BLK_SIZE; + if (outlen) { + *outlen = num * FELICA_BLK_SIZE; + } + + return PM3_SUCCESS; +} + +static int send_rd_multiple_plain(uint8_t flags, uint16_t datalen, uint8_t *data, uint8_t *out) { + clear_and_send_command(flags, datalen, data, false); + PacketResponseNG res; + if (waitCmdFelica(false, &res, false) == false) { + PrintAndLogEx(ERR, "\nGot no response from card"); + return PM3_ERFTRANS; + } + + uint8_t block_data[FELICA_BLK_SIZE*4]; + memset(block_data, 0, sizeof(block_data)); + + uint8_t outlen = 0; + + int ret = parse_multiple_block_data(res.data.asBytes, sizeof(res.data.asBytes), block_data, &outlen); + if (ret) { + return PM3_ERFTRANS; + } + + memcpy(out, block_data, outlen); return PM3_SUCCESS; } @@ -2598,12 +2630,22 @@ cleanup: return ret; } +static void felica_auth_context_free(felica_auth_context_t *auth_ctx) { + if (!auth_ctx) { + return; + } + + mbedtls_platform_zeroize(auth_ctx->session_key, sizeof(auth_ctx->session_key)); + mbedtls_platform_zeroize(auth_ctx->random_challenge, sizeof(auth_ctx->random_challenge)); +} + static int felica_generate_mac( mbedtls_des3_context *ctx, const felica_auth_context_t *auth_ctx, const uint8_t *initialize_block, const uint8_t *block_data, const size_t length, + bool use_read_key, uint8_t *mac) { int ret = PM3_SUCCESS; @@ -2619,7 +2661,16 @@ static int felica_generate_mac( return PM3_EINVARG; } - SwapEndian64ex(auth_ctx->session_key, sizeof(auth_ctx->session_key), 8, rev_sk); + uint8_t sk[FELICA_BLK_SIZE]; + + if (use_read_key == false) { + memcpy(sk, auth_ctx->session_key + 8, 8); + memcpy(sk + 8, auth_ctx->session_key, 8); + } else { + memcpy(sk, auth_ctx->session_key, sizeof(auth_ctx->session_key)); + } + + SwapEndian64ex(sk, sizeof(sk), 8, rev_sk); memcpy(iv, auth_ctx->random_challenge, sizeof(iv)); @@ -2642,6 +2693,7 @@ static int felica_generate_mac( SwapEndian64ex(out, FELICA_BLK_HALF, 8, mac); cleanup: + mbedtls_platform_zeroize(sk, sizeof(sk)); mbedtls_platform_zeroize(rev_sk, sizeof(rev_sk)); mbedtls_platform_zeroize(iv, sizeof(iv)); mbedtls_platform_zeroize(out, sizeof(out)); @@ -2650,79 +2702,58 @@ cleanup: return ret; } -/** - * Command parser for liteauth. - * @param Cmd input data of the user. - * @return client result code. - */ -static int CmdHFFelicaAuthenticationLite(const char *Cmd) { - CLIParserContext *ctx; - CLIParserInit(&ctx, "hf felica liteauth", - "Authenticate", - "hf felica liteauth -i 11100910C11BC407\n" - "hf felica liteauth -k 46656c69436130313233343536616263\n" - "hf felica liteauth -c 701185c59f8d30afeab8e4b3a61f5cc4 -k 46656c69436130313233343536616263" - ); - void *argtable[] = { - arg_param_begin, - arg_str0("k", "key", "", "set card key, 16 bytes"), - arg_str0("c", "", "", "set random challenge, 16 bytes"), - arg_str0("i", "", "", "set custom IDm"), - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, true); +static int write_with_mac( + mbedtls_des3_context *ctx, + const felica_auth_context_t *auth_ctx, + const uint8_t *counter, + const uint8_t blk_number, + const uint8_t *block_data, + uint8_t *out) { + + uint8_t initialize_blk[FELICA_BLK_HALF]; + memset(initialize_blk, 0, sizeof(initialize_blk)); + + uint8_t wcnt[3]; + memcpy(wcnt, counter, 3); + + memcpy(initialize_blk, wcnt, sizeof(wcnt)); + initialize_blk[4] = blk_number; + initialize_blk[6] = 0x91; - uint8_t key[FELICA_BLK_SIZE]; - memset(key, 0, sizeof(key)); - int keylen = 0; - int ret = CLIParamHexToBuf(arg_get_str(ctx, 1), key, sizeof(key), &keylen); - if (ret) { - CLIParserFree(ctx); - return PM3_EINVARG; + uint8_t mac[FELICA_BLK_HALF]; + + int ret = felica_generate_mac(ctx, auth_ctx, initialize_blk, block_data, FELICA_BLK_SIZE, false, mac); + if (ret != PM3_SUCCESS) { + return ret; } - uint8_t rc[FELICA_BLK_SIZE]; - memset(rc, 0, sizeof(rc)); - int rclen = 0; - ret = CLIParamHexToBuf(arg_get_str(ctx, 2), rc, sizeof(rc), &rclen); - if (ret) { - CLIParserFree(ctx); - return PM3_EINVARG; - } + uint8_t payload[FELICA_BLK_SIZE*2]; + memset(payload, 0, sizeof(payload)); - uint8_t idm[8]; - memset(idm, 0, sizeof(idm)); - int ilen = 0; - ret = CLIParamHexToBuf(arg_get_str(ctx, 3), idm, sizeof(idm), &ilen); - if (ret) { - CLIParserFree(ctx); - return PM3_EINVARG; - } + memcpy(payload, block_data, FELICA_BLK_SIZE); + memcpy(payload + FELICA_BLK_SIZE, mac, sizeof(mac)); + memcpy(payload + FELICA_BLK_SIZE + sizeof(mac), wcnt, sizeof(wcnt)); - CLIParserFree(ctx); + memcpy(out, payload, sizeof(payload)); - if (!ilen) { - if (last_known_card.IDm[0] != 0 && last_known_card.IDm[1] != 0) { - memcpy(idm, last_known_card.IDm, sizeof(idm)); - } else { - PrintAndLogEx(WARNING, "No last known card! Use `" _YELLOW_("hf felica reader") "` first or set a custom IDm"); - return PM3_EINVARG; - } - } + return PM3_SUCCESS; +} - PrintAndLogEx(INFO, "Card Key: %s", sprint_hex(key, sizeof(key))); - PrintAndLogEx(INFO, "Random Challenge(RC): %s", sprint_hex(rc, sizeof(rc))); - - PrintAndLogEx(SUCCESS, "FeliCa lite - auth started"); +static int felica_internal_authentication( + const uint8_t *idm, + const uint8_t* rc, + const size_t rclen, + mbedtls_des3_context *ctx, + const felica_auth_context_t *auth_ctx) { uint8_t data[PM3_CMD_DATA_SIZE]; memset(data, 0, sizeof(data)); - uint8_t blk_numbers[1] = {0x80}; // RC + uint8_t blk_numbers[1] = {FELICA_BLK_NUMBER_RC}; uint16_t datalen = 0; - ret = write_without_encryption(idm, (uint8_t)sizeof(blk_numbers), blk_numbers, rc, sizeof(rc), data, &datalen); + int ret = write_without_encryption(idm, (uint8_t)sizeof(blk_numbers), blk_numbers, rc, rclen, data, &datalen); if (ret) { return PM3_ERFTRANS; } @@ -2732,28 +2763,16 @@ static int CmdHFFelicaAuthenticationLite(const char *Cmd) { uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW | FELICA_NO_DISCONNECT); - felica_status_response_t sres; - if (send_wr_plain(flags, datalen, data, false, &sres) != PM3_SUCCESS) { - return PM3_ERFTRANS; + felica_status_response_t res; + if (send_wr_plain(flags, datalen, data, false, &res) != PM3_SUCCESS) { + return PM3_ERFTRANS; } - if (sres.status_flags.status_flag1[0] != 0x00 && sres.status_flags.status_flag2[0] != 0x00) { + if (res.status_flags.status_flag1[0] != 0x00 && res.status_flags.status_flag2[0] != 0x00) { PrintAndLogEx(ERR, "\nError RC Write"); return PM3_ERFTRANS; } - mbedtls_des3_context des3_ctx; - mbedtls_des3_init(&des3_ctx); - - felica_auth_context_t auth_ctx; - - ret = felica_auth_context_init(&des3_ctx, rc, sizeof(rc), key, sizeof(key), &auth_ctx); - if (ret) { - return PM3_ERFTRANS; - } - - PrintAndLogEx(INFO, "Session Key(SK): %s", sprint_hex(auth_ctx.session_key, sizeof(auth_ctx.session_key))); - memset(data, 0, sizeof(data)); uint8_t blk_numbers2[2] = {0x82, 0x91}; @@ -2766,20 +2785,10 @@ static int CmdHFFelicaAuthenticationLite(const char *Cmd) { AddCrc(data, datalen); datalen += 2; - flags &= ~FELICA_NO_DISCONNECT; - - clear_and_send_command(flags, datalen, data, false); - PacketResponseNG pres; - if (waitCmdFelica(false, &pres, false) == false) { - PrintAndLogEx(ERR, "\nGot no response from card"); - return PM3_ERFTRANS; - } - - uint8_t pd[64]; + uint8_t pd[FELICA_BLK_SIZE*sizeof(blk_numbers2)]; memset(pd, 0, sizeof(pd)); - uint8_t pd_size = 0; - ret = parse_multiple_block_data(pres.data.asBytes, sizeof(pres.data.asBytes), pd, &pd_size); + ret = send_rd_multiple_plain(flags, datalen, data, pd); if (ret) { return PM3_ERFTRANS; } @@ -2793,32 +2802,200 @@ static int CmdHFFelicaAuthenticationLite(const char *Cmd) { uint8_t initialize_blk[8]; memset(initialize_blk, 0xFF, sizeof(initialize_blk)); - initialize_blk[0] = 0x82; // ID + initialize_blk[0] = FELICA_BLK_NUMBER_ID; initialize_blk[1] = 0x00; - initialize_blk[2] = 0x91; // MAC_A + initialize_blk[2] = FELICA_BLK_NUMBER_MACA; initialize_blk[3] = 0x00; uint8_t mac[FELICA_BLK_HALF]; - ret = felica_generate_mac(&des3_ctx, &auth_ctx, initialize_blk, id_blk, sizeof(id_blk), mac); + ret = felica_generate_mac(ctx, auth_ctx, initialize_blk, id_blk, sizeof(id_blk), true, mac); if (ret) { return PM3_ERFTRANS; } PrintAndLogEx(SUCCESS, "MAC_A: %s", sprint_hex(mac, sizeof(mac))); - mbedtls_des3_free(&des3_ctx); - if (memcmp(mac_blk, mac, FELICA_BLK_HALF) != 0) { - PrintAndLogEx(ERR, "\nAuthenticate Failed"); + PrintAndLogEx(ERR, "\nInternal Authenticate: " _RED_("Failed")); + return PM3_ERFTRANS; + } + + PrintAndLogEx(SUCCESS, "Internal Authenticate: " _GREEN_("OK")); + + return PM3_SUCCESS; +} + +static int felica_external_authentication( + const uint8_t *idm, + mbedtls_des3_context *ctx, + const felica_auth_context_t *auth_ctx, + bool keep) { + + uint8_t data[PM3_CMD_DATA_SIZE_MIX]; + memset(data, 0, sizeof(data)); + + uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW | FELICA_NO_DISCONNECT); + + uint16_t datalen = 0; + + uint8_t blk_numbers[1] = {FELICA_BLK_NUMBER_WCNT}; + + int ret = read_without_encryption(idm, (uint8_t)sizeof(blk_numbers), blk_numbers, data, &datalen); + if (ret) { return PM3_ERFTRANS; } - PrintAndLogEx(SUCCESS, "Authenticate Success"); + AddCrc(data, datalen); + datalen += 2; + + uint8_t wcnt_blk[FELICA_BLK_SIZE]; + ret = send_rd_multiple_plain(flags, datalen, data, wcnt_blk); + if (ret) { + return PM3_ERFTRANS; + } + + uint8_t ext_auth[FELICA_BLK_SIZE]; + memset(ext_auth, 0, sizeof(ext_auth)); + + ext_auth[0] = 1; // After Authenticate + + uint8_t mac_w[FELICA_BLK_SIZE*2]; + + ret = write_with_mac(ctx, auth_ctx, wcnt_blk, FELICA_BLK_NUMBER_STATE, ext_auth, mac_w); + if (ret) { + return PM3_ERFTRANS; + } + + uint8_t blk_numbers2[2] = {FELICA_BLK_NUMBER_STATE, FELICA_BLK_NUMBER_MACA}; + + ret = write_without_encryption(idm, (uint8_t)sizeof(blk_numbers2), blk_numbers2, mac_w, sizeof(mac_w), data, &datalen); + if (ret) { + return PM3_ERFTRANS; + } + + AddCrc(data, datalen); + datalen += 2; + + if (keep == false) { + flags &= ~FELICA_NO_DISCONNECT; + } + + felica_status_response_t res; + if (send_wr_plain(flags, datalen, data, false, &res) != PM3_SUCCESS) { + return PM3_ERFTRANS; + } + + if (res.status_flags.status_flag1[0] != 0x00 && res.status_flags.status_flag2[0] != 0x00) { + PrintAndLogEx(ERR, "\nExternal Authenticate: " _RED_("Failed")); + return PM3_ERFTRANS; + } + + PrintAndLogEx(SUCCESS, "External Authenticate: " _GREEN_("OK")); return PM3_SUCCESS; } +/** + * Command parser for liteauth. + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaAuthenticationLite(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica liteauth", + "Authenticate", + "hf felica liteauth -i 11100910C11BC407\n" + "hf felica liteauth -k 46656c69436130313233343536616263\n" + "hf felica liteauth -k 46656c69436130313233343536616263 -@\n" + "hf felica liteauth -c 701185c59f8d30afeab8e4b3a61f5cc4 -k 46656c69436130313233343536616263" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("k", "key", "", "set card key, 16 bytes"), + arg_str0("c", "", "", "set random challenge, 16 bytes"), + arg_str0("i", "", "", "set custom IDm"), + arg_lit0("@", "", "keep signal field ON after receive"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t key[FELICA_BLK_SIZE]; + memset(key, 0, sizeof(key)); + int keylen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), key, sizeof(key), &keylen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t rc[FELICA_BLK_SIZE]; + memset(rc, 0, sizeof(rc)); + int rclen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 2), rc, sizeof(rc), &rclen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t idm[8]; + memset(idm, 0, sizeof(idm)); + int ilen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 3), idm, sizeof(idm), &ilen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool keep_field_on = arg_get_lit(ctx, 4); + + CLIParserFree(ctx); + + if (!ilen) { + if (last_known_card.IDm[0] != 0 && last_known_card.IDm[1] != 0) { + memcpy(idm, last_known_card.IDm, sizeof(idm)); + } else { + PrintAndLogEx(WARNING, "No last known card! Use `" _YELLOW_("hf felica reader") "` first or set a custom IDm"); + return PM3_EINVARG; + } + } + + int ret = PM3_SUCCESS; + + PrintAndLogEx(INFO, "Card Key: %s", sprint_hex(key, sizeof(key))); + PrintAndLogEx(INFO, "Random Challenge(RC): %s", sprint_hex(rc, sizeof(rc))); + + PrintAndLogEx(SUCCESS, "FeliCa lite - auth started"); + + mbedtls_des3_context des3_ctx; + mbedtls_des3_init(&des3_ctx); + + felica_auth_context_t auth_ctx; + + ret = felica_auth_context_init(&des3_ctx, rc, sizeof(rc), key, sizeof(key), &auth_ctx); + if (ret) { + goto cleanup; + } + + PrintAndLogEx(INFO, "Session Key(SK): %s", sprint_hex(auth_ctx.session_key, sizeof(auth_ctx.session_key))); + + ret = felica_internal_authentication(idm, rc, sizeof(rc), &des3_ctx, &auth_ctx); + if (ret) { + goto cleanup; + } + + ret = felica_external_authentication(idm, &des3_ctx, &auth_ctx, keep_field_on); + if (ret) { + goto cleanup; + } + +cleanup: + mbedtls_des3_free(&des3_ctx); + felica_auth_context_free(&auth_ctx); + + return ret; +} + static int CmdHFFelicaCmdRaw(const char *Cmd) { CLIParserContext *ctx; From 50972c02321a05b908216d68524888403ba92105 Mon Sep 17 00:00:00 2001 From: q0jt <89930816+q0jt@users.noreply.github.com> Date: Wed, 6 Aug 2025 21:39:55 +0900 Subject: [PATCH 137/149] fix --- client/src/cmdhffelica.c | 633 +++++++++++++++++++++------------------ 1 file changed, 349 insertions(+), 284 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index dd0b5789e..300887c42 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -2157,262 +2157,6 @@ static int CmdHFFelicaSimLite(const char *Cmd) { return PM3_SUCCESS; } -static void printSep(void) { - PrintAndLogEx(INFO, "------------------------------------------------------------------------------------"); -} - -static uint16_t PrintFliteBlock(uint16_t tracepos, uint8_t *trace, uint16_t tracelen) { - if (tracepos + 19 >= tracelen) - return tracelen; - - trace += tracepos; - uint8_t blocknum = trace[0]; - uint8_t status1 = trace[1]; - uint8_t status2 = trace[2]; - - bool error = (status1 != 0x00 && (status2 == 0xB1 || status2 == 0xB2)); - - char line[110] = {0}; - for (int j = 0; j < 16; j++) { - if (error) { - snprintf(line + (j * 4), sizeof(line) - 1 - (j * 4), "?? "); - } else { - snprintf(line + (j * 4), sizeof(line) - 1 - (j * 4), "%02x ", trace[j + 3]); - } - } - - PrintAndLogEx(NORMAL, "block number %02x, status: %02x %02x", blocknum, status1, status2); - switch (blocknum) { - case 0x00: - PrintAndLogEx(NORMAL, "S_PAD0: %s", line); - break; - case 0x01: - PrintAndLogEx(NORMAL, "S_PAD1: %s", line); - break; - case 0x02: - PrintAndLogEx(NORMAL, "S_PAD2: %s", line); - break; - case 0x03: - PrintAndLogEx(NORMAL, "S_PAD3: %s", line); - break; - case 0x04: - PrintAndLogEx(NORMAL, "S_PAD4: %s", line); - break; - case 0x05: - PrintAndLogEx(NORMAL, "S_PAD5: %s", line); - break; - case 0x06: - PrintAndLogEx(NORMAL, "S_PAD6: %s", line); - break; - case 0x07: - PrintAndLogEx(NORMAL, "S_PAD7: %s", line); - break; - case 0x08: - PrintAndLogEx(NORMAL, "S_PAD8: %s", line); - break; - case 0x09: - PrintAndLogEx(NORMAL, "S_PAD9: %s", line); - break; - case 0x0a: - PrintAndLogEx(NORMAL, "S_PAD10: %s", line); - break; - case 0x0b: - PrintAndLogEx(NORMAL, "S_PAD11: %s", line); - break; - case 0x0c: - PrintAndLogEx(NORMAL, "S_PAD12: %s", line); - break; - case 0x0d: - PrintAndLogEx(NORMAL, "S_PAD13: %s", line); - break; - case 0x0E: { - uint32_t regA = trace[3] | trace[4] << 8 | trace[5] << 16 | trace[6] << 24; - uint32_t regB = trace[7] | trace[8] << 8 | trace[9] << 16 | trace[10] << 24; - line[0] = 0; - for (int j = 0; j < 8; j++) - snprintf(line + (j * 2), sizeof(line) - 1 - (j * 2), "%02x", trace[j + 11]); - - if (error) { - PrintAndLogEx(NORMAL, "REG: regA: ???????? regB: ???????? regC: ???????????????? "); - } else { - PrintAndLogEx(NORMAL, "REG: regA: %d regB: %d regC: %s ", regA, regB, line); - } - } - break; - case 0x80: - PrintAndLogEx(NORMAL, "Random Challenge, WO: %s ", line); - break; - case 0x81: - PrintAndLogEx(NORMAL, "MAC, only set on dual read: %s ", line); - break; - case 0x82: { - char idd[20]; - char idm[20]; - for (int j = 0; j < 8; j++) - snprintf(idd + (j * 2), sizeof(idd) - 1 - (j * 2), "%02x", trace[j + 3]); - - for (int j = 0; j < 6; j++) - snprintf(idm + (j * 2), sizeof(idm) - 1 - (j * 2), "%02x", trace[j + 13]); - - PrintAndLogEx(NORMAL, "ID Block, IDd: 0x%s DFC: 0x%02x%02x Arb: %s ", idd, trace[11], trace [12], idm); - } - break; - case 0x83: { - char idm[20]; - char pmm[20]; - for (int j = 0; j < 8; j++) - snprintf(idm + (j * 2), sizeof(idm) - 1 - (j * 2), "%02x", trace[j + 3]); - - for (int j = 0; j < 8; j++) - snprintf(pmm + (j * 2), sizeof(pmm) - 1 - (j * 2), "%02x", trace[j + 11]); - - PrintAndLogEx(NORMAL, "DeviceId: IDm: 0x%s PMm: 0x%s ", idm, pmm); - } - break; - case 0x84: - PrintAndLogEx(NORMAL, "SER_C: 0x%02x%02x ", trace[3], trace[4]); - break; - case 0x85: - PrintAndLogEx(NORMAL, "SYS_Cl 0x%02x%02x ", trace[3], trace[4]); - break; - case 0x86: - PrintAndLogEx(NORMAL, "CKV (key version): 0x%02x%02x ", trace[3], trace[4]); - break; - case 0x87: - PrintAndLogEx(NORMAL, "CK (card key), WO: %s ", line); - break; - case 0x88: { - PrintAndLogEx(NORMAL, "Memory Configuration (MC):"); - PrintAndLogEx(NORMAL, "MAC needed to write state: %s", trace[3 + 12] ? "on" : "off"); - //order might be off here... - PrintAndLogEx(NORMAL, "Write with MAC for S_PAD : %s ", sprint_bin(trace + 3 + 10, 2)); - PrintAndLogEx(NORMAL, "Write with AUTH for S_PAD : %s ", sprint_bin(trace + 3 + 8, 2)); - PrintAndLogEx(NORMAL, "Read after AUTH for S_PAD : %s ", sprint_bin(trace + 3 + 6, 2)); - PrintAndLogEx(NORMAL, "MAC needed to write CK and CKV: %s", trace[3 + 5] ? "on" : "off"); - PrintAndLogEx(NORMAL, "RF parameter: %02x", (trace[3 + 4] & 0x7)); - PrintAndLogEx(NORMAL, "Compatible with NDEF: %s", trace[3 + 3] ? "yes" : "no"); - PrintAndLogEx(NORMAL, "Memory config writable : %s", (trace[3 + 2] == 0xff) ? "yes" : "no"); - PrintAndLogEx(NORMAL, "RW access for S_PAD : %s ", sprint_bin(trace + 3, 2)); - } - break; - case 0x90: { - PrintAndLogEx(NORMAL, "Write counter, RO: %02x %02x %02x ", trace[3], trace[4], trace[5]); - } - break; - case 0x91: { - PrintAndLogEx(NORMAL, "MAC_A, RW (auth): %s ", line); - } - break; - case 0x92: - PrintAndLogEx(NORMAL, "State:"); - PrintAndLogEx(NORMAL, "Polling disabled: %s", trace[3 + 8] ? "yes" : "no"); - PrintAndLogEx(NORMAL, "Authenticated: %s", trace[3] ? "yes" : "no"); - break; - case 0xa0: - PrintAndLogEx(NORMAL, "CRC of all blocks match : %s", (trace[3 + 2] == 0xff) ? "no" : "yes"); - break; - default: - PrintAndLogEx(WARNING, "INVALID %d: %s", blocknum, line); - break; - } - return tracepos + 19; -} - -static int CmdHFFelicaDumpLite(const char *Cmd) { - - /* - iceman 2021, - Why does this command say it dumps a FeliCa lite card - and then tries to print a trace?!? - Is this a trace list or a FeliCa dump cmd? - */ - - - CLIParserContext *ctx; - CLIParserInit(&ctx, "hf felica litedump", - "Dump ISO/18092 FeliCa Lite tag. It will timeout after 200sec", - "hf felica litedump" - ); - void *argtable[] = { - arg_param_begin, - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, true); - CLIParserFree(ctx); - - PrintAndLogEx(SUCCESS, "FeliCa lite - dump started"); - - clearCommandBuffer(); - SendCommandNG(CMD_HF_FELICALITE_DUMP, NULL, 0); - PacketResponseNG resp; - - PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("") " to abort dumping"); - - uint8_t timeout = 0; - while (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { - - if (kbd_enter_pressed()) { - SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "\naborted via keyboard!"); - return PM3_EOPABORTED; - } - - timeout++; - PrintAndLogEx(INPLACE, "% 3i", timeout); - - fflush(stdout); - if (kbd_enter_pressed()) { - PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - DropField(); - return PM3_EOPABORTED; - } - if (timeout > 10) { - PrintAndLogEx(WARNING, "\ntimeout while waiting for reply"); - DropField(); - return PM3_ETIMEOUT; - } - } - - PrintAndLogEx(NORMAL, ""); - - if (resp.oldarg[0] == 0) { - PrintAndLogEx(WARNING, "Button pressed, aborted"); - return PM3_EOPABORTED; - } - - uint16_t tracelen = resp.oldarg[1]; - if (tracelen == 0) { - PrintAndLogEx(WARNING, "No trace data! Maybe not a FeliCa Lite card?"); - return PM3_ESOFT; - } - - uint8_t *trace = calloc(tracelen, sizeof(uint8_t)); - if (trace == NULL) { - PrintAndLogEx(WARNING, "Failed to allocate memory"); - return PM3_EMALLOC; - } - - if (GetFromDevice(BIG_BUF, trace, tracelen, 0, NULL, 0, NULL, 2500, false) == false) { - PrintAndLogEx(WARNING, "command execution time out"); - free(trace); - return PM3_ETIMEOUT; - } - - - PrintAndLogEx(SUCCESS, "Recorded Activity (trace len = %"PRIu32" bytes)", tracelen); - print_hex_break(trace, tracelen, 32); - printSep(); - - uint16_t tracepos = 0; - while (tracepos < tracelen) - tracepos = PrintFliteBlock(tracepos, trace, tracelen); - - printSep(); - - free(trace); - return PM3_SUCCESS; -} - static int felica_make_block_list(uint16_t *out, const uint8_t *blk_numbers, const size_t length) { if (length > 4) { PrintAndLogEx(ERR, "felica_make_block_list: exceeds max size"); @@ -2741,10 +2485,11 @@ static int write_with_mac( static int felica_internal_authentication( const uint8_t *idm, - const uint8_t* rc, + const uint8_t *rc, const size_t rclen, mbedtls_des3_context *ctx, - const felica_auth_context_t *auth_ctx) { + const felica_auth_context_t *auth_ctx, + bool verbose) { uint8_t data[PM3_CMD_DATA_SIZE]; memset(data, 0, sizeof(data)); @@ -2775,7 +2520,7 @@ static int felica_internal_authentication( memset(data, 0, sizeof(data)); - uint8_t blk_numbers2[2] = {0x82, 0x91}; + uint8_t blk_numbers2[2] = {FELICA_BLK_NUMBER_ID, FELICA_BLK_NUMBER_MACA}; ret = read_without_encryption(idm, (uint8_t)sizeof(blk_numbers2), blk_numbers2, data, &datalen); if (ret) { @@ -2814,7 +2559,9 @@ static int felica_internal_authentication( return PM3_ERFTRANS; } - PrintAndLogEx(SUCCESS, "MAC_A: %s", sprint_hex(mac, sizeof(mac))); + if (verbose) { + PrintAndLogEx(SUCCESS, "MAC_A: %s", sprint_hex(mac, sizeof(mac))); + } if (memcmp(mac_blk, mac, FELICA_BLK_HALF) != 0) { PrintAndLogEx(ERR, "\nInternal Authenticate: " _RED_("Failed")); @@ -2896,6 +2643,48 @@ static int felica_external_authentication( return PM3_SUCCESS; } +static int felica_mutual_authentication( + const uint8_t *idm, + const uint8_t *rc, + const size_t rclen, + const uint8_t *key, + const size_t keylen, + bool keep, + bool verbose) { + + int ret = PM3_SUCCESS; + + mbedtls_des3_context des3_ctx; + mbedtls_des3_init(&des3_ctx); + + felica_auth_context_t auth_ctx; + + ret = felica_auth_context_init(&des3_ctx, rc, rclen, key, keylen, &auth_ctx); + if (ret) { + goto cleanup; + } + + if (verbose) { + PrintAndLogEx(INFO, "Session Key(SK): %s", sprint_hex(auth_ctx.session_key, sizeof(auth_ctx.session_key))); + } + + ret = felica_internal_authentication(idm, rc, rclen, &des3_ctx, &auth_ctx, verbose); + if (ret) { + goto cleanup; + } + + ret = felica_external_authentication(idm, &des3_ctx, &auth_ctx, keep); + if (ret) { + goto cleanup; + } + +cleanup: + mbedtls_des3_free(&des3_ctx); + felica_auth_context_free(&auth_ctx); + + return ret; +} + /** * Command parser for liteauth. * @param Cmd input data of the user. @@ -2906,16 +2695,16 @@ static int CmdHFFelicaAuthenticationLite(const char *Cmd) { CLIParserInit(&ctx, "hf felica liteauth", "Authenticate", "hf felica liteauth -i 11100910C11BC407\n" - "hf felica liteauth -k 46656c69436130313233343536616263\n" - "hf felica liteauth -k 46656c69436130313233343536616263 -@\n" - "hf felica liteauth -c 701185c59f8d30afeab8e4b3a61f5cc4 -k 46656c69436130313233343536616263" + "hf felica liteauth --key 46656c69436130313233343536616263\n" + "hf felica liteauth --key 46656c69436130313233343536616263 -k\n" + "hf felica liteauth -c 701185c59f8d30afeab8e4b3a61f5cc4 --key 46656c69436130313233343536616263" ); void *argtable[] = { arg_param_begin, - arg_str0("k", "key", "", "set card key, 16 bytes"), + arg_str0(NULL, "key", "", "set card key, 16 bytes"), arg_str0("c", "", "", "set random challenge, 16 bytes"), arg_str0("i", "", "", "set custom IDm"), - arg_lit0("@", "", "keep signal field ON after receive"), + arg_lit0("k", "", "keep signal field ON after receive"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -2967,33 +2756,309 @@ static int CmdHFFelicaAuthenticationLite(const char *Cmd) { PrintAndLogEx(SUCCESS, "FeliCa lite - auth started"); - mbedtls_des3_context des3_ctx; - mbedtls_des3_init(&des3_ctx); - - felica_auth_context_t auth_ctx; - - ret = felica_auth_context_init(&des3_ctx, rc, sizeof(rc), key, sizeof(key), &auth_ctx); + ret = felica_mutual_authentication(idm, rc, sizeof(rc), key, sizeof(key), keep_field_on, true); if (ret) { - goto cleanup; + return PM3_EINVARG; } - PrintAndLogEx(INFO, "Session Key(SK): %s", sprint_hex(auth_ctx.session_key, sizeof(auth_ctx.session_key))); + return PM3_SUCCESS; +} - ret = felica_internal_authentication(idm, rc, sizeof(rc), &des3_ctx, &auth_ctx); - if (ret) { - goto cleanup; +static void printSep(void) { + PrintAndLogEx(INFO, "------------------------------------------------------------------------------------"); +} + +static uint16_t PrintFliteBlock(uint16_t tracepos, uint8_t *trace, uint16_t tracelen) { + if (tracepos + 19 >= tracelen) + return tracelen; + + trace += tracepos; + uint8_t blocknum = trace[0]; + uint8_t status1 = trace[1]; + uint8_t status2 = trace[2]; + + bool error = (status1 != 0x00 && (status2 == 0xB1 || status2 == 0xB2)); + + char line[110] = {0}; + for (int j = 0; j < 16; j++) { + if (error) { + snprintf(line + (j * 4), sizeof(line) - 1 - (j * 4), "?? "); + } else { + snprintf(line + (j * 4), sizeof(line) - 1 - (j * 4), "%02x ", trace[j + 3]); + } } - ret = felica_external_authentication(idm, &des3_ctx, &auth_ctx, keep_field_on); - if (ret) { - goto cleanup; + PrintAndLogEx(NORMAL, "block number %02x, status: %02x %02x", blocknum, status1, status2); + switch (blocknum) { + case 0x00: + PrintAndLogEx(NORMAL, "S_PAD0: %s", line); + break; + case 0x01: + PrintAndLogEx(NORMAL, "S_PAD1: %s", line); + break; + case 0x02: + PrintAndLogEx(NORMAL, "S_PAD2: %s", line); + break; + case 0x03: + PrintAndLogEx(NORMAL, "S_PAD3: %s", line); + break; + case 0x04: + PrintAndLogEx(NORMAL, "S_PAD4: %s", line); + break; + case 0x05: + PrintAndLogEx(NORMAL, "S_PAD5: %s", line); + break; + case 0x06: + PrintAndLogEx(NORMAL, "S_PAD6: %s", line); + break; + case 0x07: + PrintAndLogEx(NORMAL, "S_PAD7: %s", line); + break; + case 0x08: + PrintAndLogEx(NORMAL, "S_PAD8: %s", line); + break; + case 0x09: + PrintAndLogEx(NORMAL, "S_PAD9: %s", line); + break; + case 0x0a: + PrintAndLogEx(NORMAL, "S_PAD10: %s", line); + break; + case 0x0b: + PrintAndLogEx(NORMAL, "S_PAD11: %s", line); + break; + case 0x0c: + PrintAndLogEx(NORMAL, "S_PAD12: %s", line); + break; + case 0x0d: + PrintAndLogEx(NORMAL, "S_PAD13: %s", line); + break; + case 0x0E: { + uint32_t regA = trace[3] | trace[4] << 8 | trace[5] << 16 | trace[6] << 24; + uint32_t regB = trace[7] | trace[8] << 8 | trace[9] << 16 | trace[10] << 24; + line[0] = 0; + for (int j = 0; j < 8; j++) + snprintf(line + (j * 2), sizeof(line) - 1 - (j * 2), "%02x", trace[j + 11]); + + if (error) { + PrintAndLogEx(NORMAL, "REG: regA: ???????? regB: ???????? regC: ???????????????? "); + } else { + PrintAndLogEx(NORMAL, "REG: regA: %d regB: %d regC: %s ", regA, regB, line); + } + } + break; + case 0x80: + PrintAndLogEx(NORMAL, "Random Challenge, WO: %s ", line); + break; + case 0x81: + PrintAndLogEx(NORMAL, "MAC, only set on dual read: %s ", line); + break; + case 0x82: { + char idd[20]; + char idm[20]; + for (int j = 0; j < 8; j++) + snprintf(idd + (j * 2), sizeof(idd) - 1 - (j * 2), "%02x", trace[j + 3]); + + for (int j = 0; j < 6; j++) + snprintf(idm + (j * 2), sizeof(idm) - 1 - (j * 2), "%02x", trace[j + 13]); + + PrintAndLogEx(NORMAL, "ID Block, IDd: 0x%s DFC: 0x%02x%02x Arb: %s ", idd, trace[11], trace [12], idm); + } + break; + case 0x83: { + char idm[20]; + char pmm[20]; + for (int j = 0; j < 8; j++) + snprintf(idm + (j * 2), sizeof(idm) - 1 - (j * 2), "%02x", trace[j + 3]); + + for (int j = 0; j < 8; j++) + snprintf(pmm + (j * 2), sizeof(pmm) - 1 - (j * 2), "%02x", trace[j + 11]); + + PrintAndLogEx(NORMAL, "DeviceId: IDm: 0x%s PMm: 0x%s ", idm, pmm); + } + break; + case 0x84: + PrintAndLogEx(NORMAL, "SER_C: 0x%02x%02x ", trace[3], trace[4]); + break; + case 0x85: + PrintAndLogEx(NORMAL, "SYS_Cl 0x%02x%02x ", trace[3], trace[4]); + break; + case 0x86: + PrintAndLogEx(NORMAL, "CKV (key version): 0x%02x%02x ", trace[3], trace[4]); + break; + case 0x87: + PrintAndLogEx(NORMAL, "CK (card key), WO: %s ", line); + break; + case 0x88: { + PrintAndLogEx(NORMAL, "Memory Configuration (MC):"); + PrintAndLogEx(NORMAL, "MAC needed to write state: %s", trace[3 + 12] ? "on" : "off"); + //order might be off here... + PrintAndLogEx(NORMAL, "Write with MAC for S_PAD : %s ", sprint_bin(trace + 3 + 10, 2)); + PrintAndLogEx(NORMAL, "Write with AUTH for S_PAD : %s ", sprint_bin(trace + 3 + 8, 2)); + PrintAndLogEx(NORMAL, "Read after AUTH for S_PAD : %s ", sprint_bin(trace + 3 + 6, 2)); + PrintAndLogEx(NORMAL, "MAC needed to write CK and CKV: %s", trace[3 + 5] ? "on" : "off"); + PrintAndLogEx(NORMAL, "RF parameter: %02x", (trace[3 + 4] & 0x7)); + PrintAndLogEx(NORMAL, "Compatible with NDEF: %s", trace[3 + 3] ? "yes" : "no"); + PrintAndLogEx(NORMAL, "Memory config writable : %s", (trace[3 + 2] == 0xff) ? "yes" : "no"); + PrintAndLogEx(NORMAL, "RW access for S_PAD : %s ", sprint_bin(trace + 3, 2)); + } + break; + case 0x90: { + PrintAndLogEx(NORMAL, "Write counter, RO: %02x %02x %02x ", trace[3], trace[4], trace[5]); + } + break; + case 0x91: { + PrintAndLogEx(NORMAL, "MAC_A, RW (auth): %s ", line); + } + break; + case 0x92: + PrintAndLogEx(NORMAL, "State:"); + PrintAndLogEx(NORMAL, "Polling disabled: %s", trace[3 + 8] ? "yes" : "no"); + PrintAndLogEx(NORMAL, "Authenticated: %s", trace[3] ? "yes" : "no"); + break; + case 0xa0: + PrintAndLogEx(NORMAL, "CRC of all blocks match : %s", (trace[3 + 2] == 0xff) ? "no" : "yes"); + break; + default: + PrintAndLogEx(WARNING, "INVALID %d: %s", blocknum, line); + break; + } + return tracepos + 19; +} + +static int CmdHFFelicaDumpLite(const char *Cmd) { + + /* + iceman 2021, + Why does this command say it dumps a FeliCa lite card + and then tries to print a trace?!? + Is this a trace list or a FeliCa dump cmd? + */ + + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica litedump", + "Dump ISO/18092 FeliCa Lite tag. It will timeout after 200sec", + "hf felica litedump" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("i", "", "", "set custom IDm"), + arg_str0(NULL, "key", "", "set card key, 16 bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t idm[8]; + memset(idm, 0, sizeof(idm)); + int ilen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), idm, sizeof(idm), &ilen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; } -cleanup: - mbedtls_des3_free(&des3_ctx); - felica_auth_context_free(&auth_ctx); + uint8_t key[FELICA_BLK_SIZE]; + memset(key, 0, sizeof(key)); + int keylen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 2), key, sizeof(key), &keylen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } - return ret; + CLIParserFree(ctx); + + if (keylen != 0) { + if (!ilen) { + if (last_known_card.IDm[0] != 0 && last_known_card.IDm[1] != 0) { + memcpy(idm, last_known_card.IDm, sizeof(idm)); + } else { + PrintAndLogEx(WARNING, "No last known card! Use `" _YELLOW_("hf felica reader") "` first or set a custom IDm"); + return PM3_EINVARG; + } + } + + uint8_t rc[FELICA_BLK_SIZE] = {0}; + + int ret = felica_mutual_authentication(idm, rc, sizeof(rc), key, sizeof(key), true, false); + if (ret) { + PrintAndLogEx(WARNING, "Authenticate Failed"); + } + } + + PrintAndLogEx(NORMAL, ""); + + PrintAndLogEx(SUCCESS, "FeliCa lite - dump started"); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_FELICALITE_DUMP, NULL, 0); + PacketResponseNG resp; + + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("") " to abort dumping"); + + uint8_t timeout = 0; + while (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { + + if (kbd_enter_pressed()) { + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); + return PM3_EOPABORTED; + } + + timeout++; + PrintAndLogEx(INPLACE, "% 3i", timeout); + + fflush(stdout); + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); + DropField(); + return PM3_EOPABORTED; + } + if (timeout > 10) { + PrintAndLogEx(WARNING, "\ntimeout while waiting for reply"); + DropField(); + return PM3_ETIMEOUT; + } + } + + PrintAndLogEx(NORMAL, ""); + + if (resp.oldarg[0] == 0) { + PrintAndLogEx(WARNING, "Button pressed, aborted"); + return PM3_EOPABORTED; + } + + uint16_t tracelen = resp.oldarg[1]; + if (tracelen == 0) { + PrintAndLogEx(WARNING, "No trace data! Maybe not a FeliCa Lite card?"); + return PM3_ESOFT; + } + + uint8_t *trace = calloc(tracelen, sizeof(uint8_t)); + if (trace == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } + + if (GetFromDevice(BIG_BUF, trace, tracelen, 0, NULL, 0, NULL, 2500, false) == false) { + PrintAndLogEx(WARNING, "command execution time out"); + free(trace); + return PM3_ETIMEOUT; + } + + + PrintAndLogEx(SUCCESS, "Recorded Activity (trace len = %"PRIu32" bytes)", tracelen); + print_hex_break(trace, tracelen, 32); + printSep(); + + uint16_t tracepos = 0; + while (tracepos < tracelen) + tracepos = PrintFliteBlock(tracepos, trace, tracelen); + + printSep(); + + free(trace); + return PM3_SUCCESS; } static int CmdHFFelicaCmdRaw(const char *Cmd) { @@ -3113,8 +3178,8 @@ static command_t CommandTable[] = { //{"uprandomid", CmdHFFelicaNotImplementedYet, IfPm3Felica, "update Random ID (IDr)."}, {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("FeliCa Light") " -----------------------"}, {"litesim", CmdHFFelicaSimLite, IfPm3Felica, "Emulating ISO/18092 FeliCa Lite tag"}, - {"litedump", CmdHFFelicaDumpLite, IfPm3Felica, "Wait for and try dumping FelicaLite"}, {"liteauth", CmdHFFelicaAuthenticationLite, IfPm3Felica, "authenticate a card."}, + {"litedump", CmdHFFelicaDumpLite, IfPm3Felica, "Wait for and try dumping FelicaLite"}, // {"sim", CmdHFFelicaSim, IfPm3Felica, " -- Simulate ISO 18092/FeliCa tag"} {NULL, NULL, NULL, NULL} }; From 72e47e4fcb0b361363985c2090fceb83be5c3cf1 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 8 Aug 2025 23:08:27 +0200 Subject: [PATCH 138/149] minor usage fix in staticnested_2nt --- tools/mfc/card_only/staticnested_2nt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/mfc/card_only/staticnested_2nt.c b/tools/mfc/card_only/staticnested_2nt.c index 4409d94e2..a013a0d97 100644 --- a/tools/mfc/card_only/staticnested_2nt.c +++ b/tools/mfc/card_only/staticnested_2nt.c @@ -175,19 +175,19 @@ static void pm3_staticnested(uint32_t uid, uint32_t nt1, uint32_t ks1, uint32_t } } -static int usage(void) { +static int usage(const char *prog) { printf("\n"); printf("\nProgram tries to recover keys from static encrypted nested MFC cards\n"); printf("using two different implementations, Chameleon Ultra (CU) and Proxmark3.\n"); printf("It uses the nonce, keystream sent from pm3 device to client.\n"); printf("ie: NOT the CU data which is data in the trace.\n"); printf("\n"); - printf("syntax: staticnested \n\n"); + printf("syntax: %s \n\n", prog); printf("samples:\n"); printf("\n"); - printf(" ./staticnested 461dce03 7eef3586 ffb02eda 322bc14d ffc875ca\n"); - printf(" ./staticnested 461dce03 7eef3586 1fb6b496 322bc14d 1f4eebdd\n"); - printf(" ./staticnested 461dce03 7eef3586 7fa28c7e 322bc14d 7f62b3d6\n"); + printf(" %s 461dce03 7eef3586 ffb02eda 322bc14d ffc875ca\n", prog); + printf(" %s 461dce03 7eef3586 1fb6b496 322bc14d 1f4eebdd\n", prog); + printf(" %s 461dce03 7eef3586 7fa28c7e 322bc14d 7f62b3d6\n", prog); printf("\n"); return 1; } @@ -196,7 +196,7 @@ int main(int argc, char *const argv[]) { printf("\nMIFARE Classic static nested key recovery\n\n"); - if (argc < 5) return usage(); + if (argc < 5) return usage(argv[0]); printf("Init...\n"); NtpKs1 *pNK = calloc(2, sizeof(NtpKs1)); From d75b170cd43c7f253ef244379de8d84fdee3254b Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sat, 9 Aug 2025 10:11:12 +0200 Subject: [PATCH 139/149] fix typos --- tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c b/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c index f7b74ce0e..87b2f330d 100644 --- a/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c +++ b/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c @@ -5,7 +5,7 @@ // * keyA and keyB are different for the targeted sector // // Strategy: -// * Use f08s_nested_known_collision to crack keyA +// * Use staticnested_2x1nt_rf08s to crack keyA // * If keyB not readable, find keyB in its dictionary based on the obscure relationship between keyA, keyB and their nT // // Doegox, 2024, cf https://eprint.iacr.org/2024/1275 for more info @@ -89,7 +89,7 @@ int main(int argc, char *const argv[]) { if (argc != 4) { printf("Usage:\n %s keys___.dic\n" - " where dict file is produced by rf08s_nested_known *for the same UID and same sector* as provided nt and key\n", + " where dict file is produced by staticnested_1nt *for the same UID and same sector* as provided nt and key\n", argv[0]); return 1; } From 8ab2f2b5a0987159fd6f6a6e7183bf32a750fb4d Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 12 Aug 2025 19:06:38 +0200 Subject: [PATCH 140/149] style --- client/src/cmdhffelica.c | 20 ++++++++++---------- client/src/pm3line_vocabulary.h | 2 +- doc/commands.json | 20 ++++++++++++-------- doc/commands.md | 2 +- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 300887c42..5c48bbefe 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -2311,7 +2311,7 @@ static int send_rd_multiple_plain(uint8_t flags, uint16_t datalen, uint8_t *data return PM3_ERFTRANS; } - uint8_t block_data[FELICA_BLK_SIZE*4]; + uint8_t block_data[FELICA_BLK_SIZE * 4]; memset(block_data, 0, sizeof(block_data)); uint8_t outlen = 0; @@ -2453,13 +2453,13 @@ static int write_with_mac( const uint8_t blk_number, const uint8_t *block_data, uint8_t *out) { - + uint8_t initialize_blk[FELICA_BLK_HALF]; memset(initialize_blk, 0, sizeof(initialize_blk)); - + uint8_t wcnt[3]; memcpy(wcnt, counter, 3); - + memcpy(initialize_blk, wcnt, sizeof(wcnt)); initialize_blk[4] = blk_number; initialize_blk[6] = 0x91; @@ -2471,7 +2471,7 @@ static int write_with_mac( return ret; } - uint8_t payload[FELICA_BLK_SIZE*2]; + uint8_t payload[FELICA_BLK_SIZE * 2]; memset(payload, 0, sizeof(payload)); memcpy(payload, block_data, FELICA_BLK_SIZE); @@ -2510,7 +2510,7 @@ static int felica_internal_authentication( felica_status_response_t res; if (send_wr_plain(flags, datalen, data, false, &res) != PM3_SUCCESS) { - return PM3_ERFTRANS; + return PM3_ERFTRANS; } if (res.status_flags.status_flag1[0] != 0x00 && res.status_flags.status_flag2[0] != 0x00) { @@ -2530,7 +2530,7 @@ static int felica_internal_authentication( AddCrc(data, datalen); datalen += 2; - uint8_t pd[FELICA_BLK_SIZE*sizeof(blk_numbers2)]; + uint8_t pd[FELICA_BLK_SIZE * sizeof(blk_numbers2)]; memset(pd, 0, sizeof(pd)); ret = send_rd_multiple_plain(flags, datalen, data, pd); @@ -2565,7 +2565,7 @@ static int felica_internal_authentication( if (memcmp(mac_blk, mac, FELICA_BLK_HALF) != 0) { PrintAndLogEx(ERR, "\nInternal Authenticate: " _RED_("Failed")); - return PM3_ERFTRANS; + return PM3_ERFTRANS; } PrintAndLogEx(SUCCESS, "Internal Authenticate: " _GREEN_("OK")); @@ -2607,7 +2607,7 @@ static int felica_external_authentication( ext_auth[0] = 1; // After Authenticate - uint8_t mac_w[FELICA_BLK_SIZE*2]; + uint8_t mac_w[FELICA_BLK_SIZE * 2]; ret = write_with_mac(ctx, auth_ctx, wcnt_blk, FELICA_BLK_NUMBER_STATE, ext_auth, mac_w); if (ret) { @@ -2630,7 +2630,7 @@ static int felica_external_authentication( felica_status_response_t res; if (send_wr_plain(flags, datalen, data, false, &res) != PM3_SUCCESS) { - return PM3_ERFTRANS; + return PM3_ERFTRANS; } if (res.status_flags.status_flag1[0] != 0x00 && res.status_flags.status_flag2[0] != 0x00) { diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index 248a4a667..3bf14d7a0 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -252,8 +252,8 @@ const static vocabulary_t vocabulary[] = { { 0, "hf felica rqspecver" }, { 0, "hf felica resetmode" }, { 0, "hf felica litesim" }, - { 0, "hf felica litedump" }, { 0, "hf felica liteauth" }, + { 0, "hf felica litedump" }, { 1, "hf fido help" }, { 1, "hf fido list" }, { 0, "hf fido info" }, diff --git a/doc/commands.json b/doc/commands.json index ea1ffa469..98afb786e 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -2710,17 +2710,19 @@ "description": "Authenticate", "notes": [ "hf felica liteauth -i 11100910C11BC407", - "hf felica liteauth -k 46656c69436130313233343536616263", - "hf felica liteauth -c 701185c59f8d30afeab8e4b3a61f5cc4 -k 46656c69436130313233343536616263" + "hf felica liteauth --key 46656c69436130313233343536616263", + "hf felica liteauth --key 46656c69436130313233343536616263 -k", + "hf felica liteauth -c 701185c59f8d30afeab8e4b3a61f5cc4 --key 46656c69436130313233343536616263" ], "offline": false, "options": [ "-h, --help This help", - "-k, --key set card key, 16 bytes", + "--key set card key, 16 bytes", "-c, set random challenge, 16 bytes", - "-i, set custom IDm" + "-i, set custom IDm", + "-k, keep signal field ON after receive" ], - "usage": "hf felica liteauth [-h] [-k ] [-c ] [-i ]" + "usage": "hf felica liteauth [-hk] [--key ] [-c ] [-i ]" }, "hf felica litedump": { "command": "hf felica litedump", @@ -2730,9 +2732,11 @@ ], "offline": false, "options": [ - "-h, --help This help" + "-h, --help This help", + "-i, set custom IDm", + "--key set card key, 16 bytes" ], - "usage": "hf felica litedump [-h]" + "usage": "hf felica litedump [-h] [-i ] [--key ]" }, "hf felica litesim": { "command": "hf felica litesim", @@ -13485,6 +13489,6 @@ "metadata": { "commands_extracted": 775, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-08-04T17:50:35" + "extracted_on": "2025-08-12T16:26:32" } } diff --git a/doc/commands.md b/doc/commands.md index fc80c4404..6fbd8f18f 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -343,8 +343,8 @@ Check column "offline" for their availability. |`hf felica rqspecver `|N |`acquire the version of card OS.` |`hf felica resetmode `|N |`reset Mode to Mode 0.` |`hf felica litesim `|N |`Emulating ISO/18092 FeliCa Lite tag` -|`hf felica litedump `|N |`Wait for and try dumping FelicaLite` |`hf felica liteauth `|N |`authenticate a card.` +|`hf felica litedump `|N |`Wait for and try dumping FelicaLite` ### hf fido From b09e3614daaa931cee52a57123a5b99fa812bc94 Mon Sep 17 00:00:00 2001 From: Jakub Kramarz Date: Sun, 17 Aug 2025 16:00:34 +0200 Subject: [PATCH 141/149] AID list: removed duplicate AID for PIV --- client/resources/aidlist.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/client/resources/aidlist.json b/client/resources/aidlist.json index 9b6d58317..a0731b974 100644 --- a/client/resources/aidlist.json +++ b/client/resources/aidlist.json @@ -1247,14 +1247,6 @@ "Description": "PIV End Point Applet. Last 2 bytes designate version", "Type": "" }, - { - "AID": "A000000308000010000100", - "Vendor": "National Institute of Standards and Technology", - "Country": "United States", - "Name": "Personal Identity Verification (PIV) / ID-ONE PIV BIO", - "Description": "PIV End Point Applet. Last 2 bytes designate version", - "Type": "" - }, { "AID": "A00000031510100528", "Vendor": "Currence Holding/PIN BV", From 515d8c76fa19b874c5908cd21c51b26177e25983 Mon Sep 17 00:00:00 2001 From: Jakub Kramarz Date: Sun, 17 Aug 2025 16:22:21 +0200 Subject: [PATCH 142/149] AID list: added Crescendo AIDs from https://docs.hidglobal.com/crescendo/api/low-level/select.htm --- client/resources/aidlist.json | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/client/resources/aidlist.json b/client/resources/aidlist.json index a0731b974..53828dbc1 100644 --- a/client/resources/aidlist.json +++ b/client/resources/aidlist.json @@ -2462,5 +2462,37 @@ "Name": "Navigo", "Description": "CALYPSO-based transit card", "Type": "transport" + }, + { + "AID": "A0000000791000", + "Vendor": "HID Global", + "Country": "", + "Name": "Crescendo ACA", + "Description": "HID Crescendo ACA", + "Type": "access" + }, + { + "AID": "A0000000792300", + "Vendor": "HID Global", + "Country": "", + "Name": "Crescendo OATH #0 (HOTP)", + "Description": "HID Crescendo Key OATH instance 0 (default HOTP slot)", + "Type": "access" + }, + { + "AID": "A0000000792301", + "Vendor": "HID Global", + "Country": "", + "Name": "Crescendo OATH #1", + "Description": "HID Crescendo Key OATH instance 1", + "Type": "access" + }, + { + "AID": "A0000000792302", + "Vendor": "HID Global", + "Country": "", + "Name": "Crescendo OATH #2", + "Description": "HID Crescendo Key OATH instance 2", + "Type": "access" } ] From b1a03ec27e34feff44e0c0e87a862049725e05bd Mon Sep 17 00:00:00 2001 From: ry4000 <154689120+ry4000@users.noreply.github.com> Date: Mon, 18 Aug 2025 17:53:41 +1000 Subject: [PATCH 143/149] R&Y: Added `MEX AHORROBUS Card` to `aid_desfire.json` ### Additions - MEX AHORROBUS Card (C1B1A1) *with many thanks to Discord\`@narzaum2874`. Signed-off-by: ry4000 <154689120+ry4000@users.noreply.github.com> --- client/resources/aid_desfire.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index 88101ba67..df68b254e 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -1263,6 +1263,14 @@ "Description": "Umo Mobility Card", "Type": "transport" }, + { + "AID": "C1B1A1", + "Vendor": "AHORROBUS via MOBILITY ADO", + "Country": "MX", + "Name": "AHORROBUS Card (MEX)", + "Description": "MEX AHORROBUS Card", + "Type": "transport" + }, { "AID": "C65B80", "Vendor": "Umo Mobility via Cubic Transportation Systems", From c9f9fcb1989c3c3e761d21457877c22862214595 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 18 Aug 2025 22:53:42 +0200 Subject: [PATCH 144/149] fix bad merge in ul-c sim --- armsrc/iso14443a.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index c8fe0ce7f..3f1f4595b 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2072,14 +2072,6 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); p_response = &dynamic_response_info; order = ORDER_NONE; - // Add CRC - AddCrc14A(dynamic_response_info.response, 17); - - dynamic_response_info.response_n = 1 + 16 + 2; - - prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); - p_response = &dynamic_response_info; - order = ORDER_NONE; } else if (receivedCmd[0] == MIFARE_ULEV1_AUTH && len == 7 && tagType == 7) { // NTAG / EV-1 uint8_t pwd[4] = {0, 0, 0, 0}; From 63b4612f18d2041070dd364faca57e5d0854f5c7 Mon Sep 17 00:00:00 2001 From: kormax <3392860+kormax@users.noreply.github.com> Date: Tue, 19 Aug 2025 22:07:35 +0300 Subject: [PATCH 145/149] Support VC card reading by adding `--dfname` argument to more mfdes commands --- client/src/cmdhfmfdes.c | 175 +++++++++++++++++++----------- client/src/mifare/desfirecore.c | 52 ++++++++- client/src/mifare/desfirecrypto.c | 11 +- client/src/mifare/desfirecrypto.h | 4 + 4 files changed, 176 insertions(+), 66 deletions(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index d2160104c..220a22d78 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -479,6 +479,7 @@ static int CmdDesGetSessionParameters(CLIParserContext *ctx, DesfireContext_t *d uint8_t cmodeid, uint8_t ccsetid, uint8_t schannid, uint8_t appid, uint8_t appisoid, + uint8_t dfnameid, int *securechannel, DesfireCommunicationMode defcommmode, uint32_t *id, @@ -564,6 +565,22 @@ static int CmdDesGetSessionParameters(CLIParserContext *ctx, DesfireContext_t *d return PM3_ESOFT; } + // Handle dfname parameter + if (dfnameid && id) { + uint8_t dfname_data[16] = {0}; + int dfname_len = 0; + if (CLIParamHexToBuf(arg_get_str(ctx, dfnameid), dfname_data, sizeof(dfname_data), &dfname_len) == 0 && dfname_len > 0) { + if (dfname_len <= 16) { + DesfireSetDFName(dctx, dfname_data, dfname_len); + if (selectway) + *selectway = ISWDFName; + } else { + PrintAndLogEx(ERR, "DF name length must be between 1-16 bytes, got %d", dfname_len); + return PM3_EINVARG; + } + } + } + if (appid && id) { *id = 0x000000; if (CLIGetUint32Hex(ctx, appid, 0x000000, id, NULL, 3, "AID must have 3 bytes length")) @@ -617,7 +634,7 @@ static int CmdHF14ADesDefault(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; - int res = CmdDesGetSessionParameters(ctx, &dctx, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, &securechann, DCMNone, NULL, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, &securechann, DCMNone, NULL, NULL); if (res) { CLIParserFree(ctx); return res; @@ -1520,6 +1537,7 @@ static int CmdHF14aDesDetect(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)."), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_str0("f", "file", "", "Filename of dictionary"), arg_lit0(NULL, "save", "Save found key and parameters to defaults"), arg_param_end @@ -1533,7 +1551,7 @@ static int CmdHF14aDesDetect(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -1541,13 +1559,13 @@ static int CmdHF14aDesDetect(const char *Cmd) { uint8_t dict_filename[FILE_PATH_SIZE + 2] = {0}; int dict_filenamelen = 0; - if (CLIParamStrToBuf(arg_get_str(ctx, 13), dict_filename, FILE_PATH_SIZE, &dict_filenamelen)) { + if (CLIParamStrToBuf(arg_get_str(ctx, 14), dict_filename, FILE_PATH_SIZE, &dict_filenamelen)) { PrintAndLogEx(FAILED, "File name too long or invalid."); CLIParserFree(ctx); return PM3_EINVARG; } - bool save = arg_get_lit(ctx, 14); + bool save = arg_get_lit(ctx, 15); SetAPDULogging(APDULogging); CLIParserFree(ctx); @@ -1848,7 +1866,7 @@ static int CmdHF14aDesMAD(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMPlain, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMPlain, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -2024,7 +2042,7 @@ static int CmdHF14ADesSelectApp(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMPlain, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMPlain, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -2146,7 +2164,7 @@ static int CmdHF14ADesBruteApps(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; - int res = CmdDesGetSessionParameters(ctx, &dctx, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &securechann, DCMNone, NULL, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &securechann, DCMNone, NULL, NULL); if (res) { CLIParserFree(ctx); return res; @@ -2231,7 +2249,8 @@ static int CmdHF14ADesAuth(const char *Cmd) { "hf mfdes auth -n 0 -t des -k 0000000000000000 --kdf none -> select PICC level and authenticate with key num=0, key type=des, key=00..00 and key derivation = none\n" "hf mfdes auth -n 0 -t aes -k 00000000000000000000000000000000 -> select PICC level and authenticate with key num=0, key type=aes, key=00..00 and key derivation = none\n" "hf mfdes auth -n 0 -t des -k 0000000000000000 --save -> select PICC level and authenticate and in case of successful authentication - save channel parameters to defaults\n" - "hf mfdes auth --aid 123456 -> select application 123456 and authenticate via parameters from `default` command"); + "hf mfdes auth --aid 123456 -> select application 123456 and authenticate via parameters from `default` command\n" + "hf mfdes auth --dfname D2760000850100 -n 0 -t aes -k 00000000000000000000000000000000 -> select DF by name and authenticate"); void *argtable[] = { arg_param_begin, @@ -2247,6 +2266,7 @@ static int CmdHF14ADesAuth(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID of application for some parameters (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_lit0(NULL, "save", "saves channels parameters to defaults if authentication succeeds"), arg_param_end }; @@ -2259,13 +2279,13 @@ static int CmdHF14ADesAuth(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMPlain, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMPlain, &id, &selectway); if (res) { CLIParserFree(ctx); return res; } - bool save = arg_get_lit(ctx, 13); + bool save = arg_get_lit(ctx, 14); SetAPDULogging(APDULogging); CLIParserFree(ctx); @@ -2277,7 +2297,9 @@ static int CmdHF14ADesAuth(const char *Cmd) { return res; } - if (DesfireMFSelected(selectway, id)) + if (dctx.selectedDFNameLen > 0) { + PrintAndLogEx(SUCCESS, "DF selected and authenticated " _GREEN_("successfully")); + } else if (DesfireMFSelected(selectway, id)) PrintAndLogEx(SUCCESS, "PICC selected and authenticated " _GREEN_("succesfully")); else PrintAndLogEx(SUCCESS, "Application " _CYAN_("%s") " selected and authenticated " _GREEN_("succesfully"), DesfireWayIDStr(selectway, id)); @@ -2355,7 +2377,7 @@ static int CmdHF14ADesSetConfiguration(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMEncrypted, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, &securechann, DCMEncrypted, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -2470,7 +2492,7 @@ static int CmdHF14ADesChangeKey(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMEncrypted, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, &securechann, DCMEncrypted, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -2633,7 +2655,7 @@ static int CmdHF14ADesCreateApp(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 12, 0, &securechann, DCMMACed, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 12, 0, 0, &securechann, DCMMACed, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -2796,7 +2818,7 @@ static int CmdHF14ADesDeleteApp(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMMACed, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMMACed, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -2834,7 +2856,8 @@ static int CmdHF14ADesGetUID(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes getuid", "Get UID from card. Get the real UID if the random UID bit is on and get the same UID as in anticollision if not. Any card's key needs to be provided. ", "hf mfdes getuid -> execute with default factory setup\n" - "hf mfdes getuid --isoid df01 -t aes --schan lrp -> for desfire lights default settings"); + "hf mfdes getuid --isoid df01 -t aes --schan lrp -> for desfire lights default settings\n" + "hf mfdes getuid --dfname D2760000850100 -> select DF by name and get UID"); void *argtable[] = { arg_param_begin, @@ -2850,6 +2873,7 @@ static int CmdHF14ADesGetUID(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -2861,7 +2885,7 @@ static int CmdHF14ADesGetUID(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMEncrypted, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMEncrypted, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -2939,7 +2963,7 @@ static int CmdHF14ADesFormatPICC(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMMACed, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMMACed, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -2986,6 +3010,7 @@ static int CmdHF14ADesGetFreeMem(const char *Cmd) { arg_str0("c", "ccset", "", "Communicaton command set"), arg_str0(NULL, "schann", "", "Secure channel"), arg_lit0(NULL, "no-auth", "Execute without authentication"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -2997,7 +3022,9 @@ static int CmdHF14ADesGetFreeMem(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, &securechann, (noauth) ? DCMPlain : DCMMACed, NULL, NULL); + uint32_t id = 0x000000; + DesfireISOSelectWay selectway = ISW6bAID; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 12, &securechann, (noauth) ? DCMPlain : DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -3006,7 +3033,7 @@ static int CmdHF14ADesGetFreeMem(const char *Cmd) { SetAPDULogging(APDULogging); CLIParserFree(ctx); - res = DesfireSelectAndAuthenticateEx(&dctx, securechann, 0x000000, noauth, verbose); + res = DesfireSelectAndAuthenticateAppW(&dctx, securechann, selectway, id, noauth, verbose); if (res != PM3_SUCCESS) { DropField(); return res; @@ -3059,7 +3086,7 @@ static int CmdHF14ADesChKeySettings(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMEncrypted, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMEncrypted, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -3106,7 +3133,8 @@ static int CmdHF14ADesGetKeyVersions(const char *Cmd) { "--keynum parameter: App level: key number. PICC level: 00..0d - keys count, 21..23 vc keys, default 0x00.\n"\ "hf mfdes getkeyversions --keynum 00 -> get picc master key version with default key/channel setup\n"\ "hf mfdes getkeyversions --aid 123456 --keynum 0d -> get app 123456 all key versions with default key/channel setup\n" - "hf mfdes getkeyversions --aid 123456 --keynum 0d --no-auth -> get key version without authentication"); + "hf mfdes getkeyversions --aid 123456 --keynum 0d --no-auth -> get key version without authentication\n"\ + "hf mfdes getkeyversions --dfname D2760000850100 --keynum 00 -> select DF by name and get key versions"); void *argtable[] = { arg_param_begin, @@ -3122,36 +3150,38 @@ static int CmdHF14ADesGetKeyVersions(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)."), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_str0(NULL, "keynum", "", "Key number/count (1 hex byte). (def: 0x00)"), arg_str0(NULL, "keyset", "", "Keyset number (1 hex byte)"), arg_lit0(NULL, "no-auth", "Execute without authentication"), + arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - bool noauth = arg_get_lit(ctx, 15); + bool noauth = arg_get_lit(ctx, 16); DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; } uint32_t keynum32 = 0x00; - if (CLIGetUint32Hex(ctx, 13, 0x00, &keynum32, NULL, 1, "Key number must have 1 byte length")) { + if (CLIGetUint32Hex(ctx, 14, 0x00, &keynum32, NULL, 1, "Key number must have 1 byte length")) { CLIParserFree(ctx); return PM3_EINVARG; } uint32_t keysetnum32 = 0x00; bool keysetpresent = false; - if (CLIGetUint32Hex(ctx, 14, 0x00, &keysetnum32, &keysetpresent, 1, "Keyset number must have 1 byte length")) { + if (CLIGetUint32Hex(ctx, 15, 0x00, &keysetnum32, &keysetpresent, 1, "Keyset number must have 1 byte length")) { CLIParserFree(ctx); return PM3_EINVARG; } @@ -3169,7 +3199,7 @@ static int CmdHF14ADesGetKeyVersions(const char *Cmd) { DropField(); PrintAndLogEx(FAILED, "Select or authentication %s " _RED_("failed") ". Result [%d] %s", DesfireWayIDStr(selectway, id), res, DesfireAuthErrorToStr(res)); return res; - } + } uint8_t buf[APDU_RES_LEN] = {0}; size_t buflen = 0; @@ -3208,7 +3238,8 @@ static int CmdHF14ADesGetKeySettings(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes getkeysettings", "Get key settings for card level or application level.", "hf mfdes getkeysettings -> get picc key settings with default key/channel setup\n"\ - "hf mfdes getkeysettings --aid 123456 -> get app 123456 key settings with default key/channel setup"); + "hf mfdes getkeysettings --aid 123456 -> get app 123456 key settings with default key/channel setup\n"\ + "hf mfdes getkeysettings --dfname D2760000850100 -> select DF by name and get key settings"); void *argtable[] = { arg_param_begin, @@ -3223,6 +3254,7 @@ static int CmdHF14ADesGetKeySettings(const char *Cmd) { arg_str0("c", "ccset", "", "Communicaton command set"), arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -3233,7 +3265,8 @@ static int CmdHF14ADesGetKeySettings(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMMACed, &appid, NULL); + DesfireISOSelectWay selectway = ISW6bAID; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 12, &securechann, DCMMACed, &appid, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -3311,7 +3344,7 @@ static int CmdHF14ADesGetAIDs(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, &securechann, DCMMACed, NULL, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, &securechann, DCMMACed, NULL, NULL); if (res) { CLIParserFree(ctx); return res; @@ -3383,7 +3416,7 @@ static int CmdHF14ADesGetAppNames(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, &securechann, DCMMACed, NULL, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, &securechann, DCMMACed, NULL, NULL); if (res) { CLIParserFree(ctx); return res; @@ -3430,7 +3463,8 @@ static int CmdHF14ADesGetFileIDs(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes getfileids", "Get File IDs list from card. Master key needs to be provided or flag --no-auth set.", "hf mfdes getfileids --aid 123456 -> execute with defaults from `default` command\n" - "hf mfdes getfileids -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 -> execute with default factory setup"); + "hf mfdes getfileids -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 -> execute with default factory setup\n" + "hf mfdes getfileids --dfname D2760000850100 -> select DF by name and get file IDs"); void *argtable[] = { arg_param_begin, @@ -3446,6 +3480,7 @@ static int CmdHF14ADesGetFileIDs(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)."), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_lit0(NULL, "no-auth", "Execute without authentication"), arg_param_end }; @@ -3453,13 +3488,13 @@ static int CmdHF14ADesGetFileIDs(const char *Cmd) { bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - bool noauth = arg_get_lit(ctx, 13); + bool noauth = arg_get_lit(ctx, 14); DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -3474,6 +3509,7 @@ static int CmdHF14ADesGetFileIDs(const char *Cmd) { PrintAndLogEx(FAILED, "Select or authentication %s " _RED_("failed") ". Result [%d] %s", DesfireWayIDStr(selectway, id), res, DesfireAuthErrorToStr(res)); return res; } + uint8_t buf[APDU_RES_LEN] = {0}; size_t buflen = 0; @@ -3490,7 +3526,7 @@ static int CmdHF14ADesGetFileIDs(const char *Cmd) { for (int i = 0; i < buflen; i++) PrintAndLogEx(INFO, "File ID: %02x", buf[i]); } else { - PrintAndLogEx(INFO, "There is no files in the application %06x", id); + PrintAndLogEx(INFO, "There are no files in the application %06x", id); } DropField(); @@ -3504,7 +3540,8 @@ static int CmdHF14ADesGetFileISOIDs(const char *Cmd) { "hf mfdes getfileisoids --aid 123456 -> execute with defaults from `default` command\n" "hf mfdes getfileisoids -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 -> execute with default factory setup\n" "hf mfdes getfileisoids --isoid df01 -> get iso file ids from Desfire Light with factory card settings\n" - "hf mfdes getfileisoids --isoid df01 --schann lrp -t aes -> get iso file ids from Desfire Light via lrp channel with default key authentication"); + "hf mfdes getfileisoids --isoid df01 --schann lrp -t aes -> get iso file ids from Desfire Light via lrp channel with default key authentication\n" + "hf mfdes getfileisoids --dfname D2760000850100 -> select DF by name and get file ISO IDs"); void *argtable[] = { arg_param_begin, @@ -3520,6 +3557,7 @@ static int CmdHF14ADesGetFileISOIDs(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)."), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_lit0(NULL, "no-auth", "Execute without authentication"), arg_param_end }; @@ -3527,13 +3565,13 @@ static int CmdHF14ADesGetFileISOIDs(const char *Cmd) { bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - bool noauth = arg_get_lit(ctx, 13); + bool noauth = arg_get_lit(ctx, 14); DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -3564,7 +3602,7 @@ static int CmdHF14ADesGetFileISOIDs(const char *Cmd) { for (int i = 0; i < buflen; i += 2) PrintAndLogEx(INFO, "File ID: %04x", MemLeToUint2byte(&buf[i])); } else { - PrintAndLogEx(INFO, "There is no files in the application %06x", id); + PrintAndLogEx(INFO, "There are no files in the application %06x", id); } DropField(); @@ -3577,7 +3615,8 @@ static int CmdHF14ADesGetFileSettings(const char *Cmd) { "Get File Settings from file from application. Master key needs to be provided or flag --no-auth set (depend on cards settings).", "hf mfdes getfilesettings --aid 123456 --fid 01 -> execute with defaults from `default` command\n" "hf mfdes getfilesettings --isoid df01 --fid 00 --no-auth -> get file settings with select by iso id\n" - "hf mfdes getfilesettings -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 --fid 01 -> execute with default factory setup"); + "hf mfdes getfilesettings -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 --fid 01 -> execute with default factory setup\n" + "hf mfdes getfilesettings --dfname D2760000850100 --fid 01 -> select DF by name and get file settings"); void *argtable[] = { arg_param_begin, @@ -3593,6 +3632,7 @@ static int CmdHF14ADesGetFileSettings(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_str0(NULL, "fid", "", "File ID (1 hex byte). (def: 1)"), arg_lit0(NULL, "no-auth", "Execute without authentication"), arg_param_end @@ -3601,20 +3641,20 @@ static int CmdHF14ADesGetFileSettings(const char *Cmd) { bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - bool noauth = arg_get_lit(ctx, 14); + bool noauth = arg_get_lit(ctx, 15); DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; } uint32_t fileid = 1; - if (CLIGetUint32Hex(ctx, 13, 1, &fileid, NULL, 1, "File ID must have 1 byte length")) { + if (CLIGetUint32Hex(ctx, 14, 1, &fileid, NULL, 1, "File ID must have 1 byte length")) { CLIParserFree(ctx); return PM3_EINVARG; } @@ -3639,8 +3679,9 @@ static int CmdHF14ADesGetFileSettings(const char *Cmd) { return PM3_ESOFT; } - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "%s file %02x settings[%zu]: %s", DesfireWayIDStr(selectway, id), fileid, buflen, sprint_hex(buf, buflen)); + } DesfirePrintFileSettings(buf, buflen); @@ -3783,7 +3824,7 @@ static int CmdHF14ADesChFileSettings(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMEncrypted, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, &securechann, DCMEncrypted, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -3930,7 +3971,7 @@ static int CmdHF14ADesCreateFile(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMMACed, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMMACed, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -4067,7 +4108,7 @@ static int CmdHF14ADesCreateValueFile(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMMACed, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMMACed, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -4194,7 +4235,7 @@ static int CmdHF14ADesCreateRecordFile(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, &securechann, DCMMACed, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0,&securechann, DCMMACed, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -4312,7 +4353,7 @@ static int CmdHF14ADesCreateTrMACFile(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMEncrypted, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, &securechann, DCMEncrypted, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -4419,7 +4460,7 @@ static int CmdHF14ADesDeleteFile(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0,&securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -4499,7 +4540,7 @@ static int CmdHF14ADesValueOperations(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -4669,7 +4710,7 @@ static int CmdHF14ADesClearRecordFile(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -5079,7 +5120,7 @@ static int CmdHF14ADesReadData(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 17, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 17, 0, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -5254,7 +5295,7 @@ static int CmdHF14ADesWriteData(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 20, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 20, 0, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -5554,7 +5595,8 @@ static int CmdHF14ADesLsFiles(const char *Cmd) { "This commands List files inside application AID / ISOID.\n" "Master key needs to be provided or flag --no-auth set (depend on cards settings).", "hf mfdes lsfiles --aid 123456 -> AID 123456, list files using `default` command creds\n" - "hf mfdes lsfiles --isoid df01 --no-auth -> list files for DESFire light"); + "hf mfdes lsfiles --isoid df01 --no-auth -> list files for DESFire light\n" + "hf mfdes lsfiles --dfname D2760000850100 -> select DF by name and list files"); void *argtable[] = { arg_param_begin, @@ -5570,6 +5612,7 @@ static int CmdHF14ADesLsFiles(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_lit0(NULL, "no-auth", "Execute without authentication"), arg_param_end }; @@ -5577,13 +5620,13 @@ static int CmdHF14ADesLsFiles(const char *Cmd) { bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - bool noauth = arg_get_lit(ctx, 13); + bool noauth = arg_get_lit(ctx, 14); DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; @@ -5609,9 +5652,9 @@ static int CmdHF14ADesLsFiles(const char *Cmd) { } if (filescount == 0) { - PrintAndLogEx(INFO, "There is no files in the %s", DesfireWayIDStr(selectway, id)); + PrintAndLogEx(INFO, "There are no files in the %s", DesfireWayIDStr(selectway, id)); DropField(); - return res; + return PM3_SUCCESS; } PrintAndLogEx(INFO, "------------------------------------------ " _CYAN_("File list") " -----------------------------------------------------"); @@ -5627,7 +5670,8 @@ static int CmdHF14ADesLsApp(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes lsapp", "Show application list. Master key needs to be provided or flag --no-auth set (depend on cards settings).", "hf mfdes lsapp -> show application list with defaults from `default` command\n" - "hf mfdes lsapp --files -> show application list and show each file type/settings/etc"); + "hf mfdes lsapp --files -> show application list and show each file type/settings/etc\n" + "hf mfdes lsapp --dfname D2760000850100 -> list apps after selecting DF by name"); void *argtable[] = { arg_param_begin, @@ -5644,6 +5688,7 @@ static int CmdHF14ADesLsApp(const char *Cmd) { arg_lit0(NULL, "no-auth", "Execute without authentication"), arg_lit0(NULL, "no-deep", "not to check authentication commands that avail for any application"), arg_lit0(NULL, "files", "scan files and print file settings"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -5656,7 +5701,7 @@ static int CmdHF14ADesLsApp(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, &securechann, (noauth) ? DCMPlain : DCMMACed, NULL, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 14, &securechann, (noauth) ? DCMPlain : DCMMACed, NULL, NULL); if (res) { CLIParserFree(ctx); return res; @@ -5708,6 +5753,7 @@ static int CmdHF14ADesDump(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)"), + arg_str0(NULL, "dfname", "", "Application ISO DF Name (5-16 hex bytes, big endian)"), arg_str0("l", "length", "", "Maximum length for read data files (3 hex bytes, big endian)"), arg_lit0(NULL, "no-auth", "Execute without authentication"), arg_param_end @@ -5716,20 +5762,20 @@ static int CmdHF14ADesDump(const char *Cmd) { bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - bool noauth = arg_get_lit(ctx, 14); + bool noauth = arg_get_lit(ctx, 15); DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, &securechann, (noauth) ? DCMPlain : DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, &securechann, (noauth) ? DCMPlain : DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; } uint32_t maxlength = 0; - if (CLIGetUint32Hex(ctx, 13, 0, &maxlength, NULL, 3, "Length parameter must have 3 byte length")) { + if (CLIGetUint32Hex(ctx, 14, 0, &maxlength, NULL, 3, "Length parameter must have 3 byte length")) { CLIParserFree(ctx); return PM3_EINVARG; } @@ -5840,6 +5886,7 @@ static command_t CommandTable[] = { {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("Files") " -----------------------"}, {"getfileids", CmdHF14ADesGetFileIDs, IfPm3Iso14443a, "Get File IDs list"}, {"getfileisoids", CmdHF14ADesGetFileISOIDs, IfPm3Iso14443a, "Get File ISO IDs list"}, + {"lsfile", CmdHF14ADesLsFiles, IfPm3Iso14443a, "Show all files list"}, {"lsfiles", CmdHF14ADesLsFiles, IfPm3Iso14443a, "Show all files list"}, {"dump", CmdHF14ADesDump, IfPm3Iso14443a, "Dump all files"}, {"createfile", CmdHF14ADesCreateFile, IfPm3Iso14443a, "Create Standard/Backup File"}, diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index e1ce6c93a..1b82bd8b2 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -1100,7 +1100,57 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s DesfirePrintContext(dctx); int res = 0; - if (way == ISW6bAID && dctx->cmdSet == DCCISO) { + + // Handle DF Name selection if it's present in the context + if (dctx->selectedDFNameLen > 0) { + // Select DF by name using ISO7816 SELECT + uint8_t resp[250] = {0}; + size_t resplen = 0; + res = DesfireISOSelect(dctx, ISSDFName, dctx->selectedDFName, dctx->selectedDFNameLen, resp, &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire DF name select " _RED_("error")); + return 200; + } + if (verbose) { + PrintAndLogEx(INFO, "DF %s is " _GREEN_("selected"), sprint_hex(dctx->selectedDFName, dctx->selectedDFNameLen)); + } + + // If both dfname and aid are specified, now also select by AID + if (way == ISW6bAID && id != 0x000000) { + if (dctx->cmdSet == DCCISO) { + dctx->cmdSet = DCCNativeISO; + if (verbose) + PrintAndLogEx(INFO, "Select via " _CYAN_("native iso wrapping") " interface"); + + res = DesfireSelectAIDHex(dctx, id, false, 0); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire select " _RED_("error")); + return 200; + } + if (verbose) + PrintAndLogEx(INFO, "App %06x via native iso channel is " _GREEN_("selected"), id); + + dctx->cmdSet = DCCISO; + } else { + res = DesfireSelectEx(dctx, false, way, id, NULL); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire %s select " _RED_("error"), DesfireSelectWayToStr(way)); + return 202; + } + if (verbose) + PrintAndLogEx(INFO, "%s is " _GREEN_("selected"), DesfireWayIDStr(way, id)); + } + } else if (way == ISWIsoID && id != 0x0000) { + // Also select by ISO ID if specified + res = DesfireSelectEx(dctx, false, way, id, NULL); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire %s select " _RED_("error"), DesfireSelectWayToStr(way)); + return 202; + } + if (verbose) + PrintAndLogEx(INFO, "%s is " _GREEN_("selected"), DesfireWayIDStr(way, id)); + } + } else if (way == ISW6bAID && dctx->cmdSet == DCCISO) { dctx->cmdSet = DCCNativeISO; if (verbose) PrintAndLogEx(INFO, "Select via " _CYAN_("native iso wrapping") " interface"); diff --git a/client/src/mifare/desfirecrypto.c b/client/src/mifare/desfirecrypto.c index 335a87f4d..c7fbbb2da 100644 --- a/client/src/mifare/desfirecrypto.c +++ b/client/src/mifare/desfirecrypto.c @@ -101,8 +101,17 @@ void DesfireSetCommMode(DesfireContext_t *ctx, DesfireCommunicationMode commMode void DesfireSetKdf(DesfireContext_t *ctx, uint8_t kdfAlgo, uint8_t *kdfInput, uint8_t kdfInputLen) { ctx->kdfAlgo = kdfAlgo; ctx->kdfInputLen = kdfInputLen; - if (kdfInputLen) { + if (kdfInputLen) memcpy(ctx->kdfInput, kdfInput, kdfInputLen); +} + +void DesfireSetDFName(DesfireContext_t *ctx, uint8_t *dfname, uint8_t dfnameLen) { + ctx->selectedDFNameLen = 0; + memset(ctx->selectedDFName, 0, sizeof(ctx->selectedDFName)); + + if (dfname && dfnameLen > 0 && dfnameLen <= 16) { + ctx->selectedDFNameLen = dfnameLen; + memcpy(ctx->selectedDFName, dfname, dfnameLen); } } diff --git a/client/src/mifare/desfirecrypto.h b/client/src/mifare/desfirecrypto.h index 464fb98ee..c6b446705 100644 --- a/client/src/mifare/desfirecrypto.h +++ b/client/src/mifare/desfirecrypto.h @@ -74,6 +74,9 @@ typedef struct { bool isoChaining; bool appSelected; // for iso auth uint32_t selectedAID; + + uint8_t selectedDFName[16]; + uint8_t selectedDFNameLen; uint8_t uid[10]; uint8_t uidlen; @@ -97,6 +100,7 @@ void DesfireSetCommandSet(DesfireContext_t *ctx, DesfireCommandSet cmdSet); void DesfireSetCommMode(DesfireContext_t *ctx, DesfireCommunicationMode commMode); void DesfireSetSecureChannel(DesfireContext_t *ctx, DesfireSecureChannel schann); void DesfireSetKdf(DesfireContext_t *ctx, uint8_t kdfAlgo, uint8_t *kdfInput, uint8_t kdfInputLen); +void DesfireSetDFName(DesfireContext_t *ctx, uint8_t *dfname, uint8_t dfnameLen); bool DesfireIsAuthenticated(DesfireContext_t *dctx); size_t DesfireGetMACLength(DesfireContext_t *ctx); From 4af87a2f914d9b17eddc2be59aeaf04cff839115 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 20 Aug 2025 10:53:03 +0200 Subject: [PATCH 146/149] improve instructions --- Makefile.platform.sample | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.platform.sample b/Makefile.platform.sample index fb5114f5f..0ba8d8603 100644 --- a/Makefile.platform.sample +++ b/Makefile.platform.sample @@ -5,7 +5,7 @@ # Comment the line below and uncomment further down according to which device you have PLATFORM=PM3RDV4 -# For PM3 Easy: +# For PM3 RDV1, RDV2, Easy or rysccorps etc # uncomment the line below #PLATFORM=PM3GENERIC @@ -23,7 +23,7 @@ PLATFORM=PM3RDV4 #PLATFORM_EXTRAS=FLASH #PLATFORM_EXTRAS=SMARTCARD #PLATFORM_EXTRAS=BTADDON FLASH -#STANDALONE=LF_SAMYRUN +STANDALONE=HF_UNISNIFF # Uncomment the line below to set the correct LED order on board Proxmark3 Easy From e578d75e6657bd545a666aacbe03de64b284bf8d Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 20 Aug 2025 10:55:49 +0200 Subject: [PATCH 147/149] style --- client/src/cmdhfmfdes.c | 10 +-- client/src/cmdlfhitag.c | 4 +- client/src/cmdlfhitaghts.c | 4 +- client/src/cmdlfhitagu.c | 4 +- client/src/cmdlfpyramid.c | 15 +++-- client/src/mifare/desfirecore.c | 4 +- client/src/mifare/desfirecrypto.c | 2 +- client/src/mifare/desfirecrypto.h | 2 +- client/src/pm3line_vocabulary.h | 1 + doc/commands.json | 107 ++++++++++++++++++++++-------- doc/commands.md | 1 + 11 files changed, 104 insertions(+), 50 deletions(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 220a22d78..b13989119 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -3154,7 +3154,7 @@ static int CmdHF14ADesGetKeyVersions(const char *Cmd) { arg_str0(NULL, "keynum", "", "Key number/count (1 hex byte). (def: 0x00)"), arg_str0(NULL, "keyset", "", "Keyset number (1 hex byte)"), arg_lit0(NULL, "no-auth", "Execute without authentication"), - + arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -3199,7 +3199,7 @@ static int CmdHF14ADesGetKeyVersions(const char *Cmd) { DropField(); PrintAndLogEx(FAILED, "Select or authentication %s " _RED_("failed") ". Result [%d] %s", DesfireWayIDStr(selectway, id), res, DesfireAuthErrorToStr(res)); return res; - } + } uint8_t buf[APDU_RES_LEN] = {0}; size_t buflen = 0; @@ -3509,7 +3509,7 @@ static int CmdHF14ADesGetFileIDs(const char *Cmd) { PrintAndLogEx(FAILED, "Select or authentication %s " _RED_("failed") ". Result [%d] %s", DesfireWayIDStr(selectway, id), res, DesfireAuthErrorToStr(res)); return res; } - + uint8_t buf[APDU_RES_LEN] = {0}; size_t buflen = 0; @@ -4235,7 +4235,7 @@ static int CmdHF14ADesCreateRecordFile(const char *Cmd) { DesfireContext_t dctx; int securechann = defaultSecureChannel; uint32_t appid = 0x000000; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0,&securechann, DCMMACed, &appid, NULL); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, &securechann, DCMMACed, &appid, NULL); if (res) { CLIParserFree(ctx); return res; @@ -4460,7 +4460,7 @@ static int CmdHF14ADesDeleteFile(const char *Cmd) { int securechann = defaultSecureChannel; uint32_t id = 0x000000; DesfireISOSelectWay selectway = ISW6bAID; - int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0,&securechann, DCMMACed, &id, &selectway); + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, &securechann, DCMMACed, &id, &selectway); if (res) { CLIParserFree(ctx); return res; diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index e75cd0804..dddad1bab 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -2428,8 +2428,8 @@ int ht2_read_uid(void) { return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%08X"), uid); - PrintAndLogEx(SUCCESS, "TYPE... " _GREEN_("%s"), getHitagTypeStr(uid)); + PrintAndLogEx(SUCCESS, "UID....... " _GREEN_("%08X"), uid); + PrintAndLogEx(SUCCESS, "TYPE...... " _GREEN_("%s"), getHitagTypeStr(uid)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfhitaghts.c b/client/src/cmdlfhitaghts.c index 8af7dc998..de6efcae3 100644 --- a/client/src/cmdlfhitaghts.c +++ b/client/src/cmdlfhitaghts.c @@ -182,8 +182,8 @@ int read_hts_uid(void) { return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%08X"), uid); - PrintAndLogEx(SUCCESS, "TYPE... " _GREEN_("%s"), hts_get_type_str(uid)); + PrintAndLogEx(SUCCESS, "UID....... " _GREEN_("%08X"), uid); + PrintAndLogEx(SUCCESS, "TYPE...... " _GREEN_("%s"), hts_get_type_str(uid)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfhitagu.c b/client/src/cmdlfhitagu.c index 9a561651c..59e72aebe 100644 --- a/client/src/cmdlfhitagu.c +++ b/client/src/cmdlfhitagu.c @@ -197,8 +197,8 @@ int read_htu_uid(void) { return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%012llX"), uid); - // PrintAndLogEx(SUCCESS, "TYPE... " _GREEN_("%s"), htu_get_type_str(uid)); + PrintAndLogEx(SUCCESS, "UID....... " _GREEN_("%012llX"), uid); + // PrintAndLogEx(SUCCESS, "TYPE...... " _GREEN_("%s"), htu_get_type_str(uid)); return PM3_SUCCESS; } diff --git a/client/src/cmdlfpyramid.c b/client/src/cmdlfpyramid.c index e763c68e3..e29453263 100644 --- a/client/src/cmdlfpyramid.c +++ b/client/src/cmdlfpyramid.c @@ -498,25 +498,28 @@ int getPyramidBits(uint32_t fc, uint32_t cn, uint8_t *pyramidBits) { // FSK Demod then try to locate a Farpointe Data (pyramid) ID int detectPyramid(uint8_t *dest, size_t *size, int *waveStartIdx) { - //make sure buffer has data + // make sure buffer has data if (*size < 128 * 50) return -1; - //test samples are not just noise + // test samples are not just noise if (getSignalProperties()->isnoise) return -2; // FSK demodulator RF/50 FSK 10,8 *size = fskdemod(dest, *size, 50, 1, 10, 8, waveStartIdx); // pyramid fsk2 - //did we get a good demod? + // did we get a good demod? if (*size < 128) return -3; size_t startIdx = 0; uint8_t preamble[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}; - if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx)) - return -4; //preamble not found + if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx)) { + return -4; // preamble not found + } // wrong size? (between to preambles) - if (*size < 128) return -5; + if (*size < 128) { + return -5; + } return (int)startIdx; } diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index 1b82bd8b2..9d6fc5084 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -1100,7 +1100,7 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s DesfirePrintContext(dctx); int res = 0; - + // Handle DF Name selection if it's present in the context if (dctx->selectedDFNameLen > 0) { // Select DF by name using ISO7816 SELECT @@ -1114,7 +1114,7 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s if (verbose) { PrintAndLogEx(INFO, "DF %s is " _GREEN_("selected"), sprint_hex(dctx->selectedDFName, dctx->selectedDFNameLen)); } - + // If both dfname and aid are specified, now also select by AID if (way == ISW6bAID && id != 0x000000) { if (dctx->cmdSet == DCCISO) { diff --git a/client/src/mifare/desfirecrypto.c b/client/src/mifare/desfirecrypto.c index c7fbbb2da..b0097aab7 100644 --- a/client/src/mifare/desfirecrypto.c +++ b/client/src/mifare/desfirecrypto.c @@ -108,7 +108,7 @@ void DesfireSetKdf(DesfireContext_t *ctx, uint8_t kdfAlgo, uint8_t *kdfInput, ui void DesfireSetDFName(DesfireContext_t *ctx, uint8_t *dfname, uint8_t dfnameLen) { ctx->selectedDFNameLen = 0; memset(ctx->selectedDFName, 0, sizeof(ctx->selectedDFName)); - + if (dfname && dfnameLen > 0 && dfnameLen <= 16) { ctx->selectedDFNameLen = dfnameLen; memcpy(ctx->selectedDFName, dfname, dfnameLen); diff --git a/client/src/mifare/desfirecrypto.h b/client/src/mifare/desfirecrypto.h index c6b446705..363cef461 100644 --- a/client/src/mifare/desfirecrypto.h +++ b/client/src/mifare/desfirecrypto.h @@ -74,7 +74,7 @@ typedef struct { bool isoChaining; bool appSelected; // for iso auth uint32_t selectedAID; - + uint8_t selectedDFName[16]; uint8_t selectedDFNameLen; diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index 3bf14d7a0..8c106751f 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -477,6 +477,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf mfdes getkeyversions" }, { 0, "hf mfdes getfileids" }, { 0, "hf mfdes getfileisoids" }, + { 0, "hf mfdes lsfile" }, { 0, "hf mfdes lsfiles" }, { 0, "hf mfdes dump" }, { 0, "hf mfdes createfile" }, diff --git a/doc/commands.json b/doc/commands.json index 98afb786e..564f068d7 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -5680,7 +5680,8 @@ "hf mfdes auth -n 0 -t des -k 0000000000000000 --kdf none -> select PICC level and authenticate with key num=0, key type=des, key=00..00 and key derivation = none", "hf mfdes auth -n 0 -t aes -k 00000000000000000000000000000000 -> select PICC level and authenticate with key num=0, key type=aes, key=00..00 and key derivation = none", "hf mfdes auth -n 0 -t des -k 0000000000000000 --save -> select PICC level and authenticate and in case of successful authentication - save channel parameters to defaults", - "hf mfdes auth --aid 123456 -> select application 123456 and authenticate via parameters from `default` command" + "hf mfdes auth --aid 123456 -> select application 123456 and authenticate via parameters from `default` command", + "hf mfdes auth --dfname D2760000850100 -n 0 -t aes -k 00000000000000000000000000000000 -> select DF by name and authenticate" ], "offline": false, "options": [ @@ -5697,9 +5698,10 @@ "--schann Secure channel", "--aid Application ID of application for some parameters (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian)", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "--save saves channels parameters to defaults if authentication succeeds" ], - "usage": "hf mfdes auth [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--save]" + "usage": "hf mfdes auth [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [--save]" }, "hf mfdes bruteaid": { "command": "hf mfdes bruteaid", @@ -6190,10 +6192,11 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian).", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "-f, --file Filename of dictionary", "--save Save found key and parameters to defaults" ], - "usage": "hf mfdes detect [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [-f ] [--save]" + "usage": "hf mfdes detect [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [-f ] [--save]" }, "hf mfdes dump": { "command": "hf mfdes dump", @@ -6216,10 +6219,11 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian)", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "-l, --length Maximum length for read data files (3 hex bytes, big endian)", "--no-auth Execute without authentication" ], - "usage": "hf mfdes dump [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [-l ] [--no-auth]" + "usage": "hf mfdes dump [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [-l ] [--no-auth]" }, "hf mfdes formatpicc": { "command": "hf mfdes formatpicc", @@ -6263,9 +6267,10 @@ "-m, --cmode Communicaton mode", "-c, --ccset Communicaton command set", "--schann Secure channel", - "--no-auth Execute without authentication" + "--no-auth Execute without authentication", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)" ], - "usage": "hf mfdes getfreemem [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--no-auth]" + "usage": "hf mfdes getfreemem [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--no-auth] [--dfname ]" }, "hf mfdes getaids": { "command": "hf mfdes getaids", @@ -6318,7 +6323,8 @@ "description": "Get File IDs list from card. Master key needs to be provided or flag --no-auth set.", "notes": [ "hf mfdes getfileids --aid 123456 -> execute with defaults from `default` command", - "hf mfdes getfileids -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 -> execute with default factory setup" + "hf mfdes getfileids -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 -> execute with default factory setup", + "hf mfdes getfileids --dfname D2760000850100 -> select DF by name and get file IDs" ], "offline": false, "options": [ @@ -6335,9 +6341,10 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian).", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "--no-auth Execute without authentication" ], - "usage": "hf mfdes getfileids [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--no-auth]" + "usage": "hf mfdes getfileids [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [--no-auth]" }, "hf mfdes getfileisoids": { "command": "hf mfdes getfileisoids", @@ -6346,7 +6353,8 @@ "hf mfdes getfileisoids --aid 123456 -> execute with defaults from `default` command", "hf mfdes getfileisoids -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 -> execute with default factory setup", "hf mfdes getfileisoids --isoid df01 -> get iso file ids from Desfire Light with factory card settings", - "hf mfdes getfileisoids --isoid df01 --schann lrp -t aes -> get iso file ids from Desfire Light via lrp channel with default key authentication" + "hf mfdes getfileisoids --isoid df01 --schann lrp -t aes -> get iso file ids from Desfire Light via lrp channel with default key authentication", + "hf mfdes getfileisoids --dfname D2760000850100 -> select DF by name and get file ISO IDs" ], "offline": false, "options": [ @@ -6363,9 +6371,10 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian).", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "--no-auth Execute without authentication" ], - "usage": "hf mfdes getfileisoids [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--no-auth]" + "usage": "hf mfdes getfileisoids [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [--no-auth]" }, "hf mfdes getfilesettings": { "command": "hf mfdes getfilesettings", @@ -6373,7 +6382,8 @@ "notes": [ "hf mfdes getfilesettings --aid 123456 --fid 01 -> execute with defaults from `default` command", "hf mfdes getfilesettings --isoid df01 --fid 00 --no-auth -> get file settings with select by iso id", - "hf mfdes getfilesettings -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 --fid 01 -> execute with default factory setup" + "hf mfdes getfilesettings -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 --fid 01 -> execute with default factory setup", + "hf mfdes getfilesettings --dfname D2760000850100 --fid 01 -> select DF by name and get file settings" ], "offline": false, "options": [ @@ -6390,17 +6400,19 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian)", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "--fid File ID (1 hex byte). (def: 1)", "--no-auth Execute without authentication" ], - "usage": "hf mfdes getfilesettings [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--fid ] [--no-auth]" + "usage": "hf mfdes getfilesettings [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [--fid ] [--no-auth]" }, "hf mfdes getkeysettings": { "command": "hf mfdes getkeysettings", "description": "Get key settings for card level or application level.", "notes": [ "hf mfdes getkeysettings -> get picc key settings with default key/channel setup", - "hf mfdes getkeysettings --aid 123456 -> get app 123456 key settings with default key/channel setup" + "hf mfdes getkeysettings --aid 123456 -> get app 123456 key settings with default key/channel setup", + "hf mfdes getkeysettings --dfname D2760000850100 -> select DF by name and get key settings" ], "offline": false, "options": [ @@ -6415,9 +6427,10 @@ "-m, --cmode Communicaton mode", "-c, --ccset Communicaton command set", "--schann Secure channel", - "--aid Application ID (3 hex bytes, big endian)" + "--aid Application ID (3 hex bytes, big endian)", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)" ], - "usage": "hf mfdes getkeysettings [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ]" + "usage": "hf mfdes getkeysettings [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--dfname ]" }, "hf mfdes getkeyversions": { "command": "hf mfdes getkeyversions", @@ -6426,7 +6439,8 @@ "--keynum parameter: App level: key number. PICC level: 00..0d - keys count, 21..23 vc keys, default 0x00.", "hf mfdes getkeyversions --keynum 00 -> get picc master key version with default key/channel setup", "hf mfdes getkeyversions --aid 123456 --keynum 0d -> get app 123456 all key versions with default key/channel setup", - "hf mfdes getkeyversions --aid 123456 --keynum 0d --no-auth -> get key version without authentication" + "hf mfdes getkeyversions --aid 123456 --keynum 0d --no-auth -> get key version without authentication", + "hf mfdes getkeyversions --dfname D2760000850100 --keynum 00 -> select DF by name and get key versions" ], "offline": false, "options": [ @@ -6443,18 +6457,20 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian).", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "--keynum Key number/count (1 hex byte). (def: 0x00)", "--keyset Keyset number (1 hex byte)", "--no-auth Execute without authentication" ], - "usage": "hf mfdes getkeyversions [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--keynum ] [--keyset ] [--no-auth]" + "usage": "hf mfdes getkeyversions [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [--keynum ] [--keyset ] [--no-auth]" }, "hf mfdes getuid": { "command": "hf mfdes getuid", "description": "Get UID from card. Get the real UID if the random UID bit is on and get the same UID as in anticollision if not. Any card's key needs to be provided.", "notes": [ "hf mfdes getuid -> execute with default factory setup", - "hf mfdes getuid --isoid df01 -t aes --schan lrp -> for desfire lights default settings" + "hf mfdes getuid --isoid df01 -t aes --schan lrp -> for desfire lights default settings", + "hf mfdes getuid --dfname D2760000850100 -> select DF by name and get UID" ], "offline": false, "options": [ @@ -6470,9 +6486,10 @@ "-c, --ccset Communicaton command set", "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", - "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian)" + "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian)", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)" ], - "usage": "hf mfdes getuid [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ]" + "usage": "hf mfdes getuid [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ]" }, "hf mfdes help": { "command": "hf mfdes help", @@ -6512,7 +6529,8 @@ "description": "Show application list. Master key needs to be provided or flag --no-auth set (depend on cards settings).", "notes": [ "hf mfdes lsapp -> show application list with defaults from `default` command", - "hf mfdes lsapp --files -> show application list and show each file type/settings/etc" + "hf mfdes lsapp --files -> show application list and show each file type/settings/etc", + "hf mfdes lsapp --dfname D2760000850100 -> list apps after selecting DF by name" ], "offline": false, "options": [ @@ -6529,16 +6547,18 @@ "--schann Secure channel", "--no-auth Execute without authentication", "--no-deep not to check authentication commands that avail for any application", - "--files scan files and print file settings" + "--files scan files and print file settings", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)" ], - "usage": "hf mfdes lsapp [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--no-auth] [--no-deep] [--files]" + "usage": "hf mfdes lsapp [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--no-auth] [--no-deep] [--files] [--dfname ]" }, - "hf mfdes lsfiles": { - "command": "hf mfdes lsfiles", + "hf mfdes lsfile": { + "command": "hf mfdes lsfile", "description": "This commands List files inside application AID / ISOID. Master key needs to be provided or flag --no-auth set (depend on cards settings).", "notes": [ "hf mfdes lsfiles --aid 123456 -> AID 123456, list files using `default` command creds", - "hf mfdes lsfiles --isoid df01 --no-auth -> list files for DESFire light" + "hf mfdes lsfiles --isoid df01 --no-auth -> list files for DESFire light", + "hf mfdes lsfiles --dfname D2760000850100 -> select DF by name and list files" ], "offline": false, "options": [ @@ -6555,9 +6575,38 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian)", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", "--no-auth Execute without authentication" ], - "usage": "hf mfdes lsfiles [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--no-auth]" + "usage": "hf mfdes lsfiles [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [--no-auth]" + }, + "hf mfdes lsfiles": { + "command": "hf mfdes lsfiles", + "description": "This commands List files inside application AID / ISOID. Master key needs to be provided or flag --no-auth set (depend on cards settings).", + "notes": [ + "hf mfdes lsfiles --aid 123456 -> AID 123456, list files using `default` command creds", + "hf mfdes lsfiles --isoid df01 --no-auth -> list files for DESFire light", + "hf mfdes lsfiles --dfname D2760000850100 -> select DF by name and list files" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-a, --apdu Show APDU requests and responses", + "-v, --verbose Verbose output", + "-n, --keyno Key number", + "-t, --algo Crypt algo", + "-k, --key Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)", + "--kdf Key Derivation Function (KDF)", + "-i, --kdfi KDF input (1-31 hex bytes)", + "-m, --cmode Communicaton mode", + "-c, --ccset Communicaton command set", + "--schann Secure channel", + "--aid Application ID (3 hex bytes, big endian)", + "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian)", + "--dfname Application ISO DF Name (5-16 hex bytes, big endian)", + "--no-auth Execute without authentication" + ], + "usage": "hf mfdes lsfiles [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dfname ] [--no-auth]" }, "hf mfdes mad": { "command": "hf mfdes mad", @@ -13487,8 +13536,8 @@ } }, "metadata": { - "commands_extracted": 775, + "commands_extracted": 780, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-08-12T16:26:32" + "extracted_on": "2025-08-20T08:39:30" } } diff --git a/doc/commands.md b/doc/commands.md index 6fbd8f18f..fbae0a050 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -672,6 +672,7 @@ Check column "offline" for their availability. |`hf mfdes getkeyversions`|N |`Get Key Versions` |`hf mfdes getfileids `|N |`Get File IDs list` |`hf mfdes getfileisoids `|N |`Get File ISO IDs list` +|`hf mfdes lsfile `|N |`Show all files list` |`hf mfdes lsfiles `|N |`Show all files list` |`hf mfdes dump `|N |`Dump all files` |`hf mfdes createfile `|N |`Create Standard/Backup File` From b443f6327cdd64bfef35d2eac88a5a94657f5e6d Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 20 Aug 2025 16:20:39 +0200 Subject: [PATCH 148/149] not to forget about fpc dev mode --- Makefile.platform.sample | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.platform.sample b/Makefile.platform.sample index 0ba8d8603..b8850e76d 100644 --- a/Makefile.platform.sample +++ b/Makefile.platform.sample @@ -22,8 +22,8 @@ PLATFORM=PM3RDV4 #PLATFORM_EXTRAS=BTADDON #PLATFORM_EXTRAS=FLASH #PLATFORM_EXTRAS=SMARTCARD -#PLATFORM_EXTRAS=BTADDON FLASH -STANDALONE=HF_UNISNIFF +#PLATFORM_EXTRAS=BTADDON FPC_USART_DEV FLASH +#STANDALONE=HF_UNISNIFF # Uncomment the line below to set the correct LED order on board Proxmark3 Easy From 6ee974b935e5ddea11408151305680d2b4742697 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 20 Aug 2025 16:23:36 +0200 Subject: [PATCH 149/149] swapped from bigbuf malloc calls to calloc calls on device side. Now all allocations should start from a known state of memory --- CHANGELOG.md | 1 + armsrc/BigBuf.c | 14 +++++++------- armsrc/Standalone/hf_bog.c | 12 ++++++------ armsrc/Standalone/hf_colin.c | 8 ++++---- armsrc/Standalone/hf_iceclass.c | 6 ++++-- armsrc/Standalone/hf_mattyrun.c | 2 +- armsrc/Standalone/lf_icehid.c | 4 ++-- armsrc/Standalone/lf_tharexde.c | 4 ++-- armsrc/desfire_crypto.c | 2 +- armsrc/felica.c | 2 +- armsrc/fpgaloader.c | 5 +++-- armsrc/frozen.c | 2 +- armsrc/hfsnoop.c | 2 +- armsrc/iclass.c | 3 ++- armsrc/iso14443b.c | 14 +++++++------- armsrc/iso15693.c | 9 ++++----- armsrc/lfops.c | 4 ++-- armsrc/lfsampling.c | 4 ++-- armsrc/mifarecmd.c | 12 ++++++------ armsrc/mifaresim.c | 2 +- 20 files changed, 58 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4ca1a385..77ac7f3c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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] +- Changed from Bigbuf malloc to Bigbuf calloc calls on device side (@iceman1001) - Added `lf t55xx view` - now viewing of T55XX dump files is possible (@iceman1001) - Fixed `lf indala cone` - now writing the right bits when using `--fc` and `--cn` - Changed readline hack logic for async dbg msg to be ready for readline 8.3 (@doegox) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index b492b4205..d3478db53 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -354,7 +354,7 @@ int emlGet(uint8_t *out, uint32_t offset, uint32_t length) { tosend_t *get_tosend(void) { if (s_toSend.buf == NULL) { - s_toSend.buf = BigBuf_malloc(TOSEND_BUFFER_SIZE); + s_toSend.buf = BigBuf_calloc(TOSEND_BUFFER_SIZE); } return &s_toSend; } @@ -377,8 +377,9 @@ void tosend_stuffbit(int b) { s_toSend.bit = 0; } - if (b) + if (b) { s_toSend.buf[s_toSend.max] |= (1 << (7 - s_toSend.bit)); + } s_toSend.bit++; @@ -389,15 +390,14 @@ void tosend_stuffbit(int b) { dmabuf16_t *get_dma16(void) { if (s_dma_16.buf == NULL) { - s_dma_16.buf = (uint16_t *)BigBuf_malloc(DMA_BUFFER_SIZE * sizeof(uint16_t)); + s_dma_16.buf = (uint16_t *)BigBuf_calloc(DMA_BUFFER_SIZE * sizeof(uint16_t)); } - return &s_dma_16; } dmabuf8_t *get_dma8(void) { - if (s_dma_8.buf == NULL) - s_dma_8.buf = BigBuf_malloc(DMA_BUFFER_SIZE); - + if (s_dma_8.buf == NULL) { + s_dma_8.buf = BigBuf_calloc(DMA_BUFFER_SIZE); + } return &s_dma_8; } diff --git a/armsrc/Standalone/hf_bog.c b/armsrc/Standalone/hf_bog.c index fe607f021..d5d94fe0f 100644 --- a/armsrc/Standalone/hf_bog.c +++ b/armsrc/Standalone/hf_bog.c @@ -63,18 +63,18 @@ static void RAMFUNC SniffAndStore(uint8_t param) { set_tracing(true); // Array to store the authpwds - uint8_t *capturedPwds = BigBuf_malloc(4 * MAX_PWDS_PER_SESSION); + uint8_t *capturedPwds = BigBuf_calloc(4 * MAX_PWDS_PER_SESSION); // The command (reader -> tag) that we're receiving. - uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); - uint8_t *receivedCmdPar = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *receivedCmd = BigBuf_calloc(MAX_FRAME_SIZE); + uint8_t *receivedCmdPar = BigBuf_calloc(MAX_PARITY_SIZE); // The response (tag -> reader) that we're receiving. - uint8_t *receivedResp = BigBuf_malloc(MAX_FRAME_SIZE); - uint8_t *receivedRespPar = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *receivedResp = BigBuf_calloc(MAX_FRAME_SIZE); + uint8_t *receivedRespPar = BigBuf_calloc(MAX_PARITY_SIZE); // The DMA buffer, used to stream samples from the FPGA - uint8_t *dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); + uint8_t *dmaBuf = BigBuf_calloc(DMA_BUFFER_SIZE); uint8_t *data = dmaBuf; uint8_t previous_data = 0; diff --git a/armsrc/Standalone/hf_colin.c b/armsrc/Standalone/hf_colin.c index 0aa49c092..a5b761899 100644 --- a/armsrc/Standalone/hf_colin.c +++ b/armsrc/Standalone/hf_colin.c @@ -250,7 +250,7 @@ static char *ReadSchemasFromSPIFFS(char *filename) { int changed = rdv40_spiffs_lazy_mount(); uint32_t size = size_in_spiffs((char *)filename); - uint8_t *mem = BigBuf_malloc(size); + uint8_t *mem = BigBuf_calloc(size); rdv40_spiffs_read_as_filetype((char *)filename, (uint8_t *)mem, size, RDV40_SPIFFS_SAFETY_SAFE); if (changed) { @@ -292,7 +292,7 @@ static void ReadLastTagFromFlash(void) { DbprintfEx(FLAG_NEWLINE, "Button HELD ! Using LAST Known TAG for Simulation..."); cjSetCursLeft(); - uint8_t *mem = BigBuf_malloc(size); + uint8_t *mem = BigBuf_calloc(size); // this one will handle filetype (symlink or not) and resolving by itself rdv40_spiffs_read_as_filetype((char *)HFCOLIN_LASTTAG_SYMLINK, (uint8_t *)mem, len, RDV40_SPIFFS_SAFETY_SAFE); @@ -445,11 +445,11 @@ void RunMod(void) { }; // Can remember something like that in case of Bigbuf - keyBlock = BigBuf_malloc(ARRAYLEN(mfKeys) * 6); + keyBlock = BigBuf_calloc(ARRAYLEN(mfKeys) * MF_KEY_LENGTH); int mfKeysCnt = ARRAYLEN(mfKeys); for (int mfKeyCounter = 0; mfKeyCounter < mfKeysCnt; mfKeyCounter++) { - num_to_bytes(mfKeys[mfKeyCounter], 6, (uint8_t *)(keyBlock + mfKeyCounter * 6)); + num_to_bytes(mfKeys[mfKeyCounter], MF_KEY_LENGTH, (uint8_t *)(keyBlock + (mfKeyCounter * MF_KEY_LENGTH))); } // TODO : remember why we actually had need to initialize this array in such specific case diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c index c657d2e03..db7b922ac 100644 --- a/armsrc/Standalone/hf_iceclass.c +++ b/armsrc/Standalone/hf_iceclass.c @@ -238,7 +238,7 @@ static int reader_attack_mode(void) { BigBuf_free(); uint16_t mac_response_len = 0; - uint8_t *mac_responses = BigBuf_malloc(MAC_RESPONSES_SIZE); + uint8_t *mac_responses = BigBuf_calloc(MAC_RESPONSES_SIZE); iclass_simulate(ICLASS_SIM_MODE_READER_ATTACK, NUM_CSNS, false, csns, mac_responses, &mac_response_len); @@ -250,7 +250,7 @@ static int reader_attack_mode(void) { size_t dumplen = NUM_CSNS * 24; - uint8_t *dump = BigBuf_malloc(dumplen); + uint8_t *dump = BigBuf_calloc(dumplen); if (dump == false) { Dbprintf("Failed to allocate memory"); return PM3_EMALLOC; @@ -305,6 +305,7 @@ static int reader_dump_mode(void) { BigBuf_free(); uint8_t *card_data = BigBuf_malloc(ICLASS_16KS_SIZE); + // Don't use calloc since we set allocated memory to 0xFF's memset(card_data, 0xFF, ICLASS_16KS_SIZE); if (BUTTON_PRESS()) { @@ -442,6 +443,7 @@ static int dump_sim_mode(void) { BigBuf_free(); uint8_t *card_data = BigBuf_malloc(ICLASS_16KS_SIZE); + // Don't use calloc since we set allocated memory to 0xFF's memset(card_data, 0xFF, ICLASS_16KS_SIZE); if (BUTTON_PRESS()) { diff --git a/armsrc/Standalone/hf_mattyrun.c b/armsrc/Standalone/hf_mattyrun.c index c68d12075..fa0710b02 100644 --- a/armsrc/Standalone/hf_mattyrun.c +++ b/armsrc/Standalone/hf_mattyrun.c @@ -247,7 +247,7 @@ void RunMod(void) { // usb_disable(); // Allocate dictionary buffer - uint64_t *const mfcKeys = (uint64_t *)BigBuf_malloc( + uint64_t *const mfcKeys = (uint64_t *)BigBuf_calloc( sizeof(uint64_t) * (ARRAYLEN(MATTYRUN_MFC_ESSENTIAL_KEYS) + ARRAYLEN(MATTYRUN_MFC_DEFAULT_KEYS) + MIFARE_4K_MAXSECTOR * 2)); diff --git a/armsrc/Standalone/lf_icehid.c b/armsrc/Standalone/lf_icehid.c index 05cf039c5..c44069a12 100644 --- a/armsrc/Standalone/lf_icehid.c +++ b/armsrc/Standalone/lf_icehid.c @@ -199,7 +199,7 @@ static uint32_t IceIOdemod(void) { size_t size = MIN(12000, BigBuf_max_traceLen()); -// uint8_t *dest = BigBuf_malloc(size); +// uint8_t *dest = BigBuf_calloc(size); uint8_t *dest = BigBuf_get_addr(); //fskdemod and get start index @@ -243,7 +243,7 @@ static uint32_t IceHIDDemod(void) { // large enough to catch 2 sequences of largest format // size_t size = 50 * 128 * 2; // 12800 bytes size_t size = MIN(12800, BigBuf_max_traceLen()); - //uint8_t *dest = BigBuf_malloc(size); + //uint8_t *dest = BigBuf_calloc(size); uint8_t *dest = BigBuf_get_addr(); // FSK demodulator diff --git a/armsrc/Standalone/lf_tharexde.c b/armsrc/Standalone/lf_tharexde.c index b46ea69d8..735270328 100644 --- a/armsrc/Standalone/lf_tharexde.c +++ b/armsrc/Standalone/lf_tharexde.c @@ -103,9 +103,9 @@ static bool get_input_data_from_file(uint32_t *tag, char *inputfile) { if (exists_in_spiffs(inputfile)) { uint32_t size = size_in_spiffs(inputfile); - uint8_t *mem = BigBuf_malloc(size); + uint8_t *mem = BigBuf_calloc(size); - Dbprintf(_YELLOW_("found input file %s"), inputfile); + Dbprintf("found input file `" _YELLOW_("%s") "`", inputfile); rdv40_spiffs_read_as_filetype(inputfile, mem, size, RDV40_SPIFFS_SAFETY_SAFE); diff --git a/armsrc/desfire_crypto.c b/armsrc/desfire_crypto.c index dccbd7bb6..b27d19595 100644 --- a/armsrc/desfire_crypto.c +++ b/armsrc/desfire_crypto.c @@ -334,7 +334,7 @@ void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t le return; } - uint8_t *buffer = BigBuf_malloc(padded_data_length(len, kbs)); + uint8_t *buffer = BigBuf_calloc(padded_data_length(len, kbs)); memcpy(buffer, data, len); diff --git a/armsrc/felica.c b/armsrc/felica.c index b7f8b01c0..d924cc0b7 100644 --- a/armsrc/felica.c +++ b/armsrc/felica.c @@ -497,7 +497,7 @@ static void iso18092_setup(uint8_t fpga_minor_mode) { BigBuf_Clear_ext(false); // Initialize Demod and Uart structs - // DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); + // DemodInit(BigBuf_calloc(MAX_FRAME_SIZE)); FelicaFrameinit(BigBuf_calloc(FELICA_MAX_FRAME_SIZE)); felica_nexttransfertime = 2 * DELAY_ARM2AIR_AS_READER; // 418 diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index d9e941705..b3d739a6d 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -523,10 +523,11 @@ void FpgaDownloadAndGo(int bitstream_target) { lz4_stream_t compressed_fpga_stream; LZ4_streamDecode_t lz4StreamDecode_body = {{ 0 }}; compressed_fpga_stream.lz4StreamDecode = &lz4StreamDecode_body; - uint8_t *output_buffer = BigBuf_malloc(FPGA_RING_BUFFER_BYTES); + uint8_t *output_buffer = BigBuf_calloc(FPGA_RING_BUFFER_BYTES); - if (!reset_fpga_stream(bitstream_target, &compressed_fpga_stream, output_buffer)) + if (reset_fpga_stream(bitstream_target, &compressed_fpga_stream, output_buffer) == false) { return; + } uint32_t bitstream_length; if (bitparse_find_section(bitstream_target, 'e', &bitstream_length, &compressed_fpga_stream, output_buffer)) { diff --git a/armsrc/frozen.c b/armsrc/frozen.c index 874e81988..b4f57aa74 100644 --- a/armsrc/frozen.c +++ b/armsrc/frozen.c @@ -26,7 +26,7 @@ #include "nprintf.h" #include "BigBuf.h" -#define malloc(X) BigBuf_malloc(X) +#define malloc(X) BigBuf_calloc(X) #define free(X) #if !defined(WEAK) diff --git a/armsrc/hfsnoop.c b/armsrc/hfsnoop.c index 5443a617f..939ee4319 100644 --- a/armsrc/hfsnoop.c +++ b/armsrc/hfsnoop.c @@ -107,7 +107,7 @@ int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len, uint SpinDelay(100); *len = BigBuf_max_traceLen(); - uint8_t *mem = BigBuf_malloc(*len); + uint8_t *mem = BigBuf_calloc(*len); uint32_t trigger_cnt = 0; uint16_t r = 0, interval = 0; diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 3eb9df188..4a1563aea 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -917,8 +917,9 @@ send: LEDsoff(); - if (button_pressed) + if (button_pressed) { DbpString("button pressed"); + } return button_pressed; } diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index ed440c0c0..0a324ff30 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -786,14 +786,14 @@ void SimulateIso14443bTag(const uint8_t *pupi) { // prepare "ATQB" tag answer (encoded): CodeIso14443bAsTag(respATQB, sizeof(respATQB)); - uint8_t *encodedATQB = BigBuf_malloc(ts->max); + uint8_t *encodedATQB = BigBuf_calloc(ts->max); uint16_t encodedATQBLen = ts->max; memcpy(encodedATQB, ts->buf, ts->max); // prepare "OK" tag answer (encoded): CodeIso14443bAsTag(respOK, sizeof(respOK)); - uint8_t *encodedOK = BigBuf_malloc(ts->max); + uint8_t *encodedOK = BigBuf_calloc(ts->max); uint16_t encodedOKLen = ts->max; memcpy(encodedOK, ts->buf, ts->max); @@ -988,18 +988,18 @@ void Simulate_iso14443b_srx_tag(uint8_t *uid) { tosend_t *ts = get_tosend(); - uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); + uint8_t *receivedCmd = BigBuf_calloc(MAX_FRAME_SIZE); // prepare "ATQB" tag answer (encoded): CodeIso14443bAsTag(respATQB, sizeof(respATQB)); - uint8_t *encodedATQB = BigBuf_malloc(ts->max); + uint8_t *encodedATQB = BigBuf_calloc(ts->max); uint16_t encodedATQBLen = ts->max; memcpy(encodedATQB, ts->buf, ts->max); // prepare "OK" tag answer (encoded): CodeIso14443bAsTag(respOK, sizeof(respOK)); - uint8_t *encodedOK = BigBuf_malloc(ts->max); + uint8_t *encodedOK = BigBuf_calloc(ts->max); uint16_t encodedOKLen = ts->max; memcpy(encodedOK, ts->buf, ts->max); @@ -2405,8 +2405,8 @@ void SniffIso14443b(void) { uint8_t ua_buf[MAX_FRAME_SIZE] = {0}; Uart14bInit(ua_buf); - //Demod14bInit(BigBuf_malloc(MAX_FRAME_SIZE), MAX_FRAME_SIZE); - //Uart14bInit(BigBuf_malloc(MAX_FRAME_SIZE)); + //Demod14bInit(BigBuf_calloc(MAX_FRAME_SIZE)); + //Uart14bInit(BigBuf_calloc(MAX_FRAME_SIZE)); // Set FPGA in the appropriate mode FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_SNIFF_IQ); diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index a1ff3c721..14e3c785f 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -180,8 +180,7 @@ static void CodeIso15693AsReaderEOF(void) { static int get_uid_slix(uint32_t start_time, uint32_t *eof_time, uint8_t *uid) { - uint8_t *answer = BigBuf_malloc(ISO15693_MAX_RESPONSE_LENGTH); - memset(answer, 0x00, ISO15693_MAX_RESPONSE_LENGTH); + uint8_t *answer = BigBuf_calloc(ISO15693_MAX_RESPONSE_LENGTH); start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; @@ -1484,7 +1483,7 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo bool gotFrame = false; // the decoder data structure - DecodeReader_t *dr = (DecodeReader_t *)BigBuf_malloc(sizeof(DecodeReader_t)); + DecodeReader_t *dr = (DecodeReader_t *)BigBuf_calloc(sizeof(DecodeReader_t)); DecodeReaderInit(dr, received, max_len, 0, NULL); // wait for last transfer to complete @@ -1589,7 +1588,7 @@ void AcquireRawAdcSamplesIso15693(void) { LED_A_ON(); - uint8_t *dest = BigBuf_malloc(4000); + uint8_t *dest = BigBuf_calloc(4096); // switch field on FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); @@ -2031,7 +2030,7 @@ void ReaderIso15693(iso15_card_select_t *p_card) { LED_A_ON(); set_tracing(true); - uint8_t *answer = BigBuf_malloc(ISO15693_MAX_RESPONSE_LENGTH); + uint8_t *answer = BigBuf_calloc(ISO15693_MAX_RESPONSE_LENGTH); memset(answer, 0x00, ISO15693_MAX_RESPONSE_LENGTH); // FIRST WE RUN AN INVENTORY TO GET THE TAG UID diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 9fd9d8f58..ac65c425c 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -340,7 +340,7 @@ t55xx_configurations_t *getT55xxConfig(void) { void loadT55xxConfig(void) { #ifdef WITH_FLASH - uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN); + uint8_t *buf = BigBuf_calloc(T55XX_CONFIG_LEN); uint32_t size = 0; if (exists_in_spiffs(T55XX_CONFIG_FILE)) { @@ -2912,7 +2912,7 @@ void Cotag(uint32_t arg0, bool ledcontrol) { break; } case 1: { - uint8_t *dest = BigBuf_malloc(COTAG_BITS); + uint8_t *dest = BigBuf_calloc(COTAG_BITS); uint16_t bits = doCotagAcquisitionManchester(dest, COTAG_BITS); reply_ng(CMD_LF_COTAG_READ, PM3_SUCCESS, dest, bits); break; diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index ffa1cbcd9..a07a6aae1 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -149,7 +149,7 @@ void initSampleBufferEx(uint32_t *sample_size, bool use_malloc) { data.buffer = BigBuf_get_addr(); } else { *sample_size = MIN(*sample_size, BigBuf_max_traceLen()); - data.buffer = BigBuf_malloc(*sample_size); + data.buffer = BigBuf_calloc(*sample_size); } } else { @@ -669,7 +669,7 @@ void doT55x7Acquisition(size_t sample_size, bool ledcontrol) { void doCotagAcquisition(void) { uint16_t bufsize = BigBuf_max_traceLen(); - uint8_t *dest = BigBuf_malloc(bufsize); + uint8_t *dest = BigBuf_calloc(bufsize); dest[0] = 0; diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index a9c0f68f1..13e728be8 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -2252,7 +2252,7 @@ OUT: bar |= ((uint16_t)(found[m] & 1) << j++); } - uint8_t *tmp = BigBuf_malloc(480 + 10); + uint8_t *tmp = BigBuf_calloc(480 + 10); memcpy(tmp, k_sector, sectorcnt * sizeof(sector_t)); num_to_bytes(foo, 8, tmp + 480); tmp[488] = bar & 0xFF; @@ -2409,7 +2409,7 @@ void MifareChkKeys_file(uint8_t *fn) { int changed = rdv40_spiffs_lazy_mount(); uint32_t size = size_in_spiffs((char *)fn); - uint8_t *mem = BigBuf_malloc(size); + uint8_t *mem = BigBuf_calloc(size); rdv40_spiffs_read_as_filetype((char *)fn, mem, size, RDV40_SPIFFS_SAFETY_SAFE); @@ -3609,13 +3609,13 @@ void MifareG4ReadBlk(uint8_t blockno, uint8_t *pwd, uint8_t workFlags) { int res = 0; int retval = PM3_SUCCESS; - uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); + uint8_t *buf = BigBuf_calloc(PM3_CMD_DATA_SIZE); if (buf == NULL) { retval = PM3_EMALLOC; goto OUT; } - uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *par = BigBuf_calloc(MAX_PARITY_SIZE); if (par == NULL) { retval = PM3_EMALLOC; goto OUT; @@ -3685,7 +3685,7 @@ void MifareG4WriteBlk(uint8_t blockno, uint8_t *pwd, uint8_t *data, uint8_t work int res = 0; int retval = PM3_SUCCESS; - uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); + uint8_t *buf = BigBuf_calloc(PM3_CMD_DATA_SIZE); if (buf == NULL) { retval = PM3_EMALLOC; goto OUT; @@ -3697,7 +3697,7 @@ void MifareG4WriteBlk(uint8_t blockno, uint8_t *pwd, uint8_t *data, uint8_t work goto OUT; } - uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *par = BigBuf_calloc(MAX_PARITY_SIZE); if (par == NULL) { retval = PM3_EMALLOC; goto OUT; diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index a276ce28c..b69ebe0c6 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -459,7 +459,7 @@ bool MifareSimInit(uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t sak, tag // 53 * 8 data bits, 53 * 1 parity bits, 18 start bits, 18 stop bits, 18 correction bits -> need 571 bytes buffer #define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 571 - uint8_t *free_buffer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); + uint8_t *free_buffer = BigBuf_calloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); // modulation buffer pointer and current buffer free space size uint8_t *free_buffer_pointer = free_buffer; size_t free_buffer_size = ALLOCATED_TAG_MODULATION_BUFFER_SIZE;