Skip to content

Commit 61e0bae

Browse files
committed
Add HOL Light proof for aarch64 poly_caddq
Add the first HOL Light functional correctness proof for an aarch64 ML-DSA function: poly_caddq, which performs conditional addition of q to reduce polynomial coefficients from (-q, q) to [0, q). This commit includes: - HOL Light proof infrastructure for aarch64 (Makefile, utilities) - Functional correctness proof showing the assembly computes `ival(x i) rem &8380417` for each coefficient - autogen support for generating aarch64 HOL Light assembly - Updates of both HOL-Light and s2n-bignum - HOL-Light CI is extended and closely aligned with mlkem-native - Resolves #493 Signed-off-by: Matthias J. Kannwischer <[email protected]>
1 parent b61e84f commit 61e0bae

File tree

20 files changed

+650
-66
lines changed

20 files changed

+650
-66
lines changed

.github/workflows/hol_light.yml

Lines changed: 99 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,28 @@ on:
99
branches: ["main"]
1010
paths:
1111
- '.github/workflows/hol_light.yml'
12+
- 'proofs/hol_light/aarch64/Makefile'
13+
- 'proofs/hol_light/aarch64/**/*.S'
14+
- 'proofs/hol_light/aarch64/**/*.ml'
1215
- 'proofs/hol_light/x86_64/Makefile'
1316
- 'proofs/hol_light/x86_64/**/*.S'
1417
- 'proofs/hol_light/x86_64/**/*.ml'
18+
- 'flake.nix'
19+
- 'flake.lock'
1520
- 'nix/hol_light/*'
1621
- 'nix/s2n_bignum/*'
1722
pull_request:
1823
branches: ["main"]
1924
paths:
2025
- '.github/workflows/hol_light.yml'
26+
- 'proofs/hol_light/aarch64/Makefile'
27+
- 'proofs/hol_light/aarch64/**/*.S'
28+
- 'proofs/hol_light/aarch64/**/*.ml'
2129
- 'proofs/hol_light/x86_64/Makefile'
2230
- 'proofs/hol_light/x86_64/**/*.S'
2331
- 'proofs/hol_light/x86_64/**/*.ml'
32+
- 'flake.nix'
33+
- 'flake.lock'
2434
- 'nix/hol_light/*'
2535
- 'nix/s2n_bignum/*'
2636

