Skip to content

Commit 2005f85

Browse files
authored
Merge pull request #1593 from cryspen/lean-dev-examples
Lean backend [M2] - 2/3 - Examples
2 parents 94ecf80 + e1686c6 commit 2005f85

25 files changed

Lines changed: 1454 additions & 125 deletions

File tree

.github/workflows/install_and_test.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ jobs:
2828
nix profile install nixpkgs#yq
2929
nix profile install .#rustc
3030
nix profile install .
31-
31+
3232
- name: Ensure readme coherency
3333
run: |
3434
nix build .#check-readme-coherency -L
3535
3636
- name: Test the toolchain
3737
run: |
3838
nix build .#check-toolchain -L
39-
39+
4040
- name: Test the examples
4141
run: |
4242
cd examples
@@ -61,4 +61,3 @@ jobs:
6161
nix build .# .#fstar --json \
6262
| jq -r '.[].outputs | to_entries[].value' \
6363
| cachix push hax
64-

examples/Cargo.lock

Lines changed: 19 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
[workspace]
22
members = [
33
"chacha20",
4+
"lean_chacha20",
5+
"lean_barrett",
46
"limited-order-book",
57
"sha256",
68
"barrett",
@@ -13,4 +15,3 @@ resolver = "2"
1315
[workspace.dependencies]
1416
hax-lib = { path = "../hax-lib" }
1517
hax-bounded-integers = { path = "../hax-bounded-integers" }
16-

examples/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ default:
66
make -C barrett
77
make -C kyber_compress
88
make -C proverif-psk
9+
make -C lean_chacha20
10+
make -C lean_barrett
911

1012
clean:
1113
make -C limited-order-book clean
@@ -14,3 +16,5 @@ clean:
1416
make -C barrett clean
1517
make -C kyber_compress clean
1618
make -C proverif-psk clean
19+
make -C lean_chacha20 clean
20+
make -C lean_barrett clean

examples/README.md

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212

1313
<details>
1414
<summary><b>Requirements</b></summary>
15-
15+
1616
First, make sure to have hax installed in PATH. Then:
17-
17+
1818
* With Nix, `nix develop .#examples` setups a shell automatically for you.
19-
19+
2020
* Without Nix:
2121
1. install F* `v2025.03.25`<!---FSTAR_VERSION--> manually (see https://github.com/FStarLang/FStar/blob/master/INSTALL.md);
2222
1. make sure to have `fstar.exe` in PATH;
@@ -39,3 +39,51 @@ Note the generated modules live in the
3939

4040
For those examples, we generated Coq modules without typechecking them.
4141
The `<EXAMPLE>/proofs/coq/extraction` folders contain the generated Coq modules.
42+
43+
## Lean
44+
45+
Two examples are fine-tuned to showcase the Lean backend: `lean_barrett` and
46+
`lean_chacha20`. For both, the lean extraction can be obtained by running `cargo
47+
hax into lean`.
48+
49+
### Barrett
50+
51+
The *Barrett reduction* allows to compute remainders without using divisions. It
52+
showcases arithmetic operations, conversions between integer types (namely `i32`
53+
and `i64`). The Lean backend provides *panicking* arithmetic operations `+?`,
54+
`-?`, etc, that panic on overflows.
55+
56+
For the Lean extracted code, we prove panic freedom with regards to those
57+
arithmetic operations, and then we prove that the result is indeed the modulus
58+
(as long as the absolute value of the input is lower than the bound
59+
`BARRETT_R`). The proof is made via bit-blasting (using Lean's `bv_decide`). To
60+
limit the computation time, the bound `BARRETT_R` was lowered compared to the
61+
normal example in the `barrett` folder.
62+
63+
The proofs are backported in the rust code (in `lean_barrett/src/lib.rs`): doing
64+
`cargo hax into lean` extracts a valid lean file that contains the proof.
65+
66+
The proof can be run by doing (requires `lake`):
67+
68+
```sh
69+
cd lean_barrett/
70+
make
71+
```
72+
73+
### Chacha20
74+
75+
The Chacha20 example extracts to Lean, but requires a manual edit to be
76+
wellformed. It showcases array, vector and slices accesses, as well as loops
77+
(with loop invariants). For the Lean extracted code, we prove panic freedom,
78+
which involves arithmetic on size of arrays.
79+
80+
This edit and the proofs of panic freedom can be found in
81+
`lean_chacha20/proofs/lean/extraction/lean_chacha20_manual_edit.lean`.
82+
83+
The extraction (in `lean_chacha20.lean`) and rerun of the proofs (in
84+
`lean_chacha20_manual_edit.lean`) can be done by doing (requires `lake`):
85+
86+
```sh
87+
cd lean_chacha20/
88+
make
89+
```

examples/lean_barrett/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "lean_barrett"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
hax-lib.workspace = true

examples/lean_barrett/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.PHONY: default clean
2+
default:
3+
cargo hax into lean
4+
(cd proofs/lean/extraction && \
5+
elan default stable && \
6+
lake update && \
7+
lake build)
8+
9+
clean:
10+
-rm -f proofs/lean/extraction/lean_barrett.lean
11+
-cd proofs/lean/extraction && lake clean
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name = "lean_barrett"
2+
version = "0.1.0"
3+
defaultTargets = ["lean_barrett"]
4+
5+
[[lean_lib]]
6+
name = "lean_barrett"
7+
8+
[[require]]
9+
name = "Hax"
10+
path = "../../../../../hax-lib/proof-libs/lean"

examples/lean_barrett/src/lib.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use hax_lib as hax;
2+
use hax_lib::lean;
3+
4+
/// Values having this type hold a representative 'x' of the Kyber field.
5+
/// We use 'fe' as a shorthand for this type.
6+
pub(crate) type FieldElement = i32;
7+
8+
#[hax_lib::lean::before("@[simp, spec]")]
9+
const BARRETT_R: i64 = 0x400000; // is 0x4000000 in the normal barrett example
10+
11+
#[hax_lib::lean::before("@[simp, spec]")]
12+
const BARRETT_SHIFT: i64 = 26;
13+
14+
#[hax_lib::lean::before("@[simp, spec]")]
15+
const BARRETT_MULTIPLIER: i64 = 20159;
16+
17+
#[hax_lib::lean::before("@[simp, spec]")]
18+
pub(crate) const FIELD_MODULUS: i32 = 3329;
19+
20+
/// Signed Barrett Reduction
21+
///
22+
/// Given an input `value`, `barrett_reduce` outputs a representative `result`
23+
/// such that:
24+
///
25+
/// - result ≡ value (mod FIELD_MODULUS)
26+
/// - the absolute value of `result` is bound as follows:
27+
///
28+
/// `|result| ≤ FIELD_MODULUS / 2 · (|value|/BARRETT_R + 1)
29+
///
30+
/// In particular, if `|value| < BARRETT_R`, then `|result| < FIELD_MODULUS`.
31+
#[hax::requires((i64::from(value) >= -BARRETT_R && i64::from(value) <= BARRETT_R))]
32+
#[hax::ensures(|result| {
33+
let valid_result = value % FIELD_MODULUS;
34+
result > -FIELD_MODULUS
35+
&& result < FIELD_MODULUS
36+
&& (result == valid_result ||
37+
result == valid_result + FIELD_MODULUS ||
38+
result == valid_result - FIELD_MODULUS)
39+
})]
40+
#[hax_lib::lean::before("@[simp, spec]")]
41+
#[hax_lib::lean::after("
42+
theorem barrett_spec (value: i32) :
43+
⦃ __requires (value) = pure true ⦄
44+
(barrett_reduce value)
45+
⦃ ⇓ result => __ensures value result = pure true ⦄
46+
:= by
47+
mvcgen [__requires, __ensures]
48+
hax_bv_decide
49+
simp [__requires, __ensures] at *
50+
rw [Int32.HaxRem_spec_bv_rw] ; simp ;
51+
rw [Int32.HaxAdd_spec_bv_rw] ; simp ;
52+
rw [Int32.HaxSub_spec_bv_rw] ; simp
53+
hax_bv_decide
54+
expose_names
55+
have ⟨ h1, h2 ⟩ := h; clear h
56+
simp [Int32.eq_iff_toBitVec_eq,
57+
Int32.lt_iff_toBitVec_slt,
58+
Int32.le_iff_toBitVec_sle,
59+
Int64.eq_iff_toBitVec_eq,
60+
Int64.lt_iff_toBitVec_slt,
61+
Int64.le_iff_toBitVec_sle,
62+
] at *
63+
generalize Int32.toBitVec value = bv_value at * ; clear value
64+
bv_decide (config := {timeout := 120})
65+
")]
66+
pub fn barrett_reduce(value: FieldElement) -> FieldElement {
67+
let t = i64::from(value) * BARRETT_MULTIPLIER;
68+
let t = t + (BARRETT_R >> 1);
69+
let quotient = t >> BARRETT_SHIFT;
70+
let quotient = quotient as i32;
71+
let sub = quotient * FIELD_MODULUS;
72+
value - sub
73+
}

examples/lean_chacha20/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "lean_chacha20"
3+
version = "0.1.0"
4+
authors = ["Clement Blaudeau <clement@cryspen.com>"]
5+
edition = "2021"
6+
7+
[dependencies]
8+
hax-lib.workspace = true
9+
hax-bounded-integers.workspace = true

0 commit comments

Comments
 (0)