Skip to content

Commit 9492a8b

Browse files
authored
Merge pull request #662 from pq-code-package/hol_light
Add functional correctness proofs for AArch64-optimized NTT and invNTT in HOL Light
2 parents dfcb6ed + a1b8105 commit 9492a8b

File tree

14 files changed

+2410
-2
lines changed

14 files changed

+2410
-2
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,6 @@ jobs:
370370
acvp: false
371371
nix-shell: ${{ matrix.compiler.shell }}
372372
cflags: "-std=c17"
373-
# The purpose of this job is to test non-default yet valid configurations
374373
config_variations:
375374
name: Non-standard configurations
376375
needs: [quickcheck, quickcheck-windows, quickcheck-c90, quickcheck-lib, examples, lint, lint-markdown-link]

.github/workflows/hol_light.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
name: HOL-Light
4+
permissions:
5+
contents: read
6+
on:
7+
push:
8+
branches: ["main"]
9+
# Only run upon changes to AArch64 NTT or invNTT
10+
paths:
11+
- 'proofs/hol_light/arm/**/*.S'
12+
pull_request:
13+
branches: ["main"]
14+
paths:
15+
# Only run upon changes to AArch64 NTT or invNTT
16+
- 'proofs/hol_light/arm/**/*.S'
17+
18+
concurrency:
19+
group: ${{ github.workflow }}-${{ github.ref }}
20+
cancel-in-progress: true
21+
22+
jobs:
23+
hol_light_proofs:
24+
strategy:
25+
matrix:
26+
proof: [mlkem_ntt,mlkem_intt]
27+
name: HOL Light proof for ${{ matrix.proof }}.S
28+
runs-on: pqcp-arm64
29+
if: github.repository_owner == 'pq-code-package' && !github.event.pull_request.head.repo.fork
30+
steps:
31+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
32+
- uses: ./.github/actions/setup-shell
33+
with:
34+
gh_token: ${{ secrets.GITHUB_TOKEN }}
35+
nix-shell: 'hol_light'
36+
script: |
37+
make -C proofs/hol_light/arm mlkem/${{ matrix.proof }}.correct

flake.nix

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,28 @@
2424
in
2525
{
2626
packages.cbmc = util.cbmc;
27+
packages.hol_light = pkgs.callPackage ./nix/hol_light { };
28+
packages.s2n_bignum = pkgs.callPackage ./nix/s2n_bignum { };
2729

2830
devShells.default = util.wrapShell util.mkShell {
2931
packages =
3032
util.core { } ++
3133
util.linters ++
3234
builtins.attrValues
3335
{
34-
inherit (config.packages) cbmc;
36+
inherit (config.packages) cbmc hol_light s2n_bignum;
3537
inherit (pkgs)
3638
direnv
3739
nix-direnv;
3840
};
3941
};
4042

43+
devShells.hol_light = util.wrapShell util.mkShell {
44+
packages = builtins.attrValues {
45+
inherit (config.packages) hol_light s2n_bignum;
46+
};
47+
};
48+
4149
devShells.ci = util.wrapShell util.mkShell { packages = util.core { cross = false; }; };
4250
devShells.ci-cross = util.wrapShell util.mkShell { packages = util.core { }; };
4351
devShells.ci-cbmc = util.wrapShell util.mkShell { packages = util.core { cross = false; } ++ [ config.packages.cbmc ]; };
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
diff --git a/hol_4.14.sh b/hol_4.14.sh
3+
index 0fa0f64..313133e 100755
4+
--- a/hol_4.14.sh
5+
+++ 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+
diff --git a/hol_4.sh b/hol_4.sh
16+
index be55568..259ecae 100755
17+
--- a/hol_4.sh
18+
+++ b/hol_4.sh
19+
@@ -5,7 +5,7 @@ export HOLLIGHT_DIR=__DIR__
20+
21+
if [ "$#" -eq 1 ]; then
22+
if [ "$1" == "-pp" ]; then
23+
- echo "camlp5r pa_lexer.cmo pa_extend.cmo q_MLast.cmo -I "__DIR__" pa_j.cmo"
24+
+ echo "camlp5r pa_lexer.cmo pa_extend.cmo q_MLast.cmo -I "${HOLLIGHT_DIR}" pa_j.cmo"}
25+
exit 0
26+
elif [ "$1" == "-dir" ]; then
27+
echo "${HOLLIGHT_DIR}"
28+
@@ -27,4 +27,4 @@ if [ -d "${HOLLIGHT_DIR}/_opam" ]; then
29+
eval $(opam env --switch "${HOLLIGHT_DIR}/" --set-switch)
30+
fi
31+
32+
-${LINE_EDITOR} ${HOLLIGHT_DIR}/ocaml-hol -I `camlp5 -where` camlp5o.cma -init ${HOLLIGHT_DIR}/hol.ml -safe-string -I ${HOLLIGHT_DIR}
33+
+${LINE_EDITOR} ${HOLLIGHT_DIR}/ocaml-hol -I $(camlp5 -where) camlp5o.cma -init ${HOLLIGHT_DIR}/hol.ml -safe-string -I ${HOLLIGHT_DIR}

