Skip to content

Commit a1f27d6

Browse files
Merge pull request #148 from cryspen/artifact
Update F*, SSProve, ProVerif in sync
2 parents 020fee9 + 8d71686 commit a1f27d6

File tree

83 files changed

+31110
-9771
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+31110
-9771
lines changed

.github/workflows/scheduled.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
runs-on: ubuntu-latest
1111

1212
steps:
13-
- uses: actions/checkout@v2
13+
- uses: actions/checkout@v4
1414
- uses: EmbarkStudios/cargo-deny-action@v1
1515
# TODO: Check licenses, too.
1616
with:

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ libcrux-ecdsa = { version = "0.0.3-alpha.1", features = ["rand"] }
2828
hax-lib = { version = "0.3" }
2929

3030
[features]
31-
default = ["api", "std"]
31+
default = ["api", "std", "defensive"]
32+
defensive = []
3233
std = []
3334
test_utils = []
3435
secret_integers = []
@@ -78,4 +79,4 @@ default-members = [
7879
]
7980

8081
[lints.rust]
81-
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bench)'] }
82+
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bench)', 'cfg(hax)'] }

PROOFS.md

Lines changed: 124 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,115 @@
11
# Security and Functional Correctness Proofs for Bertie
22

3-
The `proofs/` directory holds ProVerif F\* proofs for code compiled by
3+
The `proofs/` directory holds ProVerif, F\*, and SSProve proofs for code compiled by
44
hax from the Bertie Rust implementation of TLS 1.3.
55

6-
## Correctness Proofs in F\*
6+
## Prerequisites
7+
### Setting up the hax toolchain
8+
We use the hax toolchain to extract proof artifacts from the Rust
9+
source code in `src`. To be able to reproduce this extraction for
10+
yourself, please follow the installation instructions for `hax`
11+
provided at:
12+
13+
https://github.com/cryspen/hax?tab=readme-ov-file#installation
14+
15+
#### `hax-driver.py`
16+
We use the Python 3 script `hax-driver.py` to drive extraction and
17+
verification of the proof artifacts, so an installation of Python 3 is
18+
required.
19+
20+
### Setting up F*
21+
We use F* version 2025.03.25 to prove runtime safety and transcript
22+
unambiguity as described in section 7 of the paper submission. To
23+
reproduce these results for yourself, please install F* following the
24+
instructions at:
25+
26+
https://fstar-lang.org/index.html#download
27+
28+
### Setting up Rocq + SSProve
29+
We use Rocq version 8.18.0 and SSProve for the security proofs of the
30+
key schedule as described in section 5 of the paper submission. Installation
31+
instructions can be found at:
32+
33+
https://rocq-prover.org/install
34+
https://github.com/SSProve/ssprove
35+
36+
Furthermore, Rust core library, which is used by the Hax translation, for Rocq/SSProve is located at:
37+
38+
https://github.com/cryspen/hax/tree/main/proof-libs
39+
40+
### Setting up ProVerif
41+
We use ProVerif version 2.05 to perform protocol security analysis of the
42+
implementation, as described in section 6 of the paper submission.
43+
To be able to reproduce the analysis, please follow the installation
44+
instructions provided at:
45+
46+
https://bblanche.gitlabpages.inria.fr/proverif/
47+
48+
## Proof extraction using hax
49+
### F*
50+
To regenerate the extraction to F*, run
51+
```
52+
./hax-driver.py extract-fstar
53+
```
54+
55+
### SSProve
56+
To regenerate the extraction for the key schedule in SSProve, run
57+
```
58+
./hax-driver.py extract-ssprove
59+
```
60+
61+
To get the code running we apply a patch
62+
```
63+
patch -d extraction/ < extraction.patch
64+
rm -rf fextraction/
65+
mv extraction/ fextraction/
66+
```
67+
68+
### ProVerif
69+
The ProVerif proofs described in section 6 of the paper submission can
70+
be extracted from the Rust source code by running
71+
```
72+
./hax-driver.py extract-proverif
73+
```
74+
75+
This will generate the file `proofs/proverif/extraction/lib.pvl` from
76+
the Rust source code.
77+
78+
## Running the Proofs
79+
### F*
80+
To run the F* proofs, run the following command:
81+
```
82+
./hax-driver.py typecheck
83+
```
84+
85+
### SSProve
86+
First generate the Makefile from the _CoqProject
87+
```
88+
coq_makefile -f _CoqProject -o Makefile
89+
```
90+
and run `make`.
91+
92+
### ProVerif
93+
To run the ProVerif analysis, as described in section 6 of the paper
94+
submission, run
95+
```
96+
./hax-driver.py typecheck-proverif
97+
```
98+
99+
This will run the command
100+
```
101+
proverif -lib proofs/proverif/handwritten_lib.pvl -lib
102+
proofs/proverif/extraction/lib.pvl proofs/proverif/extraction/analysis.pv
103+
```
104+
to start the analysis. The file `handwritten_lib.pvl` exists for
105+
technical reasons and contains early
106+
definitions of certain terms. The file `analysis.pvl` contains the
107+
top-level protocol process and analysis queries, as described in
108+
section 6 of the paper submission.
109+
110+
## Results
111+
112+
### Correctness Proofs in F\*
7113

