From 563abf66ed27b2d4e7168f153cfbdce264126dbe Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski <740168+henrygab@users.noreply.github.com> Date: Mon, 20 Oct 2025 17:58:59 -0700 Subject: [PATCH] Update id48lib * Add its own CMakeLists.txt * Add id48 unit tests for its recovery API * Add id48 unit tests for its generator API * Add id48 espresso files, generator for those files, and validation test * Proxmark3-specific: * update client/Makefile * update client/deps/id48lib.cmake * --- .gitignore | 6 +- CHANGELOG.md | 1 + Makefile.host | 3 + client/Makefile | 5 +- client/deps/id48/.gitignore | 52 ++ client/deps/id48/CMakeLists.txt | 13 + client/deps/id48/README.md | 22 + client/deps/id48/TECHNICAL.md | 116 +-- client/deps/id48/espresso/output_g1.pla | 46 + client/deps/id48/espresso/output_g2.pla | 46 + client/deps/id48/espresso/output_g3.pla | 46 + client/deps/id48/espresso/output_g4.pla | 46 + client/deps/id48/espresso/output_xx.pla | 270 ++++++ client/deps/id48/{ => public}/id48.h | 10 +- client/deps/id48/src/CMakeLists.txt | 25 + client/deps/id48/{ => src}/Makefile | 6 +- client/deps/id48/{ => src}/id48_data.c | 0 client/deps/id48/{ => src}/id48_generator.c | 0 client/deps/id48/{ => src}/id48_internals.h | 0 client/deps/id48/{ => src}/id48_recover.c | 0 client/deps/id48/tests/CMakeLists.txt | 6 + .../deps/id48/tests/espresso/CMakeLists.txt | 18 + client/deps/id48/tests/espresso/array_size2.h | 157 ++++ client/deps/id48/tests/espresso/main.c | 25 + .../id48/tests/espresso/verify_espresso.c | 817 ++++++++++++++++++ .../id48/tests/espresso/verify_espresso.h | 7 + .../deps/id48/tests/generator/CMakeLists.txt | 12 + .../deps/id48/tests/generator/array_size2.h | 157 ++++ client/deps/id48/tests/generator/main.c | 93 ++ .../deps/id48/tests/recovery/CMakeLists.txt | 12 + client/deps/id48/tests/recovery/array_size2.h | 157 ++++ client/deps/id48/tests/recovery/main.c | 173 ++++ client/deps/id48lib.cmake | 12 +- 33 files changed, 2284 insertions(+), 75 deletions(-) create mode 100644 client/deps/id48/.gitignore create mode 100644 client/deps/id48/CMakeLists.txt create mode 100644 client/deps/id48/espresso/output_g1.pla create mode 100644 client/deps/id48/espresso/output_g2.pla create mode 100644 client/deps/id48/espresso/output_g3.pla create mode 100644 client/deps/id48/espresso/output_g4.pla create mode 100644 client/deps/id48/espresso/output_xx.pla rename client/deps/id48/{ => public}/id48.h (97%) create mode 100644 client/deps/id48/src/CMakeLists.txt rename client/deps/id48/{ => src}/Makefile (66%) rename client/deps/id48/{ => src}/id48_data.c (100%) rename client/deps/id48/{ => src}/id48_generator.c (100%) rename client/deps/id48/{ => src}/id48_internals.h (100%) rename client/deps/id48/{ => src}/id48_recover.c (100%) create mode 100644 client/deps/id48/tests/CMakeLists.txt create mode 100644 client/deps/id48/tests/espresso/CMakeLists.txt create mode 100644 client/deps/id48/tests/espresso/array_size2.h create mode 100644 client/deps/id48/tests/espresso/main.c create mode 100644 client/deps/id48/tests/espresso/verify_espresso.c create mode 100644 client/deps/id48/tests/espresso/verify_espresso.h create mode 100644 client/deps/id48/tests/generator/CMakeLists.txt create mode 100644 client/deps/id48/tests/generator/array_size2.h create mode 100644 client/deps/id48/tests/generator/main.c create mode 100644 client/deps/id48/tests/recovery/CMakeLists.txt create mode 100644 client/deps/id48/tests/recovery/array_size2.h create mode 100644 client/deps/id48/tests/recovery/main.c diff --git a/.gitignore b/.gitignore index 37583d538..317f0d211 100644 --- a/.gitignore +++ b/.gitignore @@ -45,13 +45,15 @@ Makefile.platform.* # cmake CMakeFiles/ -deps/ client/CMakeFiles/ -client/deps/ +client/deps/** +!client/deps/id48/ +!client/deps/id48/** client/build/ client/android/build/ CMakeCache.txt *.cmake +.cache # Coverity cov-int/ diff --git a/CHANGELOG.md b/CHANGELOG.md index b5257f488..0e3462738 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] +- Updated id48lib (adds unit tests, better build management) - Added two trace files for Ultralight AES (@iceman1001) - Added support for Ultralight AES secure messaing in `hf 14a raw` (@iceman1001) - Added support for Ultralight AES secure messaging in `hf mfu info/rdbl/wrbl/dump/ndefread/wipe/setkey` (@iceman1001) diff --git a/Makefile.host b/Makefile.host index 8ca8bc50d..e89a69459 100644 --- a/Makefile.host +++ b/Makefile.host @@ -26,6 +26,9 @@ endif ifeq ($(DEFSBEENHERE),) -include ../../../Makefile.defs endif +ifeq ($(DEFSBEENHERE),) + -include ../../../../Makefile.defs +endif ifeq ($(DEFSBEENHERE),) $(error Can't find Makefile.defs) endif diff --git a/client/Makefile b/client/Makefile index ebf287d58..b4c2f7e0c 100644 --- a/client/Makefile +++ b/client/Makefile @@ -74,8 +74,9 @@ HARDNESTEDLIB = $(HARDNESTEDLIBPATH)/libhardnested.a HARDNESTEDLIBLD = ## ID48 -ID48LIBPATH = ./deps/id48 -ID48LIBINC = -I$(ID48LIBPATH) +## must be manually kept in sync with updates to deps/id48/CMakeLists.txt +ID48LIBPATH = ./deps/id48/src +ID48LIBINC = -I$(ID48LIBPATH)/../public ID48LIB = $(ID48LIBPATH)/libid48.a ID48LIBLD = diff --git a/client/deps/id48/.gitignore b/client/deps/id48/.gitignore new file mode 100644 index 000000000..3ad055ed7 --- /dev/null +++ b/client/deps/id48/.gitignore @@ -0,0 +1,52 @@ +# Now ignore any "build" directory anywhere under this directory +build/ +**/build/ + +*.log +*.eml +*.html +*.o +*.a +*.d +*.elf +*.s19 +*.map +*.bin +*.dll +*.moc.cpp +*.z +*.gz +*.Td +*.DS_Store +*.exe +*.dsym +*.json +*.old +*.swp +*.json.bak +*.pyc +*.bmp + + +# cmake +CMakeFiles/ +client/CMakeFiles/ +client/deps/** +!client/deps/id48/ +!client/deps/id48/** +client/build/ +client/android/build/ +CMakeCache.txt +*.cmake +.cache + +# Coverity +cov-int/ +.coverity.conf + +# .tmp files are created during compilation +*.tmp + +# local codeql +_codeql* +/codeql diff --git a/client/deps/id48/CMakeLists.txt b/client/deps/id48/CMakeLists.txt new file mode 100644 index 000000000..d49945510 --- /dev/null +++ b/client/deps/id48/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.21) + +project(id48_project LANGUAGES C CXX ASM) + +# Project-wide language standards (subdirectories inherit if not overridden) +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_subdirectory(src) +add_subdirectory(tests) + diff --git a/client/deps/id48/README.md b/client/deps/id48/README.md index 4c8eb4e65..491464f87 100644 --- a/client/deps/id48/README.md +++ b/client/deps/id48/README.md @@ -48,3 +48,25 @@ provided the first half of the key, and at least one successful authentication trio of nonce, challenge, and response, then the library can recover all potentially valid values for the second half of the key. + +## Building + +This project uses CMake, and thus should be easy to integrate +into any existing project using CMake. + +The following commands can be run from the root of this depot, +and places all build artifacts into `./build`: + +```bash +# Configure cmake for the current environment +# -S: explicitly specify the source directory +# -B: explicitly specify the directory for build artifacts +cmake -S . -B build + +# Optionally (recommended) do a clean build each time +cmake --build ./build --parallel --target clean + +# build everything +cmake --build ./build --parallel --target all +``` + diff --git a/client/deps/id48/TECHNICAL.md b/client/deps/id48/TECHNICAL.md index f94bb1c29..8ab0bcd80 100644 --- a/client/deps/id48/TECHNICAL.md +++ b/client/deps/id48/TECHNICAL.md @@ -160,63 +160,63 @@ of the key influences a given output bit. Start state + key bit --> output -| Start state | Key bit | Output | -|-------|-------|----------------| -| `s₀₀` | `K₃₉` | << hidden >> | -| `s₀₁` | `K₃₈` | << hidden >> | -| `s₀₂` | `K₃₇` | << hidden >> | -| `s₀₃` | `K₃₆` | << hidden >> | -| `s₀₄` | `K₃₅` | << hidden >> | -| `s₀₅` | `K₃₄` | << hidden >> | -| `s₀₆` | `K₃₃` | << hidden >> | -| `s₀₇` | `K₃₂` | `O₀₀ == frn₀₀` | -| `s₀₈` | `K₃₁` | `O₀₁ == frn₀₁` | -| `s₀₉` | `K₃₀` | `O₀₂ == frn₀₂` | -| `s₁₀` | `K₂₉` | `O₀₃ == frn₀₃` | -| `s₁₁` | `K₂₈` | `O₀₄ == frn₀₄` | -| `s₁₂` | `K₂₇` | `O₀₅ == frn₀₅` | -| `s₁₃` | `K₂₆` | `O₀₆ == frn₀₆` | -| `s₁₄` | `K₂₅` | `O₀₇ == frn₀₇` | -| `s₁₅` | `K₂₄` | `O₀₈ == frn₀₈` | -| `s₁₆` | `K₂₃` | `O₀₉ == frn₀₉` | -| `s₁₇` | `K₂₂` | `O₁₀ == frn₁₀` | -| `s₁₈` | `K₂₁` | `O₁₁ == frn₁₁` | -| `s₁₉` | `K₂₀` | `O₁₂ == frn₁₂` | -| `s₂₀` | `K₁₉` | `O₁₃ == frn₁₃` | -| `s₂₁` | `K₁₈` | `O₁₄ == frn₁₄` | -| `s₂₂` | `K₁₇` | `O₁₅ == frn₁₅` | -| `s₂₃` | `K₁₆` | `O₁₆ == frn₁₆` | -| `s₂₄` | `K₁₅` | `O₁₇ == frn₁₇` | -| `s₂₅` | `K₁₄` | `O₁₈ == frn₁₈` | -| `s₂₆` | `K₁₃` | `O₁₉ == frn₁₉` | -| `s₂₇` | `K₁₂` | `O₂₀ == frn₂₀` | -| `s₂₈` | `K₁₁` | `O₂₁ == frn₂₁` | -| `s₂₉` | `K₁₀` | `O₂₂ == frn₂₂` | -| `s₃₀` | `K₀₉` | `O₂₃ == frn₂₃` | -| `s₃₁` | `K₀₈` | `O₂₄ == frn₂₄` | -| `s₃₂` | `K₀₇` | `O₂₅ == frn₂₅` | -| `s₃₃` | `K₀₆` | `O₂₆ == frn₂₆` | -| `s₃₄` | `K₀₅` | `O₂₇ == frn₂₇` | -| `s₃₅` | `K₀₄` | `O₂₈ == grn₀₀` | -| `s₃₆` | `K₀₃` | `O₂₉ == grn₀₁` | -| `s₃₇` | `K₀₂` | `O₃₀ == grn₀₂` | -| `s₃₈` | `K₀₁` | `O₃₁ == grn₀₃` | -| `s₃₉` | `K₀₀` | `O₃₂ == grn₀₄` | -| `s₄₀` | `0₁₄` | `O₃₃ == grn₀₅` | -| `s₄₁` | `0₁₃` | `O₃₄ == grn₀₆` | -| `s₄₂` | `0₁₂` | `O₃₅ == grn₀₇` | -| `s₄₃` | `0₁₁` | `O₃₆ == grn₀₈` | -| `s₄₄` | `0₁₀` | `O₃₇ == grn₀₉` | -| `s₄₅` | `0₀₉` | `O₃₈ == grn₁₀` | -| `s₄₆` | `0₀₈` | `O₃₉ == grn₁₁` | -| `s₄₇` | `0₀₇` | `O₄₀ == grn₁₂` | -| `s₄₈` | `0₀₆` | `O₄₁ == grn₁₃` | -| `s₄₉` | `0₀₅` | `O₄₂ == grn₁₄` | -| `s₅₀` | `0₀₄` | `O₄₃ == grn₁₅` | -| `s₅₁` | `0₀₃` | `O₄₄ == grn₁₆` | -| `s₅₂` | `0₀₂` | `O₄₅ == grn₁₇` | -| `s₅₃` | `0₀₁` | `O₄₆ == grn₁₈` | -| `s₅₄` | `0₀₀` | `O₄₇ == grn₁₉` | +| Start state | Key bit | Output | Notes | +|-------|-------|----------------|-------| +| `s₀₀` | `K₃₉` | << hidden >> | Seven ... | +| `s₀₁` | `K₃₈` | << hidden >> | .. iterations ... | +| `s₀₂` | `K₃₇` | << hidden >> | .. before ... | +| `s₀₃` | `K₃₆` | << hidden >> | .. any ... | +| `s₀₄` | `K₃₅` | << hidden >> | .. output ... | +| `s₀₅` | `K₃₄` | << hidden >> | .. bits ... | +| `s₀₆` | `K₃₃` | << hidden >> | .. seen. | +| `s₀₇` | `K₃₂` | `O₀₀ == frn₀₀` | | +| `s₀₈` | `K₃₁` | `O₀₁ == frn₀₁` | | +| `s₀₉` | `K₃₀` | `O₀₂ == frn₀₂` | | +| `s₁₀` | `K₂₉` | `O₀₃ == frn₀₃` | | +| `s₁₁` | `K₂₈` | `O₀₄ == frn₀₄` | | +| `s₁₂` | `K₂₇` | `O₀₅ == frn₀₅` | | +| `s₁₃` | `K₂₆` | `O₀₆ == frn₀₆` | | +| `s₁₄` | `K₂₅` | `O₀₇ == frn₀₇` | | +| `s₁₅` | `K₂₄` | `O₀₈ == frn₀₈` | | +| `s₁₆` | `K₂₃` | `O₀₉ == frn₀₉` | | +| `s₁₇` | `K₂₂` | `O₁₀ == frn₁₀` | | +| `s₁₈` | `K₂₁` | `O₁₁ == frn₁₁` | | +| `s₁₉` | `K₂₀` | `O₁₂ == frn₁₂` | | +| `s₂₀` | `K₁₉` | `O₁₃ == frn₁₃` | | +| `s₂₁` | `K₁₈` | `O₁₄ == frn₁₄` | | +| `s₂₂` | `K₁₇` | `O₁₅ == frn₁₅` | | +| `s₂₃` | `K₁₆` | `O₁₆ == frn₁₆` | | +| `s₂₄` | `K₁₅` | `O₁₇ == frn₁₇` | | +| `s₂₅` | `K₁₄` | `O₁₈ == frn₁₈` | | +| `s₂₆` | `K₁₃` | `O₁₉ == frn₁₉` | | +| `s₂₇` | `K₁₂` | `O₂₀ == frn₂₀` | | +| `s₂₈` | `K₁₁` | `O₂₁ == frn₂₁` | | +| `s₂₉` | `K₁₀` | `O₂₂ == frn₂₂` | | +| `s₃₀` | `K₀₉` | `O₂₃ == frn₂₃` | | +| `s₃₁` | `K₀₈` | `O₂₄ == frn₂₄` | | +| `s₃₂` | `K₀₇` | `O₂₅ == frn₂₅` | | +| `s₃₃` | `K₀₆` | `O₂₆ == frn₂₆` | | +| `s₃₄` | `K₀₅` | `O₂₇ == frn₂₇` | Last key bit that affects `frn` | +| `s₃₅` | `K₀₄` | `O₂₈ == grn₀₀` | Five bits of key affect `grn` | +| `s₃₆` | `K₀₃` | `O₂₉ == grn₀₁` | | +| `s₃₇` | `K₀₂` | `O₃₀ == grn₀₂` | | +| `s₃₈` | `K₀₁` | `O₃₁ == grn₀₃` | | +| `s₃₉` | `K₀₀` | `O₃₂ == grn₀₄` | | +| `s₄₀` | `0₁₄` | `O₃₃ == grn₀₅` | Zero-fill start | +| `s₄₁` | `0₁₃` | `O₃₄ == grn₀₆` | | +| `s₄₂` | `0₁₂` | `O₃₅ == grn₀₇` | | +| `s₄₃` | `0₁₁` | `O₃₆ == grn₀₈` | | +| `s₄₄` | `0₁₀` | `O₃₇ == grn₀₉` | | +| `s₄₅` | `0₀₉` | `O₃₈ == grn₁₀` | | +| `s₄₆` | `0₀₈` | `O₃₉ == grn₁₁` | | +| `s₄₇` | `0₀₇` | `O₄₀ == grn₁₂` | | +| `s₄₈` | `0₀₆` | `O₄₁ == grn₁₃` | | +| `s₄₉` | `0₀₅` | `O₄₂ == grn₁₄` | | +| `s₅₀` | `0₀₄` | `O₄₃ == grn₁₅` | | +| `s₅₁` | `0₀₃` | `O₄₄ == grn₁₆` | | +| `s₅₂` | `0₀₂` | `O₄₅ == grn₁₇` | | +| `s₅₃` | `0₀₁` | `O₄₆ == grn₁₈` | | +| `s₅₄` | `0₀₀` | `O₄₇ == grn₁₉` | | @@ -525,7 +525,7 @@ FPGA logic if desired. There are sixteen (`16x`) blocks of memory, each storing a 16-bit value. Blocks may be protected (`P` column) as read-only (`RO`) or write-only (`WO`). -`Lock₀` === Disable writingPermanent write-prevention when set to 1. +`Lock₀` === Disable writing ... (?) write-prevention when set to 1. `Lock₁` === Pin-code lock | `Block` | `P` | `Purpose` | Bits (LSB first) | Bits (MSB first) | diff --git a/client/deps/id48/espresso/output_g1.pla b/client/deps/id48/espresso/output_g1.pla new file mode 100644 index 000000000..0b700629a --- /dev/null +++ b/client/deps/id48/espresso/output_g1.pla @@ -0,0 +1,46 @@ +# ################### +# Generating .PLA +# ################### + + +.i 5 +.o 1 +.ilb a l2 l3 l0 l6 +.ob g1 +00000 1 +00001 1 +00010 1 +00011 1 +00100 0 +00101 0 +00110 1 +00111 0 +01000 0 +01001 1 +01010 0 +01011 0 +01100 1 +01101 0 +01110 0 +01111 1 +10000 1 +10001 0 +10010 0 +10011 0 +10100 0 +10101 1 +10110 0 +10111 1 +11000 0 +11001 0 +11010 1 +11011 1 +11100 1 +11101 1 +11110 1 +11111 0 +.e + +# ################### +# END OF .PLA +# ################### diff --git a/client/deps/id48/espresso/output_g2.pla b/client/deps/id48/espresso/output_g2.pla new file mode 100644 index 000000000..60fde1794 --- /dev/null +++ b/client/deps/id48/espresso/output_g2.pla @@ -0,0 +1,46 @@ +# ################### +# Generating .PLA +# ################### + + +.i 5 +.o 1 +.ilb l5 b m0 l4 m1 +.ob g2 +00000 1 +00001 1 +00010 1 +00011 1 +00100 0 +00101 0 +00110 0 +00111 1 +01000 0 +01001 1 +01010 0 +01011 0 +01100 1 +01101 0 +01110 1 +01111 0 +10000 1 +10001 0 +10010 0 +10011 0 +10100 0 +10101 1 +10110 1 +10111 0 +11000 0 +11001 0 +11010 1 +11011 1 +11100 1 +11101 1 +11110 0 +11111 1 +.e + +# ################### +# END OF .PLA +# ################### diff --git a/client/deps/id48/espresso/output_g3.pla b/client/deps/id48/espresso/output_g3.pla new file mode 100644 index 000000000..e486ff013 --- /dev/null +++ b/client/deps/id48/espresso/output_g3.pla @@ -0,0 +1,46 @@ +# ################### +# Generating .PLA +# ################### + + +.i 5 +.o 1 +.ilb m5 c r1 m3 r3 +.ob g3 +00000 1 +00001 1 +00010 1 +00011 1 +00100 0 +00101 0 +00110 0 +00111 1 +01000 0 +01001 0 +01010 1 +01011 0 +01100 1 +01101 1 +01110 0 +01111 0 +10000 1 +10001 0 +10010 0 +10011 0 +10100 0 +10101 1 +10110 1 +10111 0 +11000 0 +11001 1 +11010 0 +11011 1 +11100 1 +11101 0 +11110 1 +11111 1 +.e + +# ################### +# END OF .PLA +# ################### diff --git a/client/deps/id48/espresso/output_g4.pla b/client/deps/id48/espresso/output_g4.pla new file mode 100644 index 000000000..c81794809 --- /dev/null +++ b/client/deps/id48/espresso/output_g4.pla @@ -0,0 +1,46 @@ +# ################### +# Generating .PLA +# ################### + + +.i 5 +.o 1 +.ilb r2 r4 r6 r0 r5 +.ob g4 +00000 1 +00001 1 +00010 1 +00011 1 +00100 0 +00101 0 +00110 0 +00111 1 +01000 0 +01001 0 +01010 1 +01011 0 +01100 1 +01101 1 +01110 0 +01111 0 +10000 0 +10001 1 +10010 0 +10011 0 +10100 1 +10101 0 +10110 1 +10111 0 +11000 1 +11001 0 +11010 0 +11011 1 +11100 0 +11101 1 +11110 1 +11111 1 +.e + +# ################### +# END OF .PLA +# ################### diff --git a/client/deps/id48/espresso/output_xx.pla b/client/deps/id48/espresso/output_xx.pla new file mode 100644 index 000000000..ffe8f5918 --- /dev/null +++ b/client/deps/id48/espresso/output_xx.pla @@ -0,0 +1,270 @@ +# ################### +# Generating .PLA +# ################### + + +.i 8 +.o 1 +.ilb l0 l4 l6 m1 m3 r0 r3 r5 +.ob XX +00000000 0 +00000001 0 +00000010 1 +00000011 0 +00000100 0 +00000101 0 +00000110 1 +00000111 0 +00001000 1 +00001001 1 +00001010 1 +00001011 1 +00001100 1 +00001101 1 +00001110 1 +00001111 1 +00010000 1 +00010001 1 +00010010 1 +00010011 1 +00010100 0 +00010101 0 +00010110 1 +00010111 0 +00011000 1 +00011001 1 +00011010 1 +00011011 1 +00011100 0 +00011101 0 +00011110 1 +00011111 0 +00100000 1 +00100001 0 +00100010 1 +00100011 0 +00100100 1 +00100101 0 +00100110 1 +00100111 0 +00101000 1 +00101001 1 +00101010 1 +00101011 1 +00101100 1 +00101101 1 +00101110 1 +00101111 1 +00110000 1 +00110001 0 +00110010 1 +00110011 0 +00110100 1 +00110101 0 +00110110 1 +00110111 0 +00111000 1 +00111001 0 +00111010 1 +00111011 0 +00111100 1 +00111101 0 +00111110 1 +00111111 0 +01000000 0 +01000001 0 +01000010 1 +01000011 1 +01000100 0 +01000101 0 +01000110 1 +01000111 1 +01001000 1 +01001001 1 +01001010 1 +01001011 1 +01001100 1 +01001101 1 +01001110 1 +01001111 1 +01010000 1 +01010001 1 +01010010 1 +01010011 1 +01010100 0 +01010101 0 +01010110 1 +01010111 1 +01011000 1 +01011001 1 +01011010 1 +01011011 1 +01011100 0 +01011101 0 +01011110 1 +01011111 0 +01100000 0 +01100001 0 +01100010 1 +01100011 1 +01100100 0 +01100101 0 +01100110 1 +01100111 1 +01101000 1 +01101001 1 +01101010 1 +01101011 1 +01101100 1 +01101101 1 +01101110 1 +01101111 1 +01110000 0 +01110001 0 +01110010 1 +01110011 1 +01110100 0 +01110101 0 +01110110 1 +01110111 1 +01111000 0 +01111001 0 +01111010 1 +01111011 0 +01111100 0 +01111101 0 +01111110 1 +01111111 0 +10000000 0 +10000001 0 +10000010 0 +10000011 0 +10000100 0 +10000101 0 +10000110 0 +10000111 0 +10001000 0 +10001001 0 +10001010 0 +10001011 0 +10001100 0 +10001101 0 +10001110 0 +10001111 0 +10010000 1 +10010001 1 +10010010 1 +10010011 1 +10010100 0 +10010101 0 +10010110 0 +10010111 0 +10011000 1 +10011001 1 +10011010 1 +10011011 1 +10011100 0 +10011101 0 +10011110 0 +10011111 0 +10100000 1 +10100001 0 +10100010 1 +10100011 0 +10100100 1 +10100101 0 +10100110 1 +10100111 0 +10101000 1 +10101001 0 +10101010 1 +10101011 0 +10101100 1 +10101101 0 +10101110 1 +10101111 0 +10110000 1 +10110001 0 +10110010 1 +10110011 0 +10110100 1 +10110101 0 +10110110 1 +10110111 0 +10111000 1 +10111001 0 +10111010 1 +10111011 0 +10111100 1 +10111101 0 +10111110 1 +10111111 0 +11000000 0 +11000001 0 +11000010 1 +11000011 1 +11000100 0 +11000101 0 +11000110 1 +11000111 1 +11001000 0 +11001001 0 +11001010 0 +11001011 0 +11001100 0 +11001101 0 +11001110 0 +11001111 0 +11010000 1 +11010001 1 +11010010 1 +11010011 1 +11010100 0 +11010101 0 +11010110 1 +11010111 1 +11011000 1 +11011001 1 +11011010 1 +11011011 1 +11011100 0 +11011101 0 +11011110 0 +11011111 0 +11100000 0 +11100001 0 +11100010 1 +11100011 1 +11100100 0 +11100101 0 +11100110 1 +11100111 1 +11101000 0 +11101001 0 +11101010 0 +11101011 0 +11101100 0 +11101101 0 +11101110 0 +11101111 0 +11110000 0 +11110001 0 +11110010 1 +11110011 1 +11110100 0 +11110101 0 +11110110 1 +11110111 1 +11111000 0 +11111001 0 +11111010 0 +11111011 0 +11111100 0 +11111101 0 +11111110 0 +11111111 0 +.e + +# ################### +# END OF .PLA +# ################### diff --git a/client/deps/id48/id48.h b/client/deps/id48/public/id48.h similarity index 97% rename from client/deps/id48/id48.h rename to client/deps/id48/public/id48.h index 87f94a6cc..502d8a1e5 100644 --- a/client/deps/id48/id48.h +++ b/client/deps/id48/public/id48.h @@ -31,13 +31,13 @@ #include #include #if defined(NDEBUG) -#define ASSERT(x) ((void)0) + #define ASSERT(x) ((void)0) #elif defined(ID48_NO_STDIO) -#define ASSERT(x) ((void)0) + #define ASSERT(x) ((void)0) #else // neither NDEBUG nor ID48_NO_STDIO defined -#include -#include -#define ASSERT(x) assert((x)) + #include + #include + #define ASSERT(x) assert((x)) #endif diff --git a/client/deps/id48/src/CMakeLists.txt b/client/deps/id48/src/CMakeLists.txt new file mode 100644 index 000000000..9e5fcc617 --- /dev/null +++ b/client/deps/id48/src/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.21) + +project(id48lib LANGUAGES C CXX ASM) + +add_library(id48lib STATIC + id48_data.c + id48_generator.c + id48_recover.c +) + +# Compile options/warnings for C sources only (use generator expression) +target_compile_options(id48lib PRIVATE + $<$:-Wpedantic -Wall -Werror -O3 -Wno-unknown-pragmas -Wno-inline -Wno-unused-function> +) + +# Prefer compile_definitions over embedding -D in compile options +target_compile_definitions(id48lib PRIVATE ID48_NO_STDIO) + +# Private include dirs for internal headers, PUBLIC for the library API +target_include_directories(id48lib + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/id48 + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../public +) + +set_property(TARGET id48lib PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/id48/Makefile b/client/deps/id48/src/Makefile similarity index 66% rename from client/deps/id48/Makefile rename to client/deps/id48/src/Makefile index 0a824355e..245ef95d5 100644 --- a/client/deps/id48/Makefile +++ b/client/deps/id48/src/Makefile @@ -1,6 +1,6 @@ -# Makefile for ID48LIB library +# Proxmark3-specific Makefile for ID48LIB library MYSRCPATHS = -MYINCLUDES = -I. +MYINCLUDES = -I../public MYCFLAGS = -Wpedantic -Wall -Werror -O3 -Wno-unknown-pragmas -Wno-inline -Wno-unused-function MYDEFS = -DID48_NO_STDIO MYSRCS = \ @@ -10,4 +10,4 @@ MYSRCS = \ LIB_A = libid48.a -include ../../../Makefile.host +include ../../../../Makefile.host diff --git a/client/deps/id48/id48_data.c b/client/deps/id48/src/id48_data.c similarity index 100% rename from client/deps/id48/id48_data.c rename to client/deps/id48/src/id48_data.c diff --git a/client/deps/id48/id48_generator.c b/client/deps/id48/src/id48_generator.c similarity index 100% rename from client/deps/id48/id48_generator.c rename to client/deps/id48/src/id48_generator.c diff --git a/client/deps/id48/id48_internals.h b/client/deps/id48/src/id48_internals.h similarity index 100% rename from client/deps/id48/id48_internals.h rename to client/deps/id48/src/id48_internals.h diff --git a/client/deps/id48/id48_recover.c b/client/deps/id48/src/id48_recover.c similarity index 100% rename from client/deps/id48/id48_recover.c rename to client/deps/id48/src/id48_recover.c diff --git a/client/deps/id48/tests/CMakeLists.txt b/client/deps/id48/tests/CMakeLists.txt new file mode 100644 index 000000000..84070bc0f --- /dev/null +++ b/client/deps/id48/tests/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.21) + +add_subdirectory(espresso) +add_subdirectory(generator) +add_subdirectory(recovery) + diff --git a/client/deps/id48/tests/espresso/CMakeLists.txt b/client/deps/id48/tests/espresso/CMakeLists.txt new file mode 100644 index 000000000..96fcfa2c8 --- /dev/null +++ b/client/deps/id48/tests/espresso/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.21) + +add_executable( + id48lib_test_espresso + main.c + verify_espresso.c +) + +target_link_libraries(id48lib_test_espresso PRIVATE id48lib) + +target_include_directories(id48lib_test_espresso PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_compile_options(id48lib_test_espresso PRIVATE + $<$:-O0> + $<$:-O0> +) diff --git a/client/deps/id48/tests/espresso/array_size2.h b/client/deps/id48/tests/espresso/array_size2.h new file mode 100644 index 000000000..2f9a18e38 --- /dev/null +++ b/client/deps/id48/tests/espresso/array_size2.h @@ -0,0 +1,157 @@ +/** + +The MIT License (MIT) + +Copyright (c) SimpleHacks, Henry Gabryjelski +https://github.com/SimpleHacks/UtilHeaders + +All rights reserved. + +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. + +*/ + +#ifndef ARRAYSIZE2_H +#define ARRAYSIZE2_H + +/** + The following, if defined prior to inclusion of this header file, + will modify its behavior as noted: + + ARRAYSIZE2_SHOW_VERSION_MESSAGE + -- if defined, will show which version of ARRAY_SIZE2 macro is selected + */ + + +/** + see example source at: + https://godbolt.org/z/zzYoeK6Mf +*/ + +#ifndef __has_feature + #define __has_feature(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (defined(__cplusplus) && __cplusplus >= 201103L) || /* any compiler claiming C++11 support */ \ + (defined(__cplusplus) && _MSC_VER >= 1900 && __cplusplus != 199711L) || /* Visual C++ 2015 or higher */ \ + __has_feature(cxx_constexpr) /* CLang versions supporting constexp */ + + #include /* required for size_t */ + #if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE) + #pragma message( "ARRAY_SIZE2 -- Using C++11 version" ) + #endif + + namespace detail + { + template + constexpr size_t ARRAY_SIZE2_ARGUMENT_CANNOT_BE_POINTER(T const (&)[N]) noexcept + { + return N; + } + } /* namespace detail */ + #define ARRAY_SIZE2(arr) detail::ARRAY_SIZE2_ARGUMENT_CANNOT_BE_POINTER(arr) + +#elif defined(__cplusplus) && __cplusplus >= 199711L && ( /* C++ 98 trick */ \ + defined(__INTEL_COMPILER) || \ + defined(__clang__) || \ + (defined(__GNUC__) && ( \ + (__GNUC__ > 4) || \ + (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) \ + ))) + + #include /* required for size_t */ + #if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE) + #pragma message "ARRAY_SIZE2 -- Using C++98 version" + #endif + template + char(&_ArraySizeHelperRequiresArray(T(&)[N]))[N]; + #define ARRAY_SIZE2(x) sizeof(_ArraySizeHelperRequiresArray(x)) + +#elif defined(__cplusplus) /* && ((__cplusplus >= 199711L) || defined(__INTEL_COMPILER) || defined(__clang__)) */ + + #if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE) + #pragma message( "ARRAY_SIZE2 -- Using Ivan J. Johnson's C++ version" ) + #endif + /* + Works on older compilers, even Visual C++ 6.... + Created by Ivan J. Johnson, March 06, 2007 + See http://drdobbs.com/cpp/197800525?pgno=1 + + Full description is in markdown file array_size2.md + */ + #define ARRAY_SIZE2(arr) ( \ + 0 * sizeof(reinterpret_cast(arr)) + /*check1*/ \ + 0 * sizeof(::Bad_arg_to_ARRAY_SIZE2::check_type((arr), &(arr))) + /*check2*/ \ + sizeof(arr) / sizeof((arr)[0]) /* eval */ \ + ) + + struct Bad_arg_to_ARRAY_SIZE2 { + class Is_pointer; /* incomplete */ + class Is_array {}; + template + static Is_pointer check_type(T const *, T const * const *); + static Is_array check_type(void const *, void const *); + }; + +#elif !defined(__cplusplus) && defined(__GNUC__) + + #include + + /** + Even C can have type-safety for equivalent of ARRAY_SIZE() macro, + when using the following two GCC extensions: + typeof() + __builtin_types_compatible_p() + */ + + #if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE) + #pragma message( "ARRAY_SIZE2 -- Using GNUC version" ) + #endif + + /** + validated using: + MSP430 gcc 4.5.3 + x86-64 icc 16.0.3 + x86-64 gcc 4.1.2 + x86-64 clang 3.0.0 + AVR gcc 4.5.4 + ARM gcc 4.5.4 + */ + + #define __SIMPLEHACKS_COMPATIBLE_TYPES__(a,b) __builtin_types_compatible_p(__typeof__(a), __typeof__(b)) /* GCC extensions */ + #define __SIMPLEHACKS_BUILD_ERROR_ON_NONZERO__(x) (sizeof(struct { uint8_t q: (-!!(x)*0x1ee7)+1u;})-1u) /* if x is zero, reports "error: negative width in bit-field ''" */ + #define __SIMPLEHACKS_MUST_BE_ARRAY__(x) __SIMPLEHACKS_BUILD_ERROR_ON_NONZERO__(__SIMPLEHACKS_COMPATIBLE_TYPES__((x), &(*x))) + #define ARRAY_SIZE2(_arr) ( (sizeof(_arr) / sizeof((_arr)[0])) + __SIMPLEHACKS_MUST_BE_ARRAY__(_arr) ) /* compile-time error if not an array */ + +#else + + /** + The good news is that all compilers (as of 20202-05-08) + on godbolt.org are fully supported. Therefore, if some + other compiler does not support any of the above method, + it's important to force a compile-time error, to avoid + any suggestion that this provides a safe macro. + */ + + #error "Unable to provide type-safe ARRAY_SIZE2 macro" + + +#endif + +#endif // ARRAYSIZE2_H diff --git a/client/deps/id48/tests/espresso/main.c b/client/deps/id48/tests/espresso/main.c new file mode 100644 index 000000000..5dbd54765 --- /dev/null +++ b/client/deps/id48/tests/espresso/main.c @@ -0,0 +1,25 @@ + + + +#include +#include +#include "verify_espresso.h" +#include "id48.h" + +int main(void) { + bool result = true; + + if (result) { + printf("Verifying Espresso Results...\n"); + result = verify_espresso_results(); + printf("Verifying Espresso Results: %s\n", + result ? "SUCCESS" : "----> FAILURE <----" + ); + } + // if (result) { + // printf("Hello2...\n"); + // generate_all_lut_espresso_files(); + // } + + return result ? 0 : -120; +} diff --git a/client/deps/id48/tests/espresso/verify_espresso.c b/client/deps/id48/tests/espresso/verify_espresso.c new file mode 100644 index 000000000..9e9abeb13 --- /dev/null +++ b/client/deps/id48/tests/espresso/verify_espresso.c @@ -0,0 +1,817 @@ + + + +#include +#include +#include +#include "array_size2.h" +#include "id48.h" +#include "verify_espresso.h" + +#if !defined true + #define true ((bool)1u) +#endif +#if !defined false + #define false ((bool)0u) +#endif + +#if 1 // constant names for use in LUT_PLA_NAMES_xx + static const char * const NAME_A = "a"; + static const char * const NAME_B = "b"; + static const char * const NAME_C = "c"; + static const char * const NAME_L0 = "l0"; + static const char * const NAME_L2 = "l2"; + static const char * const NAME_L3 = "l3"; + static const char * const NAME_L4 = "l4"; + static const char * const NAME_L5 = "l5"; + static const char * const NAME_L6 = "l6"; + static const char * const NAME_M0 = "m0"; + static const char * const NAME_M1 = "m1"; + static const char * const NAME_M3 = "m3"; + static const char * const NAME_M5 = "m5"; + static const char * const NAME_R0 = "r0"; + static const char * const NAME_R1 = "r1"; + static const char * const NAME_R2 = "r2"; + static const char * const NAME_R3 = "r3"; + static const char * const NAME_R4 = "r4"; + static const char * const NAME_R5 = "r5"; + static const char * const NAME_R6 = "r6"; +#endif // constant names for use in LUT_PLA_NAMES_xx +#if 1 // LUT_PLA_NAMES_xx -- name arrays for generating espresso input files + // NOTE: these arrays are in order of the most significant to least significant bit. + // did i get confused by that? + const char * const LUT_PLA_NAMES_g1[5] = { NAME_A , NAME_L2, NAME_L3, NAME_L0, NAME_L6, }; + const char * const LUT_PLA_NAMES_g2[5] = { NAME_L5, NAME_B , NAME_M0, NAME_L4, NAME_M1, }; + const char * const LUT_PLA_NAMES_g3[5] = { NAME_M5, NAME_C , NAME_R1, NAME_M3, NAME_R3, }; + const char * const LUT_PLA_NAMES_g4[5] = { NAME_R2, NAME_R4, NAME_R6, NAME_R0, NAME_R5, }; + const char * const LUT_PLA_NAMES_XX[8] = { + NAME_L0, NAME_L4, NAME_L6, NAME_M1, + NAME_M3, NAME_R0, NAME_R3, NAME_R5, + }; +#endif // LUT_PLA_NAMES_xx -- name arrays for generating espresso input files + +#if 1 // actual lookup tables + // LUTs were internal-only, and not even exposed to library internals. + // so just copy/paste here for now for initial testing + /// initial_idx = (l0 << 7) | (l4 << 6) | (l6 << 5) | (m1 << 4) | (m3 << 3) | (r0 << 2) | (r3 << 1) | (r5 << 0) + static const uint8_t small_lut_XX[32] = { // aka 256 bits + 0x44, 0xff, 0x4f, 0x4f, 0x55, 0xff, 0x55, 0x55, + 0xcc, 0xff, 0xcf, 0x4f, 0xcc, 0xff, 0xcc, 0x44, + 0x00, 0x00, 0x0f, 0x0f, 0x55, 0x55, 0x55, 0x55, + 0xcc, 0x00, 0xcf, 0x0f, 0xcc, 0x00, 0xcc, 0x00, + }; + /// g1_idx = (a << 4) | (l2 << 3) | (l3 << 2) | (l0 << 1) | (l6 << 0) + static const uint8_t small_lut_group1[4] = { // aka 32 bits + 0x4f, 0x92, 0xa1, 0x7c, + }; + /// g2_idx = (l5 << 4) | (b << 3) | (m0 << 2) | (l4 << 1) | (m1 << 0) + static const uint8_t small_lut_group2[4] = { // aka 32 bits + 0x8f, 0x52, 0x61, 0xbc, + }; + /// g3_idx = (m5 << 4) | (c << 3) | (r1 << 2) | (m3 << 1) | (r3 << 0) + static const uint8_t small_lut_group3[4] = { // aka 32 bits + 0x8f, 0x34, 0x61, 0xda, + }; + /// g4_idx = (r2 << 4) | (r4 << 3) | (r6 << 2) | (r0 << 1) | (r5 << 0) + static const uint8_t small_lut_group4[4] = { // aka 32 bits + 0x8f, 0x34, 0x52, 0xe9, + }; + static inline bool get_bit(const uint8_t *table_start, uint32_t bit_idx) { + const uint32_t byte = bit_idx / 8u; + const uint8_t mask = 1u << (bit_idx % 8u); + return (table_start[byte] & mask) != 0; + } +#endif // actual lookup tables + + + +// 1. validated by hand that LUT --> PLA generation was accurate for all five LUTs +// 2. Ran the generated PLA through WASM-based espresso: +// https://nudelerde.github.io/Espresso-Wasm-Web/index.html +// 3. Copied the output to each validation function +// 4. Converted the output to C-style boolean logic, and column-aligned formatting for easier review +// +// Same process used for each, but only g1 validates against the lookup table. +// Maybe just use the LUTs in an FPGA instead of trying to minimize to two-level logic? +// However, performance will be best with two-level, glitch-free logic.... +// +// WHAT IS GOING WRONG WITH ESPRESSO OUTPUT (OR MY VALIDATION)? +// +// After adding a whole set of secondary validation functions... the root cause was .... +// Accidentally initializing any_failures to true instead of false in the other functions. +// +// :-P +// +// I'll check this comment into my dev branch, even if I squash-merge it out later. +// +typedef struct _OUTPUT_INDEX3 { + // the index is calculated from unstable ID48LIBX_STATE_REGISTERS + // by function calculate_output_index(), which creates a single + // 20-bit result: + // Fₒ( abc l₀l₂l₃l₄l₅l₆ m₀m₁m₃m₅ r₀r₁r₂r₃r₄r₅r₆ ) + // msb 19 ---^ lsb 00 ---^^ + // This union allows debugger to view the individual bit values more easily. + union { + uint32_t Raw32; + struct { + // seven bits from r: r₀..r₆ + uint32_t r6 : 1; // bit 00 + uint32_t r5 : 1; // bit 01 + uint32_t r4 : 1; // bit 02 + uint32_t r3 : 1; // bit 03 + uint32_t r2 : 1; // bit 04 + uint32_t r1 : 1; // bit 05 + uint32_t r0 : 1; // bit 06 + // four bits from m: m₀m₁m₃m₅ + uint32_t m5 : 1; // bit 07 + uint32_t m3 : 1; // bit 08 + uint32_t m1 : 1; // bit 09 + uint32_t m0 : 1; // bit 10 + // six bits from l: l₀l₂l₃l₄l₅l₆ + uint32_t l6 : 1; // bit 11 + uint32_t l5 : 1; // bit 12 + uint32_t l4 : 1; // bit 13 + uint32_t l3 : 1; // bit 14 + uint32_t l2 : 1; // bit 15 + uint32_t l0 : 1; // bit 16 + // and finally, the three bits a, b, c + uint32_t c : 1; // bit 17 + uint32_t b : 1; // bit 18 + uint32_t a : 1; // bit 19 + }; + }; +} OUTPUT_INDEX3; + + + +#if 1 // structures for mapping LUT indices to named fields +/// g1_idx = (a << 4) | (l2 << 3) | (l3 << 2) | (l0 << 1) | (l6 << 0) +typedef struct _INDEX_G1 { + union { + uint8_t as_uint8_t; + struct { + uint8_t l6 : 1; // least significant bit + uint8_t l0 : 1; + uint8_t l3 : 1; + uint8_t l2 : 1; + uint8_t a : 1; + }; + }; +} INDEX_G1; +/// g2_idx = (l5 << 4) | (b << 3) | (m0 << 2) | (l4 << 1) | (m1 << 0) +typedef struct _INDEX_G2 { + union { + uint8_t as_uint8_t; + struct { + uint8_t m1 : 1; + uint8_t l4 : 1; + uint8_t m0 : 1; + uint8_t b : 1; + uint8_t l5 : 1; + }; + }; +} INDEX_G2; +/// g3_idx = (m5 << 4) | (c << 3) | (r1 << 2) | (m3 << 1) | (r3 << 0) +typedef struct _INDEX_G3 { + union { + uint8_t as_uint8_t; + struct { + uint8_t r3 : 1; + uint8_t m3 : 1; + uint8_t r1 : 1; + uint8_t c : 1; + uint8_t m5 : 1; + }; + }; +} INDEX_G3; +/// g4_idx = (r2 << 4) | (r4 << 3) | (r6 << 2) | (r0 << 1) | (r5 << 0) +typedef struct _INDEX_G4 { + union { + uint8_t as_uint8_t; + struct { + uint8_t r5 : 1; + uint8_t r0 : 1; + uint8_t r6 : 1; + uint8_t r4 : 1; + uint8_t r2 : 1; + }; + }; +} INDEX_G4; +/// initial_idx = (l0 << 7) | (l4 << 6) | (l6 << 5) | (m1 << 4) | (m3 << 3) | (r0 << 2) | (r3 << 1) | (r5 << 0) +typedef struct _INDEX_XX { + union { + uint8_t as_uint8_t; + struct { + uint8_t r5 : 1; + uint8_t r3 : 1; + uint8_t r0 : 1; + uint8_t m3 : 1; + uint8_t m1 : 1; + uint8_t l6 : 1; + uint8_t l4 : 1; + uint8_t l0 : 1; + }; + }; +} INDEX_XX; +#endif // structures for mapping LUT indices to named fields + +typedef struct _ESPRESSO_GENERATOR_ARGUMENTS { + uint8_t const input_count; + char const * const * names; + char const * const output_name; + uint8_t const * lut_start; +} ESPRESSO_GENERATOR_ARGUMENTS; + +static bool verify_lut_g1(void); +static bool verify_lut_g2(void); +static bool verify_lut_g3(void); +static bool verify_lut_g4(void); +static bool verify_lut_XX(void); + +// Later, define a struct with functions for each thing to be validated +typedef bool (*lookuptable_func_t)(uint8_t); + + +/// g1_idx = (a << 4) | (l2 << 3) | (l3 << 2) | (l0 << 1) | (l6 << 0) +static bool espresso_implementation_g1_alt0(uint8_t idx) { + // output_g1 = (a&l2&l0&!l6) | (a&l3&!l0&l6) | (!a&!l2&!l3) | (!l2&!l3&!l0&!l6) | (l2&l3&!l0&!l6) | (!a&!l2&l0&!l6) | (!a&!l3&!l0&l6) | (a&l2&!l3&l0) | (a&!l2&l3&l6) | (!a&l2&l3&l0&l6); + + static const bool _ = true; + bool a = idx & (1u << 4); + bool l2 = idx & (1u << 3); + bool l3 = idx & (1u << 2); + bool l0 = idx & (1u << 1); + bool l6 = idx & (1u << 0); + return + ( a && l2 && _ && l0 && !(l6) ) || + ( a && _ && l3 && !(l0) && l6 ) || + ( !(a) && !(l2) && !(l3) && _ && _ ) || + ( _ && !(l2) && !(l3) && !(l0) && !(l6) ) || + ( _ && l2 && l3 && !(l0) && !(l6) ) || + ( !(a) && !(l2) && _ && l0 && !(l6) ) || + ( !(a) && _ && !(l3) && !(l0) && l6 ) || + ( a && l2 && !(l3) && l0 && _ ) || + ( a && !(l2) && l3 && _ && l6 ) || + ( !(a) && l2 && l3 && l0 && l6 ) ; +} +static bool espresso_implementation_g1_alt1(uint8_t idx) { + // output_g1 = (a&l2&l0&!l6) | (a&l3&!l0&l6) | (!a&!l2&!l3) | (!l2&!l3&!l0&!l6) | (l2&l3&!l0&!l6) | (!a&!l2&l0&!l6) | (!a&!l3&!l0&l6) | (a&l2&!l3&l0) | (a&!l2&l3&l6) | (!a&l2&l3&l0&l6); + static const bool _ = true; + INDEX_G1 t = { .as_uint8_t = idx }; + bool a = t.a; + bool l2 = t.l2; + bool l3 = t.l3; + bool l0 = t.l0; + bool l6 = t.l6; + return + ( a && l0 && l2 && _ && !(l6) ) || + ( a && !(l0) && _ && l3 && l6 ) || + ( !(a) && _ && !(l2) && !(l3) && _ ) || + ( _ && !(l0) && !(l2) && !(l3) && !(l6) ) || + ( _ && !(l0) && l2 && l3 && !(l6) ) || + ( !(a) && l0 && !(l2) && _ && !(l6) ) || + ( !(a) && !(l0) && _ && !(l3) && l6 ) || + ( a && l0 && l2 && !(l3) && _ ) || + ( a && _ && !(l2) && l3 && l6 ) || + ( !(a) && l0 && l2 && l3 && l6 ) ; +} +static const lookuptable_func_t alt_for_g1[] = { + espresso_implementation_g1_alt0, + espresso_implementation_g1_alt1, +}; + + +static bool espresso_implementation_g2_alt0(uint8_t idx) { + // output_g2 = (b&m0&!l4&!m1) | (l5&b&l4&m1) | (!l5&!b&!m0) | (!b&!m0&!l4&!m1) | (l5&b&!m0&l4) | (l5&!b&m0&l4&!m1) | (!l5&b&m0&!m1) | (!l5&!m0&!l4&m1) | (l5&m0&!l4&m1) | (!l5&!b&l4&m1); + // g2_idx = (l5 << 4) | (b << 3) | (m0 << 2) | (l4 << 1) | (m1 << 0) + bool l5 = idx & (1u << 4); + bool b = idx & (1u << 3); + bool m0 = idx & (1u << 2); + bool l4 = idx & (1u << 1); + bool m1 = idx & (1u << 0); + static const bool _ = true; + return + ( _ && b && m0 && !(l4) && !(m1) ) | + ( l5 && b && _ && l4 && m1 ) | + ( !(l5) && !(b) && !(m0) && _ && _ ) | + ( _ && !(b) && !(m0) && !(l4) && !(m1) ) | + ( l5 && b && !(m0) && l4 && _ ) | + ( l5 && !(b) && m0 && l4 && !(m1) ) | + ( !(l5) && b && m0 && _ && !(m1) ) | + ( !(l5) && _ && !(m0) && !(l4) && m1 ) | + ( l5 && _ && m0 && !(l4) && m1 ) | + ( !(l5) && !(b) && _ && l4 && m1 ) ; +} +static bool espresso_implementation_g2_alt1(uint8_t idx) { + // output_g2 = (b&m0&!l4&!m1) | (l5&b&l4&m1) | (!l5&!b&!m0) | (!b&!m0&!l4&!m1) | (l5&b&!m0&l4) | (l5&!b&m0&l4&!m1) | (!l5&b&m0&!m1) | (!l5&!m0&!l4&m1) | (l5&m0&!l4&m1) | (!l5&!b&l4&m1); + INDEX_G2 t = { .as_uint8_t = idx }; + bool l5 = t.l5; + bool b = t.b; + bool m0 = t.m0; + bool l4 = t.l4; + bool m1 = t.m1; + static const bool _ = true; + return + ( _ && b && m0 && !(l4) && !(m1) ) | + ( l5 && b && _ && l4 && m1 ) | + ( !(l5) && !(b) && !(m0) && _ && _ ) | + ( _ && !(b) && !(m0) && !(l4) && !(m1) ) | + ( l5 && b && !(m0) && l4 && _ ) | + ( l5 && !(b) && m0 && l4 && !(m1) ) | + ( !(l5) && b && m0 && _ && !(m1) ) | + ( !(l5) && _ && !(m0) && !(l4) && m1 ) | + ( l5 && _ && m0 && !(l4) && m1 ) | + ( !(l5) && !(b) && _ && l4 && m1 ) ; +} + +static const lookuptable_func_t alt_for_g2[] = { + espresso_implementation_g2_alt0, + espresso_implementation_g2_alt1, + // espresso_implementation_g1_alt2, // fails ... generated via https://github.com/omritriki/espresso-py ... +}; + + +static bool espresso_implementation_g3_alt0(uint8_t idx) { + // output_g3 = (c&r1&!m3&!r3) | (m5&c&m3&r3) | (!m5&!c&!r1) | (!c&!r1&!m3&!r3) | (!m5&!r1&m3&!r3) | (m5&r1&m3&!r3) | (m5&c&!r1&r3) | (m5&!c&r1&!m3&r3) | (!m5&c&r1&!m3) | (!m5&!c&m3&r3); + // g3_idx = (m5 << 4) | (c << 3) | (r1 << 2) | (m3 << 1) | (r3 << 0) + bool m5 = idx & (1u << 4); + bool c = idx & (1u << 3); + bool r1 = idx & (1u << 2); + bool m3 = idx & (1u << 1); + bool r3 = idx & (1u << 0); + static const bool _ = true; + return + ( _ && c && r1 && !(m3) && !(r3) ) || + ( m5 && c && _ && m3 && r3 ) || + ( !(m5) && !(c) && !(r1) && _ && _ ) || + ( _ && !(c) && !(r1) && !(m3) && !(r3) ) || + ( !(m5) && _ && !(r1) && m3 && !(r3) ) || + ( m5 && _ && r1 && m3 && !(r3) ) || + ( m5 && c && !(r1) && _ && r3 ) || + ( m5 && !(c) && r1 && !(m3) && r3 ) || + ( !(m5) && c && r1 && !(m3) && _ ) || + ( !(m5) && !(c) && _ && m3 && r3 ) ; +} +static bool espresso_implementation_g3_alt1(uint8_t idx) { + // output_g3 = (c&r1&!m3&!r3) | (m5&c&m3&r3) | (!m5&!c&!r1) | (!c&!r1&!m3&!r3) | (!m5&!r1&m3&!r3) | (m5&r1&m3&!r3) | (m5&c&!r1&r3) | (m5&!c&r1&!m3&r3) | (!m5&c&r1&!m3) | (!m5&!c&m3&r3); + INDEX_G3 t = { .as_uint8_t = idx }; + bool c = t.c; + bool r1 = t.r1; + bool r3 = t.r3; + bool m3 = t.m3; + bool m5 = t.m5; + static const bool _ = true; + return + ( _ && c && r1 && !(m3) && !(r3) ) || + ( m5 && c && _ && m3 && r3 ) || + ( !(m5) && !(c) && !(r1) && _ && _ ) || + ( _ && !(c) && !(r1) && !(m3) && !(r3) ) || + ( !(m5) && _ && !(r1) && m3 && !(r3) ) || + ( m5 && _ && r1 && m3 && !(r3) ) || + ( m5 && c && !(r1) && _ && r3 ) || + ( m5 && !(c) && r1 && !(m3) && r3 ) || + ( !(m5) && c && r1 && !(m3) && _ ) || + ( !(m5) && !(c) && _ && m3 && r3 ) ; +} + +static const lookuptable_func_t alt_for_g3[] = { + espresso_implementation_g3_alt0, + espresso_implementation_g3_alt1, +}; + +static bool espresso_implementation_g4_alt0(uint8_t idx) { + // output_g4 = (r2&r6&r0&!r5) | (r4&r6&!r0&r5) | (!r2&!r4&!r6) | (r2&r4&!r6&!r0&!r5) | (r2&!r4&r6&!r5) | (!r2&r4&r6&!r0) | (!r2&!r6&r0&!r5) | (!r4&!r6&!r0&r5) | (r2&r4&r0&r5) | (!r2&!r4&r0&r5); + // g4_idx = (r2 << 4) | (r4 << 3) | (r6 << 2) | (r0 << 1) | (r5 << 0) + bool r2 = idx & (1u << 4); + bool r4 = idx & (1u << 3); + bool r6 = idx & (1u << 2); + bool r0 = idx & (1u << 1); + bool r5 = idx & (1u << 0); + static const bool _ = true; + return + ( r2 && _ && r6 && r0 && !(r5) ) | + ( _ && r4 && r6 && !(r0) && r5 ) | + ( !(r2) && !(r4) && !(r6) && _ && _ ) | + ( r2 && r4 && !(r6) && !(r0) && !(r5) ) | + ( r2 && !(r4) && r6 && _ && !(r5) ) | + ( !(r2) && r4 && r6 && !(r0) && _ ) | + ( !(r2) && _ && !(r6) && r0 && !(r5) ) | + ( _ && !(r4) && !(r6) && !(r0) && r5 ) | + ( r2 && r4 && _ && r0 && r5 ) | + ( !(r2) && !(r4) && _ && r0 && r5 ) ; +} +static bool espresso_implementation_g4_alt1(uint8_t idx) { + // output_g4 = (r2&r6&r0&!r5) | (r4&r6&!r0&r5) | (!r2&!r4&!r6) | (r2&r4&!r6&!r0&!r5) | (r2&!r4&r6&!r5) | (!r2&r4&r6&!r0) | (!r2&!r6&r0&!r5) | (!r4&!r6&!r0&r5) | (r2&r4&r0&r5) | (!r2&!r4&r0&r5); + // g4_idx = (r2 << 4) | (r4 << 3) | (r6 << 2) | (r0 << 1) | (r5 << 0) + INDEX_G4 t = { .as_uint8_t = idx }; + bool r2 = t.r2; + bool r4 = t.r4; + bool r6 = t.r6; + bool r0 = t.r0; + bool r5 = t.r5; + static const bool _ = true; + return + ( r2 && _ && r6 && r0 && !(r5) ) | + ( _ && r4 && r6 && !(r0) && r5 ) | + ( !(r2) && !(r4) && !(r6) && _ && _ ) | + ( r2 && r4 && !(r6) && !(r0) && !(r5) ) | + ( r2 && !(r4) && r6 && _ && !(r5) ) | + ( !(r2) && r4 && r6 && !(r0) && _ ) | + ( !(r2) && _ && !(r6) && r0 && !(r5) ) | + ( _ && !(r4) && !(r6) && !(r0) && r5 ) | + ( r2 && r4 && _ && r0 && r5 ) | + ( !(r2) && !(r4) && _ && r0 && r5 ) ; +} + +static const lookuptable_func_t alt_for_g4[] = { + espresso_implementation_g4_alt0, + espresso_implementation_g4_alt1, +}; + +static bool espresso_implementation_xx_alt0(uint8_t idx) { + // output_xx = (!l6&m1&!r0) | (l4&!m3&r3) | (!l4&l6&!r5) | (!l0&!m1&m3) | (!l0&r3&!r5); + /// xx_idx = (l0 << 7) | (l4 << 6) | (l6 << 5) | (m1 << 4) | (m3 << 3) | (r0 << 2) | (r3 << 1) | (r5 << 0) + static const bool _ = true; + bool l0 = idx & (1u << 7); + bool l4 = idx & (1u << 6); + bool l6 = idx & (1u << 5); + bool m1 = idx & (1u << 4); + bool m3 = idx & (1u << 3); + bool r0 = idx & (1u << 2); + bool r3 = idx & (1u << 1); + bool r5 = idx & (1u << 0); + + return + ( _ && _ && !(l6) && m1 && _ && !(r0) && _ && _ ) || + ( _ && l4 && _ && _ && !(m3) && _ && r3 && _ ) || + ( _ && !(l4) && l6 && _ && _ && _ && _ && !(r5) ) || + ( !(l0) && _ && _ && !(m1) && m3 && _ && _ && _ ) || + ( !(l0) && _ && _ && _ && _ && _ && r3 && !(r5) ) ; +} +static bool espresso_implementation_xx_alt1(uint8_t idx) { + // output_XX = (!l6&m1&!r0) | (l4&!m3&r3) | (!l4&l6&!r5) | (!l0&!m1&m3) | (!l0&r3&!r5); + static const bool _ = true; + /// xx_idx = (l0 << 7) | (l4 << 6) | (l6 << 5) | (m1 << 4) | (m3 << 3) | (r0 << 2) | (r3 << 1) | (r5 << 0) + INDEX_XX t = { .as_uint8_t = idx }; + bool l0 = t.l0; + bool l4 = t.l4; + bool l6 = t.l6; + bool m1 = t.m1; + bool m3 = t.m3; + bool r0 = t.r0; + bool r3 = t.r3; + bool r5 = t.r5; + return + ( _ && _ && !(l6) && m1 && _ && !(r0) && _ && _ ) || + ( _ && l4 && _ && _ && !(m3) && _ && r3 && _ ) || + ( _ && !(l4) && l6 && _ && _ && _ && _ && !(r5) ) || + ( !(l0) && _ && _ && !(m1) && m3 && _ && _ && _ ) || + ( !(l0) && _ && _ && _ && _ && _ && r3 && !(r5) ) ; +} + +static const lookuptable_func_t alt_for_xx[] = { + espresso_implementation_xx_alt0, + espresso_implementation_xx_alt1, +}; + + + +typedef struct _VERIFICATION_T { + uint8_t const input_count; + char const * const * names; + char const * const output_name; + uint8_t const * lut_start; + lookuptable_func_t const * const altFns; + size_t const altFnCount; +} VERIFICATION_T; + + +VERIFICATION_T const verification_table[] = { + { .input_count = 5, .names = LUT_PLA_NAMES_g1, .output_name = "g1", .lut_start = small_lut_group1, .altFns = alt_for_g1, .altFnCount = ARRAY_SIZE2(alt_for_g1) }, + { .input_count = 5, .names = LUT_PLA_NAMES_g2, .output_name = "g2", .lut_start = small_lut_group2, .altFns = alt_for_g2, .altFnCount = ARRAY_SIZE2(alt_for_g2) }, + { .input_count = 5, .names = LUT_PLA_NAMES_g3, .output_name = "g3", .lut_start = small_lut_group3, .altFns = alt_for_g3, .altFnCount = ARRAY_SIZE2(alt_for_g3) }, + { .input_count = 5, .names = LUT_PLA_NAMES_g4, .output_name = "g4", .lut_start = small_lut_group4, .altFns = alt_for_g4, .altFnCount = ARRAY_SIZE2(alt_for_g4) }, + { .input_count = 8, .names = LUT_PLA_NAMES_XX, .output_name = "xx", .lut_start = small_lut_XX , .altFns = alt_for_xx, .altFnCount = ARRAY_SIZE2(alt_for_xx) }, +}; + + +static bool espresso_implementation_g1(bool a, bool l0, bool l2, bool l3, bool l6) { + // output_g1 = (a&l2&l0&!l6) | (a&l3&!l0&l6) | (!a&!l2&!l3) | (!l2&!l3&!l0&!l6) | (l2&l3&!l0&!l6) | (!a&!l2&l0&!l6) | (!a&!l3&!l0&l6) | (a&l2&!l3&l0) | (a&!l2&l3&l6) | (!a&l2&l3&l0&l6); + static const bool _ = true; + return + ( a && l0 && l2 && _ && !(l6) ) | + ( a && !(l0) && _ && l3 && l6 ) | + ( !(a) && _ && !(l2) && !(l3) && _ ) | + ( _ && !(l0) && !(l2) && !(l3) && !(l6) ) | + ( _ && !(l0) && l2 && l3 && !(l6) ) | + ( !(a) && l0 && !(l2) && _ && !(l6) ) | + ( !(a) && !(l0) && _ && !(l3) && l6 ) | + ( a && l0 && l2 && !(l3) && _ ) | + ( a && _ && !(l2) && l3 && l6 ) | + ( !(a) && l0 && l2 && l3 && l6 ) ; +} +static bool espresso_implementation_g2(bool b, bool l4, bool l5, bool m0, bool m1) { + // output_g2 = (b&m0&!l4&!m1) | (l5&b&l4&m1) | (!l5&!b&!m0) | (!b&!m0&!l4&!m1) | (l5&b&!m0&l4) | (l5&!b&m0&l4&!m1) | (!l5&b&m0&!m1) | (!l5&!m0&!l4&m1) | (l5&m0&!l4&m1) | (!l5&!b&l4&m1); + static const bool _ = true; + return + ( _ && b && m0 && !(l4) && !(m1) ) | + ( l5 && b && _ && l4 && m1 ) | + ( !(l5) && !(b) && !(m0) && _ && _ ) | + ( _ && !(b) && !(m0) && !(l4) && !(m1) ) | + ( l5 && b && !(m0) && l4 && _ ) | + ( l5 && !(b) && m0 && l4 && !(m1) ) | + ( !(l5) && b && m0 && _ && !(m1) ) | + ( !(l5) && _ && !(m0) && !(l4) && m1 ) | + ( l5 && _ && m0 && !(l4) && m1 ) | + ( !(l5) && !(b) && _ && l4 && m1 ) ; +} +static bool espresso_implementation_g3(bool c, bool r1, bool r3, bool m3, bool m5) { + // output_g3 = (c&r1&!m3&!r3) | (m5&c&m3&r3) | (!m5&!c&!r1) | (!c&!r1&!m3&!r3) | (!m5&!r1&m3&!r3) | (m5&r1&m3&!r3) | (m5&c&!r1&r3) | (m5&!c&r1&!m3&r3) | (!m5&c&r1&!m3) | (!m5&!c&m3&r3); + static const bool _ = true; + return + ( _ && c && r1 && !(m3) && !(r3) ) | + ( m5 && c && _ && m3 && r3 ) | + ( !(m5) && !(c) && !(r1) && _ && _ ) | + ( _ && !(c) && !(r1) && !(m3) && !(r3) ) | + ( !(m5) && _ && !(r1) && m3 && !(r3) ) | + ( m5 && _ && r1 && m3 && !(r3) ) | + ( m5 && c && !(r1) && _ && r3 ) | + ( m5 && !(c) && r1 && !(m3) && r3 ) | + ( !(m5) && c && r1 && !(m3) && _ ) | + ( !(m5) && !(c) && _ && m3 && r3 ) ; +} +static bool espresso_implementation_g4(bool r0, bool r2, bool r4, bool r5, bool r6) { + // output_g4 = (r2&r6&r0&!r5) | (r4&r6&!r0&r5) | (!r2&!r4&!r6) | (r2&r4&!r6&!r0&!r5) | (r2&!r4&r6&!r5) | (!r2&r4&r6&!r0) | (!r2&!r6&r0&!r5) | (!r4&!r6&!r0&r5) | (r2&r4&r0&r5) | (!r2&!r4&r0&r5); + static const bool _ = true; + return + ( r2 && _ && r6 && r0 && !(r5) ) | + ( _ && r4 && r6 && !(r0) && r5 ) | + ( !(r2) && !(r4) && !(r6) && _ && _ ) | + ( r2 && r4 && !(r6) && !(r0) && !(r5) ) | + ( r2 && !(r4) && r6 && _ && !(r5) ) | + ( !(r2) && r4 && r6 && !(r0) && _ ) | + ( !(r2) && _ && !(r6) && r0 && !(r5) ) | + ( _ && !(r4) && !(r6) && !(r0) && r5 ) | + ( r2 && r4 && _ && r0 && r5 ) | + ( !(r2) && !(r4) && _ && r0 && r5 ) ; +} +static bool espresso_implementation_XX(bool l0, bool l4, bool l6, bool m1, bool m3, bool r0, bool r3, bool r5) { + // output_XX = (!l6&m1&!r0) | (l4&!m3&r3) | (!l4&l6&!r5) | (!l0&!m1&m3) | (!l0&r3&!r5); + static const bool _ = true; + return + ( _ && _ && !(l6) && m1 && _ && !(r0) && _ && _ ) | + ( _ && l4 && _ && _ && !(m3) && _ && r3 && _ ) | + ( _ && !(l4) && l6 && _ && _ && _ && _ && !(r5) ) | + ( !(l0) && _ && _ && !(m1) && m3 && _ && _ && _ ) | + ( !(l0) && _ && _ && _ && _ && _ && r3 && !(r5) ); +} + +static bool verify_alternatives(void) +{ + bool any_failures_overall = false; + + for (uint16_t q = 0; q < ARRAY_SIZE2(verification_table); ++q) { + bool any_failures_this_table = false; + VERIFICATION_T const * vt = &verification_table[q]; + const char* fstr = vt->output_name; + uint16_t limit = 1u << vt->input_count; + + for (uint16_t fn_idx = 0; fn_idx < vt->altFnCount; ++fn_idx ) { + + bool any_failures_this_table_alts = false; + const lookuptable_func_t fn = vt->altFns[fn_idx]; + + for (uint16_t ix = 0; ix < limit; ++ix) { + uint8_t i = (uint8_t)ix; + + bool lut_result = get_bit(vt->lut_start, i); + bool other_result = fn(i); + if (lut_result != other_result) { + printf("%s: verification failed for fn[ %d ]( %3d )\n", fstr, fn_idx, i); + any_failures_this_table_alts = true; + } + } + if (any_failures_this_table_alts) { + printf("FAILURE - %s alt %d\n", fstr, fn_idx); + } else { + printf("SUCCESS - %s alt %d\n", fstr, fn_idx); + } + + // percolate to outer variable.... + any_failures_this_table |= any_failures_this_table_alts; + } + + if (vt->altFnCount == 0) { + // nothing was tested ... + } else if (any_failures_this_table) { + printf("FAILURE ---------> At least one of the alt functions for %s \n", fstr); + } else { + printf("SUCCESS ---------> All alternate for %s \n", fstr); + } + + any_failures_overall |= any_failures_this_table; + } + + if (any_failures_overall) { + printf("=====> FAILURE: Some of the alternate generators are still failing\n"); + } else { + printf("=====> SUCCESS: All alternate generators are succeeding\n"); + } + return !any_failures_overall; +} + +static bool verify_lut_g1(void) +{ + static const char* fstr = "verify_lut_g1"; + bool any_failures = false; + for (uint8_t i = 0; i < 32; ++i) { + INDEX_G1 t = { .as_uint8_t = i }; + bool lut_result = get_bit(small_lut_group1, i); + bool other_result = espresso_implementation_g1(t.a, t.l0, t.l2, t.l3, t.l6); + if (lut_result != other_result) { + printf("%s: verification failed for index %d\n", fstr, t.as_uint8_t); + any_failures = true; + } + } + if (any_failures) { + printf("FAILURE - %s\n", fstr); + } else { + printf("SUCCESS - %s\n", fstr); + } + return !any_failures; +} +static bool verify_lut_g2(void) +{ + static const char* fstr = "verify_lut_g2"; + bool any_failures = false; + for (uint8_t i = 0; i < 32; ++i) { + INDEX_G2 t = { .as_uint8_t = i }; + bool lut_result = get_bit(small_lut_group2, i); + bool other_result = espresso_implementation_g2(t.b, t.l4, t.l5, t.m0, t.m1); + if (lut_result != other_result) { + printf("%s: verification failed for index %d\n", fstr, t.as_uint8_t); + any_failures = true; + } + } + if (any_failures) { + printf("FAILURE - %s\n", fstr); + } else { + printf("SUCCESS - %s\n", fstr); + } + return !any_failures; +} +static bool verify_lut_g3(void) +{ + static const char* fstr = "verify_lut_g3"; + bool any_failures = false; + for (uint8_t i = 0; i < 32; ++i) { + INDEX_G3 t = { .as_uint8_t = i }; + bool lut_result = get_bit(small_lut_group3, i); + bool other_result = espresso_implementation_g3(t.c, t.r1, t.r3, t.m3, t.m5); + if (lut_result != other_result) { + printf("%s: verification failed for index %d\n", fstr, t.as_uint8_t); + any_failures = true; + } + } + if (any_failures) { + printf("FAILURE - %s\n", fstr); + } else { + printf("SUCCESS - %s\n", fstr); + } + return !any_failures; +} +static bool verify_lut_g4(void) +{ + static const char* fstr = "verify_lut_g4"; + bool any_failures = false; + for (uint8_t i = 0; i < 32; ++i) { + INDEX_G4 t = { .as_uint8_t = i }; + bool lut_result = get_bit(small_lut_group4, i); + bool other_result = espresso_implementation_g4(t.r0, t.r2, t.r4, t.r5, t.r6); + if (lut_result != other_result) { + printf("%s: verification failed for index %d\n", fstr, t.as_uint8_t); + any_failures = true; + } + } + if (any_failures) { + printf("FAILURE - %s\n", fstr); + } else { + printf("SUCCESS - %s\n", fstr); + } + return !any_failures; +} +static bool verify_lut_XX(void) +{ + static const char* fstr = "verify_lut_XX"; + bool any_failures = false; + for (uint16_t ix = 0; ix < 256; ++ix) { + uint8_t i = (uint8_t)ix; + INDEX_XX t = { .as_uint8_t = i }; + bool lut_result = get_bit(small_lut_XX, i); + bool other_result = espresso_implementation_XX(t.l0, t.l4, t.l6, t.m1, t.m3, t.r0, t.r3, t.r5); + if (lut_result != other_result) { + printf("%s: verification failed for index %d\n", fstr, t.as_uint8_t); + any_failures = true; + } + } + if (any_failures) { + printf("FAILURE - %s\n", fstr); + } else { + printf("SUCCESS - %s\n", fstr); + } + return !any_failures; +} + +// order of input_names is based on resulting .PLA output, +// so least significant bit is at input_names[input_count-1] +static bool generate_espresso_file(uint8_t input_count, const char * const * input_names, const char * output_name, const uint8_t * lut_table_start, FILE* f_output) { + if (input_count > 8) { + printf("FAILURE - Cannot generate espresso file for more than 8 inputs (code would need update)\n"); + } + fprintf(f_output, "# ###################\n"); + fprintf(f_output, "# Generating .PLA\n"); + fprintf(f_output, "# ###################\n"); + fprintf(f_output, "\n\n"); + fprintf(f_output, ".i %d\n", input_count); + fprintf(f_output, ".o 1\n"); + const uint16_t iterations = 1u << input_count; + fprintf(f_output, ".ilb"); + for (uint16_t i = 0; i < input_count; ++i) { + const char* name = input_names[i]; + fprintf(f_output, " %s", name); + } + fprintf(f_output, "\n"); + fprintf(f_output, ".ob %s\n", output_name); + for (uint16_t ix = 0; ix < iterations; ++ix) { + + uint8_t i = (uint8_t)ix; + // NOTE: Seems we've handled it properly here, by shifting the bit to be tested appropriately + // based on the total number of inputs. So input_names[0] is the most significant bit.... + for (uint8_t bitmask = (1u << (input_count - 1u)); bitmask != 0; bitmask >>= 1) { + fputc(((i & bitmask) != 0) ? '1' : '0', f_output); + } + bool bit = get_bit(lut_table_start, i); + fprintf(f_output, " %c\n", bit ? '1' : '0'); + } + fprintf(f_output, ".e\n\n"); + + fprintf(f_output, "# ###################\n"); + fprintf(f_output, "# END OF .PLA\n"); + fprintf(f_output, "# ###################\n"); + return true; +} + +bool generate_all_lut_espresso_files(void) +{ + + bool any_failures = false; + + for (size_t i = 0; i < ARRAY_SIZE2(verification_table); ++i) { + VERIFICATION_T const * args = &verification_table[i]; + + bool current_result = false; + FILE* file = NULL; + char filename[20] = {0}; // needs: 14 for two-letter output name + + // First create the filename to be written + // Yes, presumes "safe" output_name ... + if (snprintf(NULL, 0, "output_%s.pla", args->output_name) < ARRAY_SIZE2(filename)) { + snprintf(filename, ARRAY_SIZE2(filename), "output_%s.pla", args->output_name); + file = fopen(filename, "w"); + if (file == NULL) { + printf("failed to open output file '%s'", filename); + } + } + if (file != NULL) { + current_result = generate_espresso_file( + args->input_count, + args->names, + args->output_name, + args->lut_start, + file + ); + if (!current_result) { + printf("Failed to generate espresso file for %s\n", args->output_name); + } + } + if (file != NULL) { + fclose(file); + file = NULL; + } + any_failures |= !current_result; + } + + return !any_failures; +} +bool verify_espresso_results(void) +{ + //generate_all_lut_espresso_files(); + + bool unused = verify_alternatives(); + bool r_g1 = verify_lut_g1(); + bool r_g2 = verify_lut_g2(); + bool r_g3 = verify_lut_g3(); + bool r_g4 = verify_lut_g4(); + bool r_xx = verify_lut_XX(); + return r_g1 && r_g2 && r_g3 && r_g4 && r_xx; +} diff --git a/client/deps/id48/tests/espresso/verify_espresso.h b/client/deps/id48/tests/espresso/verify_espresso.h new file mode 100644 index 000000000..0db7b257f --- /dev/null +++ b/client/deps/id48/tests/espresso/verify_espresso.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include + +bool generate_all_lut_espresso_files(void); +bool verify_espresso_results(void); diff --git a/client/deps/id48/tests/generator/CMakeLists.txt b/client/deps/id48/tests/generator/CMakeLists.txt new file mode 100644 index 000000000..71288dcd6 --- /dev/null +++ b/client/deps/id48/tests/generator/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.21) + +add_executable( + id48lib_test_generator + main.c +) + +target_link_libraries(id48lib_test_generator PRIVATE id48lib) + +target_compile_options(id48lib_test_generator PRIVATE + $<$:-O0> +) diff --git a/client/deps/id48/tests/generator/array_size2.h b/client/deps/id48/tests/generator/array_size2.h new file mode 100644 index 000000000..2f9a18e38 --- /dev/null +++ b/client/deps/id48/tests/generator/array_size2.h @@ -0,0 +1,157 @@ +/** + +The MIT License (MIT) + +Copyright (c) SimpleHacks, Henry Gabryjelski +https://github.com/SimpleHacks/UtilHeaders + +All rights reserved. + +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. + +*/ + +#ifndef ARRAYSIZE2_H +#define ARRAYSIZE2_H + +/** + The following, if defined prior to inclusion of this header file, + will modify its behavior as noted: + + ARRAYSIZE2_SHOW_VERSION_MESSAGE + -- if defined, will show which version of ARRAY_SIZE2 macro is selected + */ + + +/** + see example source at: + https://godbolt.org/z/zzYoeK6Mf +*/ + +#ifndef __has_feature + #define __has_feature(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (defined(__cplusplus) && __cplusplus >= 201103L) || /* any compiler claiming C++11 support */ \ + (defined(__cplusplus) && _MSC_VER >= 1900 && __cplusplus != 199711L) || /* Visual C++ 2015 or higher */ \ + __has_feature(cxx_constexpr) /* CLang versions supporting constexp */ + + #include /* required for size_t */ + #if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE) + #pragma message( "ARRAY_SIZE2 -- Using C++11 version" ) + #endif + + namespace detail + { + template + constexpr size_t ARRAY_SIZE2_ARGUMENT_CANNOT_BE_POINTER(T const (&)[N]) noexcept + { + return N; + } + } /* namespace detail */ + #define ARRAY_SIZE2(arr) detail::ARRAY_SIZE2_ARGUMENT_CANNOT_BE_POINTER(arr) + +#elif defined(__cplusplus) && __cplusplus >= 199711L && ( /* C++ 98 trick */ \ + defined(__INTEL_COMPILER) || \ + defined(__clang__) || \ + (defined(__GNUC__) && ( \ + (__GNUC__ > 4) || \ + (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) \ + ))) + + #include /* required for size_t */ + #if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE) + #pragma message "ARRAY_SIZE2 -- Using C++98 version" + #endif + template + char(&_ArraySizeHelperRequiresArray(T(&)[N]))[N]; + #define ARRAY_SIZE2(x) sizeof(_ArraySizeHelperRequiresArray(x)) + +#elif defined(__cplusplus) /* && ((__cplusplus >= 199711L) || defined(__INTEL_COMPILER) || defined(__clang__)) */ + + #if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE) + #pragma message( "ARRAY_SIZE2 -- Using Ivan J. Johnson's C++ version" ) + #endif + /* + Works on older compilers, even Visual C++ 6.... + Created by Ivan J. Johnson, March 06, 2007 + See http://drdobbs.com/cpp/197800525?pgno=1 + + Full description is in markdown file array_size2.md + */ + #define ARRAY_SIZE2(arr) ( \ + 0 * sizeof(reinterpret_cast(arr)) + /*check1*/ \ + 0 * sizeof(::Bad_arg_to_ARRAY_SIZE2::check_type((arr), &(arr))) + /*check2*/ \ + sizeof(arr) / sizeof((arr)[0]) /* eval */ \ + ) + + struct Bad_arg_to_ARRAY_SIZE2 { + class Is_pointer; /* incomplete */ + class Is_array {}; + template + static Is_pointer check_type(T const *, T const * const *); + static Is_array check_type(void const *, void const *); + }; + +#elif !defined(__cplusplus) && defined(__GNUC__) + + #include + + /** + Even C can have type-safety for equivalent of ARRAY_SIZE() macro, + when using the following two GCC extensions: + typeof() + __builtin_types_compatible_p() + */ + + #if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE) + #pragma message( "ARRAY_SIZE2 -- Using GNUC version" ) + #endif + + /** + validated using: + MSP430 gcc 4.5.3 + x86-64 icc 16.0.3 + x86-64 gcc 4.1.2 + x86-64 clang 3.0.0 + AVR gcc 4.5.4 + ARM gcc 4.5.4 + */ + + #define __SIMPLEHACKS_COMPATIBLE_TYPES__(a,b) __builtin_types_compatible_p(__typeof__(a), __typeof__(b)) /* GCC extensions */ + #define __SIMPLEHACKS_BUILD_ERROR_ON_NONZERO__(x) (sizeof(struct { uint8_t q: (-!!(x)*0x1ee7)+1u;})-1u) /* if x is zero, reports "error: negative width in bit-field ''" */ + #define __SIMPLEHACKS_MUST_BE_ARRAY__(x) __SIMPLEHACKS_BUILD_ERROR_ON_NONZERO__(__SIMPLEHACKS_COMPATIBLE_TYPES__((x), &(*x))) + #define ARRAY_SIZE2(_arr) ( (sizeof(_arr) / sizeof((_arr)[0])) + __SIMPLEHACKS_MUST_BE_ARRAY__(_arr) ) /* compile-time error if not an array */ + +#else + + /** + The good news is that all compilers (as of 20202-05-08) + on godbolt.org are fully supported. Therefore, if some + other compiler does not support any of the above method, + it's important to force a compile-time error, to avoid + any suggestion that this provides a safe macro. + */ + + #error "Unable to provide type-safe ARRAY_SIZE2 macro" + + +#endif + +#endif // ARRAYSIZE2_H diff --git a/client/deps/id48/tests/generator/main.c b/client/deps/id48/tests/generator/main.c new file mode 100644 index 000000000..355ea15a4 --- /dev/null +++ b/client/deps/id48/tests/generator/main.c @@ -0,0 +1,93 @@ +#include "id48.h" +#include +#include +#include "array_size2.h" + +static bool bytes_equal(const uint8_t *lhs, const uint8_t *rhs, size_t len) { + return memcmp(lhs, rhs, len) == 0; +} + +typedef struct _TEST_VECTOR_T { + const char *description; + const ID48LIB_KEY key; + const ID48LIB_NONCE nonce; + const ID48LIB_FRN expected_frn; + const ID48LIB_GRN expected_grn; +} TEST_VECTOR_T; + +// Test vectors used in various Proxmark3 client commands: +static const TEST_VECTOR_T test_vectors[] = { + // PM3 test key + { + // --key F32AA98CF5BE4ADFA6D3480B + // --rnd 45F54ADA252AAC + // --frn 4866BB70 + // --grn 9BD180 + .description = "PM3 test key", + .key = { .k = { 0xF3, 0x2A, 0xA9, 0x8C, 0xF5, 0xBE, 0x4A, 0xDF, 0xA6, 0xD3, 0x48, 0x0B, } }, + .nonce = { .rn = { 0x45, 0xF5, 0x4A, 0xDA, 0x25, 0x2A, 0xAC, } }, + .expected_frn = { .frn = { 0x48, 0x66, 0xBB, 0x70, } }, + .expected_grn = { .grn = { 0x9B, 0xD1, 0x80, } }, + }, + // Research paper key + { + // --key A090A0A02080000000000000 + // --rnd 3FFE1FB6CC513F + // --frn F355F1A0 + // --grn 609D60 + .description = "Research paper key", + .key = { .k = { 0xA0, 0x90, 0xA0, 0xA0, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } }, + .nonce = { .rn = { 0x3F, 0xFE, 0x1F, 0xB6, 0xCC, 0x51, 0x3F, } }, + .expected_frn = { .frn = { 0xF3, 0x55, 0xF1, 0xA0, } }, + .expected_grn = { .grn = { 0x60, 0x9D, 0x60, } }, + }, + // Autorecovery test key + { + // --key 022A028C02BE000102030405 + // --rnd 7D5167003571F8 + // --frn 982DBCC0 + // --grn 36C0E0 + .description = "Autorecovery test key", + .key = { .k = { 0x02, 0x2A, 0x02, 0x8C, 0x02, 0xBE, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, } }, + .nonce = { .rn = { 0x7D, 0x51, 0x67, 0x00, 0x35, 0x71, 0xF8, } }, + .expected_frn = { .frn = { 0x98, 0x2D, 0xBC, 0xC0, } }, + .expected_grn = { .grn = { 0x36, 0xC0, 0xE0, } }, + }, +}; + +bool generator_succeeds(const TEST_VECTOR_T *test_vector) { + ID48LIB_FRN actual_frn = {0}; + ID48LIB_GRN actual_grn = {0}; + + id48lib_generator(&test_vector->key, &test_vector->nonce, &actual_frn, &actual_grn); + + if (!bytes_equal(actual_frn.frn, test_vector->expected_frn.frn, sizeof(test_vector->expected_frn.frn))) { + printf("id48lib_generator: unexpected FRN for test vector '%s'\n", test_vector->description); + return false; + } + + if (!bytes_equal(actual_grn.grn, test_vector->expected_grn.grn, sizeof(test_vector->expected_grn.grn))) { + printf("id48lib_generator: unexpected GRN for test vector '%s'\n", test_vector->description); + return false; + } + + return true; +} + +int main(void) { + bool any_failures = false; + + for (size_t i = 0; i < ARRAY_SIZE2(test_vectors); i++) { + printf("Testing generator for test vector '%s'\n", test_vectors[i].description); + if (!generator_succeeds(&test_vectors[i])) { + printf("FAILURE: id48lib_generator: test vector '%s'\n", test_vectors[i].description); + any_failures = true; + } + } + if (any_failures) { + printf("id48lib_generator: some tests failed\n"); + } else { + printf("id48lib_generator: all tests passed\n"); + } + return any_failures ? 120 : 0; +} diff --git a/client/deps/id48/tests/recovery/CMakeLists.txt b/client/deps/id48/tests/recovery/CMakeLists.txt new file mode 100644 index 000000000..09fd42ff9 --- /dev/null +++ b/client/deps/id48/tests/recovery/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.21) + +add_executable( + id48lib_test_recovery + main.c +) + +target_link_libraries(id48lib_test_recovery PRIVATE id48lib) + +target_compile_options(id48lib_test_recovery PRIVATE + $<$:-O0> +) diff --git a/client/deps/id48/tests/recovery/array_size2.h b/client/deps/id48/tests/recovery/array_size2.h new file mode 100644 index 000000000..2f9a18e38 --- /dev/null +++ b/client/deps/id48/tests/recovery/array_size2.h @@ -0,0 +1,157 @@ +/** + +The MIT License (MIT) + +Copyright (c) SimpleHacks, Henry Gabryjelski +https://github.com/SimpleHacks/UtilHeaders + +All rights reserved. + +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. + +*/ + +#ifndef ARRAYSIZE2_H +#define ARRAYSIZE2_H + +/** + The following, if defined prior to inclusion of this header file, + will modify its behavior as noted: + + ARRAYSIZE2_SHOW_VERSION_MESSAGE + -- if defined, will show which version of ARRAY_SIZE2 macro is selected + */ + + +/** + see example source at: + https://godbolt.org/z/zzYoeK6Mf +*/ + +#ifndef __has_feature + #define __has_feature(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (defined(__cplusplus) && __cplusplus >= 201103L) || /* any compiler claiming C++11 support */ \ + (defined(__cplusplus) && _MSC_VER >= 1900 && __cplusplus != 199711L) || /* Visual C++ 2015 or higher */ \ + __has_feature(cxx_constexpr) /* CLang versions supporting constexp */ + + #include /* required for size_t */ + #if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE) + #pragma message( "ARRAY_SIZE2 -- Using C++11 version" ) + #endif + + namespace detail + { + template + constexpr size_t ARRAY_SIZE2_ARGUMENT_CANNOT_BE_POINTER(T const (&)[N]) noexcept + { + return N; + } + } /* namespace detail */ + #define ARRAY_SIZE2(arr) detail::ARRAY_SIZE2_ARGUMENT_CANNOT_BE_POINTER(arr) + +#elif defined(__cplusplus) && __cplusplus >= 199711L && ( /* C++ 98 trick */ \ + defined(__INTEL_COMPILER) || \ + defined(__clang__) || \ + (defined(__GNUC__) && ( \ + (__GNUC__ > 4) || \ + (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) \ + ))) + + #include /* required for size_t */ + #if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE) + #pragma message "ARRAY_SIZE2 -- Using C++98 version" + #endif + template + char(&_ArraySizeHelperRequiresArray(T(&)[N]))[N]; + #define ARRAY_SIZE2(x) sizeof(_ArraySizeHelperRequiresArray(x)) + +#elif defined(__cplusplus) /* && ((__cplusplus >= 199711L) || defined(__INTEL_COMPILER) || defined(__clang__)) */ + + #if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE) + #pragma message( "ARRAY_SIZE2 -- Using Ivan J. Johnson's C++ version" ) + #endif + /* + Works on older compilers, even Visual C++ 6.... + Created by Ivan J. Johnson, March 06, 2007 + See http://drdobbs.com/cpp/197800525?pgno=1 + + Full description is in markdown file array_size2.md + */ + #define ARRAY_SIZE2(arr) ( \ + 0 * sizeof(reinterpret_cast(arr)) + /*check1*/ \ + 0 * sizeof(::Bad_arg_to_ARRAY_SIZE2::check_type((arr), &(arr))) + /*check2*/ \ + sizeof(arr) / sizeof((arr)[0]) /* eval */ \ + ) + + struct Bad_arg_to_ARRAY_SIZE2 { + class Is_pointer; /* incomplete */ + class Is_array {}; + template + static Is_pointer check_type(T const *, T const * const *); + static Is_array check_type(void const *, void const *); + }; + +#elif !defined(__cplusplus) && defined(__GNUC__) + + #include + + /** + Even C can have type-safety for equivalent of ARRAY_SIZE() macro, + when using the following two GCC extensions: + typeof() + __builtin_types_compatible_p() + */ + + #if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE) + #pragma message( "ARRAY_SIZE2 -- Using GNUC version" ) + #endif + + /** + validated using: + MSP430 gcc 4.5.3 + x86-64 icc 16.0.3 + x86-64 gcc 4.1.2 + x86-64 clang 3.0.0 + AVR gcc 4.5.4 + ARM gcc 4.5.4 + */ + + #define __SIMPLEHACKS_COMPATIBLE_TYPES__(a,b) __builtin_types_compatible_p(__typeof__(a), __typeof__(b)) /* GCC extensions */ + #define __SIMPLEHACKS_BUILD_ERROR_ON_NONZERO__(x) (sizeof(struct { uint8_t q: (-!!(x)*0x1ee7)+1u;})-1u) /* if x is zero, reports "error: negative width in bit-field ''" */ + #define __SIMPLEHACKS_MUST_BE_ARRAY__(x) __SIMPLEHACKS_BUILD_ERROR_ON_NONZERO__(__SIMPLEHACKS_COMPATIBLE_TYPES__((x), &(*x))) + #define ARRAY_SIZE2(_arr) ( (sizeof(_arr) / sizeof((_arr)[0])) + __SIMPLEHACKS_MUST_BE_ARRAY__(_arr) ) /* compile-time error if not an array */ + +#else + + /** + The good news is that all compilers (as of 20202-05-08) + on godbolt.org are fully supported. Therefore, if some + other compiler does not support any of the above method, + it's important to force a compile-time error, to avoid + any suggestion that this provides a safe macro. + */ + + #error "Unable to provide type-safe ARRAY_SIZE2 macro" + + +#endif + +#endif // ARRAYSIZE2_H diff --git a/client/deps/id48/tests/recovery/main.c b/client/deps/id48/tests/recovery/main.c new file mode 100644 index 000000000..cdcba3157 --- /dev/null +++ b/client/deps/id48/tests/recovery/main.c @@ -0,0 +1,173 @@ +#include "id48.h" +#include +#include +#include "array_size2.h" + +static bool bytes_equal(const uint8_t *lhs, const uint8_t *rhs, size_t len) { + return memcmp(lhs, rhs, len) == 0; +} + +typedef struct _TEST_VECTOR_T { + const char *description; + const ID48LIB_KEY key; + const ID48LIB_NONCE nonce; + const ID48LIB_FRN expected_frn; + const ID48LIB_GRN expected_grn; +} TEST_VECTOR_T; + +// Test vectors used in various Proxmark3 client commands: +static const TEST_VECTOR_T test_vectors[] = { + // PM3 test key + { + // --key F32AA98CF5BE4ADFA6D3480B + // --rnd 45F54ADA252AAC + // --frn 4866BB70 + // --grn 9BD180 + .description = "PM3 test key", + .key = { .k = { 0xF3, 0x2A, 0xA9, 0x8C, 0xF5, 0xBE, 0x4A, 0xDF, 0xA6, 0xD3, 0x48, 0x0B, } }, + .nonce = { .rn = { 0x45, 0xF5, 0x4A, 0xDA, 0x25, 0x2A, 0xAC, } }, + .expected_frn = { .frn = { 0x48, 0x66, 0xBB, 0x70, } }, + .expected_grn = { .grn = { 0x9B, 0xD1, 0x80, } }, + }, + // Research paper key + { + // --key A090A0A02080000000000000 + // --rnd 3FFE1FB6CC513F + // --frn F355F1A0 + // --grn 609D60 + .description = "Research paper key", + .key = { .k = { 0xA0, 0x90, 0xA0, 0xA0, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } }, + .nonce = { .rn = { 0x3F, 0xFE, 0x1F, 0xB6, 0xCC, 0x51, 0x3F, } }, + .expected_frn = { .frn = { 0xF3, 0x55, 0xF1, 0xA0, } }, + .expected_grn = { .grn = { 0x60, 0x9D, 0x60, } }, + }, + // Autorecovery test key + { + // --key 022A028C02BE000102030405 + // --rnd 7D5167003571F8 + // --frn 982DBCC0 + // --grn 36C0E0 + .description = "Autorecovery test key", + .key = { .k = { 0x02, 0x2A, 0x02, 0x8C, 0x02, 0xBE, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, } }, + .nonce = { .rn = { 0x7D, 0x51, 0x67, 0x00, 0x35, 0x71, 0xF8, } }, + .expected_frn = { .frn = { 0x98, 0x2D, 0xBC, 0xC0, } }, + .expected_grn = { .grn = { 0x36, 0xC0, 0xE0, } }, + }, +}; + +// Comment + /// + /// Initializes to allow iterative recovery + /// of multiple potential keys. After calling + /// this init() function, can repeatedly call + /// the next() function until it returns false + /// to obtain all potential keys. + /// + /// + /// Top 48 bits of the key, such as those discovered + /// using the proxmark3 command `lf em 4x70 brute`. + /// Only k[0..5] are used from this parameter, + /// corresponding to K₉₅..K₄₈. + /// + /// + /// The nonce value. + /// Typically from a sniffed authentication. + /// + /// + /// The challenge sent from the reader (e.g., car) + /// to the tag (e.g., key). + /// Typically from a sniffed authentication. + /// + /// + /// The response sent from the tag (e.g., key) + /// to the car (e.g., car). + /// Typically from a sniffed authentication. + /// + /// + /// Note: In C++, each parameter would be a reference (not pointer). + /// + +// void id48lib_key_recovery_init( +// const ID48LIB_KEY *input_partial_key, +// const ID48LIB_NONCE *input_nonce, +// const ID48LIB_FRN *input_frn, +// const ID48LIB_GRN *input_grn +// ); + +// Comment + /// + /// This can be repeated called (after calling init()) + /// to find the next potential key for the given + /// partial key + nonce + frn + grn values. + /// I've seen combinations that have up to six + /// potential keys available, although typically + /// there are 1-3 results. + /// Each call to this function will return a single + /// value. Call repeatedly until the function returns + /// false to get all potential keys. + /// + /// + /// When the function returns true, this caller-provided + /// value will be filled with the 96-bit key that, when + /// programmed to the tag, should authenticate against + /// the nonce+frn values, with tag returning the grn value. + /// + /// + /// true when another potential key has been found. + /// false if no additional potential keys have been found. + /// + /// + /// Note: In C++, each parameter would be a reference (not pointer). + /// +// bool id48lib_key_recovery_next( +// ID48LIB_KEY *potential_key_output +// ); + + + +bool recovery_succeeds(const TEST_VECTOR_T *test_vector, bool zero_partial_key) { + + ID48LIB_KEY partial_key = {0}; + memcpy(&partial_key, &test_vector->key, sizeof(partial_key)); + if (zero_partial_key) { + // API is only supposed to be looking at the first 6 bytes of the key + memset(&partial_key.k[6], 0, sizeof(partial_key.k) - 6); + } + + id48lib_key_recovery_init(&partial_key, &test_vector->nonce, &test_vector->expected_frn, &test_vector->expected_grn); + + bool key_found = false; + uint32_t potential_keys_found = 0; + ID48LIB_KEY potential_key = {0}; + while (id48lib_key_recovery_next(&potential_key)) { + potential_keys_found++; + // just verify that the potential key matches the test vector + if (bytes_equal(potential_key.k, test_vector->key.k, sizeof(test_vector->key.k))) { + key_found = true; + } + } + return key_found; +} + +int main(void) { + bool any_failures = false; + + for (size_t i = 0; i < ARRAY_SIZE2(test_vectors); i++) { + printf("Testing recovery for test vector '%s'\n", test_vectors[i].description); + if (!recovery_succeeds(&test_vectors[i], false)) { + printf("FAILURE: id48lib_recovery: test vector '%s'\n", test_vectors[i].description); + any_failures = true; + } + printf("Testing recovery for test vector '%s' (partially-zero'd key)\n", test_vectors[i].description); + if (!recovery_succeeds(&test_vectors[i], true)) { + printf("FAILURE: id48lib_recovery: test vector '%s' (partially-zero'd key)\n", test_vectors[i].description); + any_failures = true; + } + } + if (any_failures) { + printf("id48lib_recovery: some tests failed\n"); + } else { + printf("id48lib_recovery: all tests passed\n"); + } + return any_failures ? 120 : 0; +} diff --git a/client/deps/id48lib.cmake b/client/deps/id48lib.cmake index 47205d494..39d58ceac 100644 --- a/client/deps/id48lib.cmake +++ b/client/deps/id48lib.cmake @@ -1,9 +1,11 @@ +# This is manually re-creating the contents of id48/src/CMakeLists.txt +# and thus must be manually kept in sync with updates to id48/src. add_library(pm3rrg_rdv4_id48 STATIC - id48/id48_data.c - id48/id48_generator.c - id48/id48_recover.c + id48/src/id48_data.c + id48/src/id48_generator.c + id48/src/id48_recover.c ) 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) +target_include_directories(pm3rrg_rdv4_id48 PRIVATE id48/public) +target_include_directories(pm3rrg_rdv4_id48 INTERFACE id48/public) set_property(TARGET pm3rrg_rdv4_id48 PROPERTY POSITION_INDEPENDENT_CODE ON)