nix/hol_light/default.nix

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
{ hol_light, fetchFromGitHub, writeText, ... }:
4+
hol_light.overrideAttrs (old: {
5+
setupHook = writeText "setup-hook.sh" ''
6+
export HOLDIR="$1/lib/hol_light"
7+
export HOLLIGHT_DIR="$1/lib/hol_light"
8+
'';
9+
version = "unstable-2024-12-22";
10+
src = fetchFromGitHub {
11+
owner = "jrh13";
12+
repo = "hol-light";
13+
rev = "28e4aed1019a56fab869f752695a67a4164dd2ee";
14+
hash = "sha256-Z14pED3oaz30Zp1Ue58KA5srlZc31WyDE8h9tJwCAcI=";
15+
};
16+
patches = [ ./0005-Fix-hollight-path.patch ];
17+
propagatedBuildInputs = old.propagatedBuildInputs ++ old.nativeBuildInputs;
18+
buildPhase = ''
19+
HOLLIGHT_USE_MODULE=1 make hol.sh
20+
patchShebangs hol.sh
21+
HOLLIGHT_USE_MODULE=1 make
22+
'';
23+
installPhase = ''
24+
mkdir -p "$out/lib/hol_light"
25+
cp -a . $out/lib/hol_light
26+
sed "s^__DIR__^$out/lib/hol_light^g; s^__USE_MODULE__^1^g" hol_4.14.sh > hol.sh
27+
mv hol.sh $out/lib/hol_light/
28+
'';
29+
})
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
diff --git a/tools/build-proof.sh b/tools/build-proof.sh
3+
index 39d72433..d10c3244 100755
4+
--- a/tools/build-proof.sh
5+
+++ b/tools/build-proof.sh
6+
@@ -1,6 +1,7 @@
7+
#!/bin/bash
8+
+ROOT="$(realpath "$(dirname "$0")"/../)"
9+
if [ "$#" -ne 3 ]; then
10+
- echo "../tools/build-proof.sh <.ml file path> <hol.sh> <output .native path>"
11+
+ echo "${ROOT}/tools/build-proof.sh <.ml file path> <hol.sh> <output .native path>"
12+
echo "This script builds HOL Light proof using OCaml native compiler and puts the "
13+
echo "output binary at <output .native path>."
14+
exit 1
15+
@@ -9,14 +10,11 @@ fi
16+
# Return the exit code if any statement fails
17+
set -e
18+
19+
-s2n_bignum_arch=$(basename "$(pwd)")
20+
-
21+
-cd ..
22+
-
23+
-ml_path_noarch=$1
24+
-ml_path=${s2n_bignum_arch}/${ml_path_noarch}
25+
+ml_path="$1"
26+
hol_sh_cmd=$2
27+
-output_path=${s2n_bignum_arch}/$3
28+
+output_path=$3
29+
+output_dir=$(dirname "$output_path")
30+
+[ -d "$output_dir" ] || mkdir -p "$output_dir"
31+
32+
export HOLLIGHT_DIR="$(dirname ${hol_sh_cmd})"
33+
if [ ! -f "${HOLLIGHT_DIR}/hol_lib.cmxa" ]; then
34+
@@ -34,7 +32,7 @@ echo "Generating a template .ml that loads the file...: ${template_ml}"
35+
echo "check_axioms ();;") >> ${template_ml}
36+
37+
spec_found=0
38+
-for spec in $(./tools/collect-specs.sh ${s2n_bignum_arch} ${ml_path_noarch}) ; do
39+
+for spec in $("${ROOT}"/tools/collect-specs.sh "$(dirname "${ml_path}")" "${ml_path}"); do
40+
echo "Printf.printf \"val ${spec} : thm = %s\n\" (string_of_thm ${spec});;"
41+
spec_found=1
42+
done >> ${template_ml}
43+
@@ -51,7 +49,7 @@ fi
44+
inlined_prefix="$(mktemp)"
45+
inlined_ml="${inlined_prefix}.ml"
46+
inlined_cmx="${inlined_prefix}.cmx"
47+
-ocaml ${HOLLIGHT_DIR}/inline_load.ml ${template_ml} ${inlined_ml}
48+
+(cd "$ROOT" && ocaml "${HOLLIGHT_DIR}"/inline_load.ml "${template_ml}" "${inlined_ml}")
49+
50+
# Give a large stack size.
51+
OCAMLRUNPARAM=l=2000000000 \
52+
diff --git a/tools/collect-specs.sh b/tools/collect-specs.sh
53+
index a29aa6c8..784e0af3 100755
54+
--- a/tools/collect-specs.sh
55+
+++ b/tools/collect-specs.sh
56+
@@ -6,14 +6,12 @@ if [ "$#" -ne 1 ] && [ "$#" -ne 2 ]; then
57+
exit 1
58+
fi
59+
60+
-s2n_bignum_arch=$1
61+
if [ "$#" -eq 2 ]; then
62+
filepat="$2"
63+
else
64+
- filepat="*.ml"
65+
+ filepat="$1"
66+
fi
67+
-cd $s2n_bignum_arch > /dev/null
68+
69+
# An env. var for sorting
70+
export LC_ALL=C
71+
-grep 'let [A-Z_0-9]*_SUBROUTINE_CORRECT' ${filepat} | cut -f2 -d' ' | sort
72+
+grep -r 'let [A-Z_0-9]*_SUBROUTINE_CORRECT' ${filepat} | cut -f2 -d' ' | sort