8114
Each Rust module `m` in the implementation is translated to two files:
9115
`M.fst` contains the functionalized F\* code compiled from Rust, and
@@ -54,31 +160,30 @@ the verified code will never create a panic. Of course, there is other
54160
code outside the verification boundary that may yet panic and we intend
55161
to expand the scope of our verification ocer time.
56162

57-
## Security Proofs in ProVerif
163+
### Security Proofs in ProVerif
58164

59165
We also prove security for the TLS 1.3 protocol code in Bertie.
60166
Using hax, we generate a ProVerif model of the TLS 1.3 handshake
61167
in `proofs/proverif/extraction/lib.pvl`. This file contains the bulk of the extraction,
62168
while `analysis.pv` contains the top-level processes are set up and analysis queries.
63169

64-
We provide patches that include changes to the extracted model (described below), which allow us to show
170+
We can show
65171
* that the modeled handshake can fully complete for both parties (as a baseline for further analysis)
66-
* Server authenticity, assuming neither the certificate signing key nor a potential pre-shared key have been compromised
67-
* Secrecy of the resulting session key under the same assumptions.
172+
* Server authenticity, assuming the certificate signing key is not
173+
compromised
174+
* Forward secrecy of the resulting session key under the same assumptions.
68175

69-
The intended flow using the driver is to run
176+
### Security of the Key Schedule in SSProve
70177

71-
./hax-driver.py extract-proverif to extract the ProVerif model to proofs/proverif/extraction.
72-
./hax-driver.py patch-proverif to apply the patches on the extracted model.
73-
./hax-driver.py typecheck-proverif to run the analysis on the patched model.
178+
We extract the implementation of the Key Schedule from SSProve.
179+
We then fix the implementation to only include the parts we need and make sure the translation is actually valid.
180+
This is done with a patch file to allow easier updates to the implementation.
74181

75-
The patches are necessary for parts of the model that we cannot currently generate (properly, or at all):
76-
* a top-level process structure which instantiates Client and Server with ciphersuites and psk-mode configuration,
77-
* event definitions and analysis queries,
78-
* cryptographic operations and their semantics
79-
* tls13formats related code, especially anything that relates to concatenation primitives
182+
The proof for security of the key schedule is done on a specification.
183+
The entry functions of the specification are generalized and then instantiated with the functions in the implementation.
184+
We show the implemented functions fulfill some properties to be valid in the key schedule proof.
80185

