blob: e3c17cb80d1b8cfa3aa4030dfcd60620d64906e6 [file] [log] [blame] [view]
# OTBN Formal Masking Verification Using Alma
This directory contains support files to formally verify the OTBN core using the
tool [Alma:
Execution-aware Masking Verification](https://github.com/IAIK/coco-alma).
## Prerequisites
Note that this flow is experimental. It has been developed using Yosys v0.15
(this also works: v0.9+4306 (git sha1 3931b3a03)), sv2v v0.0.9-24-gf868f06 and
Verilator 4.106 (2020-12-02 rev v4.106). Other tool versions might not be
compatible.
1. Download the Alma tool from this specific repo and check out to the
`coco-otbn-latest` branch of the tool
```sh
git clone git@github.com:abdullahvarici/coco-alma.git -b coco-otbn-latest
```
Enter the directory using
```sh
cd coco-alma
```
Set up a new virtual Python environment
```sh
python3 -m venv dev
source dev/bin/activate
```
And install the Python requirements
```sh
pip3 install -r requirements.txt
```
Update `examples/otbn/config.json` to point correct locations for `asm`,
`objdump` and `rv_objdump`.
1. Generate a Verilog netlist
A netlist of the DUT can be generated using the Yosys synthesis flow from
the OpenTitan repository. From the OpenTitan top level, run
```sh
cd hw/ip/otbn/pre_syn
```
Set up the synthesis flow as described in the corresponding README. Then run
the synthesis
```sh
./syn_yosys.sh
```
## Formally verifying the masking of the OTBN core
After downloading the Alma tool, installing dependencies and synthesizing OTBN,
the masking can finally be formally verified.
1. Enter the directory where you have downloaded Alma and load the virtual
Python environment.
```sh
source dev/bin/activate
```
1. Make sure to source the `build_consts.sh` script from the OpenTitan
repository in order to set up some shell variables.
```sh
source ../opentitan/util/build_consts.sh
```
1. Launch the Alma tool to parse, assemble, trace (simulate) and formally verify
the netlist. For simplicity, a single script is provided to launch all the
required steps with a single command. Simply run:
```sh
${REPO_TOP}/hw/ip/otbn/pre_sca/alma/verify_otbn.sh
```
This should produce output similar to the one below:
```sh
Verifying OTBN using Alma
Starting yosys synthesis...
| CircuitGraph | Total: 234238 | Linear: 22351 | Non-linear: 107502 | Registers: 21338 | Mux: 41352 |
parse.py successful (755.32s)
INSTR_LIMIT = 128
Using program file: programs/isw_and.S
Using build directory: [build_directory]
Using netlist path: ../../tmp/circuit.v
Wrote verilator testbench to [build_directory]/verilator_tb.c
It produces output VCD at [build_directory]/circuit.vcd
1: Running verilator on given netlist
2: Compiling verilated netlist library
3: Compiling provided verilator testbench
4: Simulating circuit and generating VCD
Line 0: WDR label found - u_otbn_core.u_otbn_rf_bignum.gen_rf_bignum_ff.u_otbn_rf_bignum_inner.rf [7800] = secret 0
Line 1: WDR label found - u_otbn_core.u_otbn_rf_bignum.gen_rf_bignum_ff.u_otbn_rf_bignum_inner.rf [7488] = secret 0
Line 2: WDR label found - u_otbn_core.u_otbn_rf_bignum.gen_rf_bignum_ff.u_otbn_rf_bignum_inner.rf [7176] = secret 1
Line 3: WDR label found - u_otbn_core.u_otbn_rf_bignum.gen_rf_bignum_ff.u_otbn_rf_bignum_inner.rf [6864] = secret 1
Line 4: WDR label found - u_otbn_core.u_otbn_rf_bignum.gen_rf_bignum_ff.u_otbn_rf_bignum_inner.rf [6552] = static_random
| CircuitGraph | Total: 234238 | Linear: 22351 | Non-linear: 107502 | Registers: 21338 | Mux: 41352 |
0 nodes are ignored.
tmp/circuit.vcd:57091: [WARNING] Entry for name clk_sys already exists in namemap (clk_sys -> #33)
tmp/circuit.vcd:57110: [WARNING] Entry for name imem_wdata_i already exists in namemap (imem_wdata_i -> $33)
tmp/circuit.vcd:57111: [WARNING] Entry for name imem_we_i already exists in namemap (imem_we_i -> &33)
tmp/circuit.vcd:57112: [WARNING] Entry for name imem_wmask_i already exists in namemap (imem_wmask_i -> \'33)
tmp/circuit.vcd:57116: [WARNING] Entry for name rst_sys_n already exists in namemap (rst_sys_n -> )33)
Waiting for initial delay cycles: 139
RST value: 1
1
Building formula for cycle 0:
vars 0 clauses 0
Checking cycle 0:
Checking secret 0 [1, 2]:
Checking secret 1 [3, 4]:
RST value: 1
1
Building formula for cycle 1:
vars 16 clauses 25
Checking cycle 1:
Checking secret 0 [17, 18]:
Checking secret 1 [19, 20]:
RST value: 1
1
Building formula for cycle 2:
vars 21103 clauses 57104
Checking cycle 2:
Checking secret 0 [21427, 21104]:
Checking secret 1 [21549, 21105]:
RST value: 1
1
Building formula for cycle 3:
vars 566836 clauses 1692708
Checking cycle 3:
Checking secret 0 [630522, 652105]:
Finished in 34.31
Writing a trace with the found error to [build_directory]/dbg-label-trace-0.txt
Writing a reduced circuit to [build_directory]/tmp/dbg-circuit-0.dot
The execution is not secure, here are some leaks:
leak 0: (cycle: 3, cell: mux _10580_[0], id: 223382)
3 stable mux _10580_[0] vars : ['s0:0', 's0:1']
3 stable mux _10580_[0] signals: u_otbn_core.u_otbn_rf_bignum.gen_rf_bignum_ff.u_otbn_rf_bignum_inner.rf[7800] ^ u_otbn_core.u_otbn_rf_bignum.gen_rf_bignum_ff.u_otbn_rf_bignum_inner.rf[7488]
```
## Individual steps in detail
Below we outline the individual steps performed by the `verify_otbn.sh` script.
This is useful if you, e.g., want to verify the masking of your own module.
For more details, please refer to the [Alma
tutorial](https://github.com/IAIK/coco-alma/tree/hw-verif#usage)
1. Make sure to source the `build_consts.sh` script from the OpenTitan
repository in order to set up some shell variables.
```sh
source ../opentitan/util/build_consts.sh
```
1. The first step involves the parsing of the synthesized netlist.
```sh
python3 parse.py --keep --top-module otbn_top_coco --log-yosys \
--source ${REPO_TOP}/hw/ip/otbn/pre_sca/alma/rtl/ram_1p.v \
${REPO_TOP}/hw/ip/otbn/pre_syn/syn_out/latest/generated/otbn_core.alma.v \
${REPO_TOP}/hw/ip/otbn/pre_sca/alma/rtl/otbn_top_coco.v
```
1. Next, run the `assemble.py` script to generate memory initialization file for
OTBN.
```sh
program=isw_and
cd examples/otbn
python3 assemble.py --program programs/${program}.S \
--netlist ../../tmp/circuit.v
cd ../../
```
1. Then, the Verilator testbench can be compiled and run. This step is required
to identify control signals.
```sh
python3 trace.py --testbench tmp/verilator_tb.c \
--netlist tmp/circuit.v \
--c-compiler gcc \
--make-jobs 16
```
Add `-b` argument to use cached object files from a previous Verilator run
and save some time.
1. Next, the automatically generated labeling file `tmp/labels.txt` needs to be
adapted. This file tells Alma which inputs of the DUT correspond to the
secret shares and which ones are used to provide randomness for (re-)masking.
It is pretty tedious to compute the actual indices for bignum register file
labels. Generate it with the following command:
```sh
examples/otbn/labels/generate_bignum_rf_labels.py \
-i examples/otbn/labels/${program}_labels.txt \
-o tmp/labels_updated.txt -w 1 -s 0
```
1. Finally the verification of the masking implementation can be started.
```sh
python3 verify.py --json tmp/circuit.json \
--top-module otbn_top_coco \
--label tmp/labels_updated.txt \
--vcd tmp/circuit.vcd \
--checking-mode per-location \
--rst-name rst_sys_n \
--rst-phase 0 \
--rst-cycles 2 \
--init-delay 139 \
--cycles 25 \
--excluded-signals u_otbn_core.u_otbn_controller.rf_bignum_intg_err_i[0] \
--glitch-behavior loose \
--dbg-signals otbn_cycle_cnt_o \
--mode stable
```
Run the following command to see the waveform:
```sh
gtkwave tmp/circuit.vcd
```
Run the following command to see the circuit diagramm if there is a leakage:
```sh
xdot tmp/dbg-circuit-0.dot
```