Manual Proverif
Manual Proverif
05:
Automatic Cryptographic Protocol Verifier,
User Manual and Tutorial
This manual was written with support from the Direction Générale pour l’Armement (DGA) and the
EPSRC project UbiVal (EP/D076625/2). ProVerif was developed while Bruno Blanchet was affiliated
with INRIA Paris-Rocquencourt, with CNRS, Ecole Normale Supérieure, Paris, and with Max-Planck-
Institut für Informatik, Saarbrücken. This manual was written while Bruno Blanchet was affiliated with
INRIA Paris-Rocquencourt and with CNRS, Ecole Normale Supérieure, Paris, Ben Smyth was affiliated
with Ecole Normale Supérieure, Paris and with University of Birmingham, Vincent Cheval was affiliated
with CNRS and Inria Nancy, and Marc Sylvestre was affiliated with INRIA Paris. The development of
ProVerif would not have been possible without the helpful remarks from the research community; their
contributions are greatly appreciated and further feedback is encouraged.
iii
iv
Contents
1 Introduction 1
1.1 Applications of ProVerif . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Scope of this manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4.1 Installation via OPAM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.4.2 Installation from sources (Linux/Mac/cygwin) . . . . . . . . . . . . . . . . . . . . 3
1.4.3 Installation from binaries (Windows) . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4.4 Emacs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.4.5 Atom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5 Copyright . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2 Getting started 7
3 Using ProVerif 11
3.1 Modeling protocols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.1.1 Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.1.2 Example: Declaring cryptographic primitives for the handshake protocol . . . . . . 13
3.1.3 Process macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.1.4 Processes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.1.5 Example: handshake protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.2 Security properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.2.1 Reachability and secrecy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.2.2 Correspondence assertions, events, and authentication . . . . . . . . . . . . . . . . 19
3.2.3 Example: Secrecy and authentication in the handshake protocol . . . . . . . . . . 20
3.3 Understanding ProVerif output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.3.1 Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.3.2 Example: ProVerif output for the handshake protocol . . . . . . . . . . . . . . . . 23
3.4 Interactive mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.4.1 Interface description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.4.2 Manual and auto-reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.4.3 Execution of 0, P | Q, !P , new, let, if, and event . . . . . . . . . . . . . . . . . . . 32
3.4.4 Execution of inputs and outputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.4.5 Button “Add a term to public” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.4.6 Execution of insert and get . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.4.7 Handshake run in interactive mode . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.4.8 Advanced features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4 Language features 37
4.1 Primitives and modeling features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.1.1 Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.1.2 Data constructors and type conversion . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.1.3 Natural numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.1.4 Enriched terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
v
vi CONTENTS
6 Advanced reference 87
6.1 Proving correspondence queries by induction . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.1.1 Single query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.1.2 Group of queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
6.2 Axioms, restrictions, and lemmas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.3 Predicates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
6.4 Referring to bound names in queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
6.5 Exploring correspondence assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
6.6 ProVerif options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
6.6.1 Command-line arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
6.6.2 Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
6.7 Theory and tricks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
6.7.1 The resolution strategy of ProVerif . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
6.7.2 Performance and termination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
6.7.3 Alternative encodings of protocols . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
6.7.4 Applied pi calculus encodings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
6.7.5 Sources of incompleteness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
6.7.6 Misleading syntactic constructs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
6.8 Compatibility with CryptoVerif . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
6.9 Additional programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
6.9.1 test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
6.9.2 analyze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
6.9.3 addexpectedtags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
7 Outlook 131
B Semantics 141
List of Figures
vii
viii LIST OF FIGURES
Chapter 1
Introduction
This manual describes the ProVerif software package version 2.05. ProVerif is a tool for automatically
analyzing the security of cryptographic protocols. Support is provided for, but not limited to, crypto-
graphic primitives including: symmetric and asymmetric encryption; digital signatures; hash functions;
bit-commitment; and non-interactive zero-knowledge proofs. ProVerif is capable of proving reachability
properties, correspondence assertions, and observational equivalence. These capabilities are particularly
useful to the computer security domain since they permit the analysis of secrecy and authentication
properties. Moreover, emerging properties such as privacy, traceability, and verifiability can also be
considered. Protocol analysis is considered with respect to an unbounded number of sessions and an
unbounded message space. Moreover, the tool is capable of attack reconstruction: when a property
cannot be proved, ProVerif tries to reconstruct an execution trace that falsifies the desired property.
Abadi & Blanchet [AB05b] use correspondence assertions to verify the certified email proto-
col [AGHP02].
Abadi, Blanchet & Fournet [ABF07] analyze the JFK (Just Fast Keying) [ABB+ 04] protocol, which
was one of the candidates to replace IKE as the key exchange protocol in IPSec, by combining
manual proofs with ProVerif proofs of correspondences and equivalences.
Blanchet & Chaudhuri [BC08] study the integrity of the Plutus file system [KRS+ 03] on untrusted
storage, using correspondence assertions, resulting in the discovery, and subsequent fixing, of weak-
nesses in the initial system.
Bhargavan et al. [BFGT06, BFG06, BFGS08] use ProVerif to analyze cryptographic protocol im-
plementations written in F#; in particular, the Transport Layer Security (TLS) protocol has been
studied in this manner [BCFZ08].
Chen & Ryan [CR09] evaluate authentication protocols found in the Trusted Platform Module
(TPM), a widely deployed hardware chip, and discovered vulnerabilities.
Delaune, Kremer & Ryan [DKR09, KR05] and Backes, Hritcu & Maffei [BHM08] formalize and
analyze privacy properties for electronic voting using observational equivalence.
1
2 CHAPTER 1. INTRODUCTION
Delaune, Ryan & Smyth [DRS08] and Backes, Maffei & Unruh [BMU08] analyze the anonymity
properties of the trusted computing scheme Direct Anonymous Attestation (DAA) [BCC04, SRC07]
using observational equivalence.
Küsters & Truderung [KT09, KT08] examine protocols with Diffie-Hellman exponentiation and
XOR.
Smyth, Ryan, Kremer & Kourjieh [SRKK10, SRK10] formalize and analyze verifiability properties
for electronic voting using reachability.
Bhargavan, Blanchet and Kobeissi verify Signal [KBB17] and TLS 1.3 [BBK17].
1.3 Support
Software bugs and comments should be reported by e-mail to:
User support, general discussion and new release announcements are provided by the ProVerif mailing
list. To subscribe to the list, send an email to [email protected] with the subject “subscribe proverif”
(without quotes). To post on the list, send an email to:
1.4 Installation
ProVerif is compatible with the Linux, Mac, and Windows operating systems; it can be downloaded
from:
http://proverif.inria.fr/
The remainder of this section covers installation on Linux, Mac, and Windows platforms.
1.4. INSTALLATION 3
http://lablgtk.forge.ocamlcore.org/
http://proverif.inria.fr/
cd proverif2.05
./build
(If you did not install LablGTK2, the compilation of proverif interact fails, but the executables
proverif and proveriftotex are still produced correctly, so you can use ProVerif normally, but
cannot run the interactive simulator.)
1. The installation of graphviz is required if you want to have a graphical representation of the at-
tacks that ProVerif might find. Graphviz can be downloaded from https://graphviz.gitlab.io/
_pages/Download/Download_windows.html. Make sure that the bin subdirectory of the Graphviz
installation directory is in your PATH.
2. The installation GTK+2.24 is required if you want to run the interactive simulator
proverif interact. At
https://download.gnome.org/binaries/win32/gtk%2B/2.24/
3. Download the Windows binary package proverifbin2.05.tar.gz and the documentation package
proverifdoc2.05.tar.gz from
http://proverif.inria.fr/
5. ProVerif has now been successfully installed in the directory where the file was extracted.
1.5. COPYRIGHT 5
1.4.4 Emacs
If you use the emacs text editor for editing ProVerif input files, you can install the emacs mode provided
with the ProVerif distribution.
1. Copy the file emacs/proverif.el (if you installed by OPAM in the switch ⟨switch⟩, the file ~/
.opam/⟨switch⟩/share/proverif/emacs/proverif.el) to a directory where Emacs will find it
(that is, in your emacs load-path).
2. Add the following lines to your .emacs file:
(setq auto-mode-alist
(cons '("\\.horn$" . proverif-horn-mode)
(cons '("\\.horntype$" . proverif-horntype-mode)
(cons '("\\.pv[l]?$" . proverif-pv-mode)
(cons '("\\.pi$" . proverif-pi-mode) auto-mode-alist)))))
(autoload 'proverif-pv-mode "proverif" "Major mode for editing ProVerif code." t)
(autoload 'proverif-pi-mode "proverif" "Major mode for editing ProVerif code." t)
(autoload 'proverif-horn-mode "proverif" "Major mode for editing ProVerif code." t)
(autoload 'proverif-horntype-mode "proverif" "Major mode for editing ProVerif code." t)
1.4.5 Atom
There is also a ProVerif mode for the text editor Atom (https://atom.io/), by Vincent Cheval. It can
be downloaded from the Atom web site; the package name is language-proverif.
1.5 Copyright
The ProVerif software is distributed under the GNU general public license. For details see:
http://proverif.inria.fr/LICENSE
6 CHAPTER 1. INTRODUCTION
Chapter 2
Getting started
This chapter provides a basic introduction to ProVerif and is aimed at new users; experienced users may
choose to skip this chapter. ProVerif is a command-line tool which can be executed using the syntax:
where ./proverif is ProVerif’s binary; ⟨filename⟩ is the input file; and command-line parameters
[options] will be discussed later (Section 6.6.1). ProVerif can handle input files encoded in several
languages. The typed pi calculus is currently considered to be state-of-the-art and files of this sort
are denoted by the file extension .pv. This manual will focus on protocols encoded in the typed
pi calculus. (For the interested reader, other input formats are mentioned in Section 6.6.1 and in
docs/manual-untyped.pdf.) The pi calculus is designed for representing concurrent processes that
interact using communications channels such as the Internet.
ProVerif is capable of proving reachability properties, correspondence assertions, and observational
equivalence. This chapter will demonstrate the use of reachability properties and correspondence as-
sertions in a very basic manner. The true power of ProVerif will be discussed in the remainder of this
manual.
7
8 CHAPTER 2. GETTING STARTED
attacker’s knowledge, then the communication would be on a private channel. For convenience, the final
line may be omitted and hence out(c,RSA) is an abbreviation of out(c,RSA);0.
Properties of the aforementioned script can be examined using ProVerif. For example, to test as to
whether the names Cocks and RSA are available derivable by the attacker, the following lines can be
included before the main process:
7 query attacker (RSA ) .
8 query attacker ( Cocks ) .
Internally, ProVerif attempts to prove that a state in which the names Cocks and RSA are known to the
attacker is unreachable (that is, it tests the queries not attacker(RSA) and not attacker(Cocks), and
these queries are true when the names are not derivable by the attacker). This makes ProVerif suitable
for proving the secrecy of terms in a protocol.
Executing ProVerif (./proverif docs/hello.pv) produces the output:
Process 0 (that is, the initial process):
{1}out(c, RSA)
Derivation:
2. By 1, attacker(RSA[]).
The goal is reached, represented in the following fact:
attacker(RSA[]).
--------------------------------------------------------------
Verification summary:
--------------------------------------------------------------
9
As can be interpreted from RESULT not attacker:(Cocks[]) is true, the attacker has not been able
to obtain the free name Cocks. The attacker has, however, been able to obtain the free name RSA as
denoted by the RESULT not attacker:(RSA[]) is false. ProVerif is also able to provide an attack
trace. In this instance, the trace is very short and denoted by
which means that the name RSA is output on channel c at point {1} in the process and stored by the
attacker in ~M, where point {1} is annotated on Line 2 of the output. ProVerif concludes the trace
by saying that the attacker has RSA. ProVerif also provides an English language description of the
derivation denoted by
2. By 1, attacker(RSA[]).
The goal is reached, represented in the following fact:
attacker(RSA[]).
A derivation is the ProVerif internal representation of how the attacker may break the desired property,
here may obtain RSA. It generally corresponds to an attack as in the example above, but may sometimes
correspond to a false attack because of the internal approximations made by ProVerif. In contrast, when
ProVerif presents a trace, it always corresponds to a real attack. See Section 3.3 for more details. The
output ends with a summary of the results for all queries.
Correspondence assertions. Let us now consider an extended variant docs/hello ext.pv of the
script:
1 ( * h e l l o e x t . pv : H e l l o Extended World S c r i p t * )
2
3 free c : channel .
4
5 f r e e Cocks : b i t s t r i n g [ private ] .
6 f r e e RSA : b i t s t r i n g [ private ] .
7
8 event evCocks .
9 event evRSA .
10
11 query event ( evCocks ) ==> event ( evRSA ) .
12
13 process
14 out ( c , RSA ) ;
15 in ( c , x : b i t s t r i n g ) ;
16 i f x = Cocks then
17 event evCocks ;
18 event evRSA
19 else
20 event evRSA
Lines 1-7 should be familiar. Lines 8-9 declare events evCocks and evRSA. Intuition suggests that Line 11
is some form of query. Lines 13-14 should again be standard. Line 15 contains a message input of type
bitstring on channel c which it binds to the variable x. Lines 16-20 denote an if-then-else statement;
the body of the then branch can be found on Lines 17-18 and the else branch on Line 20. We remark
that the code presented is a shorthand for the more verbose
i f x = Cocks then event evCocks ; event evRSA ; 0 e l s e event evRSA ; 0
10 CHAPTER 2. GETTING STARTED
where 0 denotes the end of a branch (termination of a process). The statement event evCocks (similarly
event evRSA) declares an event and the query
query event ( evCocks ) ==> event ( evRSA )
is true if and only if, for all executions of the protocol, if the event evCocks has been executed, then the
event evRSA has also been executed before. Executing the script produces the output:
Process 0 (that is, the initial process):
{1}out(c, RSA);
{2}in(c, x: bitstring);
{3}if (x = Cocks) then
{4}event evCocks;
{5}event evRSA
else
{6}event evRSA
--------------------------------------------------------------
Verification summary:
--------------------------------------------------------------
As expected, it is not possible to witness the event evCocks without having previously executed the event
evRSA and hence the correspondence event(evCocks) ==> event(evRSA) is true. In fact, a stronger
property is true: the event evCocks is unreachable. The reader can verify this claim with the addition of
query event(evCocks). (The authors remark that writing code with unreachable points is a common
source of errors for new users. Advice on avoiding such pitfalls will be presented in Section 4.3.1.)
Chapter 3
Using ProVerif
The primary goal of ProVerif is the verification of cryptographic protocols. Cryptographic protocols
are concurrent programs which interact using public communication channels such as the Internet to
achieve some security-related objective. These channels are assumed to be controlled by a very powerful
environment which captures an attacker with “Dolev-Yao” capabilities [DY83]. Since the attacker has
complete control of the communication channels, the attacker may: read, modify, delete, and inject
messages. The attacker is also able to manipulate data, for example: compute the ith element of a
tuple; and decrypt messages if it has the necessary keys. The environment also captures the behavior
of dishonest participants; it follows that only honest participants need to be modeled. ProVerif’s input
language allows such cryptographic protocols and associated security objectives to be encoded in a formal
manner, allowing ProVerif to automatically verify claimed security properties. Cryptography is assumed
to be perfect; that is, the attacker is only able to perform cryptographic operations when in possession
of the required keys. In other words, it cannot apply any polynomial-time algorithm, but is restricted to
apply only the cryptographic primitives specified by the user. The relationships between cryptographic
primitives are captured using rewrite rules and/or an equational theory.
In this chapter, we demonstrate how to use ProVerif for verifying cryptographic protocols, by consid-
ering a naı̈ve handshake protocol (Figure 3.1) as an example. Section 3.1 discusses how cryptographic
protocols are encoded within ProVerif’s input language, a variant of the applied pi calculus [AF01, RS11]
which supports types; Section 3.2 shows the security properties that can be proved by ProVerif; and Sec-
tion 3.3 explains how to understand ProVerif’s output.
3.1.1 Declarations
Processes are equipped with a finite set of types, free names, and constructors (function symbols) which
are associated with a finite set of destructors. The language is strongly typed and user-defined types are
declared as
type t .
All free names appearing within an input file must be declared using the syntax
free n : t .
where n is a name and t is its type. Several free names of the same type t can be declared by
f r e e n1 , . . . , nk : t .
11
12 CHAPTER 3. USING PROVERIF
I → B : pk(skI)
B → I : aenc(sign((pk(skB),k),skB),pk(skI))
A → B : pk(skA)
I → A : aenc(sign((pk(skB),k),skB),pk(skA))
A → B : senc(s ,k)
The protocol can easily be corrected by adding the identity of the intended client:
A → B : pk(skA)
B → A : aenc(sign((pk(skA),pk(skB),k),skB),pk(skA))
A → B : senc(s ,k)
With this correction, I is not able to re-use the signed key from B in her session with A.
3.1. MODELING PROTOCOLS 13
The syntax channel c. is a synonym for free c: channel. By default, free names are known by the
attacker. Free names that are not known by the attacker must be declared private:
f r e e n : t [ private ] .
Constructors (function symbols) are used to build terms modeling primitives used by cryptographic
protocols; for example: one-way hash functions, encryptions, and digital signatures. Constructors are
defined by
fun f (t1 , . . . , tn ) : t .
where f is a constructor of arity n, t is its return type, and t1 , . . . , tn are the types of its arguments.
Constructors are available to the attacker unless they are declared private:
fun f (t1 , . . . , tn ) : t [ private ] .
Private constructors can be useful for modeling tables of keys stored by the server (see Section 6.7.3),
for example.
The relationships between cryptographic primitives are captured by destructors which are used to
manipulate terms formed by constructors. Destructors are modeled using rewrite rules of the form:
reduc f o r a l l x1,1 : t1,1 , . . . , x1,n1 : t1,n1 ; g(M1,1 , . . . , M1,k ) = M1,0 ;
...
f o r a l l xm,1 : tm,1 , . . . , xm,nm : tm,nm ; g(Mm,1 , . . . , Mm,k ) = Mm,0 .
where g is a destructor of arity k. The terms M1,1 , . . . , M1,k , M1,0 are built from the application of
constructors to variables x1,1 , . . . , x1,n1 of types t1,1 , . . . , t1,n1 respectively (and similarly for the other
rewrite rules). The return type of g is the type M1,0 and M1,0 , . . . , Mm,0 must have the same type. We
similarly require that the arguments of the destructor have the same type; that is, M1,1 , . . . , M1,k have
the same types as Mi,1 , . . . , Mi,k for i ∈ [2, m], and these types are the types of the arguments of g. When
the term g(M1,1 , . . . , M1,k ) (or an instance of that term) is encountered during execution, it is replaced
by M1,0 , and similarly for the other rewrite rules. When no rule can be applied, the destructor fails,
and the process blocks (except for the let process, see Section 3.1.4). This behavior corresponds to real
world application of cryptographic primitives which include sufficient redundancy to detect scenarios in
which an operation fails. For example, in practice, encrypted messages may be assumed to come with
sufficient redundancy to discover when the ‘wrong’ key is used for decryption. It follows that destructors
capture the behavior of cryptographic primitives which can visibly fail.
When several variables have the same type, we can avoid repeating their type in the declaration,
writing for instance:
reduc f o r a l l x, y : t, z : t′ ; g(M1 , . . . , Mk ) = M0 .
Destructors must be deterministic, that is, for each terms (M1 , . . . , Mk ) given as argument to g, when
several rewrite rules apply, they must all yield the same result and, in the rewrite rules, the variables
that occur in Mi,0 must also occur in Mi,1 , . . . , Mi,k , so that the result of g(M1 , . . . , Mk ) is entirely
determined.
In a similar manner to constructors, destructors may be declared private by appending [private].
The generic mechanism by which primitives are encoded permits the modeling of various cryptographic
operators.
It is possible to use let bindings within the declaration of each rewrite rule. For example, an abstract
zero knowledge proof used in some voting protocols could be declared as follows:
reduc f o r a l l r : rand , i : id , v : vote , pub : p u b l i c k e y ;
l e t c i p h e r = r a e n c ( v , r , pub ) in
checkzkp ( zkp ( r , i , v , c i p h e r ) , i , c i p h e r ) = ok .
Symmetric encryption. For symmetric encryption, we define the type key and consider the binary
constructor senc which takes arguments of type bitstring , key and returns a bitstring .
1 type key .
2
3 fun s e n c ( b i t s t r i n g , key ) : b i t s t r i n g .
Note that the type bitstring is built-in, and hence, need not be declared as a user-defined type. The type
key is not built-in and hence we declare it on Line 1. To model the decryption operation, we introduce
the destructor:
4 reduc f o r a l l m: b i t s t r i n g , k : key ; s d e c ( s e n c (m, k ) , k ) = m.
Asymmetric encryption. For asymmetric cryptography, we consider the unary constructor pk, which
takes an argument of type skey (private key) and returns a pkey (public key), to capture the notion of
constructing a key pair. Decryption is captured in a similar manner to symmetric cryptography with a
public/private key pair used in place of a symmetric key.
5 type s k e y .
6 type pkey .
7
8 fun pk ( s k e y ) : pkey .
9 fun aenc ( b i t s t r i n g , pkey ) : b i t s t r i n g .
10
11 reduc f o r a l l m: b i t s t r i n g , k : s k e y ; adec ( aenc (m, pk ( k ) ) , k ) = m.
Digital signatures. In a similar manner to asymmetric encryption, digital signatures rely on a pair of
signing keys of types sskey (private signing key) and spkey (public signing key). We will consider digital
signatures with message recovery:
12 type s s k e y .
13 type spkey .
14
15 fun spk ( s s k e y ) : spkey .
16 fun s i g n ( b i t s t r i n g , s s k e y ) : b i t s t r i n g .
17
18 reduc f o r a l l m: b i t s t r i n g , k : s s k e y ; g e t m e s s ( s i g n (m, k ) ) = m.
19 reduc f o r a l l m: b i t s t r i n g , k : s s k e y ; c h e c k s i g n ( s i g n (m, k ) , spk ( k ) ) = m.
The constructors spk, for creating public keys, and sign, for constructing signatures, are standard.
The destructors permit message recovery and signature verification. The destructor getmess allows the
attacker to get the message m from the signature, even without having the key. The destructor checksign
checks the signature, and returns m only when the signature is correct. Honest processes typically use
only checksign. This model of signatures assumes that the signature is always accompanied with the
message m. It is also possible to model signatures that do not reveal the message m, see Section 4.2.5.
Tuples and typing. For convenience, ProVerif has built-in support for tupling. A tuple of length
n > 1 is defined as (M1 , . . . , Mn ) where M1 , . . . , Mn are terms of any type. Once in possession of a
tuple, the attacker has the ability to recover the ith element. The inverse is also true: if the attacker is
in possession of terms M1 , . . . , Mn , then it can construct the tuple (M1 , . . . , Mn ). Tuples are always of
type bitstring. Accordingly, constructors that take arguments of type bitstring may be applied to tuples.
Note that the term (M ) is not a tuple and is equivalent to M . (Parentheses are needed to override the
default precedence of infix operators.) It follows that (M ) and M have the same type and that tuples of
arity one do not exist.
3.1. MODELING PROTOCOLS 15
f r e e Cocks : b i t s t r i n g [ private ] .
f r e e RSA : b i t s t r i n g [ private ] .
query attacker ( Cocks ) .
l e t R( x : b i t s t r i n g ) = out ( c , x ) ; 0 .
l e t R ' ( y : b i t s t r i n g )= 0 .
3.1.4 Processes
The basic grammar of the language is presented in Figure 3.2; advanced features will be discussed in
Chapter 4; and the complete grammar is presented in Appendix A for reference.
Terms M, N consist of names a, b, c, k, m, n, s; variables x, y, z; tuples (M1 , . . . , Mj ) where j is the
arity of the tuple; and constructor/destructor application, denoted h(M1 , . . . , Mk ) where k is the arity
of h and arguments M1 , . . . , Mk have the required types. Some functions use the infix notation: M =
N for equality, M <> N for disequality (both equality and disequality work modulo an equational
theory; they take two arguments of the same type and return a result of type bool), M && M for the
boolean conjunction, M || M for the boolean disjunction. We use not(M ) for the boolean negation. In
boolean operations, all values different from true (modulo an equational theory) are considered as false .
Furthermore, if the first argument of M && M is not true, then the second argument is not evaluated
and the result is false . Similarly, if the first argument of M || M is true, then the second argument is
not evaluated and the result is true.
Processes P, Q are defined as follows. The null process 0 does nothing; P | Q is the parallel com-
position of processes P and Q, used to represent participants of a protocol running in parallel; and the
replication !P is the infinite composition P | P | . . ., which is often used to capture an unbounded number
of sessions. Name restriction new n : t; P binds name n of type t inside P , the introduction of restricted
names (or private names) is useful to capture both fresh random numbers (modeling nonces and keys,
for example) and private channels. Communication is captured by message input and message output.
The process in(M, x : t); P awaits a message of type t from channel M and then behaves as P with the
received message bound to the variable x; that is, every free occurrence of x in P refers to the message
received. The process out(M, N ); P is ready to send N on channel M and then run P . In both of these
cases, we may omit P when it is 0. The conditional if M then P else Q is standard: it runs P when
the boolean term M evaluates to true, it runs Q when M evaluates to some other value. It executes
nothing when the term M fails (for instance, when M contains a destructor for which no rewrite rule
applies). For example, if M = N then P else Q tests equality of M and N . For convenience, condi-
tionals may be abbreviated as if M then P when Q is the null process. The power of destructors can
be capitalized upon by let x = M in P else Q statements where M may contain destructors. When
16 CHAPTER 3. USING PROVERIF
P, Q ::= processes
0 null process
P |Q parallel composition
!P replication
new n : t; P name restriction
in(M, x : t); P message input
out(M, N ); P message output
if M then P else Q conditional
let x = M in P else Q term evaluation
R(M1 , . . . , Mk ) macro usage
this statement is encountered during process execution, there are two possible outcomes. If the term M
does not fail (that is, for all destructors in M , matching rewrite rules exist), then x is bound to M and
the P branch is taken; otherwise (rather than blocking), the Q branch is taken. (In particular, when M
never fails, the P branch will always be executed with x bound to M .) For convenience, the statement
let x = M in P else Q may be abbreviated as let x = M in P when Q is the null process. Finally, we
have R(M1 , . . . , Mn ), denoting the use of the macro R with terms M1 , . . . , Mn as arguments.
Pattern matching.
For convenience, ProVerif supports pattern matching and we extend the grammar to include patterns
(Figure 3.3). The variable pattern x : t matches any term of type t and binds the matched term to x. The
variable pattern x is similar, but can be used only when the type of x can be inferred from the context.
When the matched term is not used, the variable can be replaced with the symbol , which matches any
term (of a certain type) without binding the matched term to a variable. The tuple pattern (T1 , . . . , Tn )
matches tuples (M1 , . . . , Mn ) where each component Mi (i ∈ {1, . . . , n}) is recursively matched with Ti .
Finally, the pattern =M matches terms N where M = N . (This is equivalent to an equality test.)
To make use of patterns, the grammar for processes is modified. We omit the rule in(M, x : t); P
and instead consider in(M, T ); P which awaits a message matching the pattern T and then behaves as
P with the free variables of T bound inside P . Similarly, we replace let x = M in P else Q with the
more general let T = M in P else Q. (Note that let x = M in P else Q is a particular case in which
3.1. MODELING PROTOCOLS 17
the type of x is inferred from M ; users may also write let x : t = M in P else Q where t is the type of
M , ProVerif will produce an error if there is a type mismatch.)
Bracketing must be used to avoid ambiguities in the way processes are written down. For example,
the process ! P | Q might be interpreted as !(P | Q), or as (!P ) | Q. These processes are different.
To avoid too much bracketing, we adopt conventions about the precedence of process operators. The
binary parallel process P | Q binds most closely; followed by the binary processes if M then P else Q,
let x = M in P else Q; finally, unary processes bind least closely. It follows that ! P | Q is interpreted
as !(P | Q). Users should pay particular attention to ProVerif warning messages since these typically
arise from misunderstanding ProVerif’s binding conventions. For example, consider the process
which produces the message “Warning: identifier n rebound.” Moreover, the process will never perform
the final out(c,n) because the process is bracketed as follows:
and hence the final output is guarded by a conditional which can never be satisfied. The authors
recommend the distinct naming of names and variables to avoid confusion. New users may like to
refer to the output produced by ProVerif to ensure that they have defined processes correctly (see also
Section 3.3). Another possible ambiguity arises because of the convention of omitting else 0 in the
if-then-else construct (and similarly for let-in-else): it is not clear which if the else applies to in the
expression:
i f M = M ′ then i f N = N ′ then P e l s e Q
In this instance, we adopt the convention that the else branch belongs to the closest if and hence the
statement should be interpreted as if M = M ′ then (if N = N ′ then P else Q). The convention is
similar for let-in-else.
Reserved words. The following is a list of keywords in the ProVerif language; accordingly, they cannot
be used as identifiers.
among, axiom, channel, choice, clauses, const, def, diff, do, elimtrue, else, equation, equiva-
lence, event, expand, fail, for, forall, foreach, free, fun, get, if, implementation, in, inj-event,
insert, lemma, let, letfun, letproba, new, noninterf, noselect, not, nounif, or, otherwise, out,
param, phase, pred, proba, process, proof, public vars, putbegin, query, reduc, restriction,
secret, select, set, suchthat, sync, table, then, type, weaksecret, yield.
ProVerif also has built-in types any type, bitstring , bool, nat, sid, time, constants true, false of type
bool, destructor is nat , predicates attacker, mess, subterm; although these identifiers can be reused
as identifiers, the authors strongly discourage this practice.
18 CHAPTER 3. USING PROVERIF
correct signature, then the (omitted) else branch of the statement will be evaluated because there is
no corresponding rewrite rule, so the client halts. Finally, the server inputs a bitstring x and recovers
the cleartext as variable z. (Observe that the failure of decryption is again detectable.) Note that the
variable z in the server process is not used.
Correspondence
The syntax to query a basic correspondence assertion is:
query x1 : t1 , . . . , xn : tn ; event ( e(M1 , . . . , Mj ) ) ==> event ( e′ (N1 , . . . , Nk ) ) .
where M1 , . . . , Mj , N1 , . . . , Nk are terms built by the application of constructors to the variables x1 , . . . ,
xn of types t1 , . . . , tn and e, e′ are declared as events. The query is satisfied if, for each occurrence of the
event e(M1 , . . . , Mj ), there is a previous execution of e′ (N1 , . . . , Nk ). Moreover, the parameterization
of the events must satisfy any relationships defined by M1 , . . . , Mj , N1 , . . . , Nk ; that is, the variables
x1 , . . . , xn have the same value in M1 , . . . , Mj and in N1 , . . . , Nk .
In such a query, the variables that occur before the arrow ==> (that is, in M1 , . . . , Mj ) are universally
quantified, while the variables that occur after the arrow ==> (in N1 , . . . , Nk ) but not before are
existentially quantified. For instance,
query x : t1 , y : t2 , z : t3 ; event ( e(x, y) ) ==> event ( e′ (y, z) ) .
means that, for all x, y, for each occurrence of e(x, y), there is a previous occurrence of e′ (y, z) for some
z.
20 CHAPTER 3. USING PROVERIF
Injective correspondence
The definition of correspondence we have just discussed is insufficient to capture authentication in cases
where a one-to-one relationship between the number of protocol runs performed by each participant is
desired. Consider, for example, a financial transaction in which the server requests payment from the
client; the server should complete the transaction only once for each transaction started by the client. (If
this were not the case, the client could be charged for several transactions, even if the client only started
one.) The situation is similar for access control and other scenarios. Injective correspondence assertions
capture the one-to-one relationship and are denoted:
query x1 : t1 , . . . , xn : tn ; inj −event ( e(M1 , . . . , Mj ) ) ==> inj −event ( e′ (N1 , . . . , Nk ) ) .
Informally, this correspondence asserts that, for each occurrence of the event e(M1 , . . . , Mj ), there is
a distinct earlier occurrence of the event e′ (N1 , . . . , Nk ). It follows immediately that the number of
occurrences of e′ (N1 , . . . , Nk ) is greater than, or equal to, the number of occurrences of e(M1 , . . . , Mj ).
Note that using inj−event or event before the arrow ==> does not change the meaning of the query.
It is only important after the arrow.
event acceptsClient(key), which is used by the client to record the belief that she has accepted to
run the protocol with the server B and the supplied symmetric key.
event acceptsServer(key,pkey), which is used to record the fact that the server considers he has
accepted to run the protocol with a client, with the proposed key supplied as the first argument
and the client’s public key as the second.
event termClient(key,pkey), which means the client believes she has terminated a protocol run
using the symmetric key supplied as the first argument and the client’s public key as the second.
event termServer(key), which denotes the server’s belief that he has terminated a protocol run
with the client A with the symmetric key supplied as the first argument.
Recall that the client is only willing to share her secret with the server B; it follows that, if she completes
the protocol, then she believes she has done so with B and hence authentication of B to A should hold.
In contrast, server B is willing to run the protocol with any client (that is, he is willing to learn secrets
from many clients), and hence at the end of the protocol he only expects authentication of A to B to
hold, if he believes A was indeed his interlocutor (so termServer(x) is executed only when pkX = pkA).
We can now formalize the two authentication properties (given in Figure 3.1) for the handshake protocol.
They are, respectively:
query x : key , y : spkey ; event ( t e r m C l i e n t ( x , y))==>event ( a c c e p t s S e r v e r ( x , y ) ) .
query x : key ; inj −event ( t e r m S e r v e r ( x))==>inj −event ( a c c e p t s C l i e n t ( x ) ) .
The subtle difference between the two correspondence assertions is due to the differing authentication
properties expected by participants A and B. The first correspondence is not injective because the
protocol does not allow the client to learn whether the messages she received are fresh: the message from
the server to the client may be replayed, leading to several client sessions for a single server session. The
revised ProVerif encoding with annotations and correspondence assertions is presented below and in the
file docs/ex handshake annotated.pv (cryptographic declarations have been omitted for brevity):
1 free c : channel .
2
3.2. SECURITY PROPERTIES 21
3 f r e e s : b i t s t r i n g [ private ] .
4 query attacker ( s ) .
5
6 event a c c e p t s C l i e n t ( key ) .
7 event a c c e p t s S e r v e r ( key , pkey ) .
8 event t e r m C l i e n t ( key , pkey ) .
9 event t e r m S e r v e r ( key ) .
10
11 query x : key , y : pkey ; event ( t e r m C l i e n t ( x , y))==>event ( a c c e p t s S e r v e r ( x , y ) ) .
12 query x : key ; inj −event ( t e r m S e r v e r ( x))==>inj −event ( a c c e p t s C l i e n t ( x ) ) .
13
14 l e t c l i e n t A (pkA : pkey , skA : skey , pkB : spkey ) =
15 out ( c , pkA ) ;
16 in ( c , x : b i t s t r i n g ) ;
17 l e t y = adec ( x , skA ) in
18 l e t (=pkB , k : key ) = c h e c k s i g n ( y , pkB ) in
19 event a c c e p t s C l i e n t ( k ) ;
20 out ( c , s e n c ( s , k ) ) ;
21 event t e r m C l i e n t ( k , pkA ) .
22
23 l e t s e r v e r B ( pkB : spkey , skB : s s k e y , pkA : pkey ) =
24 in ( c , pkX : pkey ) ;
25 new k : key ;
26 event a c c e p t s S e r v e r ( k , pkX ) ;
27 out ( c , aenc ( s i g n ( ( pkB , k ) , skB ) , pkX ) ) ;
28 in ( c , x : b i t s t r i n g ) ;
29 l e t z = s d e c ( x , k ) in
30 i f pkX = pkA then event t e r m S e r v e r ( k ) .
31
32 process
33 new skA : s k e y ;
34 new skB : s s k e y ;
35 l e t pkA = pk ( skA ) in out ( c , pkA ) ;
36 l e t pkB = spk ( skB ) in out ( c , pkB ) ;
37 ( ( ! c l i e n t A (pkA , skA , pkB ) ) | ( ! s e r v e r B ( pkB , skB , pkA ) ) )
There is generally some flexibility in the placement of events in a process, but not all choices are correct.
For example, in order to prove authentication in our handshake protocol, we consider the property
query x : key ; inj −event ( t e r m S e r v e r ( x))==>inj −event ( a c c e p t s C l i e n t ( x ) ) .
and the event termServer is placed when the server terminates (typically at the end of the protocol),
while acceptsClient is placed when the client accepts (typically before the client sends its last message).
Therefore, when the last message, message n, is from the client to the server, the placement of events
follows Figure 3.4: the last message sent by the client is message n, so acceptsClient is placed before the
client sends message n, and termServer is placed after the server receives message n. The last message
sent by the server is message n − 1, so acceptsServer is placed before the server sends message n − 1, and
22 CHAPTER 3. USING PROVERIF
termClient is placed after the client receives message n − 1 (any position after that reception is fine).
More generally, the event that occurs before the arrow ==> can be placed at the end of the protocol, but
the event that occurs after the arrow ==> must be followed by at least one message output. Otherwise,
the whole protocol can be executed without executing the latter event, so the correspondence certainly
does not hold.
One can also note that moving an event that occurs before the arrow ==> towards the beginning of
the protocol strengthens the correspondence property, and moving an event that occurs after the arrow
==> towards the end of the protocol also strengthens the correspondence property. Adding arguments
to the events strengthens the correspondence property as well.
−− Query [ Query ]
Completing . . .
S t a r t i n g query [ Query ]
g o a l [ un ] r e a c h a b l e : [ Goal ]
Abbreviations :
...
[ Attack d e r i v a t i o n ]
[ Attack t r a c e ]
RESULT [ Query ] [ r e s u l t ] .
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
V e r i f i c a t i o n summary :
[ Summary o f v e r i f i c a t i o n r e s u l t s ]
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
where [Equations] summarizes the internal representation of the equations given in the input file (if any)
and [Process] presents the input process with all macros expanded and distinct identifiers assigned to
unique names/variables; in addition, parts of the process are annotated with identifiers {n} where n ∈ N∗ .
(New users may like to refer to this interpreted process to ensure they have defined the scope of variables
in the correct manner and to ensure they haven’t inadvertently bound processes inside if-then-else/let-in-
else statements.) ProVerif then begins to evaluate the [Query] provided by the user. Internally, ProVerif
attempts to prove that a state in which a property is violated is unreachable; it follows that ProVerif
shows the (un)reachability of some [Goal]. If a property is violated then ProVerif attempts to reconstruct
an [Attack derivation] in English and an [Attack trace] in the applied pi calculus. ProVerif then reports
whether the query was satisfied. Finally, ProVerif displays a summary of the verification results of all the
queries in the file. For convenience, Linux and cygwin users may make use of the following command:
./proverif ⟨filename⟩.pv | grep "RES"
which reduces the output to the results of the queries.
3.3. UNDERSTANDING PROVERIF OUTPUT 23
3.3.1 Results
In order to understand the results correctly, it is important to understand the difference between the
attack derivation and the attack trace. The attack derivation is an explanation of the actions that the
attacker has to make in order to break the security property, in the internal representation of ProVerif.
Because this internal representation uses abstractions, the derivation is not always executable in reality;
for instance, it may require the repetition of certain actions that can in fact never be repeated, for
instance because they are not under a replication. In contrast, the attack trace refers to the semantics
of the applied pi calculus, and always corresponds to an executable trace of the considered process.
ProVerif can display three kinds of results:
RESULT [Query] is true: The query is proved, there is no attack. In this case, ProVerif displays
no attack derivation and no attack trace.
RESULT [Query] is false: The query is false, ProVerif has discovered an attack against the desired
security property. The attack trace is displayed just before the result (and an attack derivation is
also displayed, but you should focus on the attack trace since it represents the real attack).
RESULT [Query] cannot be proved: This is a “don’t know” answer. ProVerif could not prove that
the query is true and also could not find an attack that proves that the query is false. Since the
problem of verifying protocols for an unbounded number of sessions is undecidable, this situation
is unavoidable. Still, ProVerif gives some additional information that can be useful in order to
determine whether the query is true. In particular, ProVerif displays an attack derivation. By
manually inspecting the derivation, it is sometimes possible to reconstruct an attack. For observa-
tional equivalence properties, it may also display an attack trace, even if this trace does not prove
that the observational equivalence does not hold. We will come back to this point when we deal
with observational equivalence, in Section 4.3.2. Sources of incompleteness, which explain why
ProVerif sometimes fails to prove properties that hold, will be discussed in Section 6.7.5.
Interpreting results. Understanding the internal manner in which ProVerif operates is useful to
interpret the results output. Recall that ProVerif attempts to prove that a state in which a property
is violated is unreachable. It follows that when ProVerif is supplied with query attacker(M )., that
internally ProVerif attempts to show not attacker(M ) and hence RESULT not attacker(M ) is true.
means that the secrecy of M is preserved by the protocol.
Error and warning messages. In case of a syntax error, ProVerif indicates the character position of
the error (line and column numbers). Please use your text editor to find the position of the error. (The er-
ror messages can be interpreted by emacs.) In addition, ProVerif may provide various warning messages.
The earlier grep command can be modified into ./proverif ⟨filename⟩.pv | egrep "RES|Err|War"
for more manageable output with notification of error/warnings, although a more complex command
is required to read any associated messages. In this case, the command ./proverif ⟨filename⟩.pv |
less can be useful.
By inspecting the output more closely, we can reconstruct the attack. For example, let us consider the
query query attacker(s) which produces the following:
1 P r o c e s s 0 ( t h a t i s , t h e i n i t i a l process ) :
2 {1}new skA : s k e y ;
3 {2}new skB : s s k e y ;
4 {3} l e t pkA : pkey = pk ( skA ) in
5 {4} out ( c , pkA ) ;
6 {5} l e t pkB : spkey = spk ( skB ) in
7 {6} out ( c , pkB ) ;
8 (
9 {7}!
10 {8} out ( c , pkA ) ;
11 {9} in ( c , x : b i t s t r i n g ) ;
12 {10} l e t y : b i t s t r i n g = adec ( x , skA ) in
13 {11} l e t (=pkB , k : key ) = c h e c k s i g n ( y , pkB ) in
14 {12} event a c c e p t s C l i e n t ( k ) ;
15 {13} out ( c , s e n c ( s , k ) ) ;
16 {14} event t e r m C l i e n t ( k , pkA)
17 ) | (
18 {15}!
19 {16} in ( c , pkX : pkey ) ;
20 {17}new k 1 : key ;
21 {18} event a c c e p t s S e r v e r ( k 1 , pkX ) ;
22 {19} out ( c , aenc ( s i g n ( ( pkB , k 1 ) , skB ) , pkX ) ) ;
23 {20} in ( c , x 1 : b i t s t r i n g ) ;
24 {21} l e t z : b i t s t r i n g = s d e c ( x 1 , k 1 ) in
25 {22} i f (pkX = pkA) then
26 {23} event t e r m S e r v e r ( k 1 )
27 )
28
29 −− Query not attacker ( s [ ] ) in process 0 .
30 Completing . . .
31 S t a r t i n g query not attacker ( s [ ] )
32 g o a l r e a c h a b l e : attacker ( s [ ] )
33
34 D e r i v a t i o n :
35 A b b r e v i a t i o n s :
36 k 2 = k 1 [ pkX = pk ( sk ) , ! 1 = @sid ]
37
38 1 . The attacker has some term sk .
39 attacker ( sk ) .
40
41 2 . By 1 , t h e attacker may know sk .
42 Using t h e f u n c t i o n pk t h e attacker may o b t a i n pk ( sk ) .
43 attacker ( pk ( sk ) ) .
44
45 3 . The message pk ( sk ) t h a t t h e attacker may have by 2 may be r e c e i v e d a t
46 i n p u t { 1 6 } .
47 So t h e message aenc ( s i g n ( ( spk ( skB [ ] ) , k 2 ) , skB [ ] ) , pk ( sk ) ) may be s e n t t o t h e
48 attacker a t output { 1 9 } .
49 attacker ( aenc ( s i g n ( ( spk ( skB [ ] ) , k 2 ) , skB [ ] ) , pk ( sk ) ) ) .
50
51 4 . By 3 , t h e attacker may know aenc ( s i g n ( ( spk ( skB [ ] ) , k 2 ) , skB [ ] ) , pk ( sk ) ) .
3.3. UNDERSTANDING PROVERIF OUTPUT 25
Step 7 uses a step of the process: by the output {4} (the one at Line 5), the attacker gets pk(skA[]).
At step 8, the attacker reencrypts sign((spk(skB []), k 2),skB[]) with pk(skA[]).
Step 9 is again a step of the process: the attacker sends aenc(sign((spk(skB []), k 2),skB []), pk(skA[]))
(obtained at step 8) to input {9} (at Line 11) and gets the reply senc(s [], k 2). In other words, the
attacker has obtained a correct message 2 for a session between A and B. It sends this message to
A who replies with senc(s [], k 2) as if it was running a session with B.
In step 10, the attacker decrypts senc(s [], k 2) since it has k 2 (by step 6), so it obtains s [] .
Finally, step 11 indicates that the query goal has been reached, that is, attacker(s[]).
As one can notice, this derivation corresponds exactly to the attack against the protocol outlined in
Figure 3.1. The display of the derivation can be tuned by some settings: set abbreviateDerivation = false
prevents the use of abbreviations for names and set explainDerivation = false switches to a display of
the derivation by explicit references to the Horn clauses used internally by ProVerif instead of relating
the derivation to the process. (See also Section 6.6.2 for details on these settings.)
Next, ProVerif reconstructs a trace in the semantics of the pi calculus, corresponding to this deriva-
tion. This trace is presented as a sequence of inputs and outputs on public channels and of events. The
internal reductions of the process are not displayed for brevity. (As mentioned in the output, it is possible
to obtain a more detailed display with the state of the process and the knowledge of the attacker at each
step by adding set traceDisplay = long. in your input file.) Each input, output, or event is followed by
its location in the process “at {n}”, which refers to the program point between braces in the process
displayed at the beginning. When the process is under replication, several copies of the process may be
generated. Each of these copies is named (by a name like “a n”), and ProVerif indicates in which copy
of the process the input, output, or event is executed. The name itself is unimportant, just the fact that
the copy is the same or different is important: the presence of different names of copies for the same
replication shows that several sessions are used. Let us explain the trace in the case of the handshake
protocol:
This trace shows that there is an attack against the secrecy of s, it corresponds to the attack against the
protocol outlined in Figure 3.1.
Another way to represent an attack found by ProVerif is by a graph. For instance, the attack explained
previously is shown in Figure 3.5. To obtain such a graph, use the command-line option -graph or -html
28 CHAPTER 3. USING PROVERIF
described in Section 6.6.1. The detailed version is built when set traceDisplay = long. has been added
to the input .pv file. The graph starts always with two processes: the honest one, and the attacker. The
progress of the attack is represented vertically. Parallel processes are represented by several columns.
Replications of processes are denoted by nodes labeled by !, with a column for each created process.
Processes fork when a parallel composition is reduced. The termination of a process is represented by a
point. An output on a public channel is represented by a horizontal arrow from the process that makes
the output to the attacker. The edge is labeled with an equality X = M where M is the sent message
and X is a fresh variable (or tuple of variables) in which the adversary stores it. An input on a public
channel is represented by an arrow from the attacker to the receiving process, labeled with an equality
R = M , where R is the computation performed by the attacker to obtain the sent message M . The
message M is omitted when it is exactly equal to R, for instance when R is a constant. A communication
made on a private channel is represented by an arrow from the process that outputs the message to the
process that receives it; this arrow is labeled with the message. Creation of nonces and other steps are
represented in boxes. Information about the attack is written in red; the displayed information depends
on the security property that is broken by the attack. The text “a trace has been found” is written at
the top of the figure, possibly with assumptions necessary for the attack. When labels are too long to
3.3. UNDERSTANDING PROVERIF OUTPUT 29
fit on arrows, a table of abbreviations appears at the top right of the figure.
Let us take a closer look at Figure 3.5. First, two new secret keys are created by the honest pro-
cess. Then the corresponding public keys are sent on a public channel; the attacker receives them
and stores them in ˜M and ˜M 1. Next, a parallel reduction is made. We obtain two processes
which replicate themselves once each. The first process (clientA) sends its public key on a pub-
lic channel, and the attacker receives it. Then the attacker sends the message pk(a 1), containing
its own public key, to the second process serverB. This process then creates a new shared key k 2
and executes the event acceptsServer(k 2,pk(a 1)). It sends the message aenc(sign((spk(skB 1), k 2),
skB 1), pk(a 1)) on a public channel; the attacker receives it and stores it in ˜M 3. The attacker
computes aenc(adec(˜M 3,a 1),˜M)), that is, it decrypts and reencrypts the message, thus obtaining
aenc(sign((spk(skB 1),k 2),skB 1),pk(skA 1)). It sends that message to clientA. The process clientA
executes the event acceptsClients(k 2) and sends the message senc(s ,k 2). The attacker receives it and
stores it in ˜M 4. Finally, the attacker computes sdec(˜M 4,2−proj−2−tuple(getmess(adec(˜M 3,a 1)))),
and obtains the secret s. This point is mentioned in the red box at the bottom right of the page. The
process clientA executes the last event termClient, and terminates. This is the end of the attack. The
line numbers of each step appear in green in boxes. The keywords are written in blue, while the names
of processes are written in green.
For completeness, we present the complete formalization of the rectified protocol, which ProVerif can
successfully verify, below and in the file docs/ex handshake annotated fixed.pv.
1 ( * Symmetric key e n c r y p t i o n * )
2
3 type key .
4 fun s e n c ( b i t s t r i n g , key ) : b i t s t r i n g .
5 reduc f o r a l l m: b i t s t r i n g , k : key ; s d e c ( s e n c (m, k ) , k ) = m.
6
7
8 ( * Asymmetric key e n c r y p t i o n * )
9
10 type s k e y .
11 type pkey .
12
13 fun pk ( s k e y ) : pkey .
14 fun aenc ( b i t s t r i n g , pkey ) : b i t s t r i n g .
15
16 reduc f o r a l l m: b i t s t r i n g , sk : s k e y ; adec ( aenc (m, pk ( sk ) ) , sk ) = m.
17
18
19 (* D i g i t a l s i g n a t u r e s *)
20
21 type s s k e y .
22 type spkey .
23
24 fun spk ( s s k e y ) : spkey .
25 fun s i g n ( b i t s t r i n g , s s k e y ) : b i t s t r i n g .
26
27 reduc f o r a l l m: b i t s t r i n g , s s k : s s k e y ; g e t m e s s ( s i g n (m, s s k ) ) = m.
28 reduc f o r a l l m: b i t s t r i n g , s s k : s s k e y ; c h e c k s i g n ( s i g n (m, s s k ) , spk ( s s k ) ) = m.
29
30
31 free c : channel .
32
33 f r e e s : b i t s t r i n g [ private ] .
34 query attacker ( s ) .
35
36 event a c c e p t s C l i e n t ( key ) .
30 CHAPTER 3. USING PROVERIF
and asks the type of the nonce. When a nonce is created, it is added to the public elements of the
current state. To go one step backward, there is a button “Backward”, or an option in the “Reduction”
menu, or a keyboard shortcut Ctrl+B. The button “Forward”, the option “Forward” of the “Reduction”
menu, or the keyboard shortcut Ctrl+F allow the user to re-execute a step that has been undone by the
“Backward” button. The button “Add a term to public ” is explained in Section 3.4.5. The interface
also allows to display a drawing of the current trace in a new window by clicking on “Display trace”
in the “Show” menu, or by hitting Ctrl+D. Each time a new reduction step is made, the drawing is
refreshed. The trace can be saved by selecting “Save File” in the “Save” menu, or hitting Ctrl+S.
One of these formats: .png, .pdf, .jpg or .eps, must be used to save the file, and the name of
the file with its extension must be given. Note that a more detailed version of the trace is available if
set traceDisplay = long. has been added to the input file. The main window and the menu also contains
two other options: “Next auto-step” and “All auto-steps”. We explain this functionality in the next
section.
The process out(M, N ); P is auto-reducible if the channel M is public, or the evaluation of the
message N or of the channel M fails. Otherwise, it is a manually-reducible process.
The process in(M, x : T ); P is auto-reducible if the evaluation of the channel M fails. Otherwise,
it is a manually-reducible process.
When auto-reducible processes are running and you press the button “All auto-steps” (or if you select this
option on the menu), it reduces all auto-reducible processes that are running. When you press the button
“Next auto-step”, it makes one step of reduction on the first auto-reducible process. Manually-reducible
processes can be reduced only by clicking on the head of their column.
32 CHAPTER 3. USING PROVERIF
If the process is auto-reducible because the evaluation of the channel M or of the message N fails,
then the process is removed.
If the evaluations of the message N and the channel M succeed and the channel M is public, then
the output is made as explained in Section 3.1.4. The message is added to the public elements of
the current state. It is displayed as follows ˜M i = N , where ˜M i is a new binder: this binder can
then be used to designate the term N in the computations that the adversary makes in the rest of
the execution. Such computations are called recipes. They are terms built from the binders ˜M i,
the nonces created by the adversary, the names that are initially public, and application of public
functions to recipes. In the general case, the public elements of the current state are represented
in the form binder = recipe = message, where the recipe is the computation that the adversary
makes to obtain the corresponding message, and the binder can be used to designate that message
in future recipes. To lighten the display, the binder is omitted when it is equal to the recipe, and
the recipe is omitted when it is equal to the message itself.
If the evaluations of the message N and the channel M succeed but the channel M is not known
to be public (this case is displayed “Output (private)” in the head of the column), then there are
two possibilities.
– Prove that the channel is in fact public, and make a public communication. To do so, a recipe
using public elements of the current state must be given. If this recipe is evaluated as equal
to the channel, a public output on this channel is made.
– Make a private communication on this channel between two processes. If this choice has been
made, the list of all the input processes on the same channel appears in the main window.
The user chooses the process that will receive the output message. If there is no such process,
the reduction is not possible and an error message appears.
If the evaluation of the channel M succeeds and the channel is public, then a pop-up window
opens, and the user gives the message to send on the channel. The message is given in the form
of a recipe, which can contain recipes of public elements of the current state, and applications of
public functions. In case the recipe is wrongly typed, if types are ignored (the default), then a
warning message box appears, allowing the user to choose to continue or go back. If types are not
ignored (the input file contains set ignoreTypes = false), an error message box appears, and a new
message must be given.
3.4. INTERACTIVE MODE 33
If the evaluation of the channel M succeeds and the channel is not known to be public (this case
is displayed “Input (private)” in the head of the column), then the program works similarly to
the case of a private output. There are again two possibilities: prove that the channel is public
by giving a recipe and make an input from the adversary, or choose an output process to make a
private communication between these processes as explained above.
In addition to the public functions explicitly defined in the input file, recipes can also contain projec-
tion functions. The syntax for projections associated to tuples differs depending on whether types
are ignored or not. If types are ignored (the default), then the i-th projection of a tuple of ar-
ity m is written i−proj−m−tuple. Otherwise, when the input file contains set ignoreTypes = false,
i−proj−<type 1 >− . . . −<type m >−tuple is the i-th projection of a tuple of arity m, when <type n > is the
type of the n-th argument of the tuple. For instance, 2−proj−channel−bitstring−tuple is the second pro-
jection of a pair with arguments of type channel and bitstring , so 2−proj−channel−bitstring−tuple((c,
m)) = m, where c is a channel and m is a bitstring. The i-th projection of a previously defined data
constructor f (see Section 4.1.2) is written i−proj−f .
First, a click on the “All auto-steps” button will lead to the situation represented in Figure 3.7:
the honest process first creates two secret keys, then output a first public key after a let, and then
a second one after another let on channel c. The attacker stores these public keys in fresh variables
˜M 2 and ˜M 3. A parallel reduction is then made after that.
The first process ClientA can now be replicated, by clicking “Replication” at the top of its column.
Three processes are obtained. The first process can make an output by clicking on “Next auto-
step”.
34 CHAPTER 3. USING PROVERIF
The process ServerB is then replicated by clicking on the column representing the third process. A
click on “New nonce” allows the attacker to create his secret key n, which is added to the public
elements of the current state. The message pk(n) can then be input on channel c by clicking on
the same column and giving pk(n) as recipe. The result is shown in Figure 3.8.
A new click on the third process creates a fresh key k 2. Another click sends the message
aenc(sign(spk(skB 2), k 2), skB 2, pk(n)), and the attacker stores this message in a fresh vari-
able ˜M 4.
The message aenc(adec(˜M 4, n),˜M 2) can then be input on channel c, by clicking on the first
process and giving aenc(adec(˜M 4, n),˜M 2) as recipe.
A click on the “All auto-steps” makes all possible reductions on the first process, leading to the
output of the message senc(s , k 2) stored by the attacker in a variable ˜M 5. It leads to the
window represented in Figure 3.9, and to a trace similar to the one represented in Figure 3.5.
Finally, by clicking the button “Add a term to public” and giving the recipe sdec(˜M 5,
2−proj−2−tuple(getmess(adec(˜M 4,n)))), the attacker computes this recipe and obtains the se-
cret s. The secret s is then added to the set of public terms.
Language features
In the previous chapter, the basic features of the language were introduced; we will now provide a more
complete coverage of the language features. These features will be used in Chapter 5 to study the
Needham-Schroeder public key protocol as a case study. More advanced features of the language will be
discussed in Chapter 6 and the complete input grammar is presented in Appendix A for reference; the
features presented in this chapter should be sufficient for most users.
4.1.1 Constants
A constant may be defined as a function of arity 0, for example “fun c() : t.” ProVerif also provides a
specific construct for constants:
const c : t .
where c is the name of the constant and t is its type. Several constants of the same type t can be declared
by
const c1 , . . . , ck : t .
37
38 CHAPTER 4. LANGUAGE FEATURES
Finally, ProVerif has a built-in boolean function is nat checking whether a term is a natural number
of not, that is, is nat (M ) returns true if and only if M is equal modulo the equational theory to a natural
number.
Note that addition between two arbitrary terms is not allowed. The order relations >, <, >=, <= are
internally represented by boolean destructor functions that compare the value of two natural numbers.
As such, M > N returns true (resp. false ) if M and N are both natural numbers and M is strictly
greater than (resp. smaller or equal to) N . Note that M > N fails if M or N is not a natural number.
Similarly, the subtraction is internally represented by a destructor function and for instance, M − i fails
if M is a natural number strictly smaller than i. It corresponds to the fact that negative numbers are
not allowed in ProVerif.
4.1. PRIMITIVES AND MODELING FEATURES 39
Restrictions. Since natural numbers are represented with a constant 0 and a data constructor succes-
sor, the attacker can generate all natural numbers. Therefore, ProVerif does not allow the declaration of
new names with the type nat, i.e., new k:nat, since it would allow a process to generate a term declared
as a natural number but that does not satisfy the Peano axioms. Similarly, user defined constructors
cannot have nat as their return type. However, this restriction does not apply to destructors. Finally,
all functions can have nat as argument type. For example, the following declarations and process are
allowed.
1 type key .
2
3 free c : channel .
4
5 f r e e s : b i t s t r i n g [ private ] .
6
7 fun i e n c ( nat , key ) : b i t s t r i n g .
8 fun i d e c ( b i t s t r i n g , key ) : nat
9 reduc f o r a l l x : nat , y : key ; i d e c ( i e n c ( x+1,y ) , y ) = x .
10
11 query attacker ( s ) .
12
13 process
14 new k : key ; (
15 out ( c , i e n c ( 2 , k ) )
16 | in ( c , x : nat ) ; in ( c , y : b i t s t r i n g ) ; i f x + 3 > i d e c ( y , k ) then out ( c , s )
17 )
The function idec is allowed to have nat as return type as it is declared as a destructor. In this
example, the query is false since the attacker can obtain s by inputting any natural number for x. Note
that the test if x + 3 > idec(y,k) then . . . is not equivalent to if x > idec(y,k) − 3 then . . .. Indeed,
in the latter, ProVerif first evaluates the terms x and idec(y,k) − 3 before comparing their values. In
our example, idec(y,k) − 3 will always fail since the only case where the evaluation of idec(y,k) would
not fail is when y is equal to ienc (2,k). In such a case, idec(y,k) would be evaluated to 1 but then the
evaluation of 1 − 3 would fail. Hence, the query attacker(s) is true for the following process:
1 process
2 new k : key ; (
3 out ( c , i e n c ( 2 , k ) )
4 | in ( c , x : nat ) ; in ( c , y : b i t s t r i n g ) ; i f x > i d e c ( y , k ) − 3 then out ( c , s )
5 )
ProVerif ’s internal encoding for enriched terms. Enriched terms are a convenient tool for the end
user; internally, ProVerif handles such constructs by encoding them: the conditional if M then N else N ′
40 CHAPTER 4. LANGUAGE FEATURES
is encoded as a special destructor also displayed as if M then N else N ′ ; the restriction new a : t; M
is expanded into a process; the term evaluation let T = M in N else N ′ is encoded as a mix of processes
and special destructors. As an example, let us consider the following process.
1 free c : channel .
2
3 f r e e A: b i t s t r i n g .
4 free B: b i t s t r i n g .
5
6 process
7 in ( c , ( x : b i t s t r i n g , y : b i t s t r i n g ) ) ;
8 i f x = A | | x = B then
9 l e t z = ( i f y = A then new n : b i t s t r i n g ; ( x , n ) e l s e ( x , y ) ) in
10 out ( c , z )
The process takes as input a pair of bitstrings x,y and checks that either x=A or x=B. The term
evaluation let z = (if y = A then new n:bitstring; (x,n) else (x,y)) in is defined using the enriched
term if y = A then new n:bitstring; (x,n) else (x,y) which evaluates to the tuple (x,n) where n is a
new name of type bitstring if y=A; or (x,y) otherwise. (Note that brackets have only been added for
readability.) Internally, ProVerif encodes the above main process as:
1 in ( c , ( x : b i t s t r i n g , y : b i t s t r i n g ) ) ;
2 i f ( ( x = A) | | ( x = B) ) then
3 new n : b i t s t r i n g ;
4 l e t z : b i t s t r i n g = ( i f ( y = A) then ( x , n ) e l s e ( x , y ) ) in
5 out ( c , z )
This encoding sometimes has visible consequences on the behavior of ProVerif. Note that this process
was obtained by beautifying the output produced by ProVerif (see Section 3.3 for details on ProVerif
output).
4.1. PRIMITIVES AND MODELING FEATURES 41
4.1.6 Phases
Many protocols can be broken into phases, and their security properties can be formulated in terms of
these phases. Typically, for instance, if a protocol discloses a session key after the conclusion of a session,
then the secrecy of the data exchanged during that session may be compromised but not its authenticity.
To enable modeling of protocols with several phases the syntax for processes is supplemented with a
phase prefix phase t; P, where t is a positive integer. Observe that all processes are under phase 0 by
default and hence the instruction phase 0 is not allowed. Intuitively, t represents a global clock, and the
process phase t; P is active only during phase t. A process with phases is executed as follows. First, all
instructions under phase 0 are executed, that is, all instructions not under phase i ≥ 1. Then, during
a stage transition from phase 0 to phase 1, all processes which have not yet reached phase i ≥ 1 are
discarded and the process may then execute instructions under phase 1, but not under phase i ≥ 2. More
generally, when changing from phase n to phase n + 1, all processes which have not reached a phase
i ≥ n + 1 are discarded and instructions under phase n + 1, but not for phase i ≥ n + 2, are executed.
It follows from our description that it is not necessary for all instructions of a particular phase to be
executed prior to phase transition. Moreover, processes may communicate only if they are under the
same phase.
Phases can be used, for example, to prove forward secrecy properties: the goal is to show that, even
if some participants get corrupted (so their secret keys are leaked to the attacker), the secrets exchanged
in sessions that took place before the corruption are preserved. Corruption can be modeled in ProVerif
by outputting the secret keys of the corrupted participants in phase 1; the secrets of the sessions run in
phase 0 should be preserved. This is done for the fixed handshake protocol of the previous chapter in
the following example (file docs/ex handshake forward secrecy skB.pv):
42 CHAPTER 4. LANGUAGE FEATURES
1 free c : channel .
2
3 f r e e s : b i t s t r i n g [ private ] .
4 query attacker ( s ) .
5
6 l e t c l i e n t A (pkA : pkey , skA : skey , pkB : spkey ) =
7 out ( c , pkA ) ;
8 in ( c , x : b i t s t r i n g ) ;
9 l e t y = adec ( x , skA ) in
10 l e t (=pkA,=pkB , k : key ) = c h e c k s i g n ( y , pkB ) in
11 out ( c , s e n c ( s , k ) ) .
12
13 l e t s e r v e r B ( pkB : spkey , skB : s s k e y , pkA : pkey ) =
14 in ( c , pkX : pkey ) ;
15 new k : key ;
16 out ( c , aenc ( s i g n ( ( pkX , pkB , k ) , skB ) , pkX ) ) ;
17 in ( c , x : b i t s t r i n g ) ;
18 let z = sdec (x , k ) .
19
20 process
21 new skA : s k e y ;
22 new skB : s s k e y ;
23 l e t pkA = pk ( skA ) in out ( c , pkA ) ;
24 l e t pkB = spk ( skB ) in out ( c , pkB ) ;
25 ( ( ! c l i e n t A (pkA , skA , pkB ) ) | ( ! s e r v e r B ( pkB , skB , pkA ) ) |
26 phase 1 ; out ( c , skB ) )
The secret key skB of the server B is leaked in phase 1 (last line). The secrecy of s is still preserved in
this example: the attacker can impersonate B in phase 1, but cannot decrypt messages of sessions run
in phase 0. (Note that one could hope for a stronger model: this model does not consider sessions that
are running precisely when the key is leaked. While the attacker can simulate B in phase 1, the model
above does not run A in phase 1; one could easily add a model of A in phase 1 if desired.) In contrast, if
the secret key of the client A is leaked, then the secrecy of s is not preserved: the attacker can decrypt
the messages of previous sessions by using skA, and thus obtain s.
4.1.7 Synchronization
The synchronization command sync t [tag] introduces a global synchronization [BS16], which has some
similarity with phases.
The synchronization level t must be a positive integer. Synchronizations sync t cannot occur under
replications. Synchronizations with the same level t and the same tag tag are considered as the “same
synchronization”, that is, synchronizations with the same level t and the same tag tag are allowed only
in different branches of if , let, let . . . suchthat, get. Since only one of these branches will be executed
at runtime, at most one synchronization with a given level t and tag tag can be reached.
The global synchronizations must be executed in increasing order of level t. The process waits until
sync t commands with all existing tags at level t are reached before executing the synchronization t.
More precisely, assuming t is the smallest synchronization level that occurs in the initial process and has
not been executed yet, if the initial process contains commands sync t with tags tag 1 , . . . , tag n , then
the process waits until it reaches exactly commands sync t with tags tag 1 , . . . , tag n , then it executes
the synchronization t and continues after the sync t commands. So, in contrast to phases, processes are
never discarded by synchronization, but the process may block in case some synchronizations cannot be
reached or are discarded for instance by a test that fails above them.
The tags of synchronizations are determined as follows:
The user can specify the tag of the synchronization by writing sync t [tag]. When the user omits
the tag and just writes sync t, ProVerif gives it a fresh tag.
4.2. FURTHER CRYPTOGRAPHIC OPERATORS 43
When a synchronization occurs inside a process macro and the process macro is expanded, a tag
prefix is added to all synchronizations inside the process macro. The prefix p is specified by writing
[sync: tag prefix p] at the expansion of the process macro. For instance:
l e t P( x : b i t s t r i n g )=
sync 1 [ T ] ;
out ( c , x ) .
process
P( a ) [ sync : t a g p r e f i x T1 ] | P( b ) [ sync : t a g p r e f i x T2 ]
(The prefix is separated from the tag by an underscore.) When the indication [sync: tag prefix p]
is omitted, ProVerif chooses a fresh prefix. One can tell ProVerif not to add a prefix, that
is, leave the tags of synchronizations unchanged, by writing [sync: no tag prefix ] instead of
[sync: tag prefix p].
Therefore, when all tags of synchronizations and tag prefixes of process macros are omitted, all synchro-
nizations in the resulting process have distinct tags. This is suitable when these synchronizations occur
in parallel processes.
When synchronizations occur in branches of tests, one typically wants them to have the same tag
(because otherwise the synchronization would block). So one would write for instance
i f . . . then ( . . . sync 1 [ T ] ; . . . ) e l s e ( . . . sync 1 [ T ] ; . . . )
or
i f . . . then ( . . . P( . . . ) [ sync : t a g p r e f i x T ] )
e l s e ( . . . P( . . . ) [ sync : t a g p r e f i x T ] )
Synchronizations cannot be used with phases. Synchronizations are implemented in ProVerif by
translating them into outputs and inputs; the translated process is displayed by ProVerif. Further
discussion of synchronization with an example can be found in Section 4.3.2, page 62.
1 fun eq ( b i t s t r i n g , b i t s t r i n g ) : b o o l
2 reduc f o r a l l x : b i t s t r i n g ; eq ( x , x ) = t r u e
3 otherwise f o r a l l x : b i t s t r i n g , y : b i t s t r i n g ; eq ( x , y ) = f a l s e .
With this definition, eq(M, N ) can be reduced to false only if M and N are different modulo the equational
theory.
As previously mentioned, when no rule can be applied, the destructor fails. However, this formalism
does not allow a destructor to succeed when one of its arguments fails. To lift this restriction, we allow
to represent the case of failure by the special value fail .
8 fun t e s t ( bool , b i t s t r i n g , b i t s t r i n g ) : b i t s t r i n g
9 reduc
10 f o r a l l x : b i t s t r i n g , y : b i t s t r i n g ; t e s t ( true , x , y ) = x
11 otherwise f o r a l l c : bool , x : b i t s t r i n g , y : b i t s t r i n g ; t e s t ( c , x , y ) = y
12 otherwise f o r a l l x : b i t s t r i n g , y : b i t s t r i n g ; t e s t ( f a i l , x , y ) = y .
In the previous example, the function test returns the third argument even when the first argument fails.
A variable x of type t can be declared as a possible failure by the syntax: x:t or fail . It indicates that
x can be any message or even the special value fail . Relying on this new declaration of variables, the
destructor test could have been defined as follows:
14 fun t e s t ( bool , b i t s t r i n g , b i t s t r i n g ) : b i t s t r i n g
15 reduc
16 f o r a l l x : b i t s t r i n g , y : b i t s t r i n g ; t e s t ( true , x , y ) = x
17 otherwise f o r a l l c : b o o l or f a i l , x : b i t s t r i n g , y : b i t s t r i n g ;
18 test (c , x , y) = y .
A variant of this test destructor is the following one:
20 fun t e s t ' ( bool , b i t s t r i n g , b i t s t r i n g ) : b i t s t r i n g
21 reduc
22 f o r a l l x : b i t s t r i n g or f a i l , y : b i t s t r i n g or f a i l ; t e s t ' ( t r u e , x , y ) = x
23 otherwise f o r a l l c : bool , x : b i t s t r i n g or f a i l , y : b i t s t r i n g or f a i l ;
24 test ' ( c , x , y) = y .
This destructor returns its second argument when the first argument c is true, its third argument when
the first argument c does not fail but is not true, and fails otherwise. With this definition, when the
first argument is true, test ' returns the second argument even when the third argument fails (which
models that the third argument does not need to be evaluated in this case). Symmetrically, when the
first argument does not fail but is not true, test ' returns the third argument even when the second
argument fails. In contrast, the previous destructor test fails when its second or third arguments fail.
It is also possible to transform the special failure value fail into a non-failure value c0 by a destructor:
27 const c0 : b i t s t r i n g .
28 fun c a t c h f a i l ( b i t s t r i n g ) : b i t s t r i n g
29 reduc
30 forall x : bitstring ; catchfail (x) = x
31 otherwise c a t c h f a i l ( f a i l ) = c0 .
Such a destructor is used internally by ProVerif.
Let bindings. Similarly to the simple way of defining destructors (see Section 3.1.1), it is possible to
use let bindings within the declaration of each rewrite rule.
4.2.2 Equations
Certain cryptographic primitives, such as the Diffie-Hellman key agreement, cannot be encoded as de-
structors, because they require algebraic relations between terms. Accordingly, ProVerif provides an
alternative model for cryptographic primitives, namely equations. The relationships between construc-
tors are captured using equations of the form
4.2. FURTHER CRYPTOGRAPHIC OPERATORS 45
equation f o r a l l x1 : t1 , . . . , xn : tn ; M = N .
where M , N are terms built from the application of (defined) constructor symbols to the variables
x1 , . . . , xn of type t1 , . . . , tn . Note that when no variables are required (that is, when terms M, N are
constants) forall x1 : t1 , . . . , xn : tn ; may be omitted.
More generally, one can declare several equations at once, as follows:
equation f o r a l l x1,1 : t1,1 , . . . , x1,n1 : t1,n1 ; M1 = N1 ;
...
f o r a l l xm,1 : tm,1 , . . . , xm,nm : tm,nm ; Mm = Nm option .
where option can either be empty, [convergent], or [ linear ]. When an option [convergent] or [ linear ]
is present, it means that the group of equations is convergent (the equations, oriented from left to right,
form a convergent rewrite system) or linear (each variable occurs at most once in the left-hand and
once in the right-hand side of each equation), respectively. In this case, this group of equations must
use function symbols that appear in no other equation. ProVerif checks that the convergent or linear
option is correct. However, in case ProVerif cannot prove termination of the rewrite system associated
to equations declared [convergent], it just displays a warning, and continues assuming that the rewrite
system terminates. Indeed, ProVerif’s algorithm for proving termination is obviously not complete,
so the rewrite system may terminate and ProVerif not be able to prove it. The main interest of the
[convergent] option is then to bypass the verification of termination of the rewrite system.
Let bindings. Similarly to destructors, it is possible to use let bindings within the declaration of each
equation.
Performance. It should be noted that destructors are more efficient than equations. The use of
destructors is therefore advocated where possible.
Limitations. ProVerif does not support all equations. It must be possible to split the set of equations
into two kinds of equations that do not share constructor symbols: convergent equations and linear
equations. Convergent equations are equations that, when oriented from left to right, form a convergent
(that is, terminating and confluent) rewriting system. Linear equations are equations such that each
variable occurs at most once in the left-hand side and at most once in the right-hand side. When
ProVerif cannot split the equations into convergent equations and linear equations, an error message is
displayed.
Moreover, even when the equations can be split as above, it may happen that the pre-treatment of
equations by ProVerif does not terminate. Essentially, ProVerif computes rewrite rules that encode the
equations and it requires that, when M1 , . . . , Mn are in normal form, the normal form of f (M1 , . . . , Mn )
can be computed by a single rewrite step. For some equations, this constraint implies generating an
infinite number of rewrite rules, so in this case ProVerif does not terminate. For instance, associativity
cannot be handled by ProVerif for this reason, which prevents the modeling of primitives such as XOR
(exclusive or) or groups. Another example that leads to non-termination for the same reason is the
equation f (g(x)) = g(f (x)). In the obtained rewrite rules, all variables that occur in the right-hand side
must also occur in the left-hand side.
It is also worth noting that, because ProVerif orients equations from left to right when it builds the
rewrite system, the orientation in which the equations are written may influence the success or failure of
ProVerif (even if the semantics of the equation obviously does not depend on the orientation). Informally,
the equations should be written with the most complex term on the left and the simplest one on the
right.
Even with these limitations, many practical primitives can be modeled by equations in ProVerif, as
illustrated below.
Diffie-Hellman key agreement. The Diffie-Hellman key agreement relies on modular exponentiation
in a cyclic group G of prime order q; let g be a generator of G. A principal A chooses a random exponent
a in Z∗q , and sends g a to B. Similarly, B chooses a random exponent b, and sends g b to A. Then A
46 CHAPTER 4. LANGUAGE FEATURES
computes (g b )a and B computes (g a )b . These two keys are equal, since (g b )a = (g a )b , and cannot be
obtained by a passive attacker who has g a and g b but neither a nor b.
We model the Diffie-Hellman key agreement as follows:
1 type G.
2 type exponent .
3
4 const g : G [ data ] .
5 fun exp (G, exponent ) : G.
6
7 equation f o r a l l x : exponent , y : exponent ; exp ( exp ( g , x ) , y ) = exp ( exp ( g , y ) , x ) .
The elements of G have type G, the exponents have type exponent, g is the generator g, and exp models
modular exponentiation exp(x,y) = xy . The equation means that (g x )y = (g y )x .
This model of Diffie-Hellman key agreement is limited in that it just takes into account the equation
needed for the protocol to work, while there exist other equations, coming from the multiplicative group
Z∗q . A more complete model is out of scope of the current treatment of equations in ProVerif, because it
requires an associative function symbol, but extensions have been proposed to handle it [KT09].
Symmetric encryption. We model a symmetric encryption scheme for which one cannot distinguish
whether decryption succeeds or not. We consider the binary constructors senc and sdec, the arguments
of which are of types bitstring and key.
1 type key .
2
3 fun s e n c ( b i t s t r i n g , key ) : b i t s t r i n g .
4 fun s d e c ( b i t s t r i n g , key ) : b i t s t r i n g .
To model the properties of decryption, we introduce the equations:
5 equation f o r a l l m: b i t s t r i n g , k : key ; s d e c ( s e n c (m, k ) , k ) = m.
6 equation f o r a l l m: b i t s t r i n g , k : key ; s e n c ( s d e c (m, k ) , k ) = m.
where k represents the symmetric key and m represents the message. The first equation is standard: it
expresses that, by decrypting the ciphertext with the correct key, one gets the cleartext. The second
equation might seem more surprising. It implies that encryption and decryption are two inverse bijections;
it is satisfied by block ciphers, for instance. One can also note that this equation is necessary to make
sure that one cannot distinguish whether decryption succeeds or not: without this equation, sdec(M,k)
succeeds if and only if senc(sdec(M,k),k) = M.
Trapdoor commitments. As a more involved example, let us consider trapdoor commitments [DDKS17].
Trapdoor commitments are commitments that can be opened to a different value than the one initially
committed, using a trapdoor. We represent a trapdoor commitment of message m with randomness r
and trapdoor td by tdcommit(m, r, td ). The normal opening of the commitment returns the message m,
so we have the equation
open(tdcommit(m, r, td ), r) = m
To change the message, we use the equation:
These equations, oriented from left to right, are not convergent. We need to complete them to obtain a
convergent system, with the following equations:
open(tdcommit(m1 , r, td ), f (m1 , r, td , m2 )) = m2
f (m1 , f (m, r, td , m1 ), td , m2 ) = f (m, r, td , m2 )
These equations are convergent, but ProVerif is unable to show termination, so it fails to handle the
equations if they are given separately. We can bypass the termination check by giving the equations
together and indicating that they are convergent, as follows:
4.2. FURTHER CRYPTOGRAPHIC OPERATORS 47
type t r a p d o o r .
type rand .
equation f o r a l l m: b i t s t r i n g , r : rand , td : t r a p d o o r ;
open ( tdcommit (m, r , td ) , r ) = m;
f o r a l l m1 : b i t s t r i n g , m2 : b i t s t r i n g , r : rand , td : t r a p d o o r ;
tdcommit (m2, f (m1, r , td , m2) , td ) = tdcommit (m1, r , td ) ;
f o r a l l m1 : b i t s t r i n g , m2 : b i t s t r i n g , r : rand , td : t r a p d o o r ;
open ( tdcommit (m1, r , td ) , f (m1, r , td , m2) ) = m2 ;
f o r a l l m: b i t s t r i n g , m1 : b i t s t r i n g , m2 : b i t s t r i n g , r : rand , td : t r a p d o o r ;
f (m1, f (m, r , td , m1) , td , m2) = f (m, r , td , m2) [ c o n v e r g e n t ] .
ProVerif still displays a warning because it cannot prove that the equations terminate:
Warning : t h e f o l l o w i n g e q u a t i o n s
open ( tdcommit (m, r , td ) , r ) = m
tdcommit (m2, f (m1, r 7 , td 8 , m2) , t d 8 ) = tdcommit (m1, r 7 , t d 8 )
open ( tdcommit ( m1 9 , r 1 1 , t d 1 2 ) , f ( m1 9 , r 1 1 , td 12 , m2 10 ) ) = m2 10
f ( m1 14 , f ( m 13 , r 1 6 , td 17 , m1 14 ) , td 17 , m2 15 ) = f ( m 13 , r 1 6 , td 17 , m2 15 )
a r e d e c l a r e d c o n v e r g e n t . I c o u l d not prove t e r m i n a t i o n .
I assume t h a t they r e a l l y t e r m i n a t e .
Expect problems ( such a s P r o V e r i f g o i n g i n t o a l o o p ) i f they do not !
but it accepts the equations.
letfun f ( x : t ) =
l e t y = x in c0 e l s e c1 .
h() is fail and f (h()) returns fail and f never returns c1.
If or fail is present and the argument fails, the failure value is passed to the function macro, which
may for instance catch it and return some non-failure result. For instance, with the same definition
of h as above and the following definition of f
letfun f ( x : t or f a i l ) =
l e t y = x in c0 e l s e c1 .
Function macros can be used as constructors/destructors h in terms (see Figure 4.2). The applicability
of function macros will be demonstrated by the following example.
Probabilistic asymmetric encryption. Recall that asymmetric cryptography makes use of the
unary constructor pk, which takes an argument of type skey (private key) and returns a pkey (public
key). Since the constructors of ProVerif always represent deterministic functions, we model probabilistic
encryption by considering a constructor that takes the random coins used inside the encryption algorithm
as an additional argument, so probabilistic asymmetric encryption is modeled by a ternary constructor
internal aenc, which takes as arguments a message of type bitstring, a public key of type pkey, and ran-
dom coins of type coins. When encryption is used properly, the random coins must be freshly chosen at
each encryption, so that the encryption of x under y is modeled by new r: coins; internal aenc (x,y,r).
In order to avoid writing this code at each encryption, we can define a function macro aenc, which
expands to this code, as shown below. Decryption is defined in the usual way.
type s k e y .
type pkey .
type c o i n s .
fun pk ( s k e y ) : pkey .
fun i n t e r n a l a e n c ( b i t s t r i n g , pkey , c o i n s ) : b i t s t r i n g .
reduc f o r a l l m: b i t s t r i n g , k : skey , r : c o i n s ;
adec ( i n t e r n a l a e n c (m, pk ( k ) , r ) , k ) = m.
Observe that the use of probabilistic cryptography increases the complexity of the model due to the
additional names introduced. This may slow down the analysis process.
l e t p(x1 : t1 [ or f a i l ] , . . . , xj : tj [ or f a i l ] ) = P .
The optional or fail after the type of each argument allows the user to control the behavior of the
process in case some of its arguments fail:
If or fail is absent and the argument fails, the process blocks. For instance, with the definitions
fun h ( ) : t
reduc h ( ) = f a i l .
let p(x : t ) =
l e t y = x in out ( c , c0 ) e l s e out ( c , c1 ) .
If or fail is present and the argument fails, the failure value is passed to the process, which may
for instance catch it and continue to run. For instance, with the same definition of h as above and
the following definition of p
l e t p ( x : t or f a i l ) =
l e t y = x in out ( c , c0 ) e l s e out ( c , c1 ) .
Hash functions. A hash function is represented as a unary constructor h with no associated destructor
or equations. The constructor takes as input, and returns, a bitstring. Accordingly, we define:
fun h ( b i t s t r i n g ) : b i t s t r i n g .
The absence of any associated destructor or equational theory captures pre-image resistance, second pre-
image resistance and collision resistance properties of cryptographic hash functions. In fact, far stronger
properties are ensured: this model of hash functions is close to the random oracle model.
Symmetric encryption. The most basic formalization of symmetric encryption is the one based on
decryption as a destructor, given in Section 3.1.2. However, formalizations that are closer to practical
cryptographic schemes are as follows:
1. For block ciphers, which are deterministic, bijective encryption schemes, a better formalization is
the one based on equations and given in Section 4.2.2.
2. Other symmetric encryption schemes are probabilistic. This can be formalized in a way similar to
what was presented for probabilistic public-key encryption in Section 4.2.3.
type key .
type c o i n s .
fun i n t e r n a l s e n c ( b i t s t r i n g , key , c o i n s ) : b i t s t r i n g .
reduc f o r a l l m: b i t s t r i n g , k : key , r : c o i n s ;
s d e c ( i n t e r n a l s e n c (m, k , r ) , k ) = m.
As shown in [CHW06], for protocols that do not test equality of ciphertexts, for secrecy and authen-
tication, one can use the simpler, deterministic model of Section 3.1.2. However, for observational
equivalence properties, or for protocols that test equality of ciphertexts, using the probabilistic
model does make a difference.
Note that these encryption schemes generally leak the length of the cleartext. (The length of
the ciphertext depends on the length of the cleartext.) This is not taken into account in this
formalization, and generally difficult to take into account in formal protocol provers, because it
requires arithmetic manipulations. For some protocols, one can argue that this is not a problem,
for example when the length of the messages is fixed in the protocol, so it is a priori known to the
attacker. Block ciphers are not concerned by this comment since they encrypt data of fixed length.
Also note that, in this formalization, encryption is authenticated. In this respect, this formal-
ization is close to IND-CPA and INT-CTXT symmetric encryption. So it does not make sense
to add a MAC (message authentication code) to such an encryption, as one often does to obtain
authenticated encryption from unauthenticated encryption: the MAC is already included in the
encryption here. If desired, it is sometimes possible to model malleability properties of some en-
cryption schemes, by adding the appropriate equations. However, it is difficult to model general
unauthenticated encryption (IND-CPA encryption) in formal protocol provers.
In this formalization, encryption hides the encryption key. If one wants to model an encryption
scheme that does not conceal the key, one can add the following destructor [ABCL09]:
50 CHAPTER 4. LANGUAGE FEATURES
This destructor allows the attacker to test whether two ciphertexts have been built with the same
key. The presence of such a destructor makes no difference for reachability properties (secrecy,
correspondences) since it does not enable the attacker to construct terms that it could not construct
otherwise. However, it does make a difference for observational equivalence properties. (Note that
it would obviously be a serious mistake to give out the encryption key to the attacker, in order to
model a scheme that does not conceal the key.)
Asymmetric encryption. A basic, deterministic model of asymmetric encryption has been given in
Section 3.1.2. However, cryptographically secure asymmetric encryption schemes must be probabilistic.
So a better model for asymmetric encryption is the probabilistic one given in Section 4.2.3. As shown
in [CHW06], for protocols that do not test equality of ciphertexts, for secrecy and authentication, one can
use the simpler, deterministic model of Section 3.1.2. However, for observational equivalence properties,
or for protocols that test equality of ciphertexts, using the probabilistic model does make a difference.
It is also possible to model that the encryption leaks the key. Since the encryption key is public, we
can do this simply by giving the key to the attacker:
reduc f o r a l l m: b i t s t r i n g , pk : pkey , r : c o i n s ; g e t k e y ( i n t e r n a l a e n c (m, pk , r ) ) = pk .
The previous models consider a unary constructor pk that computes the public key from the secret key.
An alternative (and equivalent) formalism for asymmetric encryption considers the unary constructors
pk', sk ' which take arguments of type seed ' , to capture the notion of constructing a key pair from some
seed.
type seed ' .
type pkey ' .
type skey ' .
Digital signatures. The Handbook of Applied Cryptography defines four different classes of digital
signature schemes [MvOV96, Figure 11.1], we explain how to model these four classes. Deterministic
signatures with message recovery were already modeled in Section 3.1.2. Probabilistic signatures with
message recovery can be modeled as follows, using the same ideas as for asymmetric encryption:
type s s k e y .
type spkey .
type s c o i n s .
type s s k e y ' .
type spkey ' .
For such signatures, the message must be given when verifying the signature, and signature verification
just returns true when it succeeds. Note that these signatures hide the message as if it were encrypted;
this is often a stronger property than desired. If one wants to model that these signatures do not hide
the message, then one can reintroduce a destructor that leaks the message:
reduc f o r a l l m: b i t s t r i n g , k : s s k e y ' ; getmess ' ( s i g n ' ( m, k ) ) = m.
Only the adversary should use this destructor; it may be an overapproximation of the capabilities of the
adversary, since the message may not be fully recoverable from the signature. Probabilistic signatures
with appendix can also be modeled by combining the models given above.
It is also possible to model that the signature leaks the key. Obviously, we must not leak the secret
key, but we can leak the corresponding public key using the following destructor:
reduc f o r a l l m: b i t s t r i n g , k : s s k e y , r : s c o i n s ;
g e t k e y ( i n t e r n a l s i g n (m, k , r ) ) = spk ( k ) .
This model is for probabilistic signatures; it can be straightforwardly adapted to deterministic signatures.
Finally, as for asymmetric encryption, we can also consider unary constructors pk', sk ' which take
arguments of type seed ' , to capture the notion of constructing a key pair from some seed. We leave the
construction of these models to the reader.
Message authentication codes. Message authentication codes (MACs) can be formalized by a con-
structor with no associated destructor or equation, much like a keyed hash function:
type mkey .
This model is strong: it considers the MAC essentially as a random oracle. It is probably the best
possible model if the MAC is assumed to be a pseudo-random function (PRF). If the MAC is assumed
to be unforgeable (UF-CMA), then one can add a destructor that leaks the MACed message:
reduc f o r a l l m: b i t s t r i n g , k : mkey ; g e t m e s s a g e ( mac (m, k ) ) = m.
Only the adversary should use this destructor; it may be an overapproximation of the capabilities of the
adversary, since the message may not be fully recoverable from the MAC. We also remind the reader
that using MACs in conjunction with symmetric encryption is generally useless in ProVerif since the
basic encryption is already authenticated.
Other primitives. A simple model of Diffie-Hellman key agreements is given in Section 4.2.2, bit-
commitment and blind signatures are formalized in [KR05, DKR09], trapdoor commitments are formal-
ized in Section 4.2.2, and non-interactive zero-knowledge proofs are formalized in [BMU08]. Since defining
correct models for cryptographic primitives is difficult, we recommend reusing existing definitions, such
as the ones given in this manual.
ProVerif is sound, but not complete. ProVerif’s ability to reason with reachability, correspon-
dences, and observational equivalence is sound (sometimes called correct); that is, when ProVerif says
that a property is satisfied, then the model really does guarantee that property. However, ProVerif
is not complete; that is, ProVerif may not be capable of proving a property that holds. Sources of
incompleteness are detailed in Section 6.7.5.
Reachability
This corresponds to the case in which the query q is just a fact F . Such a query is in fact an abbreviation
for F ==> false, that is, not F . In other words, ProVerif tests whether F holds, but returns the following
results:
“RESULT not F is false.” when there exists a trace in which F holds, and ProVerif displays such
a trace.
“RESULT not F cannot be proved.” when ProVerif cannot decide either way.
For instance, we have seen query attacker(M ) before: this query tests the secrecy of the term M and
ProVerif returns “RESULT not attacker(M ) is true.” when M is secret, that is, the attacker cannot
reconstruct M . When phases (see Section 4.1.6) are used, this query returns “RESULT not attacker(M )
is true.” when M is secret in all phases, or equivalently in the last phase. When M contains variables,
they must be declared with their type at the beginning of the query, and ProVerif returns “RESULT
not attacker(M ) is true.” when all instances of M are secret.
We can test secrecy in a specific phase n by query attacker(M ) phase n. which returns “RESULT
not attacker(M ) phase n is true.” when M is secret in phase n, that is, the attacker cannot reconstruct
M in phase n.
We can also test whether the protocol sends a term M on a channel N (during the last phase if
phases are used) by query mess(N, M ). This query returns “RESULT not mess(N ,M ) is true.” when
the message M is never sent on channel N . We can also specify which phase should be considered by
query mess(N, M ) phase n. This query is intended for use when the channel N is private (the attacker
does not have it). When the attacker has the channel N , this query is equivalent to query attacker(M ).
Similarly, we can test whether the element (M1 , . . . , Mn ) is present in table d by query table(d(M1 ,
. . . , Mn )).
ProVerif can also evaluate the reachability of events within a model using the following query:
4.3. FURTHER SECURITY PROPERTIES 53
H ::= hypothesis
F fact
H && H conjunction
H || H disjunction
false constant false
(F ==> H) nested correspondence
F ::= fact
M op N constraint with op ∈ {<, <=, >, >=, =; <>}
is nat (M ) natural number
AF action fact
AF @t action fact executed at time t
Basic correspondences
Basic correspondences are queries q = F1 && . . . && Fn ==> H where H does not contain nested
correspondences. They mean that, if F1 , . . . , Fn hold, then H also holds. We have seen such corre-
spondences in Section 3.2.2. We can extend them to conjunctions and disjunctions of events in H. For
54 CHAPTER 4. LANGUAGE FEATURES
instance,
query event ( e0 ) ==> event ( e1 ) && event ( e2 ) .
means that, if e0 has been executed, then e1 and e2 have been executed. Similarly,
query event ( e0 ) ==> event ( e1 ) | | event ( e2 ) .
means that, if e0 has been executed, then e1 or e2 has been executed. If the correspondence F ==> H
holds, F is an event, and H contains events, then the events in H must be executed before the event F
(or at the same time as F in case an event in H may be equal to F ). This property is proved by stopping
the execution of the process just after the event F .
Conjunctions and disjunctions can be combined:
query event ( e0 ) ==> event ( e1 ) | | ( event ( e2 ) && event ( e3 ) ) .
means that, if e0 has been executed, then either e1 has been executed, or e2 and e3 have been executed.
The conjunction has higher priority than the disjunction, but one should use parentheses to disambiguate
the expressions. The events can of course have arguments, and can also be injective events. For instance,
query inj −event ( e0 ) ==> event ( e1 ) | | ( inj −event ( e2 ) && event ( e3 ) ) .
means that each execution of e0 corresponds to either an execution of e1 (perhaps the same execution of
e1 for different executions of e0 ), or to a distinct execution of e2 and an execution of e3 . Note that using
inj−event or event before the arrow ==> does not change anything, since event is automatically
changed into inj−event before ==> when there is inj−event after the arrow ==>.
Conjunctions are also allowed before the arrow ==>. For instance,
event ( e1 ( M1 ) ) && . . . && event ( en ( Mn ) ) ==> H
means that, if events e1 (M1 ), . . . , en (Mn ) are executed, then H holds. When there are several injective
events before the arrow ==>, the query means that for each tuple of executed injective events before
the arrow, there are distinct injective events after the arrow. For instance, the query
inj −event ( e1 ) && inj −event ( e2 ) ==> inj −event ( e3 )
requires that if event e1 is executed n1 times and event e2 is executed n2 times, then event e3 is executed
at least n1 × n2 times.
Correspondences may also involve the knowledge of the attacker or the messages sent on channels.
For instance,
query attacker ( M ) ==> event ( e1 ) .
means that, when the attacker knows M , the event e1 has been executed. Conversely,
query event ( e1 ) ==> attacker ( M ) .
means that, when event e1 has been executed, the attacker knows M . (In practice, ProVerif may have
more difficulties proving the latter correspondence. Technically, ProVerif needs to conclude attacker(M )
from facts that occur in the hypothesis of a clause that concludes event(e1 ); this hypothesis may get
simplified during the resolution process in a way that makes the desired facts disappear.)
One may also use equalities, disequalities, and inequalities after the arrow ==>. For instance,
assuming a free name a,
query x : t ; event ( e ( x ) ) ==> x = a .
means that the event e(x) can be executed only when x is a. Similarly,
query x : t , y : t ' ; event ( e ( x ) ) ==> event ( e ' ( y ) ) && x = f ( y )
means that, when the event e(x) is executed, the event event(e'(y)) has been executed and x = f(y).
Using disequalities,
query x : t ; event ( e ( x ) ) ==> x <> a .
means that the event e(x) can be executed only when x is different from a.
4.3. FURTHER SECURITY PROPERTIES 55
Nested correspondences
The grammar permits the construction of nested correspondences, that is, correspondences F1 &&
. . . && Fn ==> H in which some of the events H are replaced with correspondences. Such cor-
respondences allow us to order events. More precisely, in order to explain a nested correspondence
F1 && . . . && Fn ==> H, let us define a hypothesis Hs by replacing all arrows ==> of H with con-
junctions &&. The nested correspondence F1 && . . . && Fn ==> H holds if and only if the basic
correspondence F1 && . . . && Fn ==> Hs holds and additionally, for each F ′ ==> H ′ that occurs in
F1 && . . . && Fn ==> H, if F ′ is an event, then the events of H ′ have been executed before F ′ (or at
the same time as F ′ in case events in H ′ may be equal to F ′ ).1 For example,
event ( e0 ) ==> ( event ( e1 ) ==> ( event ( e2 ) ==> event ( e3 ) ) )
is true when, if the event e0 has been executed, then events e3 , e2 , e1 have been previously executed in
that order and before e0 . In contrast, the correspondence
event ( e0 ) ==> ( event ( e1 ) ==> event ( e2 ) ) && ( event ( e3 ) ==> event ( e4 ) )
holds when, if the event e0 has been executed, then e2 has been executed before e1 and e4 before e3 , and
those occurrences of e1 and e3 have been executed before e0 .
Even if the grammar of correspondences does not explicitly require that facts F that occur before
arrows in nested correspondences are events (or injective events), in practice they are because the only
goal of nested correspondences is to order such events.
Our study of the JFK protocol, which can be found in the subdirectory examples/pitype/jfk (if you
installed by OPAM in the switch ⟨switch⟩, the directory ~/.opam/⟨switch⟩/doc/proverif/examples/
pitype/jfk), provides several interesting examples of nested correspondence assertions used to prove
the correct ordering of messages of the protocol.
ProVerif proves nested correspondences essentially by proving several correspondences. For instance,
in order to prove
event ( e0 ) ==> ( event ( e1 ) ==> event ( e2 ) )
where the events e0 , e1 , e2 may have arguments, ProVerif proves that each execution of e0 is preceded
by the execution of an instance of e1 , and that, when e0 is executed, each execution of that instance of
e1 is preceded by the execution of an instance of e2 .
A typical usage of nested correspondences is to order all messages in a protocol. One would like to
prove a correspondence in the style:
inj −event ( eend ) ==>
( inj −event ( en ) ==> . . . ==> ( inj −event ( e1 ) ==> inj −event ( e0 ) ) )
where e0 means that the first message of the protocol has been sent, ei (i > 0) means that the i-th
message of the protocol has been received and the (i + 1)-th has been sent, and finally eend means that
the last message of the protocol has been received. (These events have at least as arguments the messages
of the protocol.)
Temporal correspondences
Correspondences and nested correspondences allow one to verify the order in which facts occur in exe-
cution traces. The grammar also permits to reason on the order of facts through time variables. In a
query, each fact F can be associated with a variable t of type time with the construct F @t, meaning
that the fact F is executed at time t. When several facts are associated with time variables t, t′ , . . ., we
can reason on the order in which these facts are executed using equalities, inequalities, and disequalities
1 Although the meaning of a basic correspondence such as event(e ) ==> event(e ) is similar to a logical implication,
0 1
the meaning of a nested correspondence such as event(e0 ) ==> (event(e1 ) ==> event(e2 )) is very different from the log-
ical formula event(e0 )⇒(event(e1 )⇒event(e2 )) in classical logic, which would mean (event(e0 )∧event(e1 ))⇒event(e2 ).
The nested correspondence event(e0 ) ==> (event(e1 ) ==> event(e2 )) rather means that, if e0 is executed, then some
instance of e1 is executed (before e0 ), and if that instance of e1 is executed, then an instance of e2 is executed (before e1 ).
So the nested correspondence is similar to an abbreviation for the two correspondences event(e0 ) ==> event(σe1 ) and
event(σe1 ) ==> event(σe2 ) for some substitution σ.
56 CHAPTER 4. LANGUAGE FEATURES
between the time variables, e.g. t < t′ , t = t′ , . . . For example, in our study of the Yubikey proto-
col, which can be found in the file examples/pitype/lemma/yubikey-less-axioms-time.pv (if you
installed by OPAM in the switch ⟨switch⟩, the file ~/.opam/⟨switch⟩/doc/proverif/examples/pitype/
lemma/yubikey-less-axioms-time.pv), a server executes the event Login(pid,k, i ,x) every time it ac-
cepts a connection from a Yubikey with identity pid and key k. The value i is the value of the server’s
counter and x is the value of the Yubikey’s counter sent to the server. The following query ensures that
the server never executes two login events at different times with the same value for the identity, the key,
and Yubikey’s counter.
query t : time , t ' : time , p i d : b i t s t r i n g , k : b i t s t r i n g , i : nat , i ' : nat ,
x : nat , x ' : nat ;
event ( Login ( pid , k , i , x ) ) @t && event ( Login ( pid , k , i ' , x ) ) @t ' ==> t = t ' .
Formally, the query is true when, if two Login events are executed with the same key, identity, and
Yubikey’s counter value, then the two events are executed at the same time. Since the semantics of
ProVerif’s calculus only allows events to be executed one at a time, it also implies that the two events
are equal, i.e., i = i′ .
Using temporal correspondences allows one to be more precise than basic correspondences. For
example, the following query is not equivalent to the previous one.
query p i d : b i t s t r i n g , k : b i t s t r i n g , i : nat , i ' : nat , x : nat , x ' : nat ;
event ( Login ( pid , k , i , x ) ) && event ( Login ( pid , k , i ' , x ) ) ==> i = i ' .
Indeed, an execution trace where the Login event is executed twice with the same arguments (at different
times) would satisfy this query but not the former one.
Temporal variables can be used to compare facts from both the premise and the conclusion of the
query. For example,
event ( e0 ) && event ( e1 )@t1 ==> event ( e2 )@t2 && t2 < t1
is true when if two events e0 and e1 are executed then the event e2 must have been executed strictly
before the event e1 .
Note that temporal variables can be used in combination with injective events and nested correspon-
dences, although they overlap in some cases. For example, the query
event ( e0 ) ==> ( event ( e1 ) ==> event ( e2 ) )
is equivalent to the query
event ( e0 ) ==> event ( e1 )@t1 && event ( e2 )@t2 && t2 <= t1
The grammar of correspondences also allows attacker, message, and table facts to be associated with
time variables. However, when an inequality i > j or i >= j occurs in the conclusion of a query, with
i and j time variables associated to facts F and G respectively, the following two conditions must hold:
1) F @i occurs in the premise or F is an event; 2) G@j occurs in the conclusion or G is an event. More
generally, in practice, ProVerif is more successful in proving correspondence queries containing mainly
events. Note that the type time can only be used in queries and cannot be used in declarations of
processes, function symbols, names, . . .
Secrecy
The query query secret x provides an alternative way to test secrecy to query attacker(M ). The
latter query is meant to test whether the attacker can compute the term M , built from free names.
The query query secret x can test the secrecy of the bound name or variable x. The identifier x must
correspond to a bound variable or name inside the considered process. (Variables or names bound inside
enriched terms are not allowed because the expansion of terms may modify the conditions under which
they are defined.) This query comes in two flavors:
query secret x, query secret x [reachability], or query secret x [pv reachability] tests whether
the attacker can compute a value stored in the variable x or equal to the bound name x.
4.3. FURTHER SECURITY PROPERTIES 57
query secret x [real or random] or query secret x [pv real or random] tests whether the attacker
can distinguish each value of x from a fresh name (representing a fresh random value). This query
is in fact encoded as an observational query between processes that differ only by terms. Such
queries are explained in the next section.
This query is designed for compatibility with CryptoVerif: the options that start with pv apply only
to ProVerif; those that start with cv apply only to CryptoVerif and are ignored by ProVerif; the others
apply to both tools. The various options make it possible to test, in each tool, whether the attacker can
compute the value of x or whether it can distinguish it from a fresh random value. (The former is the
default in ProVerif while the latter is the default in CryptoVerif.)
Strong secrecy
A first class of equivalences that ProVerif can prove is strong secrecy. Strong secrecy means that the
attacker is unable to distinguish when the secret changes. In other words, the value of the secret should
not affect the observable behavior of the protocol. Such a notion is useful to capture the attacker’s ability
to learn partial information about the secret: when the attacker learns the first component of a pair, for
instance, the whole pair is secret in the sense of reachability (the attacker cannot reconstruct the whole
pair because it does not have the second component), but it is not secret in the sense of strong secrecy
(the attacker can notice changes in the value of the pair, since it has its first component). The concept
is particularly important when the secret consists of known values. Consider for instance a process P
that uses a boolean b. The variable b can take two values, true or false, which are both known to the
attacker, so it is not secret in the sense of reachability. However, one may express that b is strongly
secret by saying that P {true/b} ≈ P {false/b}: the attacker cannot determine whether b is true or false.
({true/b} denotes the substitution that replaces b with true.)
The strong secrecy of values x1 , . . . , xn is denoted by
noninterf x1 , . . . , xn .
When the process under consideration is P , this query is true if and only if
for all terms M1 , . . . , Mn , M1′ , . . . , Mn′ . ({M1 /x1 , . . . , Mn /xn } denotes the substitution that replaces x1
with M1 , . . . , xn with Mn .) In other words, the attacker cannot distinguish changes in the values of
x1 , . . . , xn . The values x1 , . . . , xn must be free names of P , declared by free xi : ti [private]. This point
is particularly important: if x1 , . . . , xn do not occur in P or occur as bound names or variables in P , the
query noninterf x1 , . . . , xn holds trivially, because P {M1 /x1 , . . . , Mn /xn } = P {M1′ /x1 , . . . , Mn′ /xn }!
To express secrecy of bound names or variables, one can use choice, described below. In the equivalence
above, the attacker is permitted to replace the values x1 , . . . , xn with any term M1 , . . . , Mn , M1′ , . . . , Mn′
it can build, that is, any term that can be built from public free names, public constructors, and fresh
names created by the attacker. These terms cannot contain bound names (or private free names).
For instance, this strong secrecy query can be used to show the secrecy of a payload sent encrypted
under a session key. Here is a trivial example of a such situation, in which we use a previously shared
long-term key k as session key (file docs/ex noninterf1.pv).
58 CHAPTER 4. LANGUAGE FEATURES
1 free c : channel .
2
3 ( * Shared key e n c r y p t i o n * )
4 type key .
5 fun s e n c ( b i t s t r i n g , key ) : b i t s t r i n g .
6 reduc f o r a l l x : b i t s t r i n g , y : key ; s d e c ( s e n c ( x , y ) , y ) = x .
7
8 ( * The s h a r e d key * )
9 f r e e k : key [ private ] .
10
11 ( * Query * )
12 f r e e s e c r e t m s g : b i t s t r i n g [ private ] .
13 noninterf s e c r e t m s g .
14
15 process ( ! out ( c , s e n c ( s e c r e t m s g , k ) ) ) |
16 ( ! in ( c , x : b i t s t r i n g ) ; l e t s = s d e c ( x , k ) in 0 )
One can also specify the set of terms in which M1 , . . . , Mn , M1′ , . . . , Mn′ are taken, using a variant of
the noninterf query:
noninterf x1 among (M1,1 , . . . , M1,k1 ) ,
...,
xn among (Mn,1 , . . . , Mn,kn ) .
This query is true if and only if
P {M1 /x1 , . . . , Mn /xn } ≈ P {M1′ /x1 , . . . , Mn′ /xn }
for all terms M1 , M1′ ∈ {M1,1 , . . . , M1,k1 }, . . . , Mn , Mn′ ∈ {Mn,1 , . . . , Mn,kn }. Obviously, the terms
Mj,1 , . . . , Mj,kj must have the same type as xj . For instance, the secrecy of a boolean b could be
expressed by noninterf b among (true, false).
Consider the following example (docs/ex noninterf2.pv) in which the attacker is asked to distin-
guish between sessions which output x ∈ {n, h(n)}, where n is a private name.
1 free c : channel .
2
3 fun h ( b i t s t r i n g ) : b i t s t r i n g .
4
5 f r e e x , n : b i t s t r i n g [ private ] .
6 noninterf x among ( n , h ( n ) ) .
7
8 process out ( c , x )
Note that free x,n: bitstring [private]. is a convenient shorthand for
f r e e x : b i t s t r i n g [ private ] .
f r e e n : b i t s t r i n g [ private ] .
More complex examples can be found in subdirectory examples/pitype/noninterf (if you installed
by OPAM in the switch ⟨switch⟩, the directory ~/.opam/⟨switch⟩/doc/proverif/examples/pitype/
noninterf).
weaksecret n .
where n is declared as a private free name: free n : t [private]. ProVerif then tries to prove that the
attacker cannot distinguish a correct guess of the secret from an incorrect guess. This can be written
formally as an observational equivalence
P | phase 1 ; out ( c , n ) ≈ P | phase 1 ; new n′ : t ; out ( c , n′ )
where P is the process under consideration and t is the type of n. In phase 0, the attacker interacts with
the protocol P . In phase 1, the attacker can no longer interact with P , but it receives either the correct
password n or a fresh (incorrect) password n′ , and it should not be able to distinguish between these
two situations.
As an example, we will consider the naı̈ve voting protocol introduced by Delaune & Jacquemard [DJ04].
The protocol proceeds as follows. The voter V constructs her ballot by encrypting her vote v with the
public key of the administrator. The ballot is then sent to the administrator whom is able to decrypt
the message and record the voter’s vote, as modeled in the file docs/ex weaksecret.pv shown below:
1 free c : channel .
2
3 type s k e y .
4 type pkey .
5
6 fun pk ( s k e y ) : pkey .
7 fun aenc ( b i t s t r i n g , pkey ) : b i t s t r i n g .
8
9 reduc f o r a l l m: b i t s t r i n g , k : s k e y ; adec ( aenc (m, pk ( k ) ) , k ) = m.
10
11 f r e e v : b i t s t r i n g [ private ] .
12 weaksecret v .
13
14 l e t V(pkA : pkey ) = out ( c , aenc ( v , pkA ) ) .
15
16 l e t A( skA : s k e y ) = in ( c , x : b i t s t r i n g ) ; l e t v ' = adec ( x , skA ) in 0 .
17
18 process
19 new skA : s k e y ;
20 l e t pkA = pk ( skA ) in
21 out ( c , pkA ) ;
22 ! (V(pkA) | A( skA ) )
The voter’s vote is syntactically secret; however, if the attacker is assumed to know a small set of possible
votes, then v can be deduced from the ballot. The off-line guessing attack can be thwarted by the use of
a probabilistic public-key encryption scheme.
More examples regarding guessing attacks can be found in subdirectory examples/pitype/weaksecr
(if you installed by OPAM in the switch ⟨switch⟩, the directory ~/.opam/⟨switch⟩/doc/proverif/
examples/pitype/weaksecr).
where P is the voter process, skA (respectively skB ) is the voter’s secret key and v1 (respectively v2 ) is
the candidate for whom the voter wishes to vote for: one cannot distinguish the situation in which A
votes for v1 and B votes from v2 from the situation in which A votes for v2 and B votes for v1 . (The
simpler equivalence P (skA , v1 ) ≈ P (skA , v2 ) typically does not hold because, if A is the only voter, one
can know for whom she voted from the final result of the election.) The pair of processes (4.1) can be
expressed as a single biprocess as follows:
Real-or-random secrecy In the computational model, one generally expresses the secrecy of a value
x by saying that x is indistinguishable from a fresh random value. One can express a similar idea in
the formal model using observational equivalence. For instance, this notion can be used for proving
secrecy of a session key k, as in the following variant of the fixed handshake protocol of Chapter 3 (file
docs/ex handshake RoR.pv).
1 free c : channel .
2
3 l e t c l i e n t A (pkA : pkey , skA : skey , pkB : spkey ) =
4 out ( c , pkA ) ;
4.3. FURTHER SECURITY PROPERTIES 61
5 in ( c , x : b i t s t r i n g ) ;
6 l e t y = adec ( x , skA ) in
7 l e t (=pkA,=pkB , k : key ) = c h e c k s i g n ( y , pkB ) in
8 new random : key ;
9 out ( c , choice [ k , random ] ) .
10
11 l e t s e r v e r B ( pkB : spkey , skB : s s k e y , pkA : pkey ) =
12 in ( c , pkX : pkey ) ;
13 new k : key ;
14 out ( c , aenc ( s i g n ( ( pkX , pkB , k ) , skB ) , pkX ) ) .
15
16 process
17 new skA : s k e y ;
18 new skB : s s k e y ;
19 l e t pkA = pk ( skA ) in out ( c , pkA ) ;
20 l e t pkB = spk ( skB ) in out ( c , pkB ) ;
21 ( ( ! c l i e n t A (pkA , skA , pkB ) ) | ( ! s e r v e r B ( pkB , skB , pkA ) ) )
In Line 9, one outputs to the attacker either the real key (k) or a random key (random), and the
equivalence holds when the attacker cannot distinguish these two situations. As ProVerif finds, the
equivalence does not hold in this example, because of a replay attack: the attacker can replay the
message from the server B to the client A, which leads several sessions of the client to have the same
key k. The attacker can distinguish this situation from a situation in which the key is a fresh random
number (random) generated at each session of the client. Another example can be found in Section 5.4.2.
When the observational equivalence proof fails on the biprocess given by the user, ProVerif tries
to simplify that biprocess by transforming as far as possible tests that occur in subprocesses into
tests done inside terms, which increases the chances of success of the proof. The proof is then re-
tried on the simplified process(es). This simplification of biprocesses can be turned off by the setting
set simplifyProcess = false . (See Section 6.6.2 for details on this setting.) More complex examples using
choice can be found in subdirectory examples/pitype/choice (if you installed by OPAM in the switch
⟨switch⟩, the directory ~/.opam/⟨switch⟩/doc/proverif/examples/pitype/choice).
Remarks The absence of off-line guessing attacks can also be expressed using choice:
P | phase 1 ; new n′ : t ; out ( c , choice [ n , n′ ] )
This is how ProVerif handles guessing attacks internally, but using weaksecret is generally more con-
venient in practice. (For instance, one can query for the secrecy of several weak secrets in the same
ProVerif script.)
Strong secrecy noninterf x1 , . . . , xn can also be formalized using choice, by inputting two messages
x′i , x′′i for each i ≤ n and defining xi by let xi = choice[x′i , x′′i ] before starting the protocol itself
(possibly in an earlier phase than the protocol). However, the query noninterf is typically much more
efficient than choice. On the other hand, in the presence of equations that can be applied to the secrets,
noninterf commonly leads to false attacks. So we recommend trying with noninterf for properties
that can be expressed with it, especially when there is no equation, and using choice in the presence of
equations or for properties that cannot be expressed using noninterf.
Strong secrecy with among can also be encoded using choice. That may require many equiva-
lences when the sets are large, even if some examples are very easy to encode. For instance, the query
noninterf b among (true, false) can also be encoded as let b = choice[true, false ] in P where P is
the protocol under consideration.
Static equivalence [AF01] is an equivalence between frames, that is, substitutions with hidden names
ϕ = new n1 : t1 ; . . . new nk : tk ; {M1 /x1 , . . . , Ml /xl }
ϕ′ = new n′1 : t′1 ; . . . new n′k′ : t′k′ ; {M1′ /x1 , . . . , Ml′ /xl }
Static equivalence corresponds to the case in which the attacker receives either the messages M1 , . . . , Ml
or M1′ , . . . , Ml′ , and should not be able to distinguish between these two situations; static equivalence
can be expressed by the observational equivalence
62 CHAPTER 4. LANGUAGE FEATURES
Observational equivalence with synchronizations Synchronizations (see Section 4.1.7) can help
proving equivalences with choice, because they allow swapping data between processes at the synchro-
nization points [BS16]. The following toy example illustrates this point:
1 free c : channel .
2 f r e e m, n : b i t s t r i n g .
3
4 process
5 (
6 out ( c ,m) ;
7 sync 1 ;
8 out ( c , choice [m, n ] )
9 )|(
10 sync 1 ;
11 out ( c , choice [ n ,m] )
12 )
The two processes represented by this biprocess are observationally equivalent, and this property is
proved by swapping m and n in the second component of choice at the synchronization point. By
default, ProVerif tries all possible swapping strategies in order to prove the equivalence. It is also
possible to choose the swapping strategy in the input file by set swapping = ”swapping stragegy”., or
to choose it interactively by adding set interactiveSwapping = true. to the input file. In the latter case,
ProVerif displays a description of the possible swappings and asks the user which swapping strategy to
choose.
A swapping strategy is described as follows. The swapping strategies are permutations of the synchro-
nizations, represented by their tag (given by the user or chosen automatically by ProVerif as explained
in Section 4.1.7; for stability of the tags, when a swapping strategy is given, it is recommend that the
user specifies the tags). They are denoted as follows:
tag 1,1 −> . . .−> tag 1,n1 ;. . .;tag k,1 −> . . .−> tag k,nk
which means that tag i,j has image tag i,j+1 when j < ni and tag i,ni has image tag i,1 by the permutation.
(In other words, we give the cycles of the permutation.) When the tag of a synchronization does not
4.3. FURTHER SECURITY PROPERTIES 63
appear in the swapping strategy, data is not swapped at that synchronization. For instance, the previous
example may the rewritten:
1 free c : channel .
2 f r e e m, n : b i t s t r i n g .
3
4 set swapping = ” t a g 1 −> t a g 2 ” .
5
6 process
7 (
8 out ( c ,m) ;
9 sync 1 [ t a g 1 ] ;
10 out ( c , choice [m, n ] )
11 )|(
12 sync 1 [ t a g 2 ] ;
13 out ( c , choice [ n ,m] )
14 )
with additional tags, and the swapping strategy is tag1 −> tag2.
When a synchronization is tagged with a tag that contains the string noswap, data is not swapped
at that synchronization.
Swapping data at synchronizations point can help for instance proving ballot secrecy in e-voting
protocols: as mentioned above, this property is proved by showing that the two processes represented
by the biprocess
P (skA , choice[v1 , v2 ]) | P (skB , choice[v2 , v1 ])
are observationally equivalent, and proving this property often requires swapping the votes v1 and v2 .
This technique is illustrated on the FOO e-voting protocol in the file examples/pitype/sync/foo.pv
of the documentation package proverifdoc2.05.tar.gz. Other examples appear in the directory
examples/pitype/sync/ in that package.
1 !
2 new k 3 9 : key ;
3 !
4 new a 4 0 : b i t s t r i n g ;
5 new k 4 1 : key ;
6 new a 4 2 : b i t s t r i n g ;
7 out ( c , choice [ mac ( a 40 , k 3 9 ) , mac ( a 42 , k 4 1 ) ] )
and to prove that the two processes are observationally equivalent.
When proving an equivalence by equivalence P Q, the processes P and Q must not contain syn-
chronizations sync n (see Section 4.1.7).
Chapter 5
The Needham-Schroeder public key protocol [NS78] is intended to provide mutual authentication of two
principals Alice A and Bob B. Although it is not stated in the original description, the protocol may
also provide a secret session key shared between the participants. In addition to the two participants,
we assume the existence of a trusted key server S.
The protocol proceeds as follows. Alice contacts the key server S and requests Bob’s public key. The
key server responds with the key pk(skB) paired with Bob’s identity, signed using his private signing key
for the purposes of authentication. Alice proceeds by generating a nonce Na, pairs it with her identity A,
and sends the message encrypted with Bob’s public key. On receipt, Bob decrypts the message to recover
Na and the identity of his interlocutor A. Bob then establishes Alice’s public key pk(skA) by requesting
it to the key server S. Bob then generates his nonce Nb and sends the message (Na,Nb) encrypted for
Alice. Finally, Alice replies with the message aenc(Nb, pk(skB)). The rationale behind the protocol is
that, since only Bob can recover Na, only he can send message 6; and hence authentication of Bob should
hold. Similarly, only Alice should be able to recover Nb; and hence authentication of Alice is expected
on receipt of message 7. Moreover, it follows that Alice and Bob have established the shared secrets
Na and Nb which can subsequently be used as session keys. The protocol can be summarized by the
following narration:
(1) A → S : (A, B)
(2) S → A : sign((B, pk(skB)), skS)
(3) A → B : aenc((Na, A), pk(skB))
(4) B → S : (B, A)
(5) S → B : sign((A, pk(skA)), skS)
(6) B → A : aenc((Na, Nb), pk(skA))
(7) A → B : aenc(Nb, pk(skB))
Informally, the protocol is expected to satisfy the following properties:
1. Authentication of A to B: if B reaches the end of the protocol and he believes he has done so with
A, then A has engaged in a session with B.
3. Secrecy for A: if A reaches the end of the protocol with B, then the nonces Na and Nb that A has
are secret; in particular, they are suitable for use as session keys for preserving the secrecy of an
arbitrary term M in the symmetric encryption senc(M,K) where K ∈ {Na, Nb}.
However, nearly two decades after the protocol’s inception, Gavin Lowe discovered a man-in-the-middle
attack [Low96]. An attacker I engages Alice in a legitimate session of the protocol; and in parallel, the
attacker is able to impersonate Alice in a session with Bob. In practice, one may like to consider the
65
66 CHAPTER 5. NEEDHAM-SCHROEDER: CASE STUDY
attacker to be a malicious retailer I whom Alice is willing to communicate with (presumably without
the knowledge that the retailer is corrupt), and Bob is an honest institution (for example, a bank) whom
Alice conducts legitimate business with. In this scenario, the honest bank B is duped by the malicious
retailer I who is pertaining to be Alice. The protocol narration below describes the attack (with the
omission of key distribution).
A → I : aenc((Na,A), pk(skI))
I → B : aenc((Na,A), pk(skB))
B → A : aenc((Na,Nb), pk(skA))
A → I : aenc(Nb, pk(skI))
I → B : aenc(Nb, pk(skB))
Lowe fixes the protocol by the inclusion of Bob’s identity in message 6; that is,
This correction allows Alice to verify whom she is running the protocol with and prevents the attack. In
the remainder of this chapter, we demonstrate how the Needham-Schroeder public key protocol can be
analyzed using ProVerif with various degrees of complexity.
A → B : aenc((Na,pk(skA)), pk(skB))
B → A : aenc((Na,Nb), pk(skA))
A → B : aenc(Nb, pk(skB))
In this formalization, the role of the trusted key server is omitted and hence we assume that participants
Alice and Bob are in possession of the necessary public keys prior to the execution of the protocol. In
addition, Alice’s identity is modeled using her public key.
19
20 ( * Shared key e n c r y p t i o n * )
21 fun s e n c ( b i t s t r i n g , b i t s t r i n g ) : b i t s t r i n g .
22 reduc f o r a l l x : b i t s t r i n g , y : b i t s t r i n g ; s d e c ( s e n c ( x , y ) , y ) = x .
Process macros for A and B can now be declared and the main process can also be specified:
l e t p r o c e s s A ( pkB : pkey , skA : s k e y ) =
in ( c , pkX : pkey ) ;
new Na : b i t s t r i n g ;
out ( c , aenc ( ( Na , pk ( skA ) ) , pkX ) ) ;
in ( c , m: b i t s t r i n g ) ;
l e t (=Na , NX: b i t s t r i n g ) = adec (m, skA ) in
out ( c , aenc (NX, pkX ) ) .
process
new skA : s k e y ; l e t pkA = pk ( skA ) in out ( c , pkA ) ;
new skB : s k e y ; l e t pkB = pk ( skB ) in out ( c , pkB ) ;
( ( ! p r o c e s s A ( pkB , skA ) ) | ( ! p r o c e s s B (pkA , skB ) ) )
The main process begins by constructing the private keys skA and skB for principals A and B respectively.
The public parts pk(skA) and pk(skB) are then output on the public communication channel c, ensuring
they are available to the attacker. (Observe that this is done using the handles pkA and pkB for
convenience.) An unbounded number of instances of processA and processB are then instantiated (with
the relevant parameters), representing A and B’s willingness to participate in arbitrarily many sessions
of the protocol.
We assume that Alice is willing to run the protocol with any other principal; the choice of her inter-
locutor will be made by the environment. This is captured by modeling the first input in(c, pkX: pkey)
to processA as the interlocutor’s public key pkX. The actual protocol then commences with Alice select-
ing her nonce Na, which she pairs with her identity pkA = pk(skA) and outputs the message encrypted
with her interlocutor’s public key pkX. Meanwhile, Bob awaits an input from his initiator; on receipt,
Bob decrypts the message to recover his initiator’s nonce NY and identity pkY. Bob then generates
his nonce Nb and sends the message (NY,Nb) encrypted for the initiator using the key pkY. Next, if
Alice believes she is talking to her interlocutor, that is, if the ciphertext she receives contains her nonce
Na, then she replies with aenc(Nb, pk(skB)). (Recall that only the interlocutor who has the secret key
corresponding to the public key part pkX should have been able to recover Na and hence if the ciphertext
contains her nonce, then she believes authentication of her interlocutor holds.) Finally, if the ciphertext
received by Bob contains his nonce Nb, then he believes that he has successfully completed the protocol
with his initiator.
event beginAparam(pkey), which is used by Bob to record the belief that the initiator whose public
key is supplied as a parameter has commenced a run of the protocol with him.
68 CHAPTER 5. NEEDHAM-SCHROEDER: CASE STUDY
event endAparam(pkey), which means that Alice believes she has successfully completed the pro-
tocol with Bob. This event is executed only when Alice believes she runs the protocol with Bob,
that is, when pkX = pkB. Alice supplies her public key pk(skA) as the parameter.
event beginBparam(pkey), which denotes Alice’s intention to initiate the protocol with an inter-
locutor whose public key is supplied as a parameter.
event endBparam(pkey), which records Bob’s belief that he has completed the protocol with Alice.
He supplies his public key pk(skB) as the parameter.
Intuitively, if Alice believes she has completed the protocol with Bob and hence executes the event
endAparam(pk(skA)), then there should have been an earlier occurrence of the event beginAparam(pk(
skA)), indicating that Bob started a session with Alice; moreover, the relationship should be injective.
A similar property should hold for Bob.
In addition, we wish to test if, at the end of the protocol, the nonces Na and Nb are secret. These
nonces are names created by new or variables such as NX and NY, while the standard secrecy queries
of ProVerif deal with the secrecy of private free names. To solve this problem, we can use the following
general technique: instead of directly testing the secrecy of the nonces, we use them as session keys in
order to encrypt some free name and test the secrecy of that free name. For instance, in the process
for Alice, we output senc(secretANa,Na) and we test the secrecy of secretANa: secretANa is secret if
and only if the nonce Na that Alice has is secret. Similarly, we output senc(secretANb,NX) and we
test the secrecy of secretANb: secretANb is secret if and only if NX (that is, the nonce Nb that Alice
has) is secret. We proceed symmetrically for Bob using secretBNa and secretBNb. (Alternatively, we
could also define a variable NaA to store the nonce Na that Alice has at the end of the protocol, and
test its secrecy using the query query secret NaA. We can proceed similarly using NbA to store the
nonce Nb on Alice’s side, and NaB and NbB to store the nonces on Bob’s side. This is done in the file
docs/NeedhamSchroederPK-var5.pv.)
Observe that the use of four names secretANa, secretANb, secretBNa, secretBNb for secrecy queries
allows us to analyze the precise point of failure; that is, we can study secrecy for Alice and secrecy for
Bob. Moreover, we can analyze both nonces Na and Nb independently for each of Alice and Bob.
The corresponding ProVerif code annotated with events and additional code to model secrecy, along
with the relevant queries, is presented as follows (file docs/NeedhamSchroederPK-var1.pv):
23 (* A u t h e n t i c a t i o n q u e r i e s *)
24 event beginBparam ( pkey ) .
25 event endBparam ( pkey ) .
26 event beginAparam ( pkey ) .
27 event endAparam ( pkey ) .
28
29 query x : pkey ; inj −event ( endBparam ( x ) ) ==> inj −event ( beginBparam ( x ) ) .
30 query x : pkey ; inj −event ( endAparam ( x ) ) ==> inj −event ( beginAparam ( x ) ) .
31
32 (* Secrecy q u e r i e s *)
33 f r e e secretANa , secretANb , secretBNa , secretBNb : b i t s t r i n g [ private ] .
34
35 query attacker ( secretANa ) ;
36 attacker ( secretANb ) ;
37 attacker ( secretBNa ) ;
38 attacker ( secretBNb ) .
39
40 (* A l i c e *)
41 l e t p r o c e s s A ( pkB : pkey , skA : s k e y ) =
42 in ( c , pkX : pkey ) ;
43 event beginBparam (pkX ) ;
44 new Na : b i t s t r i n g ;
45 out ( c , aenc ( ( Na , pk ( skA ) ) , pkX ) ) ;
46 in ( c , m: b i t s t r i n g ) ;
5.1. SIMPLIFIED NEEDHAM-SCHROEDER PROTOCOL 69
which means that even the non-injective authentication of A to B is false; that is, Bob may end the
protocol thinking he talks to Alice while Alice has never run the protocol with Bob. For the query
attacker(secretBNa[]), ProVerif returns the following trace of an attack:
1 new skA c r e a t i n g skA 411 at {1}
2 out ( c , pk ( skA 411 ) ) at {3}
3 new skB c r e a t i n g skB 412 at {4}
4 out ( c , pk ( skB 412 ) ) at {6}
5 in ( c , pk ( a ) ) at {8} in copy a 4 0 8
6 event ( beginBparam ( pk ( a ) ) ) at {9} in copy a 4 0 8
7 new Na c r e a t i n g Na 410 at {10} in copy a 4 0 8
8 out ( c , aenc ( ( Na 410 , pk ( skA 411 ) ) , pk ( a ) ) ) at {11} in copy a 4 0 8
9 in ( c , aenc ( ( Na 410 , pk ( skA 411 ) ) , pk ( skB 412 ) ) ) at {20} in copy a 4 0 9
70 CHAPTER 5. NEEDHAM-SCHROEDER: CASE STUDY
23 fun s e n c ( b i t s t r i n g , nonce ) : b i t s t r i n g .
24 reduc f o r a l l x : b i t s t r i n g , y : nonce ; s d e c ( s e n c ( x , y ) , y ) = x .
25
26 ( * Type c o n v e r t e r * )
27 fun n o n c e t o b i t s t r i n g ( nonce ) : b i t s t r i n g [ data , typeConverter ] .
28
29 ( * Two h o n e s t h o s t names A and B * )
30 type h o s t .
31 f r e e A, B : h o s t .
32
33 ( * Key t a b l e * )
34 table k e y s ( host , pkey ) .
35
36 (* A u t h e n t i c a t i o n q u e r i e s *)
37 event beginBparam ( h o s t ) .
38 event endBparam ( h o s t ) .
39 event beginAparam ( h o s t ) .
40 event endAparam ( h o s t ) .
41
42 query x : h o s t ; inj −event ( endBparam ( x ) ) ==> inj −event ( beginBparam ( x ) ) .
43 query x : h o s t ; inj −event ( endAparam ( x ) ) ==> inj −event ( beginAparam ( x ) ) .
44
45 (* Secrecy q u e r i e s *)
46 f r e e secretANa , secretANb , secretBNa , secretBNb : b i t s t r i n g [ private ] .
47
48 query attacker ( secretANa ) ;
49 attacker ( secretANb ) ;
50 attacker ( secretBNa ) ;
51 attacker ( secretBNb ) .
52
53 (* A l i c e *)
54 l e t p r o c e s s A ( pkS : spkey , skA : s k e y ) =
55 in ( c , hostX : h o s t ) ;
56 event beginBparam ( hostX ) ;
57 out ( c , (A, hostX ) ) ; ( * msg 1 * )
58 in ( c , ms : b i t s t r i n g ) ; ( * msg 2 * )
59 l e t (pkX : pkey , =hostX ) = c h e c k s i g n (ms , pkS ) in
60 new Na : nonce ;
61 out ( c , aenc ( ( Na , A) , pkX ) ) ; ( * msg 3 * )
62 in ( c , m: b i t s t r i n g ) ; ( * msg 6 * )
63 l e t (=Na , NX: nonce ) = adec (m, skA ) in
64 out ( c , aenc ( n o n c e t o b i t s t r i n g (NX) , pkX ) ) ; ( * msg 7 * )
65 i f hostX = B then
66 event endAparam (A ) ;
67 out ( c , s e n c ( secretANa , Na ) ) ;
68 out ( c , s e n c ( secretANb , NX) ) .
69
70 ( * Bob * )
71 l e t p r o c e s s B ( pkS : spkey , skB : s k e y ) =
72 in ( c , m: b i t s t r i n g ) ; ( * msg 3 * )
73 l e t (NY: nonce , hostY : h o s t ) = adec (m, skB ) in
74 event beginAparam ( hostY ) ;
75 out ( c , (B, hostY ) ) ; ( * msg 4 * )
76 in ( c , ms : b i t s t r i n g ) ; ( * msg 5 * )
77 l e t (pkY : pkey ,= hostY ) = c h e c k s i g n (ms , pkS ) in
72 CHAPTER 5. NEEDHAM-SCHROEDER: CASE STUDY
78 new Nb : nonce ;
79 out ( c , aenc ( (NY, Nb) , pkY ) ) ; ( * msg 6 * )
80 in ( c , m3 : b i t s t r i n g ) ; ( * msg 7 * )
81 i f n o n c e t o b i t s t r i n g (Nb) = adec (m3, skB ) then
82 i f hostY = A then
83 event endBparam (B ) ;
84 out ( c , s e n c ( secretBNa , NY) ) ;
85 out ( c , s e n c ( secretBNb , Nb ) ) .
86
87 ( * T r u s t e d key s e r v e r * )
88 l e t p r o c e s s S ( skS : s s k e y ) =
89 in ( c , ( a : host , b : h o s t ) ) ;
90 get k e y s (=b , sb ) in
91 out ( c , s i g n ( ( sb , b ) , skS ) ) .
92
93 ( * Key r e g i s t r a t i o n * )
94 l e t p ro c es s K =
95 in ( c , ( h : host , k : pkey ) ) ;
96 i f h <> A && h <> B then i n s e r t k e y s ( h , k ) .
97
98 ( * Main * )
99 process
100 new skA : s k e y ; l e t pkA = pk ( skA ) in out ( c , pkA ) ; i n s e r t k e y s (A, pkA ) ;
101 new skB : s k e y ; l e t pkB = pk ( skB ) in out ( c , pkB ) ; i n s e r t k e y s (B, pkB ) ;
102 new skS : s s k e y ; l e t pkS = spk ( skS ) in out ( c , pkS ) ;
103 ( ( ! p r o c e s s A ( pkS , skA ) ) | ( ! p r o c e s s B ( pkS , skB ) ) |
104 ( ! p r o c e s s S ( skS ) ) | ( ! p r oc e ss K ) )
This process uses a key table in order to relate host names and their public keys. The key table is
declared by table keys(host, pkey). Keys are inserted in the key table in the main process (for the
honest hosts A and B, by insert keys(A, pkA) and insert keys(B, pkB)) and in a key registration
process processK for dishonest hosts. The key server processS looks up the key corresponding to host
b by get keys(=b, sb) in order to build the corresponding certificate. Since Alice is willing to run the
protocol with any other participant and she will request her interlocutor’s public key from the key server,
we must permit the attacker to register keys with the trusted key server (that is, insert keys into the key
table). This behavior is captured by the key registration process processK. Observe that the conditional
if h <> A && h <> B then prevents the attacker from changing the keys belonging to Alice and Bob.
(Recall that when several records are matched by a get query, then one possibility is chosen, but ProVerif
considers all possibilities when reasoning; without the conditional, the attacker can therefore effectively
change the keys belonging to Alice and Bob.)
1 ( * Loops i f t y p e s a r e i g n o r e d * )
2 set i g n o r e T y p e s = f a l s e .
3
4 free c : channel .
5
6 type host .
7 type nonce .
8 type pkey .
9 type skey .
10 type spkey .
11 type sskey .
12
13 fun n o n c e t o b i t s t r i n g ( nonce ) : b i t s t r i n g [ data , typeConverter ] .
14
15 ( * P u b l i c key e n c r y p t i o n * )
16 fun pk ( s k e y ) : pkey .
17 fun e n c r y p t ( b i t s t r i n g , pkey ) : b i t s t r i n g .
18 reduc f o r a l l x : b i t s t r i n g , y : s k e y ; d e c r y p t ( e n c r y p t ( x , pk ( y ) ) , y ) = x .
19
20 (* S i g n a t u r e s *)
21 fun spk ( s s k e y ) : spkey .
22 fun s i g n ( b i t s t r i n g , s s k e y ) : b i t s t r i n g .
23 reduc f o r a l l m: b i t s t r i n g , k : s s k e y ; g e t m e s s ( s i g n (m, k ) ) = m.
24 reduc f o r a l l m: b i t s t r i n g , k : s s k e y ; c h e c k s i g n ( s i g n (m, k ) , spk ( k ) ) = m.
25
26 ( * Shared key e n c r y p t i o n * )
27 fun s e n c r y p t ( b i t s t r i n g , nonce ) : b i t s t r i n g .
28 reduc f o r a l l x : b i t s t r i n g , y : nonce ; s d e c r y p t ( s e n c r y p t ( x , y ) , y ) = x .
29
30 (* Secrecy assumptions *)
31 not attacker (new skA ) .
32 not attacker (new skB ) .
33 not attacker (new skS ) .
34
35 ( * 2 h o n e s t h o s t names A and B * )
36 f r e e A, B : h o s t .
37
38 table k e y s ( host , pkey ) .
39
40 (* Queries *)
41 f r e e secretANa , secretANb , secretBNa , secretBNb : b i t s t r i n g [ private ] .
42 query attacker ( secretANa ) ;
43 attacker ( secretANb ) ;
44 attacker ( secretBNa ) ;
45 attacker ( secretBNb ) .
46
47 event beginBparam ( host , h o s t ) .
48 event endBparam ( host , h o s t ) .
49 event beginAparam ( host , h o s t ) .
50 event endAparam ( host , h o s t ) .
51 event b e g i n B f u l l ( host , host , pkey , pkey , nonce , nonce ) .
52 event e n d B f u l l ( host , host , pkey , pkey , nonce , nonce ) .
53 event b e g i n A f u l l ( host , host , pkey , pkey , nonce , nonce ) .
54 event e n d A f u l l ( host , host , pkey , pkey , nonce , nonce ) .
55
74 CHAPTER 5. NEEDHAM-SCHROEDER: CASE STUDY
56 query x : host , y : h o s t ;
57 inj −event ( endBparam ( x , y ) ) ==> inj −event ( beginBparam ( x , y ) ) .
58
59 query x1 : host , x2 : host , x3 : pkey , x4 : pkey , x5 : nonce , x6 : nonce ;
60 inj −event ( e n d B f u l l ( x1 , x2 , x3 , x4 , x5 , x6 ) )
61 ==> inj −event ( b e g i n B f u l l ( x1 , x2 , x3 , x4 , x5 , x6 ) ) .
62
63 query x : host , y : h o s t ;
64 inj −event ( endAparam ( x , y ) ) ==> inj −event ( beginAparam ( x , y ) ) .
65
66 query x1 : host , x2 : host , x3 : pkey , x4 : pkey , x5 : nonce , x6 : nonce ;
67 inj −event ( e n d A f u l l ( x1 , x2 , x3 , x4 , x5 , x6 ) )
68 ==> inj −event ( b e g i n A f u l l ( x1 , x2 , x3 , x4 , x5 , x6 ) ) .
69
70 ( * Role o f t h e i n i t i a t o r w i t h i d e n t i t y xA and s e c r e t key skxA * )
71 l e t p r o c e s s I n i t i a t o r ( pkS : spkey , skA : skey , skB : s k e y ) =
72 ( * The a t t a c k e r s t a r t s t h e i n i t i a t o r by c h o o s i n g i d e n t i t y xA ,
73 and i t s i n t e r l o c u t o r xB0 .
74 We c h e c k t h a t xA i s h o n e s t ( i . e . i s A or B)
75 and g e t i t s c o r r e s p o n d i n g key . * )
76 in ( c , (xA : host , hostX : h o s t ) ) ;
77 i f xA = A | | xA = B then
78 l e t skxA = i f xA = A then skA e l s e skB in
79 l e t pkxA = pk ( skxA ) in
80 ( * Real s t a r t o f t h e r o l e * )
81 event beginBparam (xA , hostX ) ;
82 ( * Message 1 : Get t h e p u b l i c key c e r t i f i c a t e f o r t h e o t h e r h o s t * )
83 out ( c , (xA , hostX ) ) ;
84 ( * Message 2 * )
85 in ( c , ms : b i t s t r i n g ) ;
86 l e t (pkX : pkey , =hostX ) = c h e c k s i g n (ms , pkS ) in
87 ( * Message 3 * )
88 new Na : nonce ;
89 out ( c , e n c r y p t ( ( Na , xA ) , pkX ) ) ;
90 ( * Message 6 * )
91 in ( c , m: b i t s t r i n g ) ;
92 l e t (=Na , NX2 : nonce ) = d e c r y p t (m, skxA ) in
93 event b e g i n B f u l l (xA , hostX , pkX , pkxA , Na , NX2 ) ;
94 ( * Message 7 * )
95 out ( c , e n c r y p t ( n o n c e t o b i t s t r i n g (NX2) , pkX ) ) ;
96 ( * OK * )
97 i f hostX = B | | hostX = A then
98 event endAparam (xA , hostX ) ;
99 event e n d A f u l l (xA , hostX , pkX , pkxA , Na , NX2 ) ;
100 out ( c , s e n c r y p t ( secretANa , Na ) ) ;
101 out ( c , s e n c r y p t ( secretANb , NX2 ) ) .
102
103 ( * Role o f t h e r e s p o n d e r w i t h i d e n t i t y xB and s e c r e t key skxB * )
104 l e t p r o c e s s R e s p o n d e r ( pkS : spkey , skA : skey , skB : s k e y ) =
105 ( * The a t t a c k e r s t a r t s t h e r e s p o n d e r by c h o o s i n g i d e n t i t y xB .
106 We c h e c k t h a t xB i s h o n e s t ( i . e . i s A or B ) . * )
107 in ( c , xB : h o s t ) ;
108 i f xB = A | | xB = B then
109 l e t skxB = i f xB = A then skA e l s e skB in
110 l e t pkxB = pk ( skxB ) in
5.3. GENERALIZED NEEDHAM-SCHROEDER PROTOCOL 75
111 ( * Real s t a r t o f t h e r o l e * )
112 ( * Message 3 * )
113 in ( c , m: b i t s t r i n g ) ;
114 l e t (NY: nonce , hostY : h o s t ) = d e c r y p t (m, skxB ) in
115 event beginAparam ( hostY , xB ) ;
116 ( * Message 4 : Get t h e p u b l i c key c e r t i f i c a t e f o r t h e o t h e r h o s t * )
117 out ( c , (xB , hostY ) ) ;
118 ( * Message 5 * )
119 in ( c , ms : b i t s t r i n g ) ;
120 l e t (pkY : pkey ,= hostY ) = c h e c k s i g n (ms , pkS ) in
121 ( * Message 6 * )
122 new Nb : nonce ;
123 event b e g i n A f u l l ( hostY , xB , pkxB , pkY , NY, Nb ) ;
124 out ( c , e n c r y p t ( (NY, Nb) , pkY ) ) ;
125 ( * Message 7 * )
126 in ( c , m3 : b i t s t r i n g ) ;
127 i f n o n c e t o b i t s t r i n g (Nb) = d e c r y p t (m3, skB ) then
128 ( * OK * )
129 i f hostY = A | | hostY = B then
130 event endBparam ( hostY , xB ) ;
131 event e n d B f u l l ( hostY , xB , pkxB , pkY , NY, Nb ) ;
132 out ( c , s e n c r y p t ( secretBNa , NY) ) ;
133 out ( c , s e n c r y p t ( secretBNb , Nb ) ) .
134
135 (* Server *)
136 l e t p r o c e s s S ( skS : s s k e y ) =
137 in ( c , ( a : host , b : h o s t ) ) ;
138 get k e y s (=b , sb ) in
139 out ( c , s i g n ( ( sb , b ) , skS ) ) .
140
141 ( * Key r e g i s t r a t i o n * )
142 l e t p ro c es s K =
143 in ( c , ( h : host , k : pkey ) ) ;
144 i f h <> A && h <> B then i n s e r t k e y s ( h , k ) .
145
146 ( * Main * )
147 process
148 new skA : s k e y ; l e t pkA = pk ( skA ) in out ( c , pkA ) ; i n s e r t k e y s (A, pkA ) ;
149 new skB : s k e y ; l e t pkB = pk ( skB ) in out ( c , pkB ) ; i n s e r t k e y s (B, pkB ) ;
150 new skS : s s k e y ; l e t pkS = spk ( skS ) in out ( c , pkS ) ;
151 (
152 ( * Launch an unbounded number o f s e s s i o n s o f t h e i n i t i a t o r * )
153 ( ! p r o c e s s I n i t i a t o r ( pkS , skA , skB ) ) |
154 ( * Launch an unbounded number o f s e s s i o n s o f t h e r e s p o n d e r * )
155 ( ! p r o c e s s R e s p o n d e r ( pkS , skA , skB ) ) |
156 ( * Launch an unbounded number o f s e s s i o n s o f t h e s e r v e r * )
157 ( ! p r o c e s s S ( skS ) ) |
158 ( * Key r e g i s t r a t i o n p r o c e s s * )
159 ( ! p ro c e ss K )
160 )
The main novelty of this script is that it allows Alice and Bob to play both roles of the initiator and
responder. To achieve this, we could simply duplicate the code, but it is possible to have more elegant
encodings. Above, we consider processes processInitiator and processResponder that take as argument
both skA and skB (since they can be played by Alice and Bob). Looking for instance at the initiator
(Lines 71–79), the attacker first starts the initiator by sending the identity xA of the principal playing
76 CHAPTER 5. NEEDHAM-SCHROEDER: CASE STUDY
the role of the initiator and hostX of its interlocutor. Then, we verify that the initiator is honest, and
compute its secret key skxA (skA for A, skB for B) and its corresponding public key pkxA = pk(skxA).
We can then run the role as expected. We proceed similarly for the responder.
Other encodings are also possible. For instance, we could define a destructor choosekey by
fun c h o o s e k e y ( host , host , host , skey , s k e y ) : s k e y
reduc f o r a l l x1 : host , x2 : host , sk1 : skey , sk2 : s k e y ;
c h o o s e k e y ( x1 , x1 , x2 , sk1 , sk2 ) = sk1
otherwise f o r a l l x1 : host , x2 : host , sk1 : skey , sk2 : s k e y ;
c h o o s e k e y ( x2 , x1 , x2 , sk1 , sk2 ) = sk2 .
and let skxA be choosekey(xA, A, B, skA, skB) (if xA = A, it returns skA; if xA = B, it returns skB;
otherwise, it fails). The latter encoding is perhaps less intuitive, but it avoids internal code duplication
when ProVerif expands tests that appear in terms.
Three other points are worth noting:
We use secrecy assumptions (Lines 30–33) to speed up the resolution process of ProVerif. These
lines inform ProVerif that the attacker cannot have the secret keys skA, skB, skS. This information
is checked by ProVerif, so that erroneous proofs cannot be obtained even with secrecy assumptions.
(See also Section 6.7.2.) Lines 30–33 can be removed without changing the results, ProVerif will
just be slightly slower.
We set ignoreTypes to false (Lines 1–2). By default, ProVerif ignore all types during analysis.
However, for this script, it does not terminate with this default setting. By setting ignoreTypes =
false , the semantics of processes is changed to check the types. This setting makes it possible
to obtain termination. The known attack against this protocol is detected, but it might happen
that some type flaw attacks are undetected, when they appear when the types are not checked in
processes. More details on the ignoreTypes setting can be found in Section 6.6.2.
There are other ways of obtaining termination in this example, in particular by using a different
method for relating identities and keys with two function symbols, one that maps the key to the
identity, and one that maps the identity to the key. However, this method also has limitations: it
does not allow the attacker to create two principals with the same key. More information on this
method can be found in Section 6.7.3.
We use two different levels of authentication: the events that end with “full” serve in proving
Lowe’s full agreement [Low97], that is, agreement on all parameters of the protocol (here, host
names, keys, and nonces). The events that end with “param” prove agreement on the host names
only.
As expected, ProVerif is able to prove the authentication of the responder and secrecy for the initiator;
whereas authentication of the initiator and secrecy for the responder fail. The reader is invited to modify
the protocol according to Lowe’s fix and examine the results produced by ProVerif. (A script for the
corrected protocol can be found in examples/pitype/secr-auth/NeedhamSchroederPK-corr.pv. If
you installed by OPAM in the switch ⟨switch⟩, it is in ~/.opam/⟨switch⟩/doc/proverif/examples/
pitype/secr-auth/NeedhamSchroederPK-corr.pv. Note that the fixed protocol can be proved correct
by ProVerif even when types are ignored.)
tuple of the messages of the protocol) as argument of events. One can also do that in ProVerif, as in the
following example (file docs/NeedhamSchroederPK-corr-mutual-auth.pv).
1 (* Queries *)
2 fun messtermI ( host , h o s t ) : b i t s t r i n g [ data ] .
3 fun messtermR ( host , h o s t ) : b i t s t r i n g [ data ] .
4
5 event termI ( host , host , b i t s t r i n g ) .
6 event a c c e p t s I ( host , host , b i t s t r i n g ) .
7 event accept sR ( host , host , b i t s t r i n g ) .
8 event termR ( host , host , b i t s t r i n g ) .
9
10 query x : host , m: b i t s t r i n g ;
11 inj −event ( termI ( x , B,m) ) ==> inj −event ( acceptsR ( x , B,m) ) .
12 query x : host , m: b i t s t r i n g ;
13 inj −event ( termR (A, x ,m) ) ==> inj −event ( a c c e p t s I (A, x ,m) ) .
14
15 ( * Role o f t h e i n i t i a t o r w i t h i d e n t i t y xA and s e c r e t key skxA * )
16 l e t p r o c e s s I n i t i a t o r ( pkS : spkey , skA : skey , skB : s k e y ) =
17 ( * The a t t a c k e r s t a r t s t h e i n i t i a t o r by c h o o s i n g i d e n t i t y xA ,
18 and i t s i n t e r l o c u t o r xB0 .
19 We c h e c k t h a t xA i s h o n e s t ( i . e . i s A or B)
20 and g e t i t s c o r r e s p o n d i n g key .
21 *)
22 in ( c , (xA : host , hostX : h o s t ) ) ;
23 i f xA = A | | xA = B then
24 l e t skxA = i f xA = A then skA e l s e skB in
25 l e t pkxA = pk ( skxA ) in
26 ( * Real s t a r t o f t h e r o l e * )
27 ( * Message 1 : Get t h e p u b l i c key c e r t i f i c a t e f o r t h e o t h e r h o s t * )
28 out ( c , (xA , hostX ) ) ;
29 ( * Message 2 * )
30 in ( c , ms : b i t s t r i n g ) ;
31 l e t (pkX : pkey , =hostX ) = c h e c k s i g n (ms , pkS ) in
32 ( * Message 3 * )
33 new Na : nonce ;
34 l e t m3 = e n c r y p t ( ( Na , xA ) , pkX) in
35 out ( c , m3 ) ;
36 ( * Message 6 * )
37 in ( c , m: b i t s t r i n g ) ;
38 l e t (=Na , NX2 : nonce , =hostX ) = d e c r y p t (m, skA ) in
39 l e t m7 = e n c r y p t ( n o n c e t o b i t s t r i n g (NX2) , pkX) in
40 event termI (xA , hostX , (m3, m) ) ;
41 event a c c e p t s I (xA , hostX , (m3, m, m7 ) ) ;
42 ( * Message 7 * )
43 out ( c , (m7, messtermI (xA , hostX ) ) ) .
44
45 ( * Role o f t h e r e s p o n d e r w i t h i d e n t i t y xB and s e c r e t key skxB * )
46 l e t p r o c e s s R e s p o n d e r ( pkS : spkey , skA : skey , skB : s k e y ) =
47 ( * The a t t a c k e r s t a r t s t h e r e s p o n d e r by c h o o s i n g i d e n t i t y xB .
48 We c h e c k t h a t xB i s h o n e s t ( i . e . i s A or B ) . * )
49 in ( c , xB : h o s t ) ;
50 i f xB = A | | xB = B then
51 l e t skxB = i f xB = A then skA e l s e skB in
52 l e t pkxB = pk ( skxB ) in
53 ( * Real s t a r t o f t h e r o l e * )
78 CHAPTER 5. NEEDHAM-SCHROEDER: CASE STUDY
54 ( * Message 3 * )
55 in ( c , m: b i t s t r i n g ) ;
56 l e t (NY: nonce , hostY : h o s t ) = d e c r y p t (m, skxB ) in
57 ( * Message 4 : Get t h e p u b l i c key c e r t i f i c a t e f o r t h e o t h e r h o s t * )
58 out ( c , (xB , hostY ) ) ;
59 ( * Message 5 * )
60 in ( c , ms : b i t s t r i n g ) ;
61 l e t (pkY : pkey ,= hostY ) = c h e c k s i g n (ms , pkS ) in
62 ( * Message 6 * )
63 new Nb : nonce ;
64 l e t m6 = e n c r y p t ( (NY, Nb , xB ) , pkY) in
65 event accep tsR ( hostY , xB , (m, m6 ) ) ;
66 out ( c , m6 ) ;
67 ( * Message 7 * )
68 in ( c , m3 : b i t s t r i n g ) ;
69 i f n o n c e t o b i t s t r i n g (Nb) = d e c r y p t (m3, skB ) then
70 event termR ( hostY , xB , (m, m6, m3 ) ) ;
71 out ( c , messtermR ( hostY , xB ) ) .
72
73 (* Server *)
74 l e t p r o c e s s S ( skS : s s k e y ) =
75 in ( c , ( a : host , b : h o s t ) ) ;
76 get k e y s (=b , sb ) in
77 out ( c , s i g n ( ( sb , b ) , skS ) ) .
78
79 ( * Key r e g i s t r a t i o n * )
80 l e t p ro c es s K =
81 in ( c , ( h : host , k : pkey ) ) ;
82 i f h <> A && h <> B then i n s e r t k e y s ( h , k ) .
83
84 (* S t a r t p r o c e s s *)
85 process
86 new skA : s k e y ; l e t pkA = pk ( skA ) in out ( c , pkA ) ; i n s e r t k e y s (A, pkA ) ;
87 new skB : s k e y ; l e t pkB = pk ( skB ) in out ( c , pkB ) ; i n s e r t k e y s (B, pkB ) ;
88 new skS : s s k e y ; l e t pkS = spk ( skS ) in out ( c , pkS ) ;
89 (
90 ( * Launch an unbounded number o f s e s s i o n s o f t h e i n i t i a t o r * )
91 ( ! p r o c e s s I n i t i a t o r ( pkS , skA , skB ) ) |
92 ( * Launch an unbounded number o f s e s s i o n s o f t h e r e s p o n d e r * )
93 ( ! p r o c e s s R e s p o n d e r ( pkS , skA , skB ) ) |
94 ( * Launch an unbounded number o f s e s s i o n s o f t h e s e r v e r * )
95 ( ! p r o c e s s S ( skS ) ) |
96 ( * Key r e g i s t r a t i o n p r o c e s s * )
97 ( ! p ro c e ss K )
98 )
The query
10 query x : host , m: b i t s t r i n g ;
11 inj −event ( termI ( x , B,m) ) ==> inj −event ( acceptsR ( x , B,m) ) .
corresponds to the authentication of the responder B to the initiator x: when the initiator x terminates a
session apparently with B (event termI(x,B,m), executed at Line 40, when the initiator terminates, after
receiving its last message, message 6), the responder B has accepted with x (event acceptsR(x,B,m),
executed at Line 65, when the responder accepts, just before sending message 6). We use a fixed value B
for the name of the responder, and not a variable, because if a variable were used, the initiator might run
a session with a dishonest participant included in the attacker, and in this case, it is perfectly ok that
5.4. VARIANTS OF THESE SECURITY PROPERTIES 79
the event acceptsR is not executed. Since the initiator is executed with identities A and B, x is either A
or B, so the query above proves correct authentication of the responder B to the initiator x when x is A
and when it is B. The same property for the responder A holds by symmetry, swapping A and B.
Similarly, the query
12 query x : host , m: b i t s t r i n g ;
13 inj −event ( termR (A, x ,m) ) ==> inj −event ( a c c e p t s I (A, x ,m) ) .
corresponds to the authentication of the initiator A to the responder x: when the responder x terminates
a session apparently with A (event termR(A,x,m), executed at Line 70, when the responder terminates,
after receiving its last message, message 7), the initiator A has accepted with x (event acceptsI(A,x,m),
executed at Line 41, when the initiator accepts, just before sending message 7).
The position of events follows Figure 3.4. The events termR and acceptsI take as arguments the host
names of the initiator and the responder, and the tuples of messages exchanged between the initiator
and the responder. (Messages sent to or received from the server to obtain the certificates are ignored.)
Because the last message is from the initiator to the responder, that message is not known to the
responder when it accepts, so that message is omitted from the arguments of the events acceptsR and
termI.
We use events similar to those for mutual authentication, except that termR and acceptsI take the
exchanged key as an additional argument. We prove the following properties:
query x : host , m: b i t s t r i n g ;
inj −event ( termI ( x , B,m) ) ==> inj −event ( acceptsR ( x , B,m) ) .
query x : host , k : nonce , m: b i t s t r i n g ;
inj −event ( termR (A, x , k ,m) ) ==> inj −event ( a c c e p t s I (A, x , k ,m) ) .
query x : host , k : nonce , k ' : nonce , m: b i t s t r i n g ;
event ( termR (A, x , k ,m) ) && event ( a c c e p t s I (A, x , k ' ,m) ) ==> k = k ' .
When the initiator or the responder execute a session with a dishonest participant, they output
the exchanged key. (This key is also output by the test queries in this case.) We show the secrecy
of the keys established by the initiator when it runs sessions with a honest responder, in the sense
that these keys are indistinguishable from independent random numbers.
The first two correspondences imply mutual authentication. The real-or-random indistinguishability of
the key is obtained by combining the last two correspondences with the secrecy of the initiator’s key.
Intuitively, the correspondences allow us to show that each responder’s key in a session with a honest
initiator is in fact also an initiator’s key (which we can find by looking for the same session identifier), so
showing that the initiator’s key cannot be distinguished from independent random numbers is sufficient
to show the secrecy of the key.
Outputting the exchanged key in a session with a dishonest interlocutor allows to detect Unknown
Key Share (UKS) attacks [DvOW92], in which an initiator A believes he shares a key with a responder
B, but B believes he shares that key with a dishonest C. This key is then output to the attacker, so the
secrecy of the initiator’s key is broken. However, bilateral UKS attacks [CT08], in which A shares a key
80 CHAPTER 5. NEEDHAM-SCHROEDER: CASE STUDY
with a dishonest C and B shares the same key with a dishonest D, may remain undetected under this
definition of key exchange. These attacks can be detected by testing the following correspondence:
query x : host , y : host , x ' : host , y ' : host , k : nonce , k ' : nonce ,
m: b i t s t r i n g , m ' : b i t s t r i n g ;
event ( termR ( x , y , k ,m) ) && event ( a c c e p t s I ( x ' , y ' , k ,m ' ) ) ==> x = x ' && y = y ' .
to verify that, if two sessions terminate with the same key, then they are between the same hosts (and
we could additionally verify m = m' to make sure that these sessions have the same session identifiers).
The following script aims at verifying this notion of authenticated key exchange, assuming that the
exchanged key is Na (file docs/NeedhamSchroederPK-corr-ake.pv).
1 (* Queries *)
2 f r e e s e c r e t A : b i t s t r i n g [ private ] .
3 query attacker ( s e c r e t A ) .
4
5 fun messtermI ( host , h o s t ) : b i t s t r i n g [ data ] .
6 fun messtermR ( host , h o s t ) : b i t s t r i n g [ data ] .
7
8 event termI ( host , host , b i t s t r i n g ) .
9 event a c c e p t s I ( host , host , nonce , b i t s t r i n g ) .
10 event accept sR ( host , host , b i t s t r i n g ) .
11 event termR ( host , host , nonce , b i t s t r i n g ) .
12
13 query x : host , m: b i t s t r i n g ;
14 inj −event ( termI ( x , B,m) ) ==> inj −event ( acceptsR ( x , B,m) ) .
15 query x : host , k : nonce , m: b i t s t r i n g ;
16 inj −event ( termR (A, x , k ,m) ) ==> inj −event ( a c c e p t s I (A, x , k ,m) ) .
17
18 query x : host , k : nonce , k ' : nonce , m: b i t s t r i n g ;
19 event ( termR (A, x , k ,m) ) && event ( a c c e p t s I (A, x , k ' ,m) ) ==> k = k ' .
20
21 ( * Query f o r d e t e c t i n g b i l a t e r a l UKS a t t a c k s * )
22 query x : host , y : host , x ' : host , y ' : host , k : nonce , k ' : nonce ,
23 m: b i t s t r i n g , m ' : b i t s t r i n g ;
24 event ( termR ( x , y , k ,m) ) && event ( a c c e p t s I ( x ' , y ' , k ,m ' ) ) ==> x = x ' && y = y ' .
25
26 ( * Role o f t h e i n i t i a t o r w i t h i d e n t i t y xA and s e c r e t key skxA * )
27 l e t p r o c e s s I n i t i a t o r ( pkS : spkey , skA : skey , skB : s k e y ) =
28 ( * The a t t a c k e r s t a r t s t h e i n i t i a t o r by c h o o s i n g i d e n t i t y xA ,
29 and i t s i n t e r l o c u t o r xB0 .
30 We c h e c k t h a t xA i s h o n e s t ( i . e . i s A or B)
31 and g e t i t s c o r r e s p o n d i n g key .
32 *)
33 in ( c , (xA : host , hostX : h o s t ) ) ;
34 i f xA = A | | xA = B then
35 l e t skxA = i f xA = A then skA e l s e skB in
36 l e t pkxA = pk ( skxA ) in
37 ( * Real s t a r t o f t h e r o l e * )
38 ( * Message 1 : Get t h e p u b l i c key c e r t i f i c a t e f o r t h e o t h e r h o s t * )
39 out ( c , (xA , hostX ) ) ;
40 ( * Message 2 * )
41 in ( c , ms : b i t s t r i n g ) ;
42 l e t (pkX : pkey , =hostX ) = c h e c k s i g n (ms , pkS ) in
43 ( * Message 3 * )
44 new Na : nonce ;
45 l e t m3 = e n c r y p t ( ( Na , xA ) , pkX) in
5.4. VARIANTS OF THESE SECURITY PROPERTIES 81
46 out ( c , m3 ) ;
47 ( * Message 6 * )
48 in ( c , m: b i t s t r i n g ) ;
49 l e t (=Na , NX2 : nonce , =hostX ) = d e c r y p t (m, skA ) in
50 l e t m7 = e n c r y p t ( n o n c e t o b i t s t r i n g (NX2) , pkX) in
51 event termI (xA , hostX , (m3, m) ) ;
52 event a c c e p t s I (xA , hostX , Na , (m3, m, m7 ) ) ;
53 ( * Message 7 * )
54 i f hostX = A | | hostX = B then
55 (
56 out ( c , s e n c r y p t ( s e c r e t A , Na ) ) ;
57 out ( c , (m7, messtermI (xA , hostX ) ) )
58 )
59 else
60 (
61 out ( c , Na ) ;
62 out ( c , (m7, messtermI (xA , hostX ) ) )
63 ).
64
65 ( * Role o f t h e r e s p o n d e r w i t h i d e n t i t y xB and s e c r e t key skxB * )
66 l e t p r o c e s s R e s p o n d e r ( pkS : spkey , skA : skey , skB : s k e y ) =
67 ( * The a t t a c k e r s t a r t s t h e r e s p o n d e r by c h o o s i n g i d e n t i t y xB .
68 We c h e c k t h a t xB i s h o n e s t ( i . e . i s A or B ) . * )
69 in ( c , xB : h o s t ) ;
70 i f xB = A | | xB = B then
71 l e t skxB = i f xB = A then skA e l s e skB in
72 l e t pkxB = pk ( skxB ) in
73 ( * Real s t a r t o f t h e r o l e * )
74 ( * Message 3 * )
75 in ( c , m: b i t s t r i n g ) ;
76 l e t (NY: nonce , hostY : h o s t ) = d e c r y p t (m, skxB ) in
77 ( * Message 4 : Get t h e p u b l i c key c e r t i f i c a t e f o r t h e o t h e r h o s t * )
78 out ( c , (xB , hostY ) ) ;
79 ( * Message 5 * )
80 in ( c , ms : b i t s t r i n g ) ;
81 l e t (pkY : pkey ,= hostY ) = c h e c k s i g n (ms , pkS ) in
82 ( * Message 6 * )
83 new Nb : nonce ;
84 l e t m6 = e n c r y p t ( (NY, Nb , xB ) , pkY) in
85 event accept sR ( hostY , xB , (m, m6 ) ) ;
86 out ( c , m6 ) ;
87 ( * Message 7 * )
88 in ( c , m3 : b i t s t r i n g ) ;
89 i f n o n c e t o b i t s t r i n g (Nb) = d e c r y p t (m3, skB ) then
90 event termR ( hostY , xB , NY, (m, m6, m3 ) ) ;
91 i f hostY = A | | hostY = B then
92 out ( c , messtermR ( hostY , xB ) )
93 else
94 (
95 out ( c , NY) ;
96 out ( c , messtermR ( hostY , xB ) )
97 ).
98
99 (* Server *)
100 l e t p r o c e s s S ( skS : s s k e y ) =
82 CHAPTER 5. NEEDHAM-SCHROEDER: CASE STUDY
101 in ( c , ( a : host , b : h o s t ) ) ;
102 get k e y s (=b , sb ) in
103 out ( c , s i g n ( ( sb , b ) , skS ) ) .
104
105 ( * Key r e g i s t r a t i o n * )
106 l e t p ro c es s K =
107 in ( c , ( h : host , k : pkey ) ) ;
108 i f h <> A && h <> B then i n s e r t k e y s ( h , k ) .
109
110 (* S t a r t p r o c e s s *)
111 process
112 new skA : s k e y ; l e t pkA = pk ( skA ) in out ( c , pkA ) ; i n s e r t k e y s (A, pkA ) ;
113 new skB : s k e y ; l e t pkB = pk ( skB ) in out ( c , pkB ) ; i n s e r t k e y s (B, pkB ) ;
114 new skS : s s k e y ; l e t pkS = spk ( skS ) in out ( c , pkS ) ;
115 (
116 ( * Launch an unbounded number o f s e s s i o n s o f t h e i n i t i a t o r * )
117 ( ! p r o c e s s I n i t i a t o r ( pkS , skA , skB ) ) |
118 ( * Launch an unbounded number o f s e s s i o n s o f t h e r e s p o n d e r * )
119 ( ! p r o c e s s R e s p o n d e r ( pkS , skA , skB ) ) |
120 ( * Launch an unbounded number o f s e s s i o n s o f t h e s e r v e r * )
121 ( ! p r o c e s s S ( skS ) ) |
122 ( * Key r e g i s t r a t i o n p r o c e s s * )
123 ( ! p ro c e ss K )
124 )
ProVerif finds a bilateral UKS attack: if C as responder runs a session with A, it gets Na, then D as
initiator can use the same nonce Na in a session with responder B, thus obtaining two sessions, between
A and C and between D and B, that share the same key Na. (Such an attack appears more generally
when the key is determined by a single participant of the protocol.) The other properties are proved by
ProVerif.
The above script verifies syntactic secrecy of the initiator’s key Na. To be even closer to the compu-
tational definition, we can verify its secrecy using the real-or-random secrecy notion (page 60), as in the
following script (file docs/NeedhamSchroederPK-corr-ake-RoR.pv):
1 ( * Termination messages * )
2 fun messtermI ( host , h o s t ) : b i t s t r i n g [ data ] .
3 fun messtermR ( host , h o s t ) : b i t s t r i n g [ data ] .
4
5 set i g n o r e T y p e s = f a l s e .
6
7 ( * Role o f t h e i n i t i a t o r w i t h i d e n t i t y xA and s e c r e t key skxA * )
8 l e t p r o c e s s I n i t i a t o r ( pkS : spkey , skA : skey , skB : s k e y ) =
9 ( * The a t t a c k e r s t a r t s t h e i n i t i a t o r by c h o o s i n g i d e n t i t y xA ,
10 and i t s i n t e r l o c u t o r xB0 .
11 We c h e c k t h a t xA i s h o n e s t ( i . e . i s A or B)
12 and g e t i t s c o r r e s p o n d i n g key .
13 *)
14 in ( c , (xA : host , hostX : h o s t ) ) ;
15 i f xA = A | | xA = B then
16 l e t skxA = i f xA = A then skA e l s e skB in
17 l e t pkxA = pk ( skxA ) in
18 ( * Real s t a r t o f t h e r o l e * )
19 ( * Message 1 : Get t h e p u b l i c key c e r t i f i c a t e f o r t h e o t h e r h o s t * )
20 out ( c , (xA , hostX ) ) ;
21 ( * Message 2 * )
22 in ( c , ms : b i t s t r i n g ) ;
5.4. VARIANTS OF THESE SECURITY PROPERTIES 83
78 l e t p r o c e s s S ( skS : s s k e y ) =
79 in ( c , ( a : host , b : h o s t ) ) ;
80 get k e y s (=b , sb ) in
81 out ( c , s i g n ( ( sb , b ) , skS ) ) .
82
83 ( * Key r e g i s t r a t i o n * )
84 l e t p ro c es s K =
85 in ( c , ( h : host , k : pkey ) ) ;
86 i f h <> A && h <> B then i n s e r t k e y s ( h , k ) .
87
88 (* S t a r t p r o c e s s *)
89 process
90 new skA : s k e y ; l e t pkA = pk ( skA ) in out ( c , pkA ) ; i n s e r t k e y s (A, pkA ) ;
91 new skB : s k e y ; l e t pkB = pk ( skB ) in out ( c , pkB ) ; i n s e r t k e y s (B, pkB ) ;
92 new skS : s s k e y ; l e t pkS = spk ( skS ) in out ( c , pkS ) ;
93 (
94 ( * Launch an unbounded number o f s e s s i o n s o f t h e i n i t i a t o r * )
95 ( ! p r o c e s s I n i t i a t o r ( pkS , skA , skB ) ) |
96 ( * Launch an unbounded number o f s e s s i o n s o f t h e r e s p o n d e r * )
97 ( ! p r o c e s s R e s p o n d e r ( pkS , skA , skB ) ) |
98 ( * Launch an unbounded number o f s e s s i o n s o f t h e s e r v e r * )
99 ( ! p r o c e s s S ( skS ) ) |
100 ( * Key r e g i s t r a t i o n p r o c e s s * )
101 ( ! p ro c e ss K )
102 )
Line 36 outputs either the real key Na or a fresh random key, and the goal is to prove that the attacker
cannot distinguish these two situations. In order to obtain termination, we require that all code including
the attacker be well-typed (Line 5). This prevents in particular the generation of certificates in which
the host names are themselves nested signatures of unbounded depth. Unfortunately, ProVerif finds
a false attack in which the output key is used to build message 3 (either encrypt((Na, A), pkB) or
encrypt((random, A), pkB)), send it to the responder, which replies with message 6 (that is, encrypt((Na,
Nb, A), pkA) or encrypt((random, Nb, A), pkA)), which is accepted by the initiator if and only if the
key is the real key Na.
A similar verification can be done with other possible keys (for instance, Nb, h(Na), h(Nb), h(Na,Nb)
where h is a hash function). We leave these verifications to the reader and just note that the false attack
above disappears for the key h(Na) (but we still have to restrict ourselves to a well-typed attacker).
In order to obtain this result, a trick is necessary: if random is generated at the end of the protocol,
ProVerif represents it internally as a function of the previously received messages, including message 6.
This leads to a false attack in which two different values of random (generated after receiving different
messages 6) are associated to the same Na. This false attack can be eliminated by moving the generation
of random just after the generation of Na.
Advanced reference
This chapter introduces ProVerif’s advanced capabilities. We provide the complete grammar in Ap-
pendix A.
87
88 CHAPTER 6. ADVANCED REFERENCE
4 f r e e d Q : c h a n n e l [ private ] .
5
6 fun s e n c ( nat , b i t s t r i n g ) : b i t s t r i n g .
7 reduc f o r a l l K: b i t s t r i n g ,M: nat ; s d e c ( s e n c (M,K) ,K) = M.
8
9 event CheckNat ( nat ) .
10
11 query i : nat ; event ( CheckNat ( i ) ) ==> i s n a t ( i ) .
12
13 let P =
14 in ( c , x : b i t s t r i n g ) ;
15 in ( d P , ( i : nat , j : nat ) ) ;
16 l e t j ' : nat = s d e c ( x , k ) in
17 event CheckNat ( i ) ;
18 event CheckNat ( j ) ;
19 if j ' > j
20 then out ( d P , ( i +1, j ' ) )
21 e l s e out ( d P , ( i , j ) ) .
22
23 let Q =
24 in ( d Q , i : nat ) ;
25 out ( c , s e n c ( i , k ) ) ;
26 out ( d Q , i +1).
27
28 process
29 out ( d P , ( 0 , 0 ) ) | out ( d Q , 0 ) | ! P | ! Q
In this protocol, the processes P and Q share a private key k and they both have a memory cell
respectively represented by the private channels d P and d Q. Every time the process Q increments the
value stored in its memory cell, it also outputs the previous value encrypted with the shared key k, i.e.
out(c,senc(i ,k)). On the other hand, the process P stores in its memory cell two values : the number of
time it received a fresh encryption from Q, represented by i :nat in in(d P,(i :nat, j :nat)) and the last
value it received from Q, represented by j :nat.
We aim to prove that the values of the memory cell of P are always natural numbers, which is
represented by the query:
query i : nat ; event ( CheckNat ( i ) ) ==> i s n a t ( i ) .
However, verifying this protocol with ./proverif docs/ex induction.pv | grep "RES" produces
the following output:
RESULT event ( CheckNat ( i 2 ) ) ==> i s n a t ( i 2 ) cannot be proved .
If we look more closely at the output, we can observe that ProVerif considers the following reachable
goal
i s n o t n a t ( i 2 + 1 ) && j 1 ≥ j 2 + 1 && mess ( d P [ ] , ( i 2 , j 2 ) ) &&
mess ( d Q [ ] , j 1 ) && mess ( d Q [ ] , j ' 1 ) −> end ( CheckNat ( i 2 + 1 ) )
To ensure termination, ProVerif avoids resolving upon facts that would lead to trivial infinite loops. This
is the case for the facts representing the memory cells, which are mess(d P[],(i 2, j 2 )), mess(d Q[],j 1),
and mess(d Q[],j' 1), so resolution stops with the clause above. Since the clause contradicts the query,
ProVerif concludes that it cannot prove the query.
By adding the option induction after the query as follows
query i : nat ; event ( CheckNat ( i ) ) ==> i s n a t ( i ) [ induction ] .
ProVerif would initially generate the following reachable goal:
j 1 ≥ j 2 + 1 && b e g i n ( CheckNat ( j 2 ) ) && b e g i n ( CheckNat ( i 2 ) ) &&
mess ( d P [ ] , ( i 2 , j 2 ) ) && mess ( d Q [ ] , j 1 ) −> end ( CheckNat ( i 2 + 1 ) )
6.1. PROVING CORRESPONDENCE QUERIES BY INDUCTION 89
Furthermore, ProVerif understands that the event CheckNat(i 2) occurs strictly before CheckNat(i 2 + 1).
By applying the induction hypothesis on CheckNat(i 2), it adds is nat ( i 2 ) in the hypotheses of the
clause, yielding
i s n a t ( i 2 ) && j 1 ≥ j 2 + 1 && b e g i n ( CheckNat ( j 2 ) ) && b e g i n ( CheckNat ( i 2 ) ) &&
mess ( d P [ ] , ( i 2 , j 2 ) ) && mess ( d Q [ ] , j 1 ) −> end ( CheckNat ( i 2 + 1 ) )
Since this clause does not contradict the query, ProVerif is able to prove the query: Verifying this protocol
with ./proverif docs/ex induction proof.pv | grep "RES" produces the output
RESULT event ( CheckNat ( i 2 ) ) ==> i s n a t ( i 2 ) i s t r u e .
Remark. When the setting inductionQueries is set to true, all queries are proved by induction. In
such a case, one can use the option [noInduction] on one specific query to enforce that it is not proved
by induction.
FINAL RESULT:
RESULT mess ( d Q [ ] , i 2 ) ==> i s n a t ( i 2 ) cannot be proved .
RESULT event ( CheckNatQ ( i 2 ) ) ==> i s n a t ( i 2 ) cannot be proved .
RESULT event ( CheckNat ( i 2 ) ) ==> i s n a t ( i 2 ) i s t r u e .
The proof of a group of queries by induction is done in multiple steps. In the first step, ProVerif
assumes that the inductive hypotheses of all individual queries hold and it tries to prove the group
of queries under this assumption. If the verification succeeds, then ProVerif concludes that the group
of queries is true. When however ProVerif cannot verify all the queries, it will refine the inductive
hypotheses to consider. More specifically, it will try to prove the group of queries again, but only under
the inductive hypotheses of the individual queries that it was previously able to prove. ProVerif repeats
this refinement of inductive queries until it can prove all of them.
In our example, the first three partial results correspond to the first step where ProVerif assumed as
inductive hypotheses the three queries. Under this assumption, it was only able to prove two of them,
namely event(CheckNat(i 2)) ==> is nat(i 2) and event(CheckNatQ(i 2)) ==> is nat(i 2). The next
three partial results therefore correspond to the second step where ProVerif only assumes as inductive hy-
potheses the queries event(CheckNat(i 2)) ==> is nat(i 2) and event(CheckNatQ(i 2)) ==> is nat(i 2).
In this second step, the query event(CheckNatQ(i 2)) ==> is nat(i 2) cannot be proved anymore. Since
ProVerif did not prove the two inductive queries, it refines again its inductive hypotheses by consider-
ing only event(CheckNat(i 2)) ==> is nat(i 2). Since it is able to prove this query in the third step,
ProVerif can conclude that it is true.
Note that the verification summary only displays the final results.
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
V e r i f i c a t i o n summary :
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
We explain in Section 6.7.2 why ProVerif is not able to prove the query mess(d Q[],i 2) ==> is nat(i 2)
and how one can help ProVerif to prove it.
Remark. By default, for a group of queries, ProVerif does not apply the induction hypothesis during
saturation, since some of the queries may not be true. The user may add the option proveAll to the
group:
query x1 : t1 , . . . , xm : tm ; q1 ; . . . ; qn [ induction , proveAll ] .
in order to tell ProVerif that it should prove all queries; it can then use them as induction hypothesis
during saturation. In case some of the queries cannot be proved, all queries of the group are considered
as not proved since the proof was attempted with an induction hypothesis that does not hold.
6.2. AXIOMS, RESTRICTIONS, AND LEMMAS 91
facts of H can only be non-injective events, equalities, inequalities, disequalities, attacker facts,
message facts, or table facts;
F1 , . . . , Fm can only be non-injective events, attacker facts, message facts, table facts, inequalities,
disequalities, or is nat ;
The semantics of lemmas, axioms, and restrictions is the same as the semantics of queries, except
for lemmas, axioms, and restrictions that conclude attacker or table facts: For queries, we allow the
attacker facts in the conclusion to be derived by computations from attacker knowledge collected before
F1 , . . . , Fm , and the table facts to be derived by phase changes from table facts true before F1 , . . . , Fm .
For lemmas, axioms, and restrictions, such additional computations and phase changes are forbidden
and the attacker and table facts in the conclusion themselves must be true before F1 , . . . , Fm .
These lemmas, axioms, and restrictions will be used internally by ProVerif to remove, simplify, or
instantiate clauses during the saturation procedure of the main query. Intuitively, a lemma F1 && . . . &&
Fm ==> H is applied on a clause H ′ −> C ′ when there exists a substitution σ such that Fi σ ⊆ H ′ for
all i = 1 . . . m; and the resulting clause being H ′ && Hσ −> C ′ .
For attacker, message, and table facts in H, a so-called “blocking” version of them is added to the
clause instead of the fact itself: ProVerif does not perform resolution on blocking facts and keeps them,
which enables the proof of such facts in queries. For example, writing the trivially true lemma
lemma x1 : t1 , . . . , xn : tn ; attacker ( M ) ==> attacker ( M ) .
adds the blocking version of attacker facts instance of attacker(M ) to clauses that contain such attacker
facts in hypothesis. Then, the blocking facts are preserved in subsequent resolutions, enabling the proof
of queries . . . ==> attacker(M ). Without this lemma, such queries could not be proved because
attacker(M ) is resolved upon, making the information that attacker(M ) was true disappear.
When restrictions are declared, ProVerif will prove the main queries only on all traces (resp. bitraces)
of the input process (resp. biprocess) that satisfy the restrictions.
To preserve soundness, ProVerif proves all the lemmas as if they were standard queries (still taking
into account the different semantics mentioned above) before using them in the saturation procedure.
ProVerif will produce an error if it is not able to prove one of the lemmas. Note that if restrictions are
also declared, the lemmas are proved only on the traces satisfying the restrictions. However, ProVerif
assumes that all axioms are true on the input process and does not attempt to prove them. When
axioms are declared, it is important to note that a security proof holds assuming that the axioms also
hold. Axioms are typically useful for hand-proved properties that cannot be proved with ProVerif.
Depending on the lemmas, restrictions, and axioms declared, precision and termination of ProVerif
can be improved. ProVerif ignores the number of repetitions of actions due to the transformation of
processes into Horn clauses. Hence, the following example yields a false attack:
new k : key ; out ( c , s e n c ( s e n c ( s , k ) , k ) ) ;
in ( c , x : b i t s t r i n g ) ; out ( c , s d e c ( x , k ) )
where c is a public channel, s is a private free name which should be kept secret, and senc and
sdec are symmetric encryption and decryption respectively. ProVerif thinks that one can decrypt
senc(senc(s ,k),k) by sending it to the input, so that the process replies with senc(s ,k), and then sending
92 CHAPTER 6. ADVANCED REFERENCE
this message again to the input, so that the process replies with s. However, this is impossible in reality
because the input can be executed only once.
However, a generic transformation on processes, presented in [CCT18], using events allows to partially
take into account the number of repetitions of actions. Intuitively after each input, an event recording
the input message is added.
new s t : stamp ; new k : key ; out ( c , s e n c ( s e n c ( s , k ) , k ) ) ;
in ( c , x : b i t s t r i n g ) ; event UAction ( s t , x ) ; out ( c , s d e c ( x , k ) )
It was shown in [CCT18] that adding such events preserves the security properties and, moreover, that
the following query always holds:
f o r a l l s t : stamp , x : b i t s t r i n g , y : b i t s t r i n g ;
event ( UAction ( s t , x ) ) && event ( UAction ( s t , y ) ) ==> x = y .
Intuitively, the input action in(c, x: bitstring ) is executed at most once for each value of the stamp st.
Hence, if the value of the stamp st is the same, then the value of the input message x must also be the
same. Ideally, we would declare this property as a lemma, but ProVerif is unable to prove it. Hence,
since that property was shown by hand in [CCT18], we can declare it as an axiom. In the following
complete script, ProVerif is thus able to prove the secrecy of s.
1 free c : channel .
2 f r e e s : b i t s t r i n g [ private ] .
3
4 type key .
5 type stamp .
6 fun s e n c ( b i t s t r i n g , key ) : b i t s t r i n g .
7 reduc f o r a l l x : b i t s t r i n g , y : key ; s d e c ( s e n c ( x , y ) , y ) = x .
8
9 event UAction ( stamp , b i t s t r i n g ) .
10
11 axiom s t : stamp , x : b i t s t r i n g , y : b i t s t r i n g ;
12 event ( UAction ( s t , x ) ) && event ( UAction ( s t , y ) ) ==> x = y .
13
14 query attacker ( s ) .
15
16 process
17 new k : key ; out ( c , s e n c ( s e n c ( s , k ) , k ) ) ;
18 in ( c , x : b i t s t r i n g ) ;
19 new s t [ ] : stamp ;
20 event UAction ( s t , x ) ;
21 out ( c , s d e c ( x , k ) )
In fact, this generic transformation has been natively added in ProVerif and can be activated by
adding the option [precise] after the input. In our example, it would correspond to the following
process.
new k : key ; out ( c , s e n c ( s e n c ( s , k ) , k ) ) ;
in ( c , x : b i t s t r i n g ) [ precise ] ; out ( c , s d e c ( x , k ) )
Similarly, the option [precise] can be added in the get . . . in P else Q and let . . . suchthat (see
Section 6.3) constructs as follows.
get d(T1 , . . . , Tn ) [ precise ] in P e l s e Q
get d(T1 , . . . , Tn ) suchthat M [ precise ] in P e l s e Q
1 free c : channel .
2 f r e e s : b i t s t r i n g [ private ] .
3
4 type key .
5 fun s e n c ( b i t s t r i n g , key ) : b i t s t r i n g .
6 reduc f o r a l l x : b i t s t r i n g , y : key ; s d e c ( s e n c ( x , y ) , y ) = x .
7
8 set p r e c i s e A c t i o n s = t r u e .
9
10 query attacker ( s ) .
11
12 process
13 new k : key ; out ( c , s e n c ( s e n c ( s , k ) , k ) ) ;
14 in ( c , x : b i t s t r i n g ) ;
15 out ( c , s d e c ( x , k ) )
Subterm predicate
We allow in the premise of an axiom or a restriction a special predicate subterm(M,N). This predicate
holds when M is a subterm of N modulo the equational theory. For example, the following restriction
r e s t r i c t i o n x : b i t s t r i n g , y : nat ; event (A( x ) ) && subterm ( y , x ) ==> y <> 0
restricts the verification of queries to traces such that if the event A(M) is emitted then 0 is not a subterm
of M .
Note that the predicate subterm is not allowed in lemmas or queries.
Order of lemmas
As for queries, lemmas can be either grouped inside a single lemma declaration or they can be separately
declared with multiple lemma declarations. While grouping the lemmas may improve the performance of
ProVerif, declaring them separately may improve its completeness. Indeed, ProVerif proves the lemmas
in the order they are declared in the input file. Moreover, it also uses proven lemmas to help proving
new lemmas. For example, by declaring the following lemmas,
lemma x11 : t11 , . . . , x1n : t1n ; cq1 .
lemma x21 : t21 , . . . , x2n : t2n ; cq2 .
lemma x31 : t31 , . . . , x3n : t3n ; cq3 .
ProVerif first tries to prove cq1 alone then tries to prove cq2 by using cq1 in the saturation procedure
and finally tries to prove cq3 by using cq1 and cq2 in the saturation procedure.
The order of axiom and restriction declarations does not matter as they are globally applied to all
lemmas and queries.
Options
Adding lemmas in most cases improves the completeness of ProVerif. However, it is less clear how lem-
mas influence its termination as it heavily depends on the process and declared lemmas. Thus, lemmas
can be declared with several options to parameterize how lemmas should be applied during the satura-
tion procedure. The following exclusive options are available: noneSat, discardSat, instantiateSat
(default), fullSat. The option noneSat indicates that the lemma should not be used in the saturation.
The saturation behaves as if the lemma was declared as a query. The option discardSat enforces that
a lemma should only be applied if its application on a Horn clause renders its hypotheses unsatisfiable.
The option instantiateSat enforces that the lemma should instantiate at least one variable of the Horn
clause or render the hypotheses of the clause unsatisfiable. Finally, with the option fullSat, the lemma
is applied without restriction. These options can also be given for declared axioms.
When defining a lemma with the option removeEvents, ProVerif removes from clauses the events
that correspond to the premise of this lemma and that do not seem useful anymore because the lemma
94 CHAPTER 6. ADVANCED REFERENCE
has already been applied or it will never be applicable using this event. The latter is approximated hence
may result in a loss of precision (ProVerif remains sound) but it also speeds up the resolution and may
even allow it to terminate. The opposite option keepEvents ensures that ProVerif will never remove
from clauses any event corresponding to the premise of this lemma. This is the default. The default
can be changed using the global setting removeEventsForLemma (see Section 6.6.2), so that all lemmas
without explicit option can be considered as being declared with the option removeEvents.
In the case of correspondence queries and once the saturation completes, ProVerif will also rely on
lemmas, restrictions, and axioms when verifying queries. We therefore have similar options parame-
terizing how lemmas should be applied during the verification procedure: noneVerif, discardVerif,
instantiateVerif, and fullVerif (default). Note that, in contrast to the default option for the satura-
tion procedure, ProVerif applies lemmas and axioms without restriction by default during the verification
procedure.
Finally, lemmas can be declared with the option maxSubset. By default, when ProVerif is unable
to prove a lemma or a group of lemmas, it raises an error. With the option maxSubset, ProVerif aims
to find the maximal subset of provable lemmas in a group and discards the remaining ones. Soundness
is guaranteed by the fact that ProVerif only keeps the lemmas in the group that it is able to prove.
Note that this option is not allowed for axioms. Moreover, this option is not exclusive with the options
noneSat, discardSat, instantiateSat, and fullSat. For example, in the following script, ProVerif
first tries to find the maximal subset S of lemmas cq1 , . . . , cqn that it can prove. Second, it will prove
the query attacker(s) by only using the lemmas in S when they refute the hypotheses of Horn clauses
during the saturation procedure for the query attacker(s).
lemma x1 : t1 , . . . , xn : tn ; cq1 ; . . . ; cqn [ maxSubset , discardSat ] .
query attacker ( s ) .
As any query, a lemma can be proved by induction by adding the option induction. By default, since
a lemma must be proved by ProVerif (otherwise an error is raised), the inductive hypothesis corresponding
to the lemma is also applied during the saturation procedure, which may enforce its termination. (For
groups of queries, this happens only with the option proveAll.) When a group of lemmas is declared
with the option maxSubset, the inductive hypothesis is not applied during the saturation procedure
and is only applied during the verification procedure (similarly to the default situation for queries). Note
that the options noneSat, noneVerif, discardSat, . . . can also modify how the inductive hypothesis
is applied during the saturation and verification procedures.
Remark 1. When the setting inductionLemmas is set to true, all lemmas are proved by induction.
In such a case, one can use the option [noInduction] on one specific lemma to enforce that it is not
proved by induction. Moreover, the default applications of lemmas during the saturation and verification
procedures can also be modified using global settings (see Section 6.6.2).
Remark 2. When ProVerif fails to prove an equivalence query (or a real or random query) on the
initial process, ProVerif tries to generate simplified processes on which to prove the query. Though the
simplification preserves equivalence properties, an axiom (resp. restriction) that holds on the initial
process does not necessarily hold on the simplified process. Therefore by default, ProVerif does not
consider the axioms (resp. restrictions) on these simplified processes. These axioms (resp. restrictions)
can however still be considered on simplified processes by declaring the axiom (resp. restriction) with
the option keep.
on a biprocess. Since ProVerif does not saturate the same set of Horn clauses, one may hope that
ProVerif terminates for the proof of the lemma which would then be used to enforce termination for the
equivalence proof. As an example, consider the simplified Yubikey protocol introduced in Section 6.1
modified as follows.
1 free c : channel .
2 free k : b i t s t r i n g [ private ] .
3 free d P : c h a n n e l [ private ] .
4 free d Q : c h a n n e l [ private ] .
5
6 fun s e n c ( nat , b i t s t r i n g ) : b i t s t r i n g .
7 reduc f o r a l l K: b i t s t r i n g ,M: nat ; s d e c ( s e n c (M,K) ,K) = M.
8
9 let P =
10 in ( c , x : b i t s t r i n g ) ;
11 in ( d P , ( i : nat , j : nat ) ) ;
12 l e t j ' : nat = s d e c ( x , k ) in
13 if j ' > j
14 then out ( d P , ( i +1, choice [ j ' , j ' + 1 ] ) )
15 e l s e out ( d P , ( i , j ) ) .
16
17 let Q =
18 in ( d Q , i : nat ) ;
19 out ( c , s e n c ( i , k ) ) ;
20 out ( d Q , i +1).
21
22 process
23 out ( d P , ( 0 , 0 ) ) | out ( d Q , 0 ) | ! P | ! Q
In this protocol, we compare the case where the process P either records the value encrypted by Q or
its value plus one. Executing ProVerif on docs/ex lemmas equiv.pv yields many termination warnings
on increasingly large clauses (a good indicator of the non-termination of the saturation procedure).
However, the clauses of almost all warnings contain the fact mess2(d Q[],j 1 ,d Q[], j ' 2) suggesting that
ProVerif is considering derivations where the value of the memory cell stored by Q differs on the two
sides of the equivalence. One can use a lemma to show that these derivations cannot happen and so
should be discarded.
The function choice can be used in a lemma to specify different terms on the two sides of the bitrace.
In our example, the following lemma can be declared (see docs/ex lemmas equiv proof.pv)
lemma i : nat , i ' : nat ; mess ( d Q , choice [ i , i ' ] ) ==> i = i ' .
Executing ProVerif on docs/ex lemmas equiv proof.pv, one can see that the lemma is actually encoded
as
mess2 ( d Q [ ] , i 3 , d Q [ ] , i ' ) ==> i 3 = i '
and that ProVerif is able to prove the lemma and the equivalence.
Remark 1. In the example docs/ex lemmas equiv proof.pv, ProVerif first proves the lemma in the
biprocess given as input. However, ProVerif fails to prove the equivalence on this biprocess despite the
lemma. Thus it simplifies the biprocess into a new one (named biprocess 1). Even though the lemma was
proved on the input biprocess, it does not necessarily imply that the lemma holds on biprocess 1. It was
only shown that simplification of biprocesses preserves observational equivalence. Therefore, ProVerif
proves the lemma on biprocess 1 again, and finally proves the desired equivalence. In this case, the
verification summary just shows the results on the biprocess on which the equivalence was proved, as
follows:
--------------------------------------------------------------
Verification summary:
96 CHAPTER 6. ADVANCED REFERENCE
Query(ies):
- Observational equivalence is true.
Associated lemma(s):
- Lemma mess(d_Q[],choice[i_3,i']) ==> i_3 = i' encoded as
mess2(d_Q[],i_3,d_Q[],i') ==> i_3 = i' is true in biprocess 1.
--------------------------------------------------------------
Remark 2. Lemmas on biprocesses can also be proved by induction by adding the option induction.
Remark 3. In fact, to prove a lemma on a biprocess, ProVerif does not remove all clauses with
bad as conclusion during the initial generation of Horn clauses. It preserves the clauses corresponding
to the attacker power to distinguish messages but removes the ones that focus on the control flow of
the biprocess. Keeping these clauses allows ProVerif to activate an optimization during the saturation
procedure which improves termination. After completion of the saturation procedure, if bad is shown to
be derivable, then ProVerif considers that it cannot prove the lemma. Leaving these clauses with bad as
conclusion does not sacrifice soundness since the lemma is rejected when bad is derivable.
Lemmas and axioms cqi apply to queries without public variables and that are not real-or-random
secrecy queries, that is, correspondence queries with public variables cq, strong secrecy queries,
off-line guessing attacks queries and secrecy queries secret x [reachability], as well as equivalence
queries between two processes. Only in the last case, the lemmas or axioms may contain the
function choice (but not necessarily).
Lemmas and axioms cqi for { public vars y1 , . . . , ym } apply to queries with public variables
y1 , . . . , ym and that are not real-or-random secrecy queries, that is, cq public vars y1 , . . . , ym
and secret x public vars y1 , . . . , ym [reachability]. These lemmas and axioms must not contain
the function choice.
Lemmas and axioms cqi for { secret x public vars y1 , . . . , ym [real or random] } apply only to
the query secret x public vars y1 , . . . , ym [real or random], and similarly lemmas and axioms
cqi for { secret x [real or random] } apply only to the query secret x [real or random]. These
lemmas and axioms may contain the function choice (but not necessarily).
6.3 Predicates
ProVerif supports predicates defined by Horn clauses as a means of performing complex tests or computa-
tions. Such predicates are convenient because they can easily be encoded into the internal representation
of ProVerif which also uses clauses. Predicates are defined as follows:
pred p(t1 , . . . , tk ) .
declares a predicate p of arity k that takes arguments of types t1 , . . . , tk . The predicates attacker,
mess, ev, and evinj are reserved for internal use by ProVerif and cannot be declared by the user. The
declaration
clauses C1 ; . . . ; Cn .
declares the clauses C1 , . . . , Cn which define the meaning of predicates. Clauses are built from facts which
can be p(M1 , . . . , Mk ) for some predicate declared by pred, M1 = M2 , or M1 <> M2 . The clauses Ci
can take the following forms:
f o r a l l x1 : t1 , . . . , xn : tn ; F
which means that the fact F holds for all values of the variables x1 , . . . , xn of type t1 , . . . , tn
respectively; F must be of the form p(M1 , . . . , Mk ).
which means that F1 , . . . , and Fm imply F for all values of the variables x1 , . . . , xn of type t1 , . . . , tn
respectively; F must be of the form p(M1 , . . . , Mk ); F1 , . . . , Fm can be any fact.
In all clauses, the fact F is considered to hold only if its arguments do not fail and when the arguments
of the facts in the hypothesis of the clause do not fail: for facts p(M1 , . . . , Mk ), M1 , . . . , Mk do not fail,
for equalities M1 = M2 and disequalities M1 <> M2 , M1 and M2 do not fail.
Additionally, ProVerif allows the following equivalence declaration in place of a clause
f o r a l l x1 : t1 , . . . , xn : tn ; F1 && . . . && Fm <−> F
which means that F1 , . . . , and Fm hold if and only if F holds; F1 , . . . , Fm , F must be of the form
p(M1 , . . . , Mk ). Moreover, σFi must be of smaller size than σF for all substitutions σ and two facts
F of different equivalence declarations must not unify. (ProVerif will check these conditions.) This
equivalence declaration can be considered as an abbreviation for the clauses
98 CHAPTER 6. ADVANCED REFERENCE
Predicate evaluation. Predicates can be used in if tests. As a trivial example, consider the script:
pred p ( b i t s t r i n g , b i t s t r i n g ) .
elimtrue x : b i t s t r i n g , y : b i t s t r i n g ; p ( x , y ) .
event e .
query event ( e ) .
Example: Modeling sets with predicates. As an example, we will demonstrate how to model sets
with predicates (see file docs/ex predicates.pv).
type b s e t .
fun c o n s s e t ( b i t s t r i n g , b s e t ) : b s e t [ data ] .
const emptyset : b s e t [ data ] .
Sets are represented by lists: emptyset is the empty list and consset(M ,N ) concatenates M at the head
of the list N .
pred mem( b i t s t r i n g , b s e t ) .
clauses
f o r a l l x : b i t s t r i n g , y : b s e t ; mem( x , c o n s s e t ( x , y ) ) ;
f o r a l l x : b i t s t r i n g , y : b s e t , z : b i t s t r i n g ; mem( x , y ) −> mem( x , c o n s s e t ( z , y ) ) .
The predicate mem represents set membership. The first clause states that mem(M ,N ) holds for some
terms M , N if N is of the form consset(M ,N ′ ), that is, M is at the head of N . The second clause states
that mem(M ,N ) holds if N = consset(M ′ ,N ′ ) and mem(M ,N ′ ) holds, that is, if M is in the tail of N .
We conclude our example with a look at the following ProVerif script:
1 event e.
2 event e '.
3 query event ( e ) .
4 query event ( e ' ) .
5
6 type b s e t .
7 fun c o n s s e t ( b i t s t r i n g , b s e t ) : b s e t [ data ] .
8 const emptyset : b s e t [ data ] .
9 pred mem( b i t s t r i n g , b s e t ) .
10 clauses
11 f o r a l l x : b i t s t r i n g , y : b s e t ; mem( x , c o n s s e t ( x , y ) ) ;
12 f o r a l l x : b i t s t r i n g , y : b s e t , z : b i t s t r i n g ; mem( x , y ) −> mem( x , c o n s s e t ( z , y ) ) .
13
14 process
15 new a : b i t s t r i n g ; new b : b i t s t r i n g ; new c : b i t s t r i n g ;
16 l e t x = c o n s s e t ( a , emptyset ) in
17 l e t y = c o n s s e t ( b , x ) in
18 l e t z = c o n s s e t ( c , y ) in (
19 i f mem( a , z ) then
20 i f mem( b , z ) then
21 i f mem( c , z ) then
22 event e
23 )|(
24 l e t w : b i t s t r i n g suchthat mem(w, x ) in event e '
25 )
As expected, ProVerif demonstrates reachability of both e and e′ . Observe that e′ is reachable by binding
the name a to the variable w.
Using predicates in queries. User-defined predicates can also be used in queries, so that the grammar
of facts F in Figure 4.3 is extended with user-defined facts p(M1 , . . . , Mn ). As an example, the query
query x : b i t s t r i n g ; event ( e ( x ) ) ==> p ( x )
holds when, if the event e(x) has been executed, then p(x) holds. (If this property depends on the code
of the protocol but not on the definition of p, for instance because the event e(x) can be executed only
after a successful test if p(x) then, a good way to prove this query is to declare the predicate p with
option block and to omit the clauses that define p, so that ProVerif does not use the definition of p.
See below for additional information on the predicate option block.)
100 CHAPTER 6. ADVANCED REFERENCE
where f is a data constructor. (Note that it corresponds to the case in which p is the membership
predicate and f (x, y) represents the union of element x and set y.)
memberOptim enables the following optimization: attacker(x) && p(M1 , x) && . . . && p(Mn , x)
where p is declared memberOptim is replaced with attacker(x) && attacker(M1 ) && . . . &&
attacker(Mn ) when x does not occur elsewhere (just take x = f (M1 , . . . f (Mn , x′ )) and notice that
attacker(x) if and only if attacker(M1 ), . . . , attacker(Mn ), and attacker(x′ )).
User-defined predicates are allowed after ==> in lemmas and axioms. When these predicates are not
blocking, applying the lemma adds a blocking version of the predicate to the hypothesis of the clause,
not the predicate itself.
the scope of. For example, the name a in the process new a:nonce is not in the scope of any vari-
ables and hence the name is modeled without arguments as a[ ]; whereas the name b in the process
in(c ,( x: bitstring ,y: bitstring )); new b:nonce is in the scope of variables x, y and hence will be repre-
sented by b[x=M ,y=N ] where the terms M , N are the values of x and y at run time, respectively.)
Consider, for example, the process:
1 free c : channel .
2 f r e e A: b i t s t r i n g .
3 event e ( b i t s t r i n g ) .
4 query event ( e (new a [ x=A; y=new B ] ) ) .
5
6 process
7 ( in ( c , ( y : b i t s t r i n g , x : b i t s t r i n g ) ) ; new a : b i t s t r i n g ; event e ( a ) )
8 | (new B : b i t s t r i n g ; out ( c , B) )
The query query event(e(new a[x=A;y=new B])) tests whether event e can be executed with argument
a name created by the restriction new a:bitstring when x is A and y is a name created by the restriction
new B:bitstring. In the example process, such an event can be executed.
Furthermore, in addition to the value of the variables defined above the considered restriction new,
one can also specify the value of !i, which represents the session identifier associated with the i-th
replication above the considered new, where i is a positive integer. (Replications are numbered from the
top of the process: !1 corresponds to the first replication at the top of the syntax tree.) These session
identifiers take a different value in each copy of the process created by the replication. It does not make
much sense to give a non-variable value to these session identifiers, but they can be useful to designate
names created in the same copy or in different copies of the process. Consider the following example:
1 free c : channel .
2 event e ( b i t s t r i n g , b i t s t r i n g ) .
3 query i : s i d ; event ( e (new A [ ! 1 = i ] , new B [ ! 1 = i ] ) ) .
4
5 process
6 ( in ( c , ( y : b i t s t r i n g , x : b i t s t r i n g ) ) ; event e ( x , y ) )
7 | ! (new A: b i t s t r i n g ; new B : b i t s t r i n g ; out ( c , ( A, B ) ) )
The query event(e(new A[!1 = i], new B[!1 = i])) tests if one can execute events e(x,y) where x is a
name created at the restriction new A: bitstring and y is a name created at new B: bitstring in the
same copy as x (of session identifier i).
It is also possible to use let bindings in queries: let x = M in binds the term M to x inside a
query. Such bindings can be used anywhere in a query: they are added to reachability or correspondence
queries, hypotheses, and facts in the grammar of correspondence assertions given in Figure 4.3. In such
bindings, the term M must be a term without destructor. These bindings are specially useful in the
presence of references to bound names. For instance, in the query query attacker(h((new n,new n))),
the two occurrences of new n may represent different names created at the same restriction new n:t
in the process. In contrast, in the query query let x = new n in attacker(h((x,x))), x represents any
name created at the restriction new n:t and (x,x) is a pair containing twice the same name. Let bindings
let x = M in therefore allow us to designate several times exactly the same value, even if the term M
may designate several possible values due to the use of the new n construct.
References to bound names in queries were used, for instance, in [BC08].
where proverif is ProVerif’s binary, ⟨filename⟩ is the input file, and the command-line parameters
[⟨options⟩] are of the following form:
6.6. PROVERIF OPTIONS 103
-in ⟨format⟩
Choose the input format (horn, horntype, pi, or pitype). When the -in option is absent, the
input format is chosen according to the file extension, as detailed below. The input format described
in this manual is the typed pi calculus, which corresponds to the option -in pitype, and is the
default when the file extension is .pv. We recommend using this format. The other formats are
no longer actively developed. Input may also be provided using the untyped pi calculus (option
-in pi, the default when the file extension is .pi), typed Horn clauses (option -in horntype, the
default when the file extension is .horntype), and untyped Horn clauses (option -in horn, the
default for all other file extensions). The untyped Horn clauses and the untyped pi calculus input
formats are documented in the file docs/manual-untyped.pdf.
-out ⟨format⟩
Choose the output format, either solve (analyze the protocol) or spass (stop the analysis before
resolution, and output the clauses in the format required for use in the Spass first-order theorem
prover, see http://www.spass-prover.org/). The default is solve. When you select -out spass,
you must add the option -o ⟨filename⟩ to specify the file in which the clauses will be output.
-TulaFale ⟨version⟩
For compatibility with the web service analysis tool TulaFale (see the tool download at http:
//research.microsoft.com/projects/samoa/). The version number is the version of TulaFale
with which you would like compatibility. Currently, only version 1 is supported.
-lib ⟨filename⟩
Specify a particular library file. Library files may contain declarations (including process macros).
They are therefore useful for code reuse. Library files must be given the file extension .pvl, and
this extension can be omitted from ⟨filename⟩. For example, the library file crypto.pvl can be
specified as -lib crypto. Multiple libraries can be specified by using -lib for each library. The
libraries are loaded in the same order as they appear on the command line.
When no library is mentioned, ProVerif looks for a library named default.pvl, in the current
directory and in the directory that contains the executable of ProVerif, and loads it if it is found.
You can use this library for instance to give default settings.
-graph ⟨directory⟩
This option is available only when the command-line option -html ⟨directory⟩ is not set. It gen-
erates PDF files containing graphs representing traces of attacks that ProVerif had found. These
PDF files are stored in the specified directory. That directory must already exist. By default,
graphviz is used to create these graphs from the dot files generated by ProVerif. However, the user
may specify a command of his own choice to generate graphs with the command line argument
-commandLineGraph. Two versions of the graphs are available: a standard and a detailed version.
The detailed version is built when set traceDisplay = long. has been added to the input file.
-html ⟨directory⟩
This option is available only when the command-line option -graph ⟨directory⟩ is not set. It
generates HTML output in the specified directory. That directory must already exist. ProVerif
may overwrite files in that directory, so you should create a fresh directory the first time you use
this option. You may reuse the same directory for several runs of ProVerif if you do not want to
keep the output of previous runs.
ProVerif includes a CSS file cssproverif.css in the main directory of the distribution. You should
copy that file to ⟨directory⟩. You may edit it to suit your preferences if you wish.
After running ProVerif, you should open the file ⟨directory⟩/index.html with your favorite web
browser to display the results.
If graphviz is installed and you did not specify a command line with the option -commandLineGraph,
then drawings of the traces are available by clicking on graph trace. Two versions of the
drawings are available: a standard and a detailed version. The detailed version is built when
set traceDisplay = long. has been added to the input file.
104 CHAPTER 6. ADVANCED REFERENCE
-parse-only
This option stops ProVerif after parsing the input file. It just reports errors in the input file if any.
It is useful when calling ProVerif from some IDE in order to report errors.
-help or --help
Display a short summary of command-line options
6.6.2 Settings
The manner in which ProVerif performs analysis can be modified by the use of parameters defined in the
form set ⟨param⟩ = ⟨value⟩. The parameters below are supported, where the default value is the first
mentioned. ProVerif also accepts no instead of false and yes instead of true.
Simplification of processes
With the setting set rejectChoiceTrueFalse = true, ProVerif does not try to prove observational
equivalence for simplified processes that still contain tests if choice[true, false ] then, because
the observational equivalence proof has little chance of succeeding in this case. With the setting
set rejectChoiceTrueFalse = false, ProVerif still tries to observational equivalence for simplified
processes that contain tests if choice[true, false ] then.
By default (set saturationApplication = instantiate .), lemmas, axioms, and inductive hypotheses
are only applied in the saturation procedure when they instantiate at least one variable. With
saturationApplication = none, they are never applied during the saturation procedure. With
saturationApplication = discard, they are only applied when they imply that the hypotheses of the
clause are not satisfiable (hence discarding the clause). Finally, with saturationApplication = full,
they are always applied.
set verificationApplication = full .
set verificationApplication = none.
set verificationApplication = discard.
set verificationApplication = instantiate .
By default (set verificationApplication = full .), lemmas, axioms, and inductive hypotheses are
always applied during the verification procedure. The different options have the same meaning as
the ones for the setting saturationApplication but applied to the verification procedure.
Precision, performance, and termination settings. The performance settings may result in more
or fewer false attacks, but they never sacrifice soundness. It follows that when ProVerif says that a
property is satisfied, then the model really does guarantee that property, regardless of how ProVerif has
been configured using the settings presented here.
can be used to enforce termination of the solving process, at the cost of precision. This setting
is not recommended: it often causes too much imprecision. Using nounif (see Section 6.7.2) is
delicate but may be more successful in practice.
other hand, on big examples, in particular when they contain many events (or blocking facts), this
technique can yield huge speedups.
tests with constant conditions. However, the transformation will be performed even if the constant
was already there in the initial process, which may cut part of the process, and for instance remove
restrictions that occur in the initial process and are needed for some queries or secrecy assumptions.
With the setting set expandSimplifyIfCst = false., this transformation is not performed.
occurrences in the hypothesis and in the conclusion of the clause of any function or name symbol
not used in the previous features.
Swapping settings.
Display settings.
You can also activate ANSI colors in shell buffers by default by adding the following to your .emacs:
This option is active by default when the output is a terminal, on Unix (including Mac).
User tricks. You are invited to submit your own ProVerif tricks, which we may include in future
revisions of this manual.
where σ is the most general unifier of C and F0 , C is selected in R, and F0 is selected in R′ . The selected
literal of each clause is determined by a selection function, which can be chosen by set selFun = name.,
where name is the name of the selection function, Nounifset, NounifsetMaxsize, Term, or TermMaxsize.
The selection functions work as follows:
Hypotheses of the form p(. . .) when p is declared with option block and internal predicates begin
and testunif are unselectable. (The predicate testunif is handled by a specific internal treatment.
The predicates with option block and the predicate begin have no clauses that conclude them; the
goal is to produce a result valid for any definition of these predicates, so they must not be selected.)
The conclusion bad is also unselectable. (The goal is to determine whether bad is derivable, so we
should select a hypothesis if there is some, to determine whether the hypothesis is derivable.)
Facts p(x) when p is an internal predicate attacker or comp are also unselectable. (Due to data-
decomposition clauses, selecting such facts would lead to non-termination.)
Unselectable hypotheses are never selected. An unselectable conclusion is selected only when all
hypotheses are unselectable (or there is no hypothesis).
If there is a selectable literal, the selection function selects the literal of maximum weight among the
selectable literals. In case several literals have the maximum weight, the conclusion is selected in
priority if it has the maximum weight, then the first hypothesis with maximum weight is selected.
The weight of each literal is determined as follows:
– If the selection function is Term or TermMaxsize (the default), and a hypothesis is a looping
instance of the conclusion, then the conclusion has weight −7000. (A fact F is a looping
instance of a fact F ′ when there is a substitution σ such that F = σF ′ and σ maps some
variable x to a term that contains x and is not a variable. In this case, repeated instantiations
of F ′ by σ generate an infinite number of distinct facts σ n F ′ .)
The goal has weight −3000. (The goal is the fact for which we want to determine whether
it is derivable or not. It appears as a conclusion in the second stage of ProVerif’s resolution
algorithm.)
6.7. THEORY AND TRICKS 115
If the conclusion is a fact F whose weight has been manually set by a declaration select,
noselect, or nounif . . . [conclusion] (see Section 6.7.2), then the conclusion has the weight
in question.
In all other cases, the conclusion has weight −1.
– If the selection function is Term or TermMaxsize (the default), and the conclusion is a looping
instance of a hypothesis, then this hypothesis has weight −7000.
If the hypothesis is a fact F whose weight has been set by a declaration select, noselect, or
nounif (see Section 6.7.2) or by a previous selection step (see below), then the hypothesis has
the weight in question.
All other hypotheses have as weight their size with the selection functions TermMaxsize (the
default) and NounifsetMaxsize. They have weight 0 with the selection functions Term and
Nounifset.
If the selection function is Term or TermMaxsize (the default) and the conclusion is selected in a
clause, then for each hypothesis F of that clause such that the conclusion C is a looping instance
of F (C = σF ), the weight of hypotheses σ ′ F , where σ and σ ′ have disjoint supports, is set to
−5000 for the rest of the resolution. (σ and σ ′ have disjoint supports means that, if σx is not a
variable, then σ ′ x must be a variable.)
The selection functions Term and TermMaxsize try to favor termination by auto-detecting loops and
tuning the selection function to avoid them. For instance, suppose that the conclusion is a looping
instance of a hypothesis, so the clause is of the form H && F −> σF .
Assume that F is selected in this clause, and there is a clause H ′ −> F ′ , where F ′ unifies with F ,
and the conclusion is selected in H ′ −> F ′ . Let σ ′ be the most general unifier of F and F ′ . So
the algorithm generates:
σ ′ H ′ && σ ′ H −> σ ′ σF
...
σ ′ H ′ && σ ′ H && σ ′ σH & & . . . && σ ′ σ n−1 H −> σ ′ σ n F
assuming that the conclusion is selected in all these clauses, and that no clause is removed because
it is subsumed by another clause. So the algorithm would not terminate. Therefore, in order to
avoid this situation, we should avoid selecting F in the clause H && F −> σF . That is why we
give F weight −7000 in this case. A symmetric situation happens when a hypothesis is a looping
instance of the conclusion, so we give weight −7000 to the conclusion in this case.
Assume that the conclusion is selected in the clause H && F −> σF , and there is a clause
H ′ && σ ′ F −> C (up to renaming of variables), where σ ′ commutes with σ (in particular, when σ
and σ ′ have disjoint supports), and that σ ′ F is selected in this clause. So the algorithm generates:
σ ′ H && σH ′ && σ ′ F −> σC
...
σ ′ H && σ ′ σH && . . . && σ ′ σ n−1 H && σ n H ′ && σ ′ F −> σ n C
assuming that σ ′ F is selected in all these clauses, and that no clause is removed because it is
subsumed by another clause. So the algorithm would not terminate. Therefore, in order to avoid
this situation, if the conclusion is selected in the clause H && F −> σF , we should avoid selecting
facts of the form σ ′ F , where σ ′ and σ have disjoint supports, in other clauses. That is why we
automatically set the weight to −5000 for these facts.
Obviously, these heuristics do not avoid all loops. One can use manual select, noselect, or nounif
declarations to tune the selection function further, as explained in Section 6.7.2.
The selection functions TermMaxsize and NounifsetMaxsize preferably select large facts. This can
yield important speed-ups for some examples.
116 CHAPTER 6. ADVANCED REFERENCE
which states that F cannot be derived, where F can be a fact attacker(M ), attacker(M ) phase n,
mess(N, M ), mess(N, M ) phase n, table(d(M1 , . . . , Mn )), table(d(M1 , . . . , Mn )) phase n as defined
in Figure 4.3 or a user-defined predicate p(M1 , . . . , Mk ) (see Section 6.3). When F contains variables,
the secrecy assumption not x1 : t1 , . . . , xn : tn ; F . means that no instance of F is derivable.
ProVerif can then optimize its internal clauses by removing clauses that contain F in hypotheses,
thus simplifying the clause set and resulting in a performance advantage. The use of secrecy assumptions
preserves soundness because ProVerif also checks that F cannot be derived; if it can be derived, ProVerif
fails with an error message. Secrecy assumptions can be extended using the binding let x = M in and
bound names designated by new a[. . .] as discussed in Section 6.4; these two constructs are allowed as
part of F .
The name “secrecy assumptions” comes from the particular case
not attacker ( M ) .
the assumption not attacker(k) indicates that k cannot be deduced by the attacker in P and Q at
the same time. Secrecy assumptions can also differ between P and Q using choice. The declaration
not attacker(choice[k1,k2]) indicates that the attacker cannot deduce k1 in P and k2 in Q at the same
time. Note that if the attacker can deduce k1 in P but is not able to deduce k2 in Q then the secrecy
assumption is satisfied. As such, it is possible to declare a secrecy assumption only for P as follows.
not x : b i t s t r i n g ; attacker ( choice [ k , x ] ) .
This secrecy assumption intuitively only indicates that the attacker cannot deduce k in P but does not
say anything about Q.
Grouping queries
As mentioned in Section 6.1, queries may also be stated in the form:
query x1 : t1 , . . . , xm : tm ; q1 ; . . . ; qn .
where each qi is a query as defined in Figure 4.3, or a putbegin declaration (see Section 6.5). A
single query declaration containing q1 ; . . . ; qn is evaluated by building one set of clauses and performing
resolution on it, whilst independent query declarations
query x1 : t1 , . . . , xm : tm ; q1 .
...
query x1 : t1 , . . . , xm : tm ; qn .
are evaluated by rebuilding a new set of clauses from scratch for each qi . So the way queries are grouped
influences the sharing of work between different queries, and therefore performance. For optimization,
one should group queries that involve the same events; but separate queries that involve different events,
because the more events appear in the query, the more complex the generated clauses are, which can
slow down ProVerif considerably, especially on complex examples. If one does not want to optimize, one
can simply put a single query in each query declaration.
6.7. THEORY AND TRICKS 117
hypothesis, conclusion: When the option conclusion is not mentioned (e.g., nounif x1 : t1 , . . .,
xk : tk ; F /w [hypothesis]. or nounif x1 : t1 , . . . , xk : tk ; F /w.), the declaration modifies the weight
of hypotheses matching F and leaves the weight of conclusions matching F unchanged.
When the option conclusion alone is mentioned (e.g., nounif x1 : t1 , . . . , xk : tk ; F /w [conclusion].),
the declaration modifies the weight of conclusions matching F and leaves the weight of hypotheses
matching F unchanged.
When the option conclusion and another option are mentioned (e.g., nounif x1 : t1 , . . . , xk : tk ;
F /w [hypothesis,conclusion].), the declaration modifies the weight of both hypotheses and con-
clusions matching F .
For example, the declarations
nounif x1 : t1 , . . . , xk : tk ; F /w1 .
nounif x1 : t1 , . . . , xk : tk ; F /w2 [ conclusion ] .
indicate that the weight of conclusions matching F is −w2 whereas the weight of hypotheses
matching F is −w1 .
ignoreAFewTimes: This option is accepted only when the weight is negative, that is w > 0 for
noselect and nounif declarations, and w < 0 for select declarations. The nounif declarations help
the saturation procedure to terminate but they may lower the precision of ProVerif by preventing
resolution steps. In the second stage of the resolution algorithm, i.e. after saturation has completed
and when ProVerif determines whether the goal is derivable or not, we allow a hypothesis F
in a clause F ∧ H → C matching a nounif declaration with option ignoreAFewTimes to be
selected instead of selecting the conclusion C. To prevent the non-termination issue, such selection,
118 CHAPTER 6. ADVANCED REFERENCE
which ignores the nounif declaration, can only happen a limited number of times, determined by
the setting set nounifIgnoreNtimes = n. (By default, it happens only once.) When we resolve
F ∧ H → C with H ′ → C ′ upon F ignoring a nounif declaration and that yields a clause
σH ′ ∧ σH → σC, we consider that the nounif have already been ignored once for all facts in σH ′ ,
so if nounif declarations could be ignored n times for F , then they can be ignored n − 1 times for
facts in σH ′ . With the default setting, the nounif declarations can no longer be ignored for facts
in σH ′ .
For example, consider a clause among the saturated clauses such that the conclusion is a looping
instance of an hypothesis, so the clause is of the form H ∧ F → σF . Suppose now that during the
second step of ProVerif’s algorithm, a clause F → C needs to be resolved. Since σF is a looping
instance of F , ProVerif would have automatically associated to F the weight −5000. In such a
case, the conclusion C would be selected and the clause F → C would be considered as resolved.
By declaring
nounif x1 : t1 , . . . , xk : tk ; F [ ignoreAFewTimes ] .
the hypothesis F in F → C is selected and allows a resolution step on F . Among possibly others,
this will generate the clause H ∧ F → σC. However on this new clause, F will not be selected since
it was already selected in F → C and this clause was used to generate H ∧ F → σC.
The option ignoreAFewTimes is best used when proving a query by induction as it may allow
ProVerif to apply additional inductive hypotheses. Let us come back to the simplified Yubikey
protocol docs/ex induction group.pv introduced in Section 6.1.
1 free c : channel .
2 free k : b i t s t r i n g [ private ] .
3 free d P : c h a n n e l [ private ] .
4 free d Q : c h a n n e l [ private ] .
5
6 fun s e n c ( nat , b i t s t r i n g ) : b i t s t r i n g .
7 reduc f o r a l l K: b i t s t r i n g ,M: nat ; s d e c ( s e n c (M,K) ,K) = M.
8
9 event CheckNat ( nat ) .
10 event CheckNatQ ( nat ) .
11
12 query i : nat ;
13 event ( CheckNat ( i ) ) ==> i s n a t ( i ) ;
14 event ( CheckNatQ ( i ) ) ==> i s n a t ( i ) ;
15 mess ( d Q , i ) ==> i s n a t ( i ) [ induction ] .
16
17 let P =
18 in ( c , x : b i t s t r i n g ) ;
19 in ( d P , ( i : nat , j : nat ) ) ;
20 l e t j ' : nat = s d e c ( x , k ) in
21 event CheckNat ( i ) ;
22 event CheckNat ( j ) ;
23 event CheckNatQ ( j ' ) ;
24 if j ' > j
25 then out ( d P , ( i +1, j ' ) )
26 e l s e out ( d P , ( i , j ) ) .
27
28 let Q =
29 in ( d Q , i : nat ) ;
30 out ( c , s e n c ( i , k ) ) ;
31 out ( d Q , i +1).
32
33 process
6.7. THEORY AND TRICKS 119
34 out ( d P , ( 0 , 0 ) ) | out ( d Q , 0 ) | ! P | ! Q
ProVerif is not able to prove the query mess(d Q,i) ==> is nat(i). By looking at the output of
ProVerif on the execution of docs/ex induction group.pv, one can notice the following:
– ProVerif automatically assigns weight −5000 to mess(d Q[],i 2), which is displayed select
mess(d Q[],i 2)/−5000, because the process Q generates the Horn clause mess(d Q,i) −>
mess(d Q,i+1) where the conclusion is a looping instance of the hypothesis.
– ProVerif generates the reachable goal is not nat ( i 2 ) && mess(d Q[],i 2) −> mess(d Q[],i 2).
Because of the nounif declaration, ProVerif does not try to solve the hypothesis mess(d Q[],i 2)
which prevents it from proving the query.
To help ProVerif, one can add in the input file the nounif declaration on mess(d Q[],i 2) with the
option ignoreAFewTimes to allow ProVerif to resolve once upon the fact mess(d Q[],i 2) during
the verification procedure. In the file docs/ex induction group proof.pv, we added the line
12 nounif i : nat ; mess ( d Q , i ) [ ignoreAFewTimes ] .
By looking at the output of the execution of docs/ex induction group proof.pv, one can notice
that ProVerif is able to prove all queries and in particular, it generates the following reachable
goals for the query mess(d Q,i) ==> is nat(i):
g o a l r e a c h a b l e : i s n a t ( i 2 ) && mess ( d Q [ ] , i 2 ) −> mess ( d Q [ ] , i 2 + 1 )
g o a l r e a c h a b l e : mess ( d Q [ ] , 0 )
inductionOn=i: This option is accepted only when the weight is negative, that is w > 0 for
noselect and nounif declarations, and w < 0 for select declarations. i must be a variable of type
nat that has been declared in the environment of the noselect, nounif, select declaration (i.e., i
is one of the variables x1 , . . . , xk ), i must occur in F , but ∗i must not occur in F .
During the saturation procedure, when a clause is of the form F σ1 ∧ F σ2 ∧ H → C where H implies
iσ1 ≥ iσ2 , the hypotheses containing the variable iσ2 will be removed from the clause.
Due to the presence of natural numbers, the saturation procedure may not terminate in some cases
because it generates infinitely many clauses of the following form H ∧ F (j1 ) ∧ j1 < j2 ∧ F (j2 ) ∧ j2 <
j3 ∧ F (j3 ) ∧ . . . → C that may not be removed by subsumption. This usually occurs when the
facts F (j1 ), F (j2 ), . . . are unselectable (e.g. events) or match a nounif. In our example, adding the
option inductionOn=i may ensure termination as it will simplify the clauses into H ∧ F (j) → C.
Note that similarly to the setting set maxHyp = n., this option can be used to ensure termination
at the cost of precision. However, this option is best used when proving a query by induction,
specifically when F is part of the goal of the query and when iσ1 ≥ iσ2 implies that F σ1 occurs
after F σ2 . In such a case, the application of the inductive hypothesis on F σ1 may be precise
enough to prove the query by induction.
When F contains multiple natural variables, one can add the option inductionOn={i1 , . . . , in }.
In such a case, the hypotheses
Vn containing the variables i1 σ2 , . . . , in σ2 will be removed from the
clause when H implies k=1 ik σ1 ≥ ik σ2 .
In order to determine the desired select, noselect, and nounif declarations, one typically uses set
verboseRules = true. to display the clauses generated by ProVerif. One can then observe the loops that
occur, and one can try to avoid them by using a nounif declaration that prevents the selection of the
literal that causes the loop.
Tagged protocols
A typical cause of non-termination of ProVerif is the existence of loops inside protocols. Consider for
instance a protocol with the following messages:
B → A : s e n c (Nb , k )
A → B : s e n c ( f (Nb) , k )
120 CHAPTER 6. ADVANCED REFERENCE
(This example is inspired from the Needham-Schroeder shared-key protocol.) Suppose that A does not
know the value of Nb (nonce generated by B). In this case, in A’s role, Nb is a variable. Then, the
attacker can send the second message to A as if it were the first one, and obtain as reply senc(f ( f (Nb), k),
which can itself be sent as if it were the first message, and so on, yielding to a loop that generates
senc(f n (Nb), k) for any integer n.
A way to avoid such loops is to add tags. A tag is a distinct constant for each application of a
cryptographic primitive (encryption, signatures, . . . ) in the protocol. Instead of applying the primitive
just to the initial message, one applies it to a pair containing a tag and the message. For instance, after
adding tags, the previous example becomes:
B → A : s e n c ( ( c0 , Nb) , k )
A → B : s e n c ( ( c1 , f (Nb ) ) , k )
After adding tags, the second message cannot be mixed with the first one because of the different tags c0
and c1, so the previous loop is avoided. More generally, one can show that ProVerif always terminates
for tagged protocols (modulo some restrictions on the primitives in use and on the properties that are
proved) [BP05], [Bla09, Section 8.1]. Adding tags is a good design practice [AN96]: the tags facilitate
the parsing of messages, and they also prevent type-flaw attacks (in which messages of different types
are mixed) [HLS00]. Tags are used in some practical protocols such as SSH. However, if one verifies
a protocol with tags, one should implement the protocol with these tags: the security of the tagged
protocol does not imply the security of the untagged version.
Internally, fresh names created by new are represented as functions of the inputs located above that
new. So, by moving new upwards or downwards, one can influence the internal representation of the
names and tune the performance and precision of the analysis. Typically, the more the new are moved
downwards in the process, the more precise and the more costly the analysis is. (There are exceptions
to this general rule, see for example the end of Section 5.4.2.)
The setting set movenew = true. allows one to move new automatically downwards, potentially
yielding a more precise analysis. By default, the new are left where they are, so the user can manually
tune the precision of the analysis. Furthermore, it is possible to indicate explicitly at each replication
which variables should be included as arguments in the internal representation of the corresponding fresh
name: inside a process
new a [ x1 , . . . , xn ] : t
means that the internal representation of names created by that restriction is going to include x1 , . . . , xn
as arguments. In any case, the internal representation of names always includes session identifiers
(necessary for soundness) and variables needed to answer queries. These annotations are ignored in
the case of observational equivalence proof between two processes (keyword equivalence) or when the
biprocess is simplified before an observational equivalence proof. (Otherwise, the transformations of the
processes might be prevented by these annotations.)
In general, we advise generating the fresh names by new when they are needed. Generating all fresh
names at the beginning of the protocol is a bad idea: the names will essentially have no arguments, so
ProVerif will merge all of them and the analysis will be so imprecise that it will not be able to prove
anything. On the other hand, if the new take too many arguments, the analysis can become very costly
or even not terminate. By the setting set verboseRules = true., one can observe the clauses generated
by ProVerif; if these clauses contain names with very large arguments that grow more and more, moving
new upwards or giving an explicit list of arguments to remove some arguments can improve the speed of
ProVerif or make it terminate. The size of the arguments of names associated with random coins is the
reason of the cost of the analysis in the presence of probabilistic encryption (see Section 4.2.3). When
one uses function macros to represent encryption, one cannot easily move the new upwards. If needed,
we advise manually expanding the encryption macro and moving the new that comes from this macro
upwards or giving it explicit arguments.
6.7. THEORY AND TRICKS 121
1. Key distribution by scope. The first alternative key distribution mechanism simply relies on variable
scope and was used in our exemplar handshake protocol and in Section 5.1 without discussion. In
this formalism, we simply ensure that the required keys are within the scope of the desired processes.
The main limitation of this encoding is that it does not allow one to establish a correspondence
between host names and keys for an unbounded number of hosts.
2. Key distribution over private channels. In an equivalent manner to tables, keys may be distributed
over private channels.
Instead of declaring a table d, we declare a private channel by free cd: channel [private].
Instead of inserting an element, say (h,k), in table d, we output an unbounded number of
copies of that element on channel cd by !out(cd, (h,k)). (The rest of the process should be
in parallel with that output so that it does not get replicated as well.)
Instead of getting an element, say by get(d, (=h,k)) to get the key k for host h, we read on
the private channel cd by in(cd, (=h,k:key)).
With this encoding, all keys inserted in the table become available (in an unbounded number of
copies) on the private channel cd.
We present this encoding as an example of what can be done using private channels. It does not
have advantages with respect to using the specific ProVerif constructs for inserting and getting
elements of tables.
3. Key distribution by constructors and destructors. Finally, as we alluded in Section 3.1.1, private
constructors can be used to model the server’s key table. In this case, we make use of the following
constructors and associated destructors:
type h o s t .
type s k e y .
type pkey .
fun pk ( s k e y ) : pkey .
122 CHAPTER 6. ADVANCED REFERENCE
fun f h o s t ( pkey ) : h o s t .
reduc x : pkey ; g e t k e y ( f h o s t ( x ) ) = x [ private ] .
The constructor fhost generates a host name from a public key, while the destructor getkey returns
the public key from the host name. The constructor fhost is public so that the attacker can
create host names for the keys it creates. The destructor getkey is private; this is not essential for
public keys, but when this technique is used with long-term secret keys rather than public keys,
it is important that getkey be private so that the attacker cannot obtain all secret keys from the
(public) host names.
This technique allows one to model an unbounded number of participants, each with a distinct
key. This is however not necessary for most examples: one honest participant for each role is
sufficient, the other participants can be considered dishonest and included in the attacker. An
advantage of this technique is that it sometimes makes it possible for ProVerif to terminate
while it does not terminate with the table of host names and keys used in previous chapters
(because host names and keys that are complex terms may be registered by the attacker). For
instance, in the file examples/pitype/choice/NeedhamSchroederPK-corr1.pv (if you installed
by OPAM in the switch ⟨switch⟩, the file ~/.opam/⟨switch⟩/doc/proverif/examples/pitype/
choice/NeedhamSchroederPK-corr1.pv), we had to perform key registration in an earlier phase
than the protocol in order to obtain termination. Using the fhost/getkey encoding, we can obtain
termination with a single phase (see examples/pitype/choice/NeedhamSchroederPK-corr1-host-getkey.pv).
However, this encoding also has limitations: for instance, it does not allow the attacker to register
several host names with the same key, which is sometimes possible in reality, so this can lead to
missing some attacks.
Asymmetric channels
Up to now, we have considered only public channels (on which the attacker can read and write) and pri-
vate channels (on which the attacker can neither read nor write). It is also possible to encode asymmetric
channels (on which the attacker can either read or write, but not both).
A channel cwrite on which the attacker can write but not read can be encoded as follows: declare
cwrite as a private channel by free cwrite :channel [private]. and add in your process !in(c, x:t);
out(cwrite, x) where c is a public channel. This allows the attacker to send any value of type t on
channel cwrite, and can be done for several types if desired. When types are ignored (the default),
it in fact allows the attacker to send any value of any type on channel cwrite.
A channel cread on which the attacker can read but not write can be encoded as follows: declare
cread as a private channel by free cread:channel [private]. and add in your process !in(cread, x:t);
out(c, x) where c is a public channel. This allows the attacker to obtain any value of type t sent
6.7. THEORY AND TRICKS 123
on channel cread, and can be done for several types if desired. As above, when types are ignored,
it in fact allows the attacker to obtain any value sent on channel cread.
Memory cell
One can encode a memory cell in which one can read and write. We declare three private channels: one
for the cell itself, one for reading and one for writing in the cell.
f r e e c e l l , cread , c w r i t e : c h a n n e l [ private ] .
and include the following process
out ( c e l l , init ) |
( ! in ( c e l l , x : t ) ; in ( c w r i t e , y : t ) ; out ( c e l l , y ) ) |
( ! in ( c e l l , x : t ) ; out ( cread , x ) ; out ( c e l l , x ) )
where t is the type of the content of the cell, and init is its initial value. The current value of the cell is
the one available as an output on channel cell . We can then write in the cell by outputting on channel
cwrite and read from the cell by reading on channel cread.
We can give the attacker the capability to read and/or write the cell by defining cread as a channel
on which the attacker can read and/or cwrite as a channel on which the attacker can write, using the
asymmetric channels presented above.
It is important for the soundness of this encoding that one never reads on cwrite or writes on cread,
except in the code of the cell itself.
Due to the abstractions performed by ProVerif, such a cell is treated in an approximate way: all
values written in the cell are considered as a set, and when one reads the cell, ProVerif just guarantees
that the obtained value is one of the written values (not necessarily the last one, and not necessarily one
written before the read).
Repetition of actions. The Horn clauses can be applied any number of times, so the translation
ignores the number of repetitions of actions. For instance, ProVerif finds a false attack in the following
process, already mentioned in Section 6.2:
new k : key ; out ( c , s e n c ( s e n c ( s , k ) , k ) ) ;
in ( c , x : b i t s t r i n g ) ; out ( c , s d e c ( x , k ) )
124 CHAPTER 6. ADVANCED REFERENCE
It thinks that one can decrypt senc(senc(s ,k),k) by sending it to the input, so that the process replies
with senc(s ,k), and then sending this message again to the input, so that the process replies with s.
However, this is impossible in reality because the input can be executed only once. The previous process
has the same translation into Horn clauses as the process
new k : key ; out ( c , s e n c ( s e n c ( s , k ) , k ) ) ;
! in ( c , x : b i t s t r i n g ) ; out ( c , s d e c ( x , k ) )
with an additional replication, and the latter process is subject to the attack outlined above.
This approximation is the main approximation made by ProVerif. In fact, for secrecy (and probably
also for basic non-injective correspondences), when all channels are public and the fresh names are
generated by new as late as possible, this is the only approximation [Bla05]. The option [precise],
introduced in Section 6.2, allows the user to eliminate many false attacks coming from this approximation.
Position of new. The position of new in the process influences the internal representation of fresh
names in ProVerif: fresh names created by new are represented as functions of the inputs located above
that new. So the more the new are moved downwards in the process, the more arguments they have,
and in general the more precise and the more costly the analysis is. (See also Section 6.7.2 for additional
discussion of this point.)
Private channels. Private channels are a powerful tool for encoding many features in the pi calculus.
However, because of their power and complexity, they also lead to additional approximations in ProVerif.
In particular, when c is a private channel, the process P that follows out(c, M ); P can be executed only
when some input listens on channel c; ProVerif does not take that into account and considers that P can
always be executed.
Moreover, ProVerif just computes a set of messages sent on a private channel, and considers that any
input on that private channel can receive any of these messages (independently of the order in which
they are sent). This point can be considered as a particular case of the general approximation that
repetitions of actions are ignored: if a message has been sent on a private channel at some point, it may
be sent again later. Ignoring the number of repetitions of actions then tends to become more important
in the presence of private channels than with public channels only.
Let us consider for instance the process
new c : c h a n n e l ; ( out ( c , M ) | in ( c , x : t ) ; in ( c , y : t ) ; P )
The process P cannot be executed, because a single message is sent on channel c, but two inputs must
be performed on that channel before being able to execute P . ProVerif cannot take that into account
because it ignores the number of repetitions of actions: the process above has the same translation into
Horn clauses as the variant with replication
new c : c h a n n e l ; ( ( ! out ( c , M ) ) | in ( c , x : t ) ; in ( c , y : t ) ; P )
which can execute P .
Similarly, the process
new c : c h a n n e l ; ( out ( c , s ) | in ( c , x : t ) ; out ( d , c ) )
preserves the secrecy of s because the attacker gets the channel c too late to be able to obtain s. However,
ProVerif cannot prove this property because the translation treats it like the following variant
new c : c h a n n e l ; ( ( ! out ( c , s ) ) | in ( c , x : t ) ; out ( d , c ) )
with an additional replication, which does not preserve the secrecy of s.
i f M = N then R1 e l s e R2
if the then branch is taken in P and the else branch is taken in Q, then ProVerif cannot prove obser-
vational equivalence. However, P and Q may still be observationally equivalent if the attacker cannot
distinguish what R1 does from what R2 does.
Along similar lines, the biprocess
P = out ( c , choice [m, n ] ) | out ( c , choice [ n ,m] )
satisfies observational equivalence but ProVerif cannot show this: the first component of the parallel
composition outputs either m or n, and the attacker has these two names, so ProVerif cannot prove
observational equivalence because it thinks that the attacker can distinguish these two situations. In fact,
the difference in the first output is compensated by the second output, so that observational equivalence
holds. In this simple example, it is easy to prove observational equivalence by rewriting the process
into the structurally equivalent process out(c,choice[m,m]) | out(c,choice[n,n]) for which ProVerif can
obviously prove observational equivalence. It becomes more difficult when a configuration similar to the
one above happens in the middle of the execution of the process. Ben Smyth et al. are working on an
extension of ProVerif to tackle such cases [DRS08].
Limitations of attack reconstruction. Some limitations also come from attack reconstruction. The
reconstruction of attacks against injective correspondences is based on heuristics that sometimes fail.
For observational equivalences, ProVerif can reconstruct a trace that reaches the first point at which the
two processes start reducing differently. However, such a trace does not guarantee that observational
equivalence is wrong; for this reason, ProVerif never says that an observational equivalence is false.
the else branch refers to let construct, not to the if . The constructs if , let, and get can all
have else branches, and else always refers to the latest one. This is true even if the else branch
of let can never be executed because the let always succeeds. Hence, the code above is correctly
indented as follows:
i f . . . then
l e t x = . . . in
...
else
...
is equivalent to
l e t x = M in . . . e l s e . . .
Hence, its else branch will be executed only if the evaluation of M fails. When M never fails, this
is clearly not what was intended.
In patterns, identifiers without argument are always variables bound by the pattern. For instance,
consider
const c : b i t s t r i n g .
l e t ( c , x ) = M in . . .
Even if c is defined before, c is redefined by the pattern-matching, and the pattern (c, x) matches
any pair. ProVerif displays a warning saying that c is rebound. If you want to refer to the constant
c in the pattern, please write:
const c : b i t s t r i n g .
l e t (=c , x ) = M in . . .
The pattern (=c, x) matches pairs whose first component is equal to c. If you want to refer to a
data function without argument, the following syntax is also possible:
const c : b i t s t r i n g [ data ] .
l e t ( c ( ) , x ) = M in . . .
The construct if M then P else Q does not catch failure inside the term M , that is, it executes
nothing when the evaluation of M fails. Its else branch is executed only when the evaluation of
M succeeds and its result is different from true.
In contrast, the construct let T = M in P else Q catches failure inside T and M . That is, its
else branch is executed when the evaluation of T or M fails, or when these evaluations succeed
and the result of M does not match T .
equation f o r a l l m: b l o c k s i z e , r : k e y s e e d ;
dec ( enc (m, kgen ( r ) ) , kgen ( r ) ) = m.
equation f o r a l l m: b l o c k s i z e , r : k e y s e e d ;
enc ( dec (m, kgen ( r ) ) , kgen ( r ) ) = m.
}
SPRP stands for Super Pseudo-Random Permutation, a standard computational assumption on block
ciphers; here, the ProVerif model tries to be close to this assumption, even if it is probably stronger.
Penc is the probability of breaking this assumption; it makes sense only for CryptoVerif, but the goal to
use the same macros with different definitions in ProVerif and in CryptoVerif.
We can then declare a block cipher by
expand SPRP cipher ( keyseed , key , b l o c k s i z e , kgen , enc , dec , Penc ) .
without repeating the whole definition.
The definitions of macros are typically stored in a library. Such a library can be specified by the
command-line option -lib. The file cryptoverif.pvl (at the root of the ProVerif distribution or, if you
installed by OPAM in the switch ⟨switch⟩, in ~/.opam/⟨switch⟩/share/proverif/cryptoverif.pvl) is
an example of such a library. It can be included by calling
proverif -lib cryptoverif ⟨filename⟩.pv
The definitions of cryptographic primitives need to be included in such a library, because they are
typically very different between ProVerif and CryptoVerif. We can then use a different library for each
tool.
ProVerif also supports but ignores the CryptoVerif declarations letproba, param, proba, proof,
implementation, as well as the implementation annotations. It supports options after a type declara-
tion, as in type t [option]. These options are ignored. It supports channel c1 , . . . , cn . as a synonym of
free c1 , . . . , cn : channel. (Only the former is supported by CryptoVerif.) It supports yield as a synonym
of 0. It allows ! i <= n instead of just !. (CryptoVerif uses the former.) It also allows the following
constructs:
New syntax Original equivalent syntax
foreach i <= N do P ! i <= n P
x <−R T ; P new x:T ; P
x[:T ] <− M ; P let x[:T ] = M in P
x <−R T ; N new x:T ; N
x[:T ] <− M ; N let x[:T ] = M in N
ProVerif accepts
equation [ f o r a l l x1 : T1 , . . . , xn : Tn ; ] M .
as a synonym for
equation [ f o r a l l x1 : T1 , . . . , xn : Tn ; ] M = t r u e .
ProVerif also allows disequations
equation [ f o r a l l x1 : T1 , . . . , xn : Tn ; ] M1 <> M2 .
It just checks that they are valid (the terms M1 and M2 do not unify modulo the equational theory) and
otherwise ignores them. Examples of protocols written with CryptoVerif compatibility in mind can be
found in subdirectory examples/cryptoverif/. You can run them by
./proverif -lib cryptoverif examples/cryptoverif/⟨filename⟩.pcv
from the main directory of the ProVerif distribution. If you installed ProVerif by OPAM in the switch
⟨switch⟩, these files are in ~/.opam/⟨switch⟩/doc/proverif/examples/cryptoverif and you can run
them by
128 CHAPTER 6. ADVANCED REFERENCE
inside your .pcv files. When ProVerif is called with a .pcv file as argument, it automatically preprocesses
it with m4, as if you ran
before analyzing it. Similarly, when CryptoVerif is called with a .pcv file as argument, it automatically
preprocesses it with m4, as if you ran
where -timeout ⟨n⟩ sets the timeout for each execution of the tested program to n seconds (by default,
there is no timeout), ⟨mode⟩ can be:
test add: test the mentioned scripts and add the expected result in the script when it is missing
add: add the expected result in the script when it is missing, do not test scripts that already have
an expected result
update: test the mentioned scripts and update the expected result in the script
typedopt runs ProVerif on examples for the typed front-ends, with various additional options
dir ⟨prefix ⟩ ⟨list of directories⟩ analyzes the mentioned directories using ProVerif, using ⟨prefix ⟩
as prefix for the output files.
⟨test set⟩ can be omitted when it is typed, and ⟨mode⟩ ⟨test set⟩ can both be omitted when they are
test typed.
The script test is a bash shell script, so you must have bash installed. On Windows, the best is to
install Cygwin and run test from a Cygwin terminal.
The script test must be run in the ProVerif main directory; the programs analyze and proverif
must be present in that directory.
The script test first runs the script prepare in each directory when it is present. That allows for
instance to generate the ProVerif scripts to run. Then it runs the program analyze described below.
6.9.2 analyze
The program analyze is mainly meant to be called from test, but it can also be called directly.
Usage:
analyze [⟨options⟩] ⟨prog⟩ ⟨mode⟩ ⟨tmp directory⟩ ⟨prefix for output files⟩ dirs ⟨directories⟩
analyze [⟨options⟩] ⟨prog⟩ ⟨mode⟩ ⟨tmp directory⟩ ⟨prefix for output files⟩ file ⟨directory⟩ ⟨filename⟩
-timeout ⟨n⟩ sets the timeout for each execution of the tested program to n seconds (by default,
there is no timeout);
-progopt ⟨command − line options⟩ -endprogopt passes the additional ⟨command − line options⟩
to the tested program (ProVerif or CryptoVerif);
⟨prog⟩ is either CV for CryptoVerif or PV for ProVerif and ⟨mode⟩ is as for the test program above.
Temporary files are stored in directory ⟨tmp directory⟩, and the output files are:
This program analyzes a series of scripts using the program specified by ⟨prog⟩.
130 CHAPTER 6. ADVANCED REFERENCE
In the first command line, it analyzes scripts in the mentioned directories and in their subdirectories.
The files whose name contains .m4. or .out. are excluded. (The first ones are supposed to be
files to preprocess by m4 before actually analyzing them; the second ones are supposed to be output
files.) When the program is CryptoVerif, the files whose name ends with .cv, .ocv, or .pcv are
analyzed. When the program is ProVerif, the files whose name ends with .pcv, .pv, .pi, .horn,
or .horntype are analyzed.
In the second command line, the specified file in the specified directory is analyzed, provided it
has one of the extensions above. (The directory and the file are mentioned separately because the
directory may be used to locate the library mylib.*, see below.)
The executable for CryptoVerif is searched in the current directory, in $HOME/CryptoVerif, and in the
PATH. The executable for ProVerif is searched in the current directory, in $HOME/proverif/proverif,
and in the PATH.
When mylib.cvl is present in a directory, its files with extension .cv or .pcv are analyzed using
that library of primitives for CryptoVerif. Otherwise, the default library is used.
When mylib.ocvl is present in a directory, its files with extension .ocv are analyzed using that
library of primitives for CryptoVerif. Otherwise, the default library is used.
When mylib.pvl is present in a directory, its files with extension .pcv or .pv are analyzed using that
library of primitives for ProVerif. Otherwise, the library cryptoverif.pvl is used for .pcv files and no
library for .pv files. The file cryptoverif.pvl is searched in the current directory, $HOME/CryptoVerif
and $HOME/proverif/proverif. If it is not found and mylib.pvl is not present in the directory, .pcv
files are not analyzed using ProVerif.
The result of running each script is compared to the expected result. The expected result is found
in the script itself in a comment that starts with EXPECTED for CryptoVerif and EXPECTPV for ProVerif,
and ends with END. (The entire lines that contain EXPECTED, resp. EXPECTPV and END do not belong to
the expected result.) For CryptoVerif, the expected result consists of the line RESULT Could not prove
. . . or All queries proved in the output of CryptoVerif. For ProVerif, it consists of the lines that
start with RESULT in the output of ProVerif. It also includes a runtime of the script or an error message
xtime: ... if the execution terminates with an error.
In the modes update (resp. test add or add), the expected result is updated (resp. added if it is
absent or empty). To deal with generated files, the EXPECTED, resp. EXPECTPV line may contain the
indications
FILENAME: name of the file TAG: distinct tag
In this case, the expected result is not updated in the script itself, but in the file whose name is men-
tioned after FILENAME:, and inside this file after an exact copy of the line that contains EXPECTED,
resp. EXPECTPV. (This line is unique thanks to the tag.) The idea is that this file is the file from which
the script was generated. Hence regenerating the script from this file with an updated expected result
will update the expected result in the script.
6.9.3 addexpectedtags
Usage:
addexpectedtags ⟨directories⟩
For each mentioned directory, for each file in that directory or its subdirectories that contains .m4. in
its name and ends with .cv, .ocv, .pcv, .pv, .pi, .horntype, .horn, this program adds at the end of
each line that contains EXPECTED or EXPECTPV the indications
These files are supposed to be initial models used to generate CryptoVerif or ProVerif scripts by the
m4 preprocessor. The additional indications will propagate to the generated scripts, and will allow the
analyze program above to find from which m4 file the script was generated (indicated after FILENAME:)
and inside this m4 file, which expected result indication ended up in the considered script (identified by
the integer after TAG:). It can then update the expected results in the mode update, add, or test add
(the last two when the expected result was initially empty).
Chapter 7
Outlook
The ProVerif software tool is the result of more than a decade of theoretical research. This manual
explained how to use ProVerif in practice. More information on the theory behind ProVerif can be found
in research papers:
For a general survey, please see [Bla16].
131
132 CHAPTER 7. OUTLOOK
Appendix A
Language reference
In this appendix, we provide a reference for the typed pi calculus input language of ProVerif. We adopt
the following conventions. X ∗ means any number of repetitions of X; and [X] means X or nothing.
seq⟨X ⟩ is a sequence of X, that is, seq⟨X ⟩ = [(⟨X ⟩,)∗ ⟨X ⟩] = ⟨X ⟩, . . . ,⟨X ⟩. (The sequence can be
empty, it can be one element, or it can be several elements separated by commas.) seq+ ⟨X ⟩ is a non-
empty sequence of X: seq+ ⟨X ⟩ = (⟨X ⟩,)∗ ⟨X ⟩ = ⟨X ⟩, . . . ,⟨X ⟩. (It can be one or several elements of ⟨X ⟩
separated by commas.) Text in typewriter style should appear as it is in the input file. Text between ⟨
and ⟩ represents non-terminals of the grammar. In particular, we will use:
⟨ident⟩ to denote identifiers (Section 3.1.4) which range over an unlimited sequence of letters (a-z,
A-Z), digits (0-9), underscores ( ), single-quotes (’), and accented letters from the ISO Latin 1
character set where the first character of the identifier is a letter and the identifier is distinct from
the reserved words of the language.
⟨options⟩ ::= [[seq+ ⟨ident⟩]], where the allowed identifiers in the sequence are data, private,
and typeConverter for the fun and const declarations; private for the reduc and free dec-
larations; memberOptim and block for the pred declaration; precise for processes (Figure A.8);
noneSat, discardSat, instantiateSat, fullSat, noneVerif, discardVerif, instantiateVerif
and fullVerif for the query, lemma and axiom declarations; induction and noInduction for the
lemma and query declarations; maxSubset for the lemma declaration; proveAll for the query dec-
laration; reachability, pv reachability, real or random, pv real or random, and all options
starting with cv for the secret query.
⟨infix ⟩ ::= || | && | = | <> | <= | >= | < | > to denote some infix symbols on terms.
The input file consists of a list of declarations, followed by the keyword process and a process:
or a list of declarations followed by an equivalence query between two processes (see end of Section 4.3.2):
Libraries (loaded with the command-line option -lib) are lists of declarations ⟨decl ⟩∗ .
We start by presenting the grammar for terms in Figure A.1. The grammar for declarations is
considered in Figure A.2. Finally, Figure A.8 covers the grammar for processes.
133
134 APPENDIX A. LANGUAGE REFERENCE
Figure A.1 Grammar for terms (see Sections 3.1.4, 4.1.3, and 4.1.4)
The precedences of infix symbols, from low to high, are: ||, &&, =, <>, <=, >=, <, >, and +, -, which
both have the same precedence and associate to the left as usual. The grammar of terms ⟨term⟩ is
further restricted after parsing. In reduc and equation declarations, the only allowed function symbols
are constructors, so ||, &&, =, <>, <=, >=, <, >, -, not are not allowed, and names are not allowed as
identifiers. In noninterf declarations, the only allowed function symbols are constructors and names are
allowed as identifiers. In elimtrue declarations, the term can only be a fact of the form p(M1 , . . . , Mk ) for
some predicate p; names are not allowed as identifiers. In clauses (Figure A.7), the hypothesis of clauses
can be conjunctions of facts of the form p(M1 , . . . , Mk ) for some predicate p, equalities, disequalities, or
inequalities; the conclusion of clauses can only be a fact of the form p(M1 , . . . , Mk ) for some predicate
p; names are not allowed as identifiers.
135
Figure A.3 Grammar for destructors (see Sections 3.1.1 and 4.2.1) and equations (see Section 4.2.2)
Figure A.4 Grammar for not (see Section 6.7.2), queries (see Sections 3.2 and 4.3.1), and lemmas (see
Section 6.2)
The precedences of infix symbols, from low to high, are: ==>, ||, &&, =, <>,<=, >=, <, >, and +, -, which
both have the same precedence and associate to the left as usual. The grammar above is useful to know
exactly how terms are parsed and where parentheses are needed. However, it is further restricted after
parsing, so that the grammar of ⟨gterm⟩ in queries and lemmas is in fact the one of q in Figure A.5 and
the grammar of ⟨gterm⟩ in not declarations is the one of F in Figure A.5 excluding events, equalities,
disequalities, and inequalities.
137
Figure A.5 Grammar for not, queries, and lemmas restricted after parsing
q ::= query
F1 && . . . && Fn reachability
F1 && . . . && Fn ==> H correspondence
let x = A in q let binding, see Section 6.4
H ::= hypothesis
F fact
M =N equality
M <>N disequality
M >N greater
M <N smaller
M >=N greater or equal
M <=N smaller or equal
is nat (M ) M is a natural number
H && H conjunction
H || H disjunction
false constant false
F ==> H nested correspondence
let x = A in H let binding, see Section 6.4
F ::= fact
AF action fact
AF @x action fact executed at time x
p(M1 , . . . , Mn ) user-defined predicate, see Section 6.3
let x = A in F let binding, see Section 6.4
AF ::= action fact
attacker(A) the adversary has A (in any phase)
attacker(A) phase n the adversary has A in phase n
mess(B, A) A is sent on channel B (in the last phase)
mess(B, A) phase n A is sent on channel B in phase n
event(e(A1 , . . . , An )) non-injective event
inj−event(e(A1 , . . . , An )) injective event
M, N ::= term
x, a, c variable, free name, or constant
0, 1, . . . natural numbers
f (M1 , . . . , Mn ) constructor application
(M1 , . . . , Mn ) tuple
M +i addition, i ∈ N
i+M addition, i ∈ N
M −i subtraction, i ∈ N
new a[g1 = M1 , . . ., gk = Mk ] bound name (g ::= !n | x), see Section 6.4
let x = M in N let binding, see Section 6.4
A, B ::= biterm
... same cases as terms
choice[A,B] choice
138 APPENDIX A. LANGUAGE REFERENCE
⟨process⟩ ::= 0
| yield (see Section 6.8)
| ⟨ident⟩[(seq⟨pterm⟩)]
| (⟨process⟩)
| ⟨process⟩ | ⟨process⟩
| !⟨process⟩
| ! ⟨ident⟩ <= ⟨ident⟩ ⟨process⟩ (see Section 6.8)
| foreach ⟨ident⟩ <= ⟨ident⟩ do ⟨process⟩ (see Section 6.8)
| new ⟨ident⟩[[seq⟨ident⟩]]: ⟨typeid ⟩ [; ⟨process⟩]
| ⟨ident⟩ <-R ⟨typeid ⟩ [; ⟨process⟩] (see Section 6.8)
| if ⟨pterm⟩ then ⟨process⟩ [else ⟨process⟩]
| in(⟨pterm⟩,⟨pattern⟩) ⟨options⟩ [; ⟨process⟩]
| out(⟨pterm⟩,⟨pterm⟩)) [; ⟨process⟩]
| let ⟨pattern⟩ = ⟨pterm⟩ [in ⟨process⟩ [else ⟨process⟩]]
| ⟨ident⟩[:⟨typeid ⟩] <- ⟨pterm⟩ [;⟨process⟩] (see Section 6.8)
| let ⟨typedecl ⟩ suchthat ⟨pterm⟩ ⟨options⟩ [in ⟨process⟩ [else ⟨process⟩]]
(see Section 6.3)
| insert ⟨ident⟩(seq⟨pterm⟩) [; ⟨process⟩] (see Section 4.1.5)
| get ⟨ident⟩(seq⟨pattern⟩) [suchthat ⟨pterm⟩] ⟨options⟩ [in ⟨process⟩ [else ⟨process⟩]]
(see Section 4.1.5)
| event ⟨ident⟩[(seq⟨pterm⟩)] [; ⟨process⟩] (see Section 3.2.2)
| phase ⟨nat⟩ [; ⟨process⟩] (see Section 4.1.6)
| sync ⟨nat⟩ [[⟨tag⟩]] [; ⟨process⟩] (see Section 4.1.7)
140 APPENDIX A. LANGUAGE REFERENCE
Appendix B
Semantics
In this appendix, we provide the semantics for the input language of ProVerif. A simpler semantics for
the core language of ProVerif can be found in research papers, for instance [Bla16].
We consider an infinite set of names N . We denote by Npub all names declared with the channel
and free (without option private) declarations from Figure A.2. We denote by Fc the set containing
the constructor function symbols declared with the fun declaration without reduc from Figure A.2 as
well as built-in constructors: true, false, 0, +1, tuples. Similarly, we denote by Fd the set containing
destructor function symbols declared by the reduc declaration or the fun declaration with reduc as well
as built-in destructors: not, is nat, ||, &&, =, <>, <=, >=, <, >, -k for each k natural number. (More
built-in constructors and destructors are used by ProVerif for some encodings; we omit them here.)
We denote by M, N, . . . a ⟨term⟩ that does not contain destructor function symbols. The equational
theory is denoted E and is the set of all equations defined with the equation declaration from Figure A.2.
We will denote by M =E N when M and N are equal modulo the equational theory E.
Note that in Figure A.1, ⟨term⟩ includes the syntax for natural numbers. Internally, they are repre-
sented by a constant 0 and a unary constructor function symbol +1 such that a natural number n is in
fact n successive applications of +1 to 0. For example, 2 is the term +1(+1(0)). The term M + k and
k + M are therefore syntactic sugar for k applications of +1 to the term M . A similar comment applies
to process terms ⟨pterm⟩ and patterns ⟨pattern⟩.
Rewrite rules We denote by U, V, . . . a ⟨mayfailterm⟩ that does not contain destructor function sym-
bols. A conditional rewrite rule is a classic rewrite rule h(U1 , . . . , Un ) → U to which a conditional
formula ϕ is added, denoted h(U1 , . . . , Un ) → U || ϕ. Note that the formula is always of the form
Vn V n′ ′
Vn′′ ′′
Vn′′′ ′′′ ′′′
i=1 Mi ≥ Ni ∧ i′ =1 ¬nat(Mi ) ∧ i′′ =1 nat(Mi ) ∧ i′′′ =1 ∀x̃i .Mi ̸= Ni where x̃ stands for a sequence
of variables and nat is the predicate returning true iff its argument is a natural number. From the rewrite
rules ⟨reduc⟩ and ⟨reduc ′ ⟩ in the declaration of a destructor from Figure A.2, ProVerif generates a set of
equivalent conditional rewrite rules.
For example, assume that enc/2 ∈ Fc and consider the destructor dec declared by
141
142 APPENDIX B. SEMANTICS
Predicates We denote by Fp the set of predicates defined by the pred declaration. For all p/n ∈ Fp ,
we denote by def(p) the set (possibly infinite) of tuples (M1 , . . . , Mn ) such that p(M1 , . . . , Mn ) is true.
When the predicate p is defined with the option [block], def(p) is not given to ProVerif and so ProVerif
will try to prove the query for all possible def(p). Otherwise def(p) is defined as the set of tuples
(M1 , . . . , Mn ) such that p(M1 , . . . , Mn ) is derivable by the clauses ⟨clauses⟩ defined with the clauses
declaration from Figure A.2.
matches(x, M ) = {M /x }
matches(=M, N ) = ∅ if M =E N
[
matches(f (cpat 1 , . . . , cpat n ), f (M1 , . . . , Mn )) = σi
i∈{1,...,n}
Note that if cpat contains an occurrence of = fail or if U = fail then matches(cpat, U ) is not defined.
The semantics of process terms and pattern-matching are respectively given by two labeled transition
ℓ ℓ
relations →
− t and →− p on term and pattern configurations where ℓ can either be empty or an event
143
ev(M1 , . . . , Mn ) with ev being one of the events declared with the event declaration from Figure A.2. A
term configuration and pattern configuration are respectively the tuples T , Npr , D and T , Npr , pat where
T is the state of tables, that is a set of elements of the form tbl(M1 , . . . , Mn ) where tbl is one of
the tables declared with the table declaration from Figure A.2.
if then D else D′
and Cpat is the set containing the following contexts of process terms
let = U in D else D′
The conditions D of get never contain new, <-R, let . . . suchthat, event, insert, so in the rules for
get, T and Npr are indeed unchanged during the evaluation of D.
Semantics of processes
A process corresponds to an element of ⟨process⟩ from Figure A.8. Some processes are in fact syntactic
sugar for others:
Similarly to process terms, when else ⟨process⟩, in ⟨process⟩ else ⟨process⟩, ; ⟨process⟩, and suchthat
⟨pterm⟩ are omitted, they are respectively syntactic sugar for else 0, in 0 else 0, ; 0, and suchthat
true.
Given a process P , we define synchro(P ) the set collecting all synchronization parameters that occur
in P . Hence, synchro(sync n [tag];Q) = {(n, tag)} ∪ synchro(Q) and in all other cases, synchro(P ) is
the union of the synchronization parameters of the immediate subprocesses of P . When the tag [tag]
of a synchronization is omitted, ProVerif uses a new fresh tag.
A process configuration is a tuple S, ph, T , Npr , P where S is a set of pairs (integer,tag) representing
the synchronization parameters, ph is an integer representing the phase, and P is a multiset of processes.
′ ′
The initial configuration for a closed process P is synchro(P ), 0, ∅, Npr , {P } where Npr is the set of
names declared with the free declaration from Figure A.2 with the option [private]. The semantics of
ℓ
processes is given by a labeled transition relation →
− between process configurations where ℓ can either be
empty or an event ev(M1 , . . . , Mn ) with ev being one of the events declared with the event declaration
from Figure A.2.
This labeled transition relation is defined in Figure B.2, where Ct is the set containing the following
contexts of process terms
144 APPENDIX B. SEMANTICS
ℓ
− p T ′ , Npr
T , Npr , f (cpat 1 , . . . ,cpat i−1 ,pat i , . . . ,pat n ) → ′
, f (cpat 1 , . . . ,cpat i−1 ,pat ′i ,pat i+1 , . . . ,pat n )
ℓ
− p T ′ , Npr
if T , Npr , pati → ′
, pat′i
ℓ ℓ
− p T ′ , Npr
T , Npr , =D → ′
, =D′ − t T ′ , Npr
if T , Npr , D → ′
, D′
ℓ ℓ
− t T ′ , Npr
T , Npr , C[D] → ′
, C[D′ ] − t T ′ , Npr
if T , Npr , D → ′
, D′ and C[ ] ∈ Ct
ℓ ℓ
− t T ′ , Npr
T , Npr , C[pat] → ′
, C[pat ′ ] − p T ′ , Npr
if T , Npr , pat → ′
, pat ′ and C[ ] ∈ Cpat
145
ℓ
− S, ph, T ′ , Npr
S, ph, T , Npr , P ∪ {C[D]} → ′
, P ∪ {C[D′ ]}
ℓ
− t T ′ , Npr
if T , Npr , D → ′
, D′ and C[ ] ∈ Ct
ℓ
− S, ph, T ′ , Npr
S, ph, T , Npr , P ∪ {C[pat]} → ′
, P ∪ {C[pat ′ ]}
ℓ
− p T ′ , Npr
if T , Npr , pat → ′
, pat ′ and C[ ] ∈ Cpat
146 APPENDIX B. SEMANTICS
out( ,D);P
out(U , );P
in( ,pat);P
if then P else Q
let pat = in P else Q
let x1 , . . . , xm suchthat p(U1 , . . . ,Ui−1 , ,Di+1 , . . . ,Dn ) in P else Q
[AB05a] Martı́n Abadi and Bruno Blanchet. Analyzing security protocols with secrecy types and logic
programs. Journal of the ACM, 52(1):102–146, January 2005.
[AB05b] Martı́n Abadi and Bruno Blanchet. Computer-assisted verification of a protocol for certified
email. Science of Computer Programming, 58(1–2):3–27, October 2005. Special issue SAS’03.
[AB05c] Xavier Allamigeon and Bruno Blanchet. Reconstruction of attacks against cryptographic
protocols. In 18th IEEE Computer Security Foundations Workshop (CSFW-18), pages 140–
154, Aix-en-Provence, France, June 2005. IEEE.
[Aba00] Martı́n Abadi. Security protocols and their properties. In F.L. Bauer and R. Steinbrueggen,
editors, Foundations of Secure Computation, NATO Science Series, pages 39–60. IOS Press,
2000. Volume for the 20th International Summer School on Foundations of Secure Compu-
tation, held in Marktoberdorf, Germany (1999).
[ABB+ 04] William Aiello, Steven M. Bellovin, Matt Blaze, Ran Canetti, John Ioannidis, Keromytis
Keromytis, and Omer Reingold. Just Fast Keying: Key agreement in a hostile Internet.
ACM Transactions on Information and System Security, 7(2):242–273, May 2004.
[ABCL09] Martı́n Abadi, Bruno Blanchet, and Hubert Comon-Lundh. Models and proofs of protocol
security: A progress report. In Ahmed Bouajjani and Oded Maler, editors, 21st International
Conference on Computer Aided Verification (CAV’09), volume 5643 of Lecture Notes in
Computer Science, pages 35–49, Grenoble, France, June 2009. Springer.
[ABF07] Martı́n Abadi, Bruno Blanchet, and Cédric Fournet. Just Fast Keying in the pi calculus.
ACM Transactions on Information and System Security (TISSEC), 10(3):1–59, July 2007.
[ABF17] Martı́n Abadi, Bruno Blanchet, and Cédric Fournet. The applied pi calculus: Mobile values,
new names, and secure communication. Journal of the ACM, 65(1):1:1–1:41, October 2017.
[AF01] Martı́n Abadi and Cédric Fournet. Mobile values, new names, and secure communication. In
28th Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages
(POPL’01), pages 104–115, London, United Kingdom, January 2001. ACM Press.
[AFP06] Michel Abdalla, Pierre-Alain Fouque, and David Pointcheval. Password-based authenticated
key exchange in the three-party setting. IEE Proceedings Information Security, 153(1):27–39,
March 2006.
[AGHP02] Martı́n Abadi, Neal Glew, Bill Horne, and Benny Pinkas. Certified email with a light on-line
trusted third party: Design and implementation. In 11th International World Wide Web
Conference, pages 387–395, Honolulu, Hawaii, May 2002. ACM Press.
[AN95] Ross Anderson and Roger Needham. Programming Satan’s computer. In J. van Leeuven,
editor, Computer Science Today: Recent Trends and Developments, volume 1000 of Lecture
Notes in Computer Science, pages 426–440. Springer, 1995.
[AN96] Martı́n Abadi and Roger Needham. Prudent engineering practice for cryptographic protocols.
IEEE Transactions on Software Engineering, 22(1):6–15, January 1996.
147
148 BIBLIOGRAPHY
[BAF08] Bruno Blanchet, Martı́n Abadi, and Cédric Fournet. Automated verification of selected
equivalences for security protocols. Journal of Logic and Algebraic Programming, 75(1):3–51,
February–March 2008.
[BAN89] Michael Burrows, Martı́n Abadi, and Roger Needham. A logic of authentication. Proceedings
of the Royal Society of London A, 426(1871):233–271, dec 1989. A preliminary version ap-
peared as Digital Equipment Corporation Systems Research Center report No. 39, February
1989.
[BBK17] Karthikeyan Bhargavan, Bruno Blanchet, and Nadim Kobeissi. Verified models and reference
implementations for the TLS 1.3 standard candidate. In IEEE Symposium on Security and
Privacy (S&P’17), pages 483–503, Los Alamitos, CA, May 2017. IEEE Computer Society
Press.
[BC08] Bruno Blanchet and Avik Chaudhuri. Automated formal analysis of a protocol for secure file
sharing on untrusted storage. In IEEE Symposium on Security and Privacy, pages 417–431,
Oakland, CA, May 2008. IEEE.
[BCC04] Ernie Brickell, Jan Camenisch, and Liqun Chen. Direct Anonymous Attestation. In CCS
’04: 11th ACM conference on Computer and communications security, pages 132–145, New
York, USA, 2004. ACM Press.
[BCFZ08] Karthikeyan Bhargavan, Ricardo Corin, Cédric Fournet, and Eugen Zălinescu. Cryptograph-
ically verified implementations for TLS. In Proceedings of the 15th ACM Conference on
Computer and Communications Security (CCS’08), pages 459–468. ACM, October 2008.
[BDM20] David Baelde, Stéphanie Delaune, and Solène Moreau. A method for proving unlinkability
of stateful protocols. In 33rd IEEE Computer Security Foundations Symposium (CSF’20),
pages 169–183. IEEE, June 2020.
[BFG06] Karthikeyan Bhargavan, Cédric Fournet, and Andrew Gordon. Verified reference implemen-
tations of WS-Security protocols. In Mario Bravetti, Manuel Núñez, and Gianluigi Zavattaro,
editors, 3rd International Workshop on Web Services and Formal Methods (WS-FM 2006),
volume 4184 of Lecture Notes in Computer Science, pages 88–106, Vienna, Austria, Septem-
ber 2006. Springer.
[BFGS08] Karthikeyan Bhargavan, Cédric Fournet, Andrew Gordon, and Nikhil Swamy. Verified im-
plementations of the information card federated identity-management protocol. In ACM
Symposium on Information, Computer and Communications Security (ASIACCS’08), pages
123–135, Tokyo, Japan, March 2008. ACM.
[BFGT06] Karthikeyan Bhargavan, Cédric Fournet, Andrew Gordon, and Stephen Tse. Verified interop-
erable implementations of security protocols. In 19th IEEE Computer Security Foundations
Workshop (CSFW’06), pages 139–152, Venice, Italy, July 2006. IEEE Computer Society.
[BHM08] Michael Backes, Catalin Hritcu, and Matteo Maffei. Automated verification of remote elec-
tronic voting protocols in the applied pi-calculus. In 21st IEEE Computer Security Foun-
dations Symposium (CSF’08), pages 195–209, Pittsburgh, PA, June 2008. IEEE Computer
Society.
[Bla04] Bruno Blanchet. Automatic proof of strong secrecy for security protocols. In IEEE Sympo-
sium on Security and Privacy, pages 86–100, Oakland, California, May 2004.
[Bla05] Bruno Blanchet. Security protocols: From linear to classical logic by abstract interpretation.
Information Processing Letters, 95(5):473–479, September 2005.
[Bla07] Bruno Blanchet. Computationally sound mechanized proofs of correspondence assertions.
In 20th IEEE Computer Security Foundations Symposium (CSF’07), pages 97–111, Venice,
Italy, July 2007. IEEE. Extended version available as ePrint Report 2007/128, http://
eprint.iacr.org/2007/128.
BIBLIOGRAPHY 149
[Bla09] Bruno Blanchet. Automatic verification of correspondences for security protocols. Journal
of Computer Security, 17(4):363–434, July 2009.
[Bla11] Bruno Blanchet. Using Horn clauses for analyzing security protocols. In Véronique Cortier
and Steve Kremer, editor, Formal Models and Techniques for Analyzing Security Protocols,
volume 5 of Cryptology and Information Security Series, chapter 5, pages 86–111. IOS Press,
March 2011.
[Bla16] Bruno Blanchet. Modeling and verifying security protocols with the applied pi calculus and
ProVerif. Foundations and Trends in Privacy and Security, 1(1–2):1–135, October 2016.
[Bla17] Bruno Blanchet. Symbolic and computational mechanized verification of the ARINC823
avionic protocols. In 30th IEEE Computer Security Foundations Symposium (CSF’17), pages
68–82, Los Alamitos, CA, August 2017. IEEE Computer Society Press.
[BM92] Steven M. Bellovin and Michael Merritt. Encrypted Key Exchange: Password-based proto-
cols secure against dictionary attacks. In Proceedings of the 1992 IEEE Computer Society
Symposium on Research in Security and Privacy, pages 72–84, May 1992.
[BM93] Steven M. Bellovin and Michael Merritt. Augmented Encrypted Key Exchange: a password-
based protocol secure against dictionary attacks and password file compromise. In Proceedings
of the First ACM Conference on Computer and Communications Security, pages 244–250,
November 1993.
[BMU08] Michael Backes, Matteo Maffei, and Dominique Unruh. Zero-knowledge in the applied pi-
calculus and automated verification of the direct anonymous attestation protocol. In 29th
IEEE Symposium on Security and Privacy, pages 202–215, Oakland, CA, May 2008. IEEE.
Technical report version available at http://eprint.iacr.org/2007/289.
[BP05] Bruno Blanchet and Andreas Podelski. Verification of cryptographic protocols: Tagging
enforces termination. Theoretical Computer Science, 333(1-2):67–90, March 2005. Special
issue FoSSaCS’03.
[BS16] Bruno Blanchet and Ben Smyth. Automated reasoning for equivalences in the applied pi
calculus with barriers. In 29th IEEE Computer Security Foundations Symposium (CSF’16),
Lisboa, Portugal, June 2016. IEEE.
[BS18] Bruno Blanchet and Ben Smyth. Automated reasoning for equivalences in the applied pi
calculus with barriers. Journal of Computer Security, 26(3):367–422, 2018.
[CB13] Vincent Cheval and Bruno Blanchet. Proving more observational equivalences with ProVerif.
In David Basin and John Mitchell, editors, 2nd Conference on Principles of Security and
Trust (POST 2013), volume 7796 of Lecture Notes in Computer Science, pages 226–246,
Rome, Italy, March 2013. Springer.
[CCT18] Vincent Cheval, Véronique Cortier, and Mathieu Turuani. A little more conversation, a little
less action, a lot more satisfaction: Global states in proverif. In Steve Chong and Stéphanie
Delaune, editors, Proceedings of the 30th IEEE Computer Security Foundations Symposium
(CSF’18), Oxford, UK, July 2018. IEEE Computer Society Press.
[CHW06] Véronique Cortier, Heinrich Hördegen, and Bogdan Warinschi. Explicit randomness is not
necessary when modeling probabilistic encryption. In C. Dima, M. Minea, and F.L. Tiplea,
editors, Workshop on Information and Computer Security (ICS 2006), volume 186 of Elec-
tronic Notes in Theoretical Computer Science, pages 49–65, Timisoara, Romania, September
2006. Elsevier.
[CR09] Liqun Chen and Mark Ryan. Attack, solution and verification for shared authorisation data
in TCG TPM. In Proc. Sixth Formal Aspects in Security and Trust (FAST’09), volume 5983
of Lecture Notes in Computer Science. Springer, 2009.
150 BIBLIOGRAPHY
[CT08] Liqun Chen and Qiang Tang. Bilateral unknown key-share attacks in key agreement proto-
cols. Journal of Universal Computer Science, 14(3):416–440, February 2008.
[DDKS17] Jannik Dreier, Charles Duménil, Steve Kremer, and Ralf Sasse. Beyond subterm-convergent
equational theories in automated verification of stateful protocols. In Matteo Maffei and Mark
Ryan, editors, 6th International Conference on Principles of Security and Trust (POST),
volume 10204 of Lecture Notes in Computer Science, pages 117–140, Uppsala, Sweden, April
2017. Springer.
[DJ04] Stéphanie Delaune and Florent Jacquemard. A theory of dictionary attacks and its complex-
ity. In Proceedings of the 17th IEEE Computer Security Foundations Workshop (CSFW’04),
pages 2–15, Asilomar, Pacific Grove, California, USA, June 2004. IEEE Computer Society
Press.
[DKR09] Stéphanie Delaune, Steve Kremer, and Mark D. Ryan. Verifying privacy-type properties of
electronic voting protocols. Journal of Computer Security, 17(4):435–487, 2009.
[DRS08] Stéphanie Delaune, Mark D. Ryan, and Ben Smyth. Automatic verification of privacy prop-
erties in the applied pi-calculus. In Yuecel Karabulut, John Mitchell, Peter Herrmann, and
Christian Damsgaard Jensen, editors, Proceedings of the 2nd Joint iTrust and PST Con-
ferences on Privacy, Trust Management and Security (IFIPTM’08), volume 263 of IFIP
Conference Proceedings, pages 263–278. Springer, June 2008. An extended version of this
paper appears in [Smy11, Chapters 4 & 5].
[DS81] Dorothy E. Denning and Giovanni Maria Sacco. Timestamps in key distribution protocols.
Communications of the ACM, 24(8):533–536, August 1981.
[DvOW92] Whitfield Diffie, Paul C. van Oorschot, and Michael J. Wiener. Authentication and authen-
ticated key exchanges. Designs, Codes and Cryptography, 2(2):107–125, June 1992.
[DY83] Danny Dolev and Andrew C. Yao. On the security of public key protocols. IEEE Transactions
on Information Theory, IT-29(12):198–208, March 1983.
[GJ03] Andrew Gordon and Alan Jeffrey. Authenticity by typing for security protocols. Journal of
Computer Security, 11(4):451–521, 2003.
[HBD19] Lucca Hirschi, David Baelde, and Stéphanie Delaune. A method for unbounded verification
of privacy-type properties. Journal of Computer Security, 27(3):277–342, June 2019.
[HLS00] James Heather, Gavin Lowe, and Steve Schneider. How to prevent type flaw attacks on
security protocols. In 13th IEEE Computer Security Foundations Workshop (CSFW-13),
pages 255–268, Cambridge, England, July 2000.
[KBB17] Nadim Kobeissi, Karthikeyan Bhargavan, and Bruno Blanchet. Automated verification for
secure messaging protocols and their implementations: A symbolic and computational ap-
proach. In 2nd IEEE European Symposium on Security and Privacy (EuroS&P’17), pages
435–450, Los Alamitos, CA, April 2017. IEEE Computer Society Press.
[KR05] Steve Kremer and Mark D. Ryan. Analysis of an electronic voting protocol in the applied pi
calculus. In Mooly Sagiv, editor, Programming Languages and Systems: 14th European Sym-
posium on Programming, ESOP 2005, volume 3444 of Lecture Notes in Computer Science,
pages 186–200, Edimbourg, UK, April 2005. Springer.
[Kra96] Hugo Krawczyk. SKEME: A versatile secure key exchange mechanism for internet. In Internet
Society Symposium on Network and Distributed Systems Security, February 1996. Available
at http://bilbo.isu.edu/sndss/sndss96.html.
[KRS+ 03] Mahesh Kallahalla, Erik Riedel, Ram Swaminathan, Qian Wang, and Kvin Fu. Plutus:
Scalable secure file sharing on untrusted storage. In 2nd Conference on File and Storage
Technologies (FAST’03), pages 29–42, San Francisco, CA, April 2003. Usenix.
BIBLIOGRAPHY 151
[KT08] Ralf Küsters and Tomasz Truderung. Reducing protocol analysis with XOR to the XOR-
free case in the Horn theory based approach. In Proceedings of the 15th ACM conference
on Computer and communications security (CCS’08), pages 129–138, Alexandria, Virginia,
USA, October 2008. ACM.
[KT09] Ralf Küsters and Tomasz Truderung. Using ProVerif to analyze protocols with Diffie-Hellman
exponentiation. In 22nd IEEE Computer Security Foundations Symposium (CSF’09), pages
157–171, Port Jefferson, New York, USA, July 2009. IEEE.
[Low96] Gavin Lowe. Breaking and fixing the Needham-Schroeder public-key protocol using FDR. In
Tools and Algorithms for the Construction and Analysis of Systems, volume 1055 of Lecture
Notes in Computer Science, pages 147–166. Springer, 1996.
[Low97] Gavin Lowe. A hierarchy of authentication specifications. In 10th Computer Security Foun-
dations Workshop (CSFW ’97), pages 31–43, Rockport, Massachusetts, June 1997. IEEE
Computer Society.
[MvOV96] Alfred J. Menezes, Paul C. van Oorschot, and Scott A. Vanstone. Handbook of applied
cryptography. CRC, 1996.
[NS78] Roger M. Needham and Michael D. Schroeder. Using encryption for authentication in large
networks of computers. Communications of the ACM, 21(12):993–999, December 1978.
[NS87] Roger M. Needham and Michael D. Schroeder. Authentication revisited. Operating Systems
Review, 21(1):7, 1987.
[OR87] Dave Otway and Owen Rees. Efficient and timely mutual authentication. Operating Systems
Review, 21(1):8–10, 1987.
[Pau98] Larry C. Paulson. The inductive approach to verifying cryptographic protocols. Journal of
Computer Security, 6(1–2):85–128, 1998.
[RS11] Mark Ryan and Ben Smyth. Applied pi calculus. In Véronique Cortier and Steve Kremer, ed-
itor, Formal Models and Techniques for Analyzing Security Protocols, volume 5 of Cryptology
and Information Security Series, chapter 6, pages 112–142. IOS Press, March 2011.
[Sch13] Stephan Schulz. Simple and efficient clause subsumptionwith feature vector indexing. In
Maria Paola Bonacina and Mark E. Stickel, editors, Automated Reasoning and Mathematics,
Essays in Memory of William W. McCune, volume 7788 of Lecture Notes in Computer
Science, pages 45–67. Springer, 2013.
[Smy11] Ben Smyth. Formal verification of cryptographic protocols with automated reasoning. PhD
thesis, School of Computer Science, University of Birmingham, 2011.
[SRC07] Ben Smyth, Mark Ryan, and Liqun Chen. Direct Anonymous Attestation (DAA): Ensuring
privacy with corrupt administrators. In ESAS’07: 4th European Workshop on Security and
Privacy in Ad hoc and Sensor Networks, volume 4572 of Lecture Notes in Computer Science,
pages 218–231. Springer, 2007. An extended version of this paper appears in [Smy11, Chapter
4].
[SRK10] Ben Smyth, Mark Ryan, and Steve Kremer. Election verifiability in electronic voting proto-
cols. In Dimitris Gritzalis, Bart Preneel, and Marianthi Theoharidou, editors, 15th European
Symposium on Research in Computer Security (ESORICS’10), volume 6345 of Lecture Notes
in Computer Science, pages 389–404, Athens, Greece, September 2010. Springer. An ex-
tended version of this paper appears in [Smy11, Chapter 3].
[SRKK10] Ben Smyth, Mark Ryan, Steve Kremer, and Mounira Kourjieh. Towards automatic analysis
of election verifiability properties. In Alessandro Armando and Gavin Lowe, editors, ARSPA-
WITS’10: Proceedings of the Joint Workshop on Automated Reasoning for Security Protocol
Analysis and Issues in the Theory of Security, volume 6186 of Lecture Notes in Computer
Science, pages 165–182. Springer, March 2010. An extended version of this paper appears
in [Smy11, Chapter 3].
152 BIBLIOGRAPHY
[WL92] Thomas Y. C. Woo and Simon S. Lam. Authentication for distributed systems. Computer,
25(1):39–52, January 1992.
[WL93] Thomas Y. C. Woo and Simon S. Lam. A semantic model for authentication protocols. In
Proceedings IEEE Symposium on Research in Security and Privacy, pages 178–194, Oakland,
California, May 1993.
[WL97] Thomas Y. C. Woo and Simon S. Lam. Authentication for distributed systems. In Dorothy
Denning and Peter Denning, editors, Internet Besieged: Countering Cyberspace Scofflaws,
pages 319–355. ACM Press and Addison-Wesley, October 1997.
[Yub10] Yubico AB. The YubiKey Manual - Usage, configuration and introduction of basic concepts
(Version 2.2), 2010.