@@ -33,8 +43,8 @@ jobs:
3343
# but we use this as a fast path to not even start the proofs
3444
# if the byte code needs updating.
3545
hol_light_bytecode:
36-
name: HOL-Light bytecode check
37-
runs-on: pqcp-x64
46+
name: AArch64 HOL-Light bytecode check
47+
runs-on: pqcp-arm64
3848
if: github.repository_owner == 'pq-code-package' && !github.event.pull_request.head.repo.fork
3949
steps:
4050
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
@@ -47,8 +57,8 @@ jobs:
4757
script: |
4858
autogen --update-hol-light-bytecode --dry-run
4959
hol_light_interactive:
50-
name: HOL-Light interactive shell test
51-
runs-on: pqcp-x64
60+
name: AArch64 HOL-Light interactive shell test
61+
runs-on: pqcp-arm64
5262
needs: [ hol_light_bytecode ]
5363
if: github.repository_owner == 'pq-code-package' && !github.event.pull_request.head.repo.fork
5464
steps:
@@ -60,21 +70,100 @@ jobs:
6070
gh_token: ${{ secrets.GITHUB_TOKEN }}
6171
nix-shell: 'hol_light'
6272
script: |
63-
# Load base infrastructure and specs to validate HOL-Light environment
64-
# When we have smaller/faster proofs, we can run them here instead:
65-
# make -C proofs/hol_light/x86_64 mldsa/mldsa_ntt.o
66-
# echo 'needs "proofs/mldsa_ntt.ml";;' | hol.sh
67-
echo 'needs "x86/proofs/base.ml";; needs "proofs/mldsa_specs.ml";; #quit;;' | hol.sh
73+
make -C proofs/hol_light/aarch64 mldsa/mldsa_poly_caddq.o
74+
echo 'needs "aarch64/proofs/mldsa_poly_caddq.ml";;' | hol.sh
6875
hol_light_proofs:
6976
needs: [ hol_light_bytecode ]
77+
strategy:
78+
fail-fast: false
79+
matrix:
80+
proof:
81+
# Dependencies on {name}.{S,ml} are implicit
82+
- name: mldsa_poly_caddq
83+
needs: ["aarch64_utils.ml"]
84+
name: AArch64 HOL Light proof for ${{ matrix.proof.name }}.S
85+
runs-on: pqcp-arm64
86+
if: github.repository_owner == 'pq-code-package' && !github.event.pull_request.head.repo.fork
87+
steps:
88+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
89+
with:
90+
fetch-depth: 0
91+
- name: Get changed files
92+
id: changed-files
93+
uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1
94+
- name: Check if dependencies changed
95+
id: check_run
96+
shell: bash
97+
run: |
98+
run_needed=0
99+
changed_files="${{ steps.changed-files.outputs.all_changed_files }}"
100+
dependencies="${{ join(matrix.proof.needs, ' ') }} ${{ format('{0}.S {0}.ml', matrix.proof.name) }}"
101+
for changed in $changed_files; do
102+
for needs in $dependencies; do
103+
if [[ "$changed" == *"$needs" ]]; then
104+
run_needed=1
105+
fi
106+
done
107+
done
108+
109+
# Always re-run upon change to nix files for HOL-Light
110+
if [[ "$changed_files" == *"nix/"* ]] || [[ "$changed_files" == *"hol_light.yml"* ]] || [[ "$changed_files" == *"flake"* ]] || [[ "$changed_files" == *"proofs/hol_light/aarch64/Makefile"* ]]; then
111+
run_needed=1
112+
fi
113+
114+
echo "run_needed=${run_needed}" >> $GITHUB_OUTPUT
115+
- uses: ./.github/actions/setup-shell
116+
if: |
117+
steps.check_run.outputs.run_needed == '1'
118+
with:
119+
gh_token: ${{ secrets.GITHUB_TOKEN }}
120+
nix-shell: 'hol_light'
121+
script: |
122+
tests hol_light -p ${{ matrix.proof.name }} --verbose
123+
124+
# x86_64 proofs
125+
hol_light_bytecode_x86_64:
126+
name: x86_64 HOL-Light bytecode check
127+
runs-on: pqcp-x64
128+
if: github.repository_owner == 'pq-code-package' && !github.event.pull_request.head.repo.fork
129+
steps:
130+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
131+
with:
132+
fetch-depth: 0
133+
- uses: ./.github/actions/setup-shell
134+
with:
135+
gh_token: ${{ secrets.GITHUB_TOKEN }}
136+
nix-shell: 'hol_light'
137+
script: |
138+
autogen --update-hol-light-bytecode --dry-run
139+
hol_light_interactive_x86_64:
140+
name: x86_64 HOL-Light interactive shell test
141+
runs-on: pqcp-x64
142+
needs: [ hol_light_bytecode_x86_64 ]
143+
if: github.repository_owner == 'pq-code-package' && !github.event.pull_request.head.repo.fork
144+
steps:
145+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
146+
with:
147+
fetch-depth: 0
148+
- uses: ./.github/actions/setup-shell
149+
with:
150+
gh_token: ${{ secrets.GITHUB_TOKEN }}
151+
nix-shell: 'hol_light'
152+
script: |
153+
# TODO: Enable when we have a cheaper proof
154+
# make -C proofs/hol_light/x86_64 mldsa/mldsa_ntt.o
155+
# echo 'needs "x86_64/proofs/mldsa_ntt.ml";;' | hol.sh
156+
echo 'needs "x86/proofs/base.ml";; needs "x86_64/proofs/mldsa_specs.ml";; #quit;;' | hol.sh
157+
hol_light_proofs_x86_64:
158+
needs: [ hol_light_bytecode_x86_64 ]
70159
strategy:
71160
fail-fast: false
72161
matrix:
73162
proof:
74163
# Dependencies on {name}.{S,ml} are implicit
75164
- name: mldsa_ntt
76165
needs: ["mldsa_specs.ml", "mldsa_utils.ml", "mldsa_zetas.ml"]
77-
name: HOL Light proof for ${{ matrix.proof.name }}.S
166+
name: x86_64 HOL Light proof for ${{ matrix.proof.name }}.S
78167
runs-on: pqcp-x64
79168
if: github.repository_owner == 'pq-code-package' && !github.event.pull_request.head.repo.fork
80169
steps:

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,12 @@ We use the [C Bounded Model Checker (CBMC)](https://github.com/diffblue/cbmc) to
5959

6060
**Note:** The `MLD_CONFIG_REDUCE_RAM` configuration option is not currently covered by CBMC proofs.
6161

62-
HOL-Light functional correctness proofs can be found in [proofs/hol_light/x86_64](proofs/hol_light/x86_64). So far, the following functions have been proven correct:
62+
HOL-Light functional correctness proofs can be found in [proofs/hol_light](proofs/hol_light). So far, the following functions have been proven correct:
6363

64+
- AArch64 poly_caddq [poly_caddq_asm.S](mldsa/src/native/aarch64/src/poly_caddq_asm.S)
6465
- x86_64 NTT [ntt.S](mldsa/src/native/x86_64/src/ntt.S)
6566

66-
67-
These proofs are utilizing the verification infrastructure in [s2n-bignum](https://github.com/awslabs/s2n-bignum).
67+
These proofs utilize the verification infrastructure in [s2n-bignum](https://github.com/awslabs/s2n-bignum).
6868

6969
## Security
7070

nix/hol_light/0005-Configure-hol-sh-for-mldsa-native.patch

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
11
# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT
22
diff --git a/hol_4.14.sh b/hol_4.14.sh
3-
index 1311255..8b2bc36 100755
43
--- a/hol_4.14.sh
54
+++ b/hol_4.14.sh
6-
@@ -5,7 +5,7 @@ export HOLLIGHT_DIR=__DIR__
7-
8-
if [ "$#" -eq 1 ]; then
9-
if [ "$1" == "-pp" ]; then
10-
- echo "camlp5r pa_lexer.cmo pa_extend.cmo q_MLast.cmo -I "__DIR__" pa_j.cmo"
11-
+ echo "camlp5r pa_lexer.cmo pa_extend.cmo q_MLast.cmo -I ${HOLLIGHT_DIR} pa_j.cmo"
12-
exit 0
13-
elif [ "$1" == "-dir" ]; then
14-
echo "${HOLLIGHT_DIR}"
15-
@@ -32,4 +32,13 @@ if [ "${HOL_ML_PATH}" == "" ]; then
5+
@@ -57,4 +57,13 @@ if [ "${HOL_ML_PATH}" == "" ]; then
166
HOL_ML_PATH="${HOLLIGHT_DIR}/hol.ml"
177
fi
188

@@ -21,22 +11,21 @@ index 1311255..8b2bc36 100755
2111
+SITELIB=$(dirname $(ocamlfind query findlib 2>/dev/null) 2>/dev/null)
2212
+
2313
+# Set HOLLIGHT_LOAD_PATH to include S2N_BIGNUM_DIR and mldsa-native proofs
24-
+export HOLLIGHT_LOAD_PATH="${S2N_BIGNUM_DIR}:${PROOF_DIR_X86_64}:${HOLLIGHT_LOAD_PATH}"
14+
+export HOLLIGHT_LOAD_PATH="${S2N_BIGNUM_DIR}:${PROOF_DIR}:${HOLLIGHT_LOAD_PATH}"
2515
+
2616
+# Change to mldsa-native proofs directory if set, so define_from_elf can find object files
27-
+[ -n "${PROOF_DIR_X86_64}" ] && cd "${PROOF_DIR_X86_64}"
17+
+[ -n "${PROOF_DIR}" ] && cd "${PROOF_DIR}"
2818
+
2919
+${LINE_EDITOR} ${HOLLIGHT_DIR}/ocaml-hol -init ${HOL_ML_PATH} -I ${HOLLIGHT_DIR} ${SITELIB:+-I "$SITELIB"}
3020
diff --git a/hol_4.sh b/hol_4.sh
31-
index 0aaa5c7..5adaf4c 100755
3221
--- a/hol_4.sh
3322
+++ b/hol_4.sh
3423
@@ -5,7 +5,7 @@ export HOLLIGHT_DIR=__DIR__
3524

3625
if [ "$#" -eq 1 ]; then
3726
if [ "$1" == "-pp" ]; then
3827
- echo "camlp5r pa_lexer.cmo pa_extend.cmo q_MLast.cmo -I "__DIR__" pa_j.cmo"
39-
+ echo "camlp5r pa_lexer.cmo pa_extend.cmo q_MLast.cmo -I "${HOLLIGHT_DIR}" pa_j.cmo"
28+
+ echo "camlp5r pa_lexer.cmo pa_extend.cmo q_MLast.cmo -I \"${HOLLIGHT_DIR}\" pa_j.cmo"
4029
exit 0
4130
elif [ "$1" == "-dir" ]; then
4231
echo "${HOLLIGHT_DIR}"

nix/hol_light/default.nix

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,21 @@ hol_light.overrideAttrs (old: {
99
export HOLLIGHT_DIR="$1/lib/hol_light"
1010
export PATH="$1/lib/hol_light:$PATH"
1111
'';
12-
version = "unstable-2025-09-22";
12+
version = "unstable-2026-01-17";
1313
src = fetchFromGitHub {
1414
owner = "jrh13";
1515
repo = "hol-light";
16-
rev = "bed58fa74649fa74015176f8f90e77f7af5cf8e3";
17-
hash = "sha256-QDubbUUChvv04239BdcKPSU+E2gdSzqAWfAETK2Xtg0=";
16+
rev = "e960dd0f636c36d48f79664c7cf11a59ba6f66a3";
17+
hash = "sha256-ZgOzAYokQsgO1Ua3m50shxvU9dVSzocuFHRLdIrINmU=";
1818
};
1919
patches = [
2020
./0005-Configure-hol-sh-for-mldsa-native.patch
2121
./0006-Add-findlib-to-ocaml-hol.patch
2222
];
2323
propagatedBuildInputs = old.propagatedBuildInputs ++ old.nativeBuildInputs ++ [ ocamlPackages.pcre2 ledit ];
2424
buildPhase = ''
25+
patchShebangs .
2526
HOLLIGHT_USE_MODULE=1 make hol.sh
26-
patchShebangs hol.sh
2727
HOLLIGHT_USE_MODULE=1 make
2828
'';
2929
installPhase = ''

nix/hol_light/hol-server.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ PORT=${1:-2012}
1515
HOL_LIGHT_DIR="@hol_light@/lib/hol_light"
1616
HOL_SERVER_SRC="@hol_server_src@"
1717

18-
# cd to x86_64 proofs directory if in mldsa-native repo
19-
PROOF_DIR="$(git rev-parse --show-toplevel 2>/dev/null)/proofs/hol_light/x86_64"
18+
# cd to proofs directory if in mldsa-native repo
19+
PROOF_DIR="$(git rev-parse --show-toplevel 2>/dev/null)/proofs/hol_light"
2020
[ -d "$PROOF_DIR" ] && cd "$PROOF_DIR"
2121

2222
echo "Starting HOL Light server on port $PORT..."

nix/s2n_bignum/default.nix

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
{ stdenv, fetchFromGitHub, writeText, ... }:
55
stdenv.mkDerivation rec {
66
pname = "s2n_bignum";
7-
version = "2ab2252b8505e58a7c3392f8ad823782032b61e7";
7+
version = "113a146ab49c19281388881b3650b63a6a67e8dc";
88
src = fetchFromGitHub {
99
owner = "awslabs";
1010
repo = "s2n-bignum";
1111
rev = "${version}";
12-
hash = "sha256-7lil3jAFo5NiyNOSBYZcRjduXkotV3x4PlxXSKt63M8=";
12+
hash = "sha256-Ub+Nrlo8DEmz3H5SdgcH9iLbNJnZmLvGk3dGgWar2kY=";
1313
};
1414
setupHook = writeText "setup-hook.sh" ''
1515
export S2N_BIGNUM_DIR="$1"

proofs/hol_light/README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
# HOL Light functional correctness proofs
44

5-
This directory contains functional correctness proofs for x86_64 assembly routines
5+
This directory contains functional correctness proofs for AArch64 and x86_64 assembly routines
66
used in mldsa-native. The proofs are written in the [HOL Light](https://hol-light.github.io/) theorem
77
prover, utilizing the assembly verification infrastructure from [s2n-bignum](https://github.com/awslabs/s2n-bignum).
88

9-
Each function is proved in a separate `.ml` file in [x86_64/proofs/](x86_64/proofs). Each file
9+
Each function is proved in a separate `.ml` file in [aarch64/proofs/](aarch64/proofs) and [x86_64/proofs/](x86_64/proofs). Each file
1010
contains the byte code being verified, as well as the specification that is being proved.
1111

1212
## Reproducing the proofs
@@ -19,12 +19,19 @@ nix develop .#hol_light --experimental-features 'nix-command flakes'
1919

2020
from mldsa-native's base directory. Then
2121

22+
```bash
23+
make -C proofs/hol_light/aarch64
24+
```
25+
or
26+
2227
```bash
2328
make -C proofs/hol_light/x86_64
2429
```
2530

2631
will build and run the proofs. Note that this may take hours even on powerful machines.
2732

33+
For convenience, you can also use `tests hol_light` which wraps the `make` invocation above; see `tests hol_light --help`.
34+
2835
## Interactive proof development
2936

3037
For interactive proof development, start the HOL Light server:
@@ -44,4 +51,5 @@ echo '1+1;;' | nc -w 5 127.0.0.1 2012
4451

4552
## What is covered?
4653

54+
- AArch64 poly_caddq: [mldsa_poly_caddq.S](aarch64/mldsa/mldsa_poly_caddq.S)
4755
- x86_64 forward NTT: [mldsa_ntt.S](x86_64/mldsa/mldsa_ntt.S)

0 commit comments

Comments
 (0)