The theoretical evaluation is stored in dilithium_theory_evaluation
.
To reproduce our theoretical evaluation, first build the docker container from the Dockerfile
located in the dilithium_theory_evaluation
directory. Here we will named the built container dilithium_theory_evaluation
:
cd dilithium_theory_evaluation
docker build . -t dilithium_theory_evaluation
Per default this container runs the script dilithium_one_attack.py
:
docker run dilithium_theory_evaluation
You can run the script dilithium.py
like this:
docker run dilithium_theory_evaluation dilithium.py
The script dilithium_one_attack.py
will run the theoretical evaluation as described in Section 6 "Simulation and Evaluation of the Attack" of the paper.
The script will only run one attack, to demonstrate the attack. To run further attacks, run the script dilithium.py
.
Please note that running dilithium.py
can take more than a day to finish or even run indefinitely if the computer is not powerful enough. Please refer to our paper for details on the hardware we used. If the output is not a logger output, it is generated by the gurobi solver. The logger outputs JSONs, each one contains information about a single attempted recovery of s_1. To ignore the gurobi output, filter for :root:
, i.e.:
docker run dilithium_theory_evaluation | grep ':root:'
The script dilithium_one_attack.py
is parametrized as follows:
nist_security_level = 2
threads = 40
params = Parameters.get_nist_security_level(nist_security_level, threads=threads)
threshold = params.beta
notion_of_success = 2*params.n # How many _correct_ equations (for each secret key polynomial) do we collect before we launch the attack?
#Parameter m measures how many coefficients are NOT faulted
#That means we fault n*l -m coefficients
#Setting m to params.n*params.l -2 will fault exactly TWO coefficients per signature
#The script will inform the user if the attack succeeded
for m in range(params.n*params.l - 2, params.n * params.l -1):
If the attack succeeds, you should see the following:
INFO:root:Successfully recovered s_1 entry!
INFO:root:Successful recovery!
DEBUG:root:{"entry_results": [{"faulted_coeffs": 535, "total_equations": 267008, "filtered_equations": 669, "duration": 0.8444759845733643, "failure": false, "equations_used": 535.0}, {"faulted_coeffs": 531, "total_equations": 267008, "filtered_equations": 676, "duration": 0.959650993347168, "failure": false, "equations_used": 531.0}, {"faulted_coeffs": 512, "total_equations": 267008, "filtered_equations": 697, "duration": 1.3874258995056152, "failure": false, "equations_used": 512.0}, {"faulted_coeffs": 512, "total_equations": 267008, "filtered_equations": 684, "duration": 1.3388750553131104, "failure": false, "equations_used": 512.0}], "m": 1022, "num_signatures": 1043, "nist_param_level": 2, "threshold": 78, "notion_of_success": 512, "timeout_limit": 300}
INFO:root:m = 1022; notion_of_success = 512 succeeded!
The attack itself is implemented in recover_s1_entry.py
. The data in the "entry_results" dictionary gives detailed information about the attack statistics, such as the number of used equations, number of filtered equations (according to the method described in Section 5.3 Attacking the Protected Implementation of Dilithium to reduce false-positives), or number of faulted coefficients (in total).
To reproduce the practical evaluation from Section 7, End-To-End Attack Proof-of-Concept, we provide the POC
directory. Start the container using
docker compose up -d
Per default this start a jupyter notebook listening at port 8080
and is thus accessable via http://localhost:8080
. You can configure the port in the docker-compose.yaml
file.
To connect to the notebook you will need to enter a token. You can find this in the log of the container, i.e. docker compose logs
.
Once connected to the notebook web interface you will find following files:
Dilithium - Glitches - Signature - Only - Attack.ipynb
: Collect the faulted signaturesStats - Signature - Only - Attack.ipynb
: Run the secret key recovery plus print attack statisticsgc.results.pickled.signature-attacks-2023-03-16_00-14-39.pickle
: Faulted signatures we physically collected
To collect the faulted signatures it is required that your computer is connected to a ChipWhisperer Lite FPGA board, which in turn needs to be connected to a STM32F4
target board mounted on a ChipWhisperer UFO board.
Additionally it is crutial to have proper fault parameters, you can configure them in the cell containing following code.
magic_numbers = [straight_line['y_intercept'] + poly_index * straight_line['slope'] for poly_index in range(d._polyz_unpack_num_iters - 1)]
ext_offsets = magic_numbers
offsets = (0.390625,)
widths = (1.562500,)
Our paramaters will most likely not work on your setup, because these parameters are very dependend on the physical setup like cable length.
To run the secret key recovery and print statistics, the notebook will per default use our collected signature from the .pickle
file.
The cell Out[13]
depicts the success rate per faulted polynomial, as described in Section 7.3.