This repository contains the datasets and analysis scripts supporting the CHES 2026 publication: "Coil-Based Detection and Concurrent Error Correction Against EMFI: An Experimental Case-Study on a Prototype ASIC" (eprint). It enables the reproduction of all results and plots presented in the paper.
- 🛠️ Prerequisites
- 📂 Repository Structure
- 🚀 Reproducing Results
- 📈 Dataset Description
- 💻 Implementation Files
- 📄 Output Files
- ✨ Post-Processing
- 📧 Contact
To reproduce the results, you need:
- Git LFS - Required for cloning large data files
- Environment setup (choose one):
- Nix package manager (recommended for reproducibility)
- Python 3.13+ with pip (tested with Python 3.13.9, see
requirements.txt)
Dataset-EMFI-Coil-Evaluation/
├── implementations/ # Target implementations source code
| ├── v3 # Design files τ₁ (Unprotected AES)
| ├── v4 # Design files τ₂ (AGEFA AES [3, 1, 3]-code)
| ├── v5 # Design files τ₃ (AGEFA AES [16, 8, 5]-code)
| ├── aes-byte-serial-tb.vhd # Testbench for all designs
├── measurements/ # Raw measurement data
│ ├── v3-10rep-1_1mx1_1mm/ # Target τ₁ (Unprotected AES)
│ │ ├── Y0_X0_Z0/ # Probe position (Y=0, X=0, Z=0)
│ │ └── ...
│ ├── v4-10rep-1_1mx1_1mm/ # Target τ₂ (AGEFA AES [3, 1, 3]-code)
│ ├── v5-10rep-1_1mx1_1mm/ # Target τ₃ (AGEFA AES [16, 8, 5]-code)
│ └── ...
├── results/ # Generated CSV files and plots
├── read.py # Main script for data processing
├── shell.nix # Nix environment configuration
└── README.md
# On Ubuntu/Debian
sudo apt-get install git-lfs
# On macOS
brew install git-lfs
# On other systems, see: https://git-lfs.github.com/Initialize Git LFS:
git lfs installgit clone https://github.com/ChairImpSec/Dataset-EMFI-Coil-Evaluation
cd Dataset-EMFI-Coil-EvaluationImportant
Due to Git LFS budget limitations, it is currently not possible to clone the preprocessed cached files (.parquet files in results/).
The corresponding error can be ignored.
However, you must regenerate all temporary files using the make full-rebuild command (see section 4 below).
This will process the raw measurement data (~30 minutes) to generate all analysis results.
Option A: Using Nix Flakes (recommended - provides pinned dependencies):
nix developFor setups without experimental features enabled:
nix --extra-experimental-features 'nix-command flakes' developOption B: Using traditional Nix:
nix-shellOption C: Using pip (without Nix):
pip install -r requirements.txtExecute the following command to process cached data and generate CSV files:
python read.pyBy default, this uses cached preprocessed data (included in the repository) and generates only pinout-based detection heatmaps. For full analysis from raw data, see the command-line options below.
The script supports the following command-line arguments (use python read.py --help to see all options):
-
--clean: Regenerate all files from raw measurement data instead of using cached preprocessed data.python read.py --clean
-
--all {all,partial,minimal}: Set the level of analysis to perform (default:partial).all: Generate all available heatmaps and plotspartial: Generate only pinout-based detection heatmapsminimal: Generate minimal set of plots
python read.py --all all
-
--no-show-plots: Disable interactive plot display (useful for batch processing).python read.py --no-show-plots
Example usage:
# Quick start - use cached data with partial analysis (default)
python read.py
# Command equivalent to the original submitted version
python read.py --clean --all partial
# Full analysis - required to run the scripts in `results` (takes ~30 minutes)
python read.py --clean --no-show-plots --all all
# Regenerate with partial analysis without showing plots
python read.py --clean --no-show-plotsNote
When all plots are generated, 4 of these plots will contain only zeros for all coordinates. The reason for this is a bug in the HDL implementation of the ASIC hosting the physical and algorithmic countermeasures: The four HVT registers with the highest index are not connected to the data bus correctly.
Experiments evaluated:
The script processes three target implementations: v3-10rep-1_1mx1_1mm, v4-10rep-1_1mx1_1mm, and v5-10rep-1_1mx1_1mm.
Note
The script generates more plots than depicted in the publication.
The measurements directory contains subfolders for each target, as described below:
| Folder | Target Description |
|---|---|
v3-10rep-1_1mx1_1mm |
τ₁ (Unprotected AES) |
v4-10rep-1_1mx1_1mm |
τ₂ (AGEFA AES [3, 1, 3]-code) |
v5-10rep-1_1mx1_1mm |
τ₃ (AGEFA AES [16, 8, 5]-code) |
Each measurement folder contains subfolders named Yy_Xx_Z0, where:
x, y ∈ [0, 11]: Coordinates in the x-y plane.
Inside these subfolders, binary files follow the naming scheme:
Expr_i_Cc_Xx_Yy_Z0_Vv_Pp_LJDd.dat
-
i: Experiment index (incremented per variable change). -
c: Cell type of the coil detection logic on the ASIC (configured via FPGA). -
x, y: Coordinates in the x-y plane (over the ASIC). -
v: Voltage for EMFI$v \in [50, 60, ..., 500]$ . -
p: Polarity of the electromagnetic field (0= negative,1= positive). -
d: Low jitter delay (delay in $d\cdot 0.7$ ns after the trigger is raised by the control FPGA).
All target implementations are provided as RTL code in the implementations directory, along with a testbench that is compatible with all designs.
The read.py script generates CSV files for each plot in the paper.
These files are saved in the results directory, mirroring the structure of the measurements folder.
Note
Some plots (e.g., the first four line plots per target) are not directly used in the publication, but post-processed.
The repository includes preprocessed data for fast reproduction:
make # Run post-processing with cached data (~1 minute)To regenerate everything from scratch:
make full-rebuild # Clean and regenerate everything (~30 minutes)make clean # Remove all generated files
make analysis-clean # Generate CSV files from raw measurements (~30 min)
make post-process # Run post-processing scripts for merged plotsmake- Quick run using cached data (default)make full-rebuild- Full regeneration from scratchmake analysis- Generate results using cached preprocessed datamake analysis-clean- Regenerate from raw measurement filesmake post-process- Run post-processing scripts onlymake clean- Remove all generated filesmake help- Show available targets
If you prefer to run scripts manually:
# Step 1: Generate initial CSV files
python read.py --clean --no-show-plots --all all
# Step 2: Run post-processing scripts
cd results
python ../merge-coil-polarity-results.py
python ../merge-coil-results.py
python ../merge-coil-types-for-polarity.py
python ../merge-coil-polarity-first-reaction.pyNote
For completeness, we provide the output of all scripts within the results folder.
Currently, no python script is available to generate exactly the same plot as shown in Figure 7a and Figure 7b, due to manually post-processing and computations from within TikZ.
Figure 7a data is generated automatically by the function plot_coil_counts_by_vlevel_and_polarity(df, export_dir, id="").
Generation pipeline:
-
Generate initial data:
python read.py --all partial
This opens plots for each target and all three cell types.
-
Merge the data:
merge-coil-polarity-results.pymerges the polarity resultsmerge-coil-types-for-polarity.pyfurther merges across cell types
-
Final visualization: The resulting CSV is processed by a TikZ script that computes ratios by dividing the detection counts by the number of experiments conducted for each voltage level and polarity.
Figure 7b requires manual post-processing after running merge-coil-polarity-first-reaction.py.
The function plot_polarity_start_comparison(df, export_dir, id="") generates 3 versions of Figure 7b (one per target).
The final Figure 7b in the paper is the average of these 3 plots, with coil 9 manually adjusted.
The file results/polarity-comparison-results-merged.csv contains the merged results.
After post-processing the result is as follows:
coil,value
coils-lvt-c0,0
coils-lvt-c1,0
coils-lvt-c2,3
coils-lvt-c3,0
coils-lvt-c4,3
coils-lvt-c5,3
coils-lvt-c6,3
coils-lvt-c7,0
coils-lvt-c8,3
coils-lvt-c9,1.5
coils-lvt-c10,3-
Value interpretation:
3= "+" (positive polarity starts earlier)0= "-" (negative polarity starts earlier)1.5= neutral (no clear preference)
-
Special handling of coil 9: The ninth coil is set to neutral (1.5) because we observed that the coil itself does not detect the EM field, but rather the wires connecting it. Therefore, the relationship between the winding direction and field polarity cannot be analyzed for this coil.
For questions or issues, please contact: [email protected] or open an issue on the repository.