81-
Additionally, the patches introduce the following modifications:
82-
* A mock certificate validation, checking that the name in the certificate agrees with the name of the expected peer from the top-level process. This is because Bertie does not include full certificate validation at this time, but some binding between the name and the certificate is necessary for showing server authentication.
83-
* A model-side fix for the issue that is fixed on the Rust side in Correct argument order for process_psk_binder_zero_rtt #101 (until that is fixed on the Rust side).
84-
* The removal of all automatically generated events, since that leads to poor performance from ProVerif and is not necessary at all for the analysis (cf. [ProVerif] Emitting events unnecessarily blows up the extracted model hacspec/hax#581)
186+
The proof for the specification follows the proof in "Key-schedule Security for the TLS 1.3 Standard."
187+
We prove the Core Key Schedule Theorem by showing the main lemma D6.
188+
The other lemmas are a direct consequence of the correct implementation of the cryptographic primitives, which we inherit from libcrux.
189+
These lemmas are therefore only stated, and the proof is Admitted.

bench.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Raspberry Pi 3
2+
```
3+
_,met$$$$$gg. jonas@raspberrypi
4+
,g$$$$$$$$$$$$$$$P. -----------------
5+
,g$$P" """Y$$.". OS: Debian GNU/Linux 11 (bullseye) aarch64
6+
,$$P' `$$$. Host: Raspberry Pi 3 Model B Rev 1.2
7+
',$$P ,ggs. `$$b: Kernel: 6.1.21-v8+
8+
`d$$' ,$P"' . $$$ Uptime: 2 hours, 41 mins
9+
$$P d$' , $$P Packages: 660 (dpkg)
10+
$$: $$. - ,d$$' Shell: bash 5.1.4
11+
$$; Y$b._ _,d$P' Terminal: /dev/pts/0
12+
Y$$. `.`"Y$$$$P"' CPU: BCM2835 (4) @ 1.200GHz
13+
`$$b "-.__ Memory: 82MiB / 909MiB
14+
`Y$$
15+
`Y$$.
16+
`$$b.
17+
`Y$$b.
18+
`"Y$b._
19+
`"""
20+
```
21+
22+
## Client
23+
Client
24+
- TLS_Chacha20Poly1305_SHA256 w/ EcdsaSecp256r1Sha256 | Secp256r1:
25+
Handshake: 7122 μs | 140.4094748062547 /s
26+
Application: 244 μs | 63.7759811051013 MB/s
27+
Message Sizes: 200, 838, 58 bytes
28+
- TLS_Chacha20Poly1305_SHA256 w/ EcdsaSecp256r1Sha256 | X25519:
29+
Handshake: 4566 μs | 218.98451159470278 /s
30+
Application: 245 μs | 63.71299735155773 MB/s
31+
Message Sizes: 167, 805, 58 bytes
32+
- TLS_Chacha20Poly1305_SHA256 w/ RsaPssRsaSha256 | Secp256r1:
33+
Handshake: 21473 μs | 46.56802728954948 /s
34+
Application: 254 μs | 61.3644019146746 MB/s
35+
Message Sizes: 200, 2230, 58 bytes
36+
- TLS_Chacha20Poly1305_SHA256 w/ RsaPssRsaSha256 | X25519:
37+
Handshake: 18850 μs | 53.05039006817865 /s
38+
Application: 254 μs | 61.42504065826636 MB/s
39+
Message Sizes: 167, 2197, 58 bytes
40+
- TLS_Chacha20Poly1305_SHA256 w/ EcdsaSecp256r1Sha256 | X25519Kyber768Draft00:
41+
Handshake: 5582 μs | 179.1359595215902 /s
42+
Application: 256 μs | 61.01392340465518 MB/s
43+
Message Sizes: 1351, 1893, 58 bytes
44+
- TLS_Chacha20Poly1305_SHA256 w/ EcdsaSecp256r1Sha256 | X25519MlKem768:
45+
Handshake: 5487 μs | 182.24313662925482 /s
46+
Application: 253 μs | 61.53509645857742 MB/s
47+
Message Sizes: 1351, 1892, 58 bytes
48+
49+
## Client Stack
50+
Client Stack Usage:
51+
===================
52+
53+
Ciphersuite: TLS_Chacha20Poly1305_SHA256 w/ EcdsaSecp256r1Sha256 | Secp256r1
54+
[Client Connect] Highest stack usage: 56.1 KB
55+
[Client Read ] Highest stack usage: 5.9 KB
56+
57+
Ciphersuite: TLS_Chacha20Poly1305_SHA256 w/ EcdsaSecp256r1Sha256 | X25519
58+
[Client Connect] Highest stack usage: 55.4 KB
59+
[Client Read ] Highest stack usage: 5.9 KB
60+
61+
Ciphersuite: TLS_Chacha20Poly1305_SHA256 w/ RsaPssRsaSha256 | Secp256r1
62+
[Client Connect] Highest stack usage: 56.1 KB
63+
[Client Read ] Highest stack usage: 5.9 KB
64+
65+
Ciphersuite: TLS_Chacha20Poly1305_SHA256 w/ RsaPssRsaSha256 | X25519
66+
[Client Connect] Highest stack usage: 55.4 KB
67+
[Client Read ] Highest stack usage: 5.9 KB
68+
69+
Ciphersuite: TLS_Chacha20Poly1305_SHA256 w/ EcdsaSecp256r1Sha256 | X25519Kyber768Draft00
70+
[Client Connect] Highest stack usage: 78.2 KB
71+
[Client Read ] Highest stack usage: 5.9 KB
72+
73+
Ciphersuite: TLS_Chacha20Poly1305_SHA256 w/ EcdsaSecp256r1Sha256 | X25519MlKem768
74+
[Client Connect] Highest stack usage: 78.2 KB
75+
[Client Read ] Highest stack usage: 5.9 KB
76+
77+
## Server
78+
Server
79+
- TLS_Chacha20Poly1305_SHA256 w/ EcdsaSecp256r1Sha256 | Secp256r1:
80+
Handshake: 5481 μs | 182.43040791837637 /s
81+
Application: 259 μs | 60.18269424576024 MB/s
82+
- TLS_Chacha20Poly1305_SHA256 w/ EcdsaSecp256r1Sha256 | X25519:
83+
Handshake: 2819 μs | 354.63931635299485 /s
84+
Application: 256 μs | 60.92005874557329 MB/s
85+
- TLS_Chacha20Poly1305_SHA256 w/ RsaPssRsaSha256 | Secp256r1:
86+
Handshake: 699193 μs | 1.430219917317012 /s
87+
Application: 262 μs | 59.581248116755916 MB/s
88+
- TLS_Chacha20Poly1305_SHA256 w/ RsaPssRsaSha256 | X25519:
89+
Handshake: 694248 μs | 1.4404070652725713 /s
90+
Application: 260 μs | 60.00107706253405 MB/s

benches/client.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![allow(dead_code)]
22

3+
use std::collections::HashMap;
34
use std::time::{Duration, Instant};
45

56
use bertie::{
@@ -23,7 +24,7 @@ use bertie::{
2324
SignatureScheme,
2425
},
2526
tls13utils::Bytes,
26-
Client, Server,
27+
Client, Server, TLSkeyscheduler,
2728
};
2829
use rand::RngCore;
2930

@@ -160,6 +161,9 @@ fn protocol() {
160161

161162
for ciphersuite in CIPHERSUITES {
162163
let mut rng = rand::rng();
164+
let mut ks: TLSkeyscheduler = TLSkeyscheduler {
165+
keys: HashMap::new(),
166+
};
163167

164168
// Server
165169
let server_name_str = "localhost";
@@ -187,24 +191,26 @@ fn protocol() {
187191
for _ in 0..ITERATIONS {
188192
let start_time = Instant::now();
189193
let (client_hello, client) =
190-
Client::connect(ciphersuite, &server_name, None, None, &mut rng).unwrap();
194+
Client::connect(ciphersuite, &server_name, None, None, &mut rng, &mut ks).unwrap();
191195
let end_time = Instant::now();
192196
handshake_time += end_time.duration_since(start_time);
193197
size1 += client_hello.declassify().len();
194198

195199
let (server_hello, server_finished, server) =
196-
Server::accept(ciphersuite, db.clone(), &client_hello, &mut rng).unwrap();
200+
Server::accept(ciphersuite, db.clone(), &client_hello, &mut rng, &mut ks).unwrap();
197201
size2 += server_hello.declassify().len();
198202
size2 += server_finished.declassify().len();
199203

200204
let start_time = Instant::now();
201-
let (_client_msg, client) = client.read_handshake(&server_hello).unwrap();
202-
let (client_msg, client) = client.read_handshake(&server_finished).unwrap();
205+
let (_client_msg, client) = client.read_handshake(&server_hello, &mut ks).unwrap();
206+
let (client_msg, client) = client.read_handshake(&server_finished, &mut ks).unwrap();
203207
let end_time = Instant::now();
204208
handshake_time += end_time.duration_since(start_time);
205209
size3 += client_msg.as_ref().unwrap().declassify().len();
206210

207-
let server = server.read_handshake(&client_msg.unwrap()).unwrap();
211+
let server = server
212+
.read_handshake(&client_msg.unwrap(), &mut ks)
213+
.unwrap();
208214

209215
let application_data = payload.clone().into();
210216

0 commit comments

Comments
 (0)