nix/s2n_bignum/default.nix

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
{ stdenv, fetchFromGitHub, writeText, ... }:
3+
stdenv.mkDerivation rec {
4+
pname = "s2n_bignum";
5+
version = "90cb5e35a823efee15cde72f0237af39a9bf7371";
6+
src = fetchFromGitHub {
7+
owner = "jargh";
8+
repo = "s2n-bignum-dev";
9+
rev = "${version}";
10+
hash = "sha256-6uDvLG04h8IKYln612wG/aXPsCB9k8Zsh/cE2Y980tQ=";
11+
};
12+
setupHook = writeText "setup-hook.sh" ''
13+
export S2N_BIGNUM_DIR="$1"
14+
'';
15+
patches = [ ./0001-fix-script-path.patch ];
16+
dontBuild = true;
17+
installPhase = ''
18+
mkdir -p $out
19+
cp -a . $out/
20+
'';
21+
}

proofs/hol_light/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
**/*.o
3+
**/*.native
4+
**/*.correct

proofs/hol_light/arm/Makefile

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#############################################################################
2+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT-0
4+
#############################################################################
5+
6+
#
7+
# This Makefile is derived from the Makefile arm/Makefile in s2n-bignum.
8+
# - Remove all s2n-bignum proofs and tutorial, add mlkem-native proofs
9+
# - Minor path modifications to support base theories from s2n-bignum
10+
# to reside in a separate read-only directory
11+
#
12+
13+
.DEFAULT_GOAL := run_proofs
14+
15+
OSTYPE_RESULT=$(shell uname -s)
16+
ARCHTYPE_RESULT=$(shell uname -m)
17+
18+
SRC ?= $(S2N_BIGNUM_DIR)
19+
SRC_ARM ?= $(SRC)/arm
20+
21+
# Add explicit language input parameter to cpp, otherwise the use of #n for
22+
# numeric literals in ARM code is a problem when used inside #define macros
23+
# since normally that means stringization.
24+
#
25+
# Some clang-based preprocessors seem to behave differently, and get confused
26+
# by single-quote characters in comments, so we eliminate // comments first.
27+
28+
ifeq ($(OSTYPE_RESULT),Darwin)
29+
PREPROCESS=sed -e 's/\/\/.*//' | $(CC) -E -I$(SRC)/include $(SYMBOL_HIDING) -xassembler-with-cpp -
30+
else
31+
PREPROCESS=$(CC) -E -I$(SRC)/include $(SYMBOL_HIDING) -xassembler-with-cpp -
32+
endif
33+
34+
# Generally GNU-type assemblers are happy with multiple instructions on
35+
# a line, but we split them up anyway just in case.
36+
37+
SPLIT=tr ';' '\n'
38+
39+
# If actually on an ARM8 machine, just use the assembler (as). Otherwise
40+
# use a cross-assembling version so that the code can still be assembled
41+
# and the proofs checked against the object files (though you won't be able
42+
# to run code without additional emulation infrastructure). For the clang
43+
# version on OS X we just add the "-arch arm64" option. For the Linux/gcc
44+
# toolchain we assume the presence of the special cross-assembler. This
45+
# can be installed via something like:
46+
#
47+
# sudo apt-get install binutils-aarch64-linux-gnu
48+
49+
ifeq ($(ARCHTYPE_RESULT),aarch64)
50+
ASSEMBLE=as
51+
OBJDUMP=objdump -d
52+
else
53+
ifeq ($(ARCHTYPE_RESULT),arm64)
54+
ASSEMBLE=as
55+
OBJDUMP=objdump -d
56+
else
57+
ifeq ($(OSTYPE_RESULT),Darwin)
58+
ASSEMBLE=as -arch arm64
59+
OBJDUMP=otool -tvV
60+
else
61+
ASSEMBLE=aarch64-linux-gnu-as
62+
OBJDUMP=aarch64-linux-gnu-objdump -d
63+
endif
64+
endif
65+
endif
66+
67+
OBJ = mlkem/mlkem_ntt.o mlkem/mlkem_intt.o
68+
69+
# According to
70+
# https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms,
71+
# x18 should not be used for Apple platforms. Check this using grep.
72+
$(OBJ): %.o : %.S
73+
$(Q)[ -d $(@D) ] || mkdir -p $(@D)
74+
cat $< | $(PREPROCESS) | $(SPLIT) | grep -v -E '^\s+.quad\s+0x[0-9a-f]+$$' | $(ASSEMBLE) -o $@ -
75+
$(OBJDUMP) $@ | ( ( ! grep --ignore-case -E 'w18|[^0]x18' ) || ( rm $@ ; exit 1 ) )
76+
cat $< | $(PREPROCESS) | $(SPLIT) | $(ASSEMBLE) -o $@ -
77+
78+
clean:; rm -f */*.o */*/*.o */*.correct */*.native
79+
80+
# Proof-related parts
81+
#
82+
# The proof files are all independent, though each one loads the
83+
# same common infrastructure "base.ml". So you can potentially
84+
# run the proofs in parallel for more speed, e.g.
85+
#
86+
# nohup make -j 16 proofs &
87+
#
88+
# If you build hol-light yourself (see https://github.com/jrh13/hol-light)
89+
# in your home directory, and do "make" inside the subdirectory hol-light,
90+
# then the following HOLDIR setting should be right:
91+
92+
HOLDIR?=$(HOLLIGHTDIR)
93+
HOLLIGHT:=$(HOLDIR)/hol.sh
94+
95+
BASE?=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
96+
97+
PROOF_BINS = $(OBJ:.o=.native)
98+
PROOF_LOGS = $(OBJ:.o=.correct)
99+
100+
# Build precompiled native binaries of HOL Light proofs
101+
102+
proofs/simulator.native: $(SRC_ARM)/proofs/simulator.ml
103+
$(Q)[ -d $(@D) ] || mkdir -p $(@D)
104+
$(SRC)/tools/build-proof.sh $(SRC_ARM)/proofs/simulator.ml "$(HOLLIGHT)" "$@"
105+
106+
.SECONDEXPANSION:
107+
%.native: proofs/$$(*F).ml %.o ; $(SRC)/tools/build-proof.sh $(BASE)/$< "$(HOLLIGHT)" "$@"
108+
109+
# Run them and print the standard output+error at *.correct
110+
%.correct: %.native
111+
$< 2>&1 | tee $@
112+
@if (grep -i "error:\|exception:" "$@" >/dev/null); then \
113+
echo "$< had errors!"; \
114+
exit 1; \
115+
else \
116+
echo "$< OK"; \
117+
fi
118+
119+
build_proofs: $(PROOF_BINS);
120+
run_proofs: build_proofs $(PROOF_LOGS);
121+
122+
proofs: run_proofs ; $(SRC)/tools/count-proofs.sh .
123+
124+
.PHONY: proofs build_proofs run_proofs sematest clean
125+
126+
# Always run sematest regardless of dependency check
127+
FORCE: ;
128+
# Always use max. # of cores because in Makefile one cannot get the passed number of -j.
129+
# A portable way of getting the number of max. cores:
130+
# https://stackoverflow.com/a/23569003/1488216
131+
NUM_CORES_FOR_SEMATEST = $(shell getconf _NPROCESSORS_ONLN)
132+
sematest: FORCE $(OBJ) $(SRC_ARM)/proofs/simulator_iclasses.ml $(SRC_ARM)/proofs/simulator.native
133+
$(SRC)/tools/run-sematest.sh arm $(NUM_CORES_FOR_SEMATEST)

0 commit comments

Comments
 (0)