There has been much recent progress in formal methods and tools for cryptography, enabling, in principle, the automated verification of complex security protocols. In practice, however, these methods and tools remain difficult to apply. Often, verification occurs independently of the development process, rather than during design, prototyping, and testing. Also, as the protocol or its implementations evolve, it is difficult to carry over security guarantees from past formal verification. More generally, the verification of a system that uses a given protocol involves more than the cryptographic verification of an abstract model; it may rely as well on more standard analyzes of code (e.g. to ensure memory safety) and system configuration (e.g. to protect a TCB). For these reasons, we are interested in the integration of modern cryptographic verifiers into the arsenal of software testing and verification tools.
Implementations vs Abstract Models Protocol specifications include many details; most (but not all) of them are of no importance for security. In the process of distilling a formal cryptographic model, most of these details are discarded. When is a protocol model oversimplified? In contrast with formal guarantees proved within the model, the relevance of the model relies on the experience of the formalist. The problem is compounded when considering protocol implementations. Thus, inasmuch as possible, we propose to verify detailed protocol implementations and deployments, rather than simplified abstract models. Using automated tools based on sound proof techniques, the details can either be safely erased, or dealt with by brute-force analysis.
From Implementations to Cryptographic Models
In recent work, Bhargavan et al. advocate the
automatic extraction and verification of cryptographic models from
executable code.
They verify protocol implementations written in F#, a
dialect of ML, by compilation to
ProVerif.
Their approach relies on sharing as much code as possible between
implementations and models: their code differs mostly in the
implementation of core cryptographic libraries, which use bitstrings
for concrete execution and symbolic terms for verification.
In this work, we rely on these tools, and also experiment with
direct computational cryptographic verifications of protocol
implementations.
For automated verification, we rely on
CryptoVerif, Blanchet's recent tool for concrete cryptography.
For automated model extraction, we develop a new compiler from
protocols coded in F# to CryptoVerif scripts.
We thus obtain computational verification results for executable code.
Methodology The picture below outlines our approach to protocol verification. It involves developing, testing, and verifying a small reference implementation of the protocol plus typical applications.
Verification consists of selecting a part of the implementation,
writing additional ``verification harness'' code that specifies the
attacker model, the cryptographic assumptions, and the target security
properties, and then compiling their combination to some automated prover.
Inasmuch as the verification toolchain is automated, one can easily
re-verify the code base as it evolves.
In our experience, symbolic and computational verifications
are complementary. Computational verification is more precise but more
difficult to achieve; we obtain results only for the cryptographic
core of the protocol implementation. Symbolic verification typically
applies to the whole protocol, sometimes even including the
application, but does not detect low-level cryptographic errors.
Overall, we believe that the overhead of verification is getting
affordable, in comparison with design, development, and testing. The
next generation of tools could enable their integration in the
development process.
Contributions
Cryptographically Verified Implementations for TLS presented at ACM CCS 2008. (corresponding long version, slides and files).
Crypto-Verifying Protocol Implementations in ML position paper presented at FCC'07. (corresponding files).