Formal verification tool for Rust: check 100% of execution cases of your programs to ensure they are safe and correct.
Even if type system of Rust prevents many mistakes, including memory errors, the code is still not immune to vulnerabilities, such as security vulnerabilities, unexpected panics, or wrongly implemented business rules.
The way to go further is to mathematically prove that it implements its specification for all inputs: this is named "formal verification" and what rocq-of-rust proposes. This is the strongest way to look for bugs or vulnerabilities, even for code that needs to be safe against state-level actors, or in applications where human life is at stake.
| We propose formal verification as a service, including designing the specification and the proofs. ➡️ Book a meeting ⬅️ |
|---|
The development of rocq-of-rust was mainly funded by the Ethereum Foundation and the Aleph Zero Foundation. We thank them for their support!
- Example
- Rationale
- Prerequisites
- Installation and User Guide
- Features
- Contact
- Alternative Projects
- Contributing
At the heart of rocq-of-rust is the translation of Rust programs to idiomatic code in the theorem prover Rocq. Once in Rocq, the code can be verified using standard proof techniques.
The translation is in three steps:
- Import of the THIR representation of Rust to Rocq, running
cargo rocq-of-rust. - Type-checking and trait inference with native Rocq types and typeclasses (we call it linking).
- Representation of the control-flow and memory manipulations in a purely functional style (we call it simulation).
Here is an example of a Rust function:
fn add_one(x: u32) -> u32 {
x + 1
}We can prove it equivalent to the purely functional Rocq code:
Definition add_one (x : u32) : u32 :=
x +i 1.For reference, here is the representation of the THIR in Rocq for this function:
Definition add_one (ε : list Value.t) (τ : list Ty.t) (α : list Value.t) : M :=
match ε, τ, α with
| [], [], [ x ] =>
ltac:(M.monadic
(let x := M.alloc (| Ty.path "u32", x |) in
M.call_closure (|
Ty.path "u32",
BinOp.Wrap.add,
[ M.read (| x |); Value.Integer IntegerKind.U32 1 ]
|)))
| _, _, _ => M.impossible "wrong number of arguments"
end.We are currently verifying Revm, a Rust implementation of the Ethereum virtual machine (EVM), to show it equivalent to a specification written in a purely functional style. This is an ongoing work in the folder RocqOfRust/revm and funded by a grant from the Ethereum Foundation.
Here is the typical workflow of usage for rocq-of-rust:
graph TB
R[Rust code 🦀] -- rocq-of-rust --> T[Translated code 🐓]
T -- types/traits resolutions --> L[Linked code 🐓]
L -- control-flow/memory --> S[Simulations 🐓]
S --> P
SP[Specifications 🐓] --> P[Proofs 🐓]
P -.-> X[100% reliable code! 🦄]
We start by generating an automatic translation of the Rust we verify to Rocq code with rocq-of-rust. The translation is originally verbose. We go through two semi-automated refinement steps, links and simulations, that gradually make the code more amenable to formal verification.
Finally, we write the specifications and prove that our Rust program fulfills them for any possible user input.
Examples of typical specifications are:
- The code cannot panic.
- This clever data structure is equivalent to its naive version, except for the execution time.
- This new release, which introduces new endpoints and does a lot of refactoring, is fully backward-compatible with the previous version.
- Data invariants are properly preserved.
- The storage system is sound, as what goes in goes out (this generally amounts to state that the serialization/deserialization functions are inverse).
- The implementation behaves as a special case of what the whitepaper describes once formally expressed.
With that in hand, you can virtually reduce your bugs and vulnerabilities to zero.
- Rust
- Rocq (see rocq-of-rust.opam)
The build tutorial provides detailed instructions on building and installing rocq-of-rust, while the user tutorial provides an introduction to the rocq-of-rust command line interface and the list of supported options.
For formal verification services (training or application) on your Rust code base, contact us at contact@formal.land. Formal verification can apply to smart contracts, database engines, or any critical Rust project.
Here are other projects working on formal verification for Rust:
- Aeneas: Translation from MIR to purely functional Rocq/F* code. Automatically put the code in a functional form. See their paper Aeneas: Rust verification by functional translation.
- Hacspec v2: Translation from THIR to Rocq/F* code
- Creusot: Translation from MIR to Why3 (and then SMT solvers)
- Verus: Automatic verification for Rust with annotations
- Kani: Model-checking with CBMC
This is all open-source software.
Open some pull requests or issues to contribute to this project. All contributions are welcome! This project is open-source under license AGPL for the Rust code (the translator) and MIT for the Rocq libraries. There is a bit of code taken from the Creusot project to make the Cargo command rocq-of-rust and run the translation in the same context as Cargo.
