1 Introduction

Formal methods provide a rigorous and convenient framework for analysing security protocols. In particular, mature push-button analysis tools have emerged and have been successfully applied to many protocols from the literature in the context of trace properties such as authentication or confidentiality. These tools employ a variety of analysis techniques, such as model checking (e.g., Avispa [6] and Scyther [31]), Horn clause resolution (e.g., ProVerif [13]), term rewriting (e.g., Scyther [31] and Tamarin [38]), and type systems [7, 12, 16,17,18,19,20,21, 34, 36, 37].

In the recent years, attention has been given also to equivalence properties, which are crucial to model privacy properties such as vote privacy [8, 33], unlikability [5], or anonymity [9]. For example, consider an authentication protocol \(P_{pass}\) embedded in a biometric passport. \(P_{pass}\) preserves anonymity of passport holders if an attacker cannot distinguish an execution with Alice from an execution with Bob. This can be expressed by the equivalence \(P_{pass}( Alice )\;\approx _t\; P_{pass}( Bob )\). Equivalence is also used to express properties closer to cryptographic games like strong secrecy.

Two main classes of tools have been developed for equivalence. First, in the case of an unbounded number of sessions (when the protocol is executed arbitrarily many times), equivalence is undecidable. Instead, the tools ProVerif [13, 15] and Tamarin [11, 38] try to prove a stronger property, namely diff-equivalence, that may be too strong e.g. in the context of voting. Tamarin covers a larger class of protocols but may require some guidance from the user. Maude-NPA [35, 40] also proves diff-equivalence but may have non-termination issues. Another class of tools aim at deciding equivalence, for bounded number of sessions. This is the case in particular of SPEC [32], APTE [23], Akiss [22], and SatEquiv [26]. SPEC, APTE, and Akiss suffer from efficiency issues and can typically not handle more than 3–4 sessions. SatEquiv is much more efficient but is limited to symmetric encryption and requires protocols to be well-typed, which often assumes some additional tagging of the protocol.

Our Contribution. Following the approach of [28], we propose a novel technique for proving equivalence properties for a bounded number of sessions as well as an unbounded number of sessions (or a mix of both), based on typing. [28] proposes a first type system that entails trace equivalence \(P \;\approx _t\; Q\), provided protocols use fixed (long-term) keys, identical in P and Q. In this paper, we target a larger class of protocols, that includes in particular key-exchange protocols and protocols whose security relies on branching on the secret. This is the case e.g. of the private authentication protocol [3], where agent B returns a true answer to A, encrypted with A’s public key if A is one of his friends, and sends a decoy message (encrypted with a dummy key) otherwise.

We devise a new type system for reasoning about keys. In particular, we introduce bikeys to cover behaviours where keys in P differ from the keys in Q. We design new typing rules to reason about protocols that may branch differently (in P and Q), depending on the input. Following the approach of [28], our type system collects sent messages into constraints that are required to be consistent. Intuitively, the type system guarantees that any execution of P can be matched by an execution of Q, while consistency imposes that the resulting sequences of messages are indistinguishable for an attacker. We had to entirely revisit the approach of [28] and prove a finer invariant in order to cope with the case where keys are used as variables. Specifically, most of the rules for encryption, signature, and decryption had to be adapted to accommodate the flexible usage of keys. For messages, we had to modify the rules for keys and encryption, in order to encrypt messages with keys of different type (bi-key type), instead of only fixed keys. We show that our type system entails equivalence for the standard notion of trace equivalence [24] and we devise a procedure for proving consistency. This yields an efficient approach for proving equivalence of protocols for a bounded and an unbounded number of sessions (or a combination of both).

We implemented a prototype of our type-checker that we evaluate on a set of examples, that includes private authentication, the BAC protocol (of the biometric passport), as well as Helios together with the setup phase. Our tool requires a light type annotation that specifies which keys and names are likely to be secret or public and the form of the messages encrypted by a given key. This can be easily inferred from the structure of the protocol. Our type-checker outperforms even the most efficient existing tools for a bounded number of sessions by two (for examples with few processes) to three (for examples with more processes) orders of magnitude. Note however that these tools decide equivalence while our type system is incomplete. In the case of an unbounded number of sessions, on our examples, the performance is comparable to ProVerif, one of the most popular tools. We consider in particular vote privacy in the Helios protocol, in the case of a dishonest ballot board, with no revote (as the protocol is insecure otherwise). ProVerif fails to handle this case as it cannot (faithfully) consider a mix of bounded and unbounded number of sessions. Compared to [28], our analysis includes the setup phase (where voters receive the election key), which could not be considered before.

The technical details and proofs omitted due to space constraints are available in the companion technical report [29].

2 High-Level Description

2.1 Background

Trace equivalence of two processes is a property that guarantees that an attacker observing the execution of either of the two processes cannot decide which one it is. Previous work [28] has shown how trace equivalence can be proved statically using a type system combined with a constraint checking procedure. The type system consists of typing rules of the form \(\varGamma \vdash P \sim Q \rightarrow C\), meaning that in an environment \(\varGamma \) two processes P and Q are equivalent if the produced set of constraints C, encoding the attacker observables, is consistent.

The typing environment \(\varGamma \) is a mapping from nonces, keys, and variables to types. Nonces are assigned security labels with a confidentiality and an integrity component, e.g. \(\mathtt {HL}\) for high confidentiality and low integrity. Key types are of the form \(\mathrm {key}^{l}(T)\) where l is the security label of the key and T is the type of the payload. Key types are crucial to convey typing information from one process to another one. Normally, we cannot make any assumptions about values received from the network – they might possibly originate from the attacker. If we however successfully decrypt a message using a secret symmetric key, we know that the result is of the key’s payload type. This is enforced on the sender side, whenever outputting an encryption.

A core assumption of virtually any efficient static analysis for equivalence is uniform execution, meaning that the two processes of interest always take the same branch in a branching statement. For instance, this means that all decryptions must always succeed or fail equally in the two processes. For this reason, previous work introduced a restriction to allow only encryption and decryption with keys whose equality could be statically proved.

2.2 Limitation

There are however protocols that require non-uniform execution for a proof of trace equivalence, e.g., the private authentication protocol [3]. The protocol aims at authenticating B to A, anonymously w.r.t. other agents. More specifically, agent B may refuse to communicate with agent A but a third agent D should not learn whether B declines communication with A or not. The protocol can be informally described as follows, where \(\mathtt {pk}(k)\) denotes the public key associated to key k, and \(\mathtt {aenc}(M,\mathtt {pk}(k))\) denotes the asymmetric encryption of message M with this public key.

$$ \begin{array}{c@{\;}c@{\;}c@{\;}c@{\quad }l} A &{}\rightarrow &{} B &{} : &{} \mathtt {aenc}(\langle N_a, \mathtt {pk}(k_a) \rangle ,\mathtt {pk}(k_b))\\ B &{}\rightarrow &{} A &{} : &{} {\left\{ \begin{array}{ll} \mathtt {aenc}(\langle N_a, \langle N_b, \mathtt {pk}(k_b) \rangle \rangle ,\mathtt {pk}(k_a)) &{} \text {if } B \text { accepts }A \text {'s request} \\ \mathtt {aenc}(N_b,\mathtt {pk}(k)) &{} \text {if }B \text { declines }A \text {'s request} \end{array}\right. } \end{array} $$
Fig. 1.
figure 1

Key types for the private authentication protocol

If B declines to communicate with A, he sends a decoy message \(\mathtt {aenc}(N_b,\mathtt {pk}(k))\) where \(\mathtt {pk}(k)\) is a decoy key (no one knows the private key k).

2.3 Encrypting with Different Keys

Let \(P_a(k_a,\mathtt {pk}(k_b))\) model agent A willing to talk with B, and \(P_b(k_b,\mathtt {pk}(k_a))\) model agent B willing to talk with A (and declining requests from other agents). We model the protocol as:

$$ \begin{array}{rcl} P_a (k_a,pk_b) &{}=&{} \mathtt {new}\; N_a. \mathtt {out}(\mathtt {aenc}(\langle N_a, \mathtt {pk}(k_a) \rangle ,pk_b)).\; \mathtt {in}(z) \\ P_b(k_b, pk_a) &{}=&{} \mathtt {new}\; N_b.\;\mathtt {in}(x).\\ &{}&{} \mathtt {let}\; y = \mathtt {adec}(x,k_b) \;\mathtt {in} ~~ \mathtt {let}\; y_1 = \pi _1(y) \;\mathtt {in} ~~ \mathtt {let}\; y_2 = \pi _2(y) \;\mathtt {in}\\ &{}&{}\quad \mathtt {if}\;y_2 = pk_a \;\mathtt {then}\\ &{}&{}\quad \quad \mathtt {out}(\mathtt {aenc}(\langle y_1, \langle N_b, \mathtt {pk}(k_b) \rangle \rangle ,pk_a))\\ &{}&{}\quad \mathtt {else}\; \mathtt {out}(\mathtt {aenc}(N_b,\mathtt {pk}(k)))\\ \end{array} $$

where \(\mathtt {adec}(M,k)\) denotes asymmetric decryption of message M with private key k. We model anonymity as the following equivalence, intuitively stating that an attacker should not be able to tell whether B accepts requests from the agent A or C:

$$ P_a(k_a,\mathtt {pk}(k_b)) \;~|~\; P_b(k_b,\mathtt {pk}(k_a))\;\approx _t\; P_a(k_a,\mathtt {pk}(k_b)) \;~|~\; P_b(k_b,\mathtt {pk}(k_c)) $$

We now show how we can type the protocol in order to show trace equivalence. The initiator \(P_a\) is trivially executing uniformly, since it does not contain any branching operations. We hence focus on typing the responder \(P_b\).

The beginning of the responder protocol can be typed using standard techniques. Then however, we perform the test \(y_2 = \mathtt {pk}(k_a)\) on the left side and \(y_2 = \mathtt {pk}(k_c)\) on the right side. Since we cannot statically determine the result of the two equality checks – and thus guarantee uniform execution – we have to typecheck the four possible combinations of \(\mathtt {then}\) and \(\mathtt {else}\) branches. This means we have to typecheck outputs of encryptions that use different keys on the left and the right side.

To deal with this we do not assign types to single keys, but rather to pairs of keys \((k,k')\) – which we call bikeys – where k is the key used in the left process and \(k'\) is the key used in the right process. The key types used for typing are presented in Fig. 1.

As an example, we consider the combination of the \(\mathtt {then}\) branch on the left with the \(\mathtt {else}\) branch on the right. This combination occurs when A is successfully authenticated on the left side, while being rejected on the right side. We then have to typecheck B’s positive answer together with the decoy message: \(\varGamma \vdash \mathtt {aenc}(\langle y_1, \langle N_b, \mathtt {pk}(k_b) \rangle \rangle ,\mathtt {pk}(k_a)) \sim \mathtt {aenc}(N_b,\mathtt {pk}(k)) : \mathtt {LL}\). For this we need the type for the bikey \((k_a,k)\).

2.4 Decrypting Non-uniformly

When decrypting a ciphertext that was potentially generated using two different keys on the left and the right side, we have to take all possibilities into account. Consider the following extension of the process \(P_a\) where agent A decrypts B’s message.

$$ \begin{array}{rcl} P_a (k_a,pk_b) &{}=&{} \mathtt {new}\; N_a. \mathtt {out}(\mathtt {aenc}(\langle N_a, \mathtt {pk}(k_a) \rangle ,pk_b)).\; \mathtt {in}(z). \\ &{}&{} \mathtt {let}\; z' = \mathtt {adec}(z,k_a) \;\mathtt {in} \; \mathtt {out}(1) \\ &{}&{} \mathtt {else}\; \mathtt {out}(0) \end{array} $$

In the decryption, there are the following possible cases:

  • The message is a valid encryption supplied by the attacker (using the public key \(\mathtt {pk}(k_a)\)), so we check the \(\mathtt {then}\) branch on both sides with \(\varGamma (z')=\mathtt {LL}\).

  • The message is not a valid encryption supplied by the attacker so we check the \(\mathtt {else}\) branch on both sides.

  • The message is a valid response from B. The keys used on the left and the right are then one of the four possible combinations \((k_a,k), (k_a,k_c), (k,k_c)\) and (kk).

    • In the first two cases the decryption will succeed on the left and fail on the right. We hence check the \(\mathtt {then}\) branch on the left with \(\varGamma (z')=\mathtt {HL}\) with the \(\mathtt {else}\) branch on the right. If the type \(\varGamma (k_a,k)\) were different from \(\varGamma (k_a,k_c)\), we would check this combination twice, using the two different payload types.

    • In the remaining two cases the decryption will fail on both sides. We hence would have to check the two \(\mathtt {else}\) branches (which however we already did).

While checking the \(\mathtt {then}\) branch together with the \(\mathtt {else}\) branch, we have to check \(\varGamma \vdash 1 \sim 0 : \mathtt {LL}\), which rightly fails, as the protocol does not guarantee trace equivalence.

3 Model

In symbolic models, security protocols are typically modelled as processes of a process algebra, such as the applied pi-calculus [2]. We present here a calculus used in [28] and inspired from the calculus underlying the ProVerif tool [14]. This section is mostly an excerpt of [28], recalled here for the sake of completeness, and illustrated with the private authentication protocol.

3.1 Terms

Messages are modelled as terms. We assume an infinite set of names \(\mathcal {N}\) for nonces, further partitioned into the set \(\mathcal {FN}\) of free nonces (created by the attacker) and the set \(\mathcal {BN}\) of bound nonces (created by the protocol parties), an infinite set of names \(\mathcal {K}\) for keys similarly split into \(\mathcal {FK}\) and \(\mathcal {BK}\), and an infinite set of variables \(\mathcal {V}\). Cryptographic primitives are modelled through a signature \(\mathcal {F}\), that is, a set of function symbols, given with their arity (i.e. the number of arguments). Here, we consider the following signature:

$$ \mathcal {F}_c= \{\mathtt {pk},\mathtt {vk},\mathtt {enc},\mathtt {aenc},\mathtt {sign},\langle \cdot , \cdot \rangle ,\mathtt {h}\} $$

that models respectively public and verification key, symmetric and asymmetric encryption, concatenation and hash. The companion primitives (symmetric and asymmetric decryption, signature check, and projections) are represented by the following signature:

$$ \mathcal {F}_d = \{\mathtt {dec},\mathtt {adec},\mathtt {checksign},\pi _1,\pi _2\} $$

We also consider a set \(\mathcal {C}\) of (public) constants (used as agent names for instance). Given a signature \(\mathcal {F}\), a set of names \(\mathcal {N}\), and a set of variables \(\mathcal {V}\), the set of terms \(\mathcal {T}(\mathcal {F},\mathcal {V},\mathcal {N})\) is the set inductively defined by applying functions to variables in \(\mathcal {V}\) and names in \(\mathcal {N}\). We denote by \(\mathrm {names}(t)\) (resp. \(\mathrm {vars}(t)\)) the set of names (resp. variables) occurring in t. A term is ground if it does not contain variables.

We consider the set \(\mathcal {T}(\mathcal {F}_c\cup \mathcal {F}_d\cup \mathcal {C},\mathcal {V},\mathcal {N}\cup \mathcal {K})\) of cryptographic terms, simply called terms. Messages are terms with constructors from \(\mathcal {T}(\mathcal {F}_c\cup \mathcal {C},\mathcal {V},\mathcal {N}\cup \mathcal {K})\). We assume the set of variables to be split into two subsets \(\mathcal {V}= \mathcal {X}\uplus \mathcal {AX}\) where \(\mathcal {X}\) are variables used in processes while \(\mathcal {AX}\) are variables used to store messages. An attacker term is a term from \(\mathcal {T}(\mathcal {F}_c\cup \mathcal {F}_d\cup \mathcal {C},\mathcal {AX},\mathcal {FN}\cup \mathcal {FK})\). In particular, an attacker term cannot use nonces and keys created by the protocol’s parties.

A substitution \(\sigma = \{M_1 / x_1, \dots , M_k / x_k\}\) is a mapping from variables \(x_1, \dots , x_k \in \mathcal {V}\) to messages \(M_1, \dots , M_k\). We let \(\mathrm {dom}(\sigma ) = \{x_1, \dots , x_k\}\). We say that \(\sigma \) is ground if all messages \(M_1, \dots , M_k\) are ground. We let \(\mathrm {names}(\sigma ) = \bigcup _{1 \le i \le k} \mathrm {names}(M_i)\). The application of a substitution \(\sigma \) to a term t is denoted \(t\sigma \) and is defined as usual.

The evaluation of a term t, denoted \(t \downarrow \), corresponds to the bottom-up application of the cryptographic primitives and is recursively defined as follows.

$$ \begin{array}{r@{\;}c@{\;}ll} u \downarrow &{} = &{} u &{} \text {if }u\in \mathcal {N}\cup \mathcal {V}\cup \mathcal {K}\cup \mathcal {C}\\ \mathtt {pk}(t) \downarrow &{} = &{} \mathtt {pk}(t \downarrow ) &{} \text {if }t \downarrow \in \mathcal {K}\\ \mathtt {vk}(t) \downarrow &{} = &{} \mathtt {vk}(t \downarrow ) &{} \text {if }t \downarrow \in \mathcal {K}\\ \mathtt {h}(t) \downarrow &{} = &{} \mathtt {h}(t \downarrow ) &{} \text {if }t \downarrow \ne \bot \\ \langle t_1, t_2 \rangle \downarrow &{} = &{} \langle t_1 \downarrow , t_2 \downarrow \rangle &{} \text {if }t_1 \downarrow \ne \bot \text { and }t_2 \downarrow \ne \bot \\ \mathtt {enc}(t_1,t_2) \downarrow &{} = &{} \mathtt {enc}(t_1 \downarrow ,t_2 \downarrow ) &{} \text {if }t_1 \downarrow \ne \bot \text { and }t_2 \downarrow \in \mathcal {K}\\ \mathtt {sign}(t_1,t_2) \downarrow &{} = &{} \mathtt {sign}(t_1 \downarrow ,t_2 \downarrow ) &{} \text {if }t_1 \downarrow \ne \bot \text { and }t_2 \downarrow \in \mathcal {K}\\ \mathtt {aenc}(t_1,t_2) \downarrow &{} = &{} \mathtt {aenc}(t_1 \downarrow ,t_2 \downarrow ) &{} \text {if }t_1 \downarrow \ne \bot \text { and }t_2 \downarrow =\mathtt {pk}(k)\\ &{}&{}\qquad \text {for some }k\in \mathcal {K}\\ \end{array}$$
$$\begin{array}{r@{\;}c@{\;}ll} \pi _1(t) \downarrow &{} = &{} t_1 &{} \text {if }t \downarrow =\langle t_1, t_2 \rangle \\ \pi _2(t) \downarrow &{} = &{} t_2 &{} \text {if }t \downarrow =\langle t_1, t_2 \rangle \\ \mathtt {dec}(t_1,t_2) \downarrow &{} = &{} t_3 &{} \text {if }t_1 \downarrow = \mathtt {enc}(t_3,t_4) \text { and }t_4=t_2 \downarrow \\ \mathtt {adec}(t_1,t_2) \downarrow &{} = &{} t_3 &{} \text {if }t_1 \downarrow = \mathtt {aenc}(t_3,\mathtt {pk}(t_4)) \text { and }t_4=t_2 \downarrow \\ \mathtt {checksign}(t_1,t_2) \downarrow &{} = &{} t_3 &{} \text {if }t_1 \downarrow = \mathtt {sign}(t_3,t_4) \text { and }t_2 \downarrow = \mathtt {vk}(t_4)\\ t \downarrow &{} = &{} \bot &{} \text {otherwise} \end{array} $$

Note that the evaluation of term t succeeds only if the underlying keys are atomic and always returns a message or \(\bot \). For example we have \(\pi _1(\langle a, b \rangle ) \downarrow =a\), while \(\mathtt {dec}(\mathtt {enc}(a,\langle b, b \rangle ),\langle b, b \rangle ) \downarrow = \bot \), because the key is non atomic. We write \(t =_\downarrow t'\) if \(t \downarrow = t' \downarrow \).

Fig. 2.
figure 2

Syntax for processes.

3.2 Processes

Security protocols describe how messages should be exchanged between participants. We model them through a process algebra, whose syntax is displayed in Fig. 2. We identify processes up to \(\alpha \)-renaming, i.e., avoiding substitution of bound names and variables, which are defined as usual. Furthermore, we assume that all bound names, keys, and variables in the process are distinct.

A configuration of the system is a tuple \((\mathcal {P}; \phi ; \sigma )\) where:

  • \(\mathcal {P}\) is a multiset of processes that represents the current active processes;

  • \(\phi \) is a substitution with \(\mathrm {dom}(\phi )\subseteq \mathcal {AX}\) and for any \(x\in \mathrm {dom}(\phi )\), \(\phi (x)\) (also denoted \(x\phi \)) is a message that only contains variables in \(\mathrm {dom}(\sigma )\). \(\phi \) represents the terms that have been sent;

  • \(\sigma \) is a ground substitution.

The semantics of processes is given through a transition relation \(\xrightarrow {\;\alpha \;}\), defined in Fig. 3 (\(\tau \) denotes a silent action). The relation \(\xrightarrow {\;w\;}_*\) is defined as the reflexive transitive closure of \(\xrightarrow {\;\alpha \;}\), where w is the concatenation of all actions. We also write equality up to silent actions \(=_\tau \).

Intuitively, process \(\mathtt {new}\; n.P\) creates a fresh nonce or key, and behaves like P. Process \(\mathtt {out}(M).P \) emits M and behaves like P, provided that the evaluation of M is successful. The corresponding message is stored in the frame \(\phi \), corresponding to the attacker knowledge. A process may input any message that an attacker can forge (rule In) from her knowledge \(\phi \), using a recipe R to compute a new message from \(\phi \). Note that all names are initially assumed to be secret. Process \(P ~|~Q\) corresponds to the parallel composition of P and Q. Process \( \mathtt {let}\; x = d \;\mathtt {in}\; P \;\mathtt {else}\; Q\) behaves like P in which x is replaced by d if d can be successfully evaluated and behaves like Q otherwise. Process \(\mathtt {if}\;M = N \;\mathtt {then}\; P \;\mathtt {else}\; Q\) behaves like P if M and N correspond to two equal messages and behaves like Q otherwise. The replicated process !P behaves as an unbounded number of copies of P.

A trace of a process P is any possible sequence of transitions in the presence of an attacker that may read, forge, and send messages. Formally, the set of traces \(\mathrm {trace}(P) \) is defined as follows.

$$ \mathrm {trace}(P) = \{(w,\phi ,\sigma ) | (\{P\} ; \emptyset ; \emptyset ) \xrightarrow {\;w\;}_* (\mathcal {P}; \phi ; \sigma )\}\ $$
Fig. 3.
figure 3

Semantics

Example 1

Consider the private authentication protocol (PA) presented in Sect. 2. The process \(P_b(k_b,\mathtt {pk}(k_a))\) corresponding to responder B answering a request from A has already been defined in Sect. 2.3. The process \(P_a(k_a, \mathtt {pk}(k_b))\) corresponding A willing to talk to B is:

$$\begin{aligned} P_a(k_a, pk_b) = \mathtt {new}\; N_a. \mathtt {out}(\mathtt {aenc}(\langle N_a, \mathtt {pk}(k_a) \rangle ,pk_b)).\; \mathtt {in}(z) \end{aligned}$$

Altogether, a session between A and B is represented by the process:

$$\begin{aligned} P_a(k_a, \mathtt {pk}(k_b)) ~|~P_b(k_b,\mathtt {pk}(k_a)) \end{aligned}$$

where \(k_a,k_b\in \mathcal {BK}\), which models that the attacker initially does not know \(k_a,k_b\).

Fig. 4.
figure 4

Types for terms

An example of a trace describing an “honest” execution, where the attacker does not interfere with the intended run of the protocol, can be written as \((tr,\phi )\) where

$$ tr =_\tau \mathtt {new}\; x_1.\mathtt {out}(x_1).\mathtt {in}(x_1).\mathtt {new}\; x_2.\mathtt {out}(x_2).\mathtt {in}(x_2) $$

and

$$ \phi = \{x_1\mapsto \mathtt {aenc}(\langle N_a, \mathtt {pk}(k_a) \rangle ,\mathtt {pk}(k_b)), x_2\mapsto \mathtt {aenc}(\langle N_a, \langle N_b, \mathtt {pk}(k_b) \rangle \rangle ,\mathtt {pk}(k_a))\}. $$

The trace \( tr \) describes A outputting the first message of the protocol, which is stored in \(\phi (x_1)\). The attacker then simply forwards \(\phi (x_1)\) to B. B then performs several silent actions (decrypting the message, comparing its content to \(\mathtt {pk}(k_a)\)), and outputs a response, which is stored in \(\phi (x_2)\) and forwarded to A by the attacker.

3.3 Equivalence

When processes evolve, sent messages are stored in a substitution \(\phi \) while the values of variables are stored in \(\sigma \). A frame is simply a substitution \(\psi \) where \(\mathrm {dom}(\psi ) \subseteq \mathcal {AX}\). It represents the knowledge of an attacker. In what follows, we will typically consider \(\phi \sigma \).

Intuitively, two sequences of messages are indistinguishable to an attacker if he cannot perform any test that could distinguish them. This is typically modelled as static equivalence [2]. Here, we consider of variant of [2] where the attacker is also given the ability to observe when the evaluation of a term fails, as defined for example in [25].

Definition 1

(Static Equivalence). Two ground frames \(\phi \) and \(\phi '\) are statically equivalent if and only if they have the same domain, and for all attacker terms RS with variables in \(\mathrm {dom}(\phi ) = \mathrm {dom}(\phi ')\), we have

$$ (R \phi =_\downarrow S \phi ) \iff (R \phi ' =_\downarrow S \phi ') $$

Then two processes P and Q are in equivalence if no matter how the adversary interacts with P, a similar interaction may happen with Q, with equivalent resulting frames.

Definition 2

(Trace Equivalence). Let P, Q be two processes. We write \(P \sqsubseteq _tQ\) if for all \((s,\phi , \sigma )\in \mathrm {trace}(P)\), there exists \((s',\phi ', \sigma ')\in \mathrm {trace}(Q)\) such that \(s =_\tau s'\) and \(\phi \sigma \) and \(\phi '\sigma '\) are statically equivalent. We say that P and Q are trace equivalent, and we write \(P \approx _tQ\), if \(P \sqsubseteq _tQ\) and \(Q \sqsubseteq _tP\).

Note that this definition already includes the attacker’s behaviour, since processes may input any message forged by the attacker.

Example 2

As explained in Sect. 2, anonymity is modelled as an equivalence property. Intuitively, an attacker should not be able to know which agents are executing the protocol. In the case of protocol PA, presented in Example 1, the anonymity property can be modelled by the following equivalence:

$$\begin{aligned} P_a(k_a,\mathtt {pk}(k_b)) \;~|~\; P_b(k_b,\mathtt {pk}(k_a))\;\approx _t\; P_a(k_a,\mathtt {pk}(k_b)) \;~|~\; P_b(k_b,\mathtt {pk}(k_c)) \end{aligned}$$

4 A Type System for Dynamic Keys

Types. In our type system we give types to pairs of messages – one from the left process and one from the right one. We store the types of nonces, variables, and keys in a typing environment \(\varGamma \). While we store a type for a single nonce or variable occurring in both processes, we assign a potentially different type to every different combination of keys \((k,k')\) used in the left and right process – so called bikeys. This is an important non-standard feature that enables us to type protocols using different encryption and decryption keys.

Fig. 5.
figure 5

Selected subtyping rules

The types for messages are defined in Fig. 4 and explained below. Selected subtyping rules are given in Fig. 5. We assume three security labels \(\mathtt {HH},\mathtt {HL}\) and \(\mathtt {LL}\), ranged over by l, whose first (resp. second) component denotes the confidentiality (resp. integrity) level. Intuitively, values of high confidentiality may never be output to the network in plain, and values of high integrity are guaranteed not to originate from the attacker. Pair types \(T * T'\) describe the type of their components and the type \(T \,\vee \,T'\) is given to messages that can have type T or type \(T'\).

The type \(\tau ^{l,{a}}_{n} \) describes nonces and constants of security level l: the label \({a}\) ranges over \(\{\infty ,1\}\), denoting whether the nonce is bound within a replication or not (constants are always typed with \({a}=1\)). We assume a different identifier n for each constant and restriction in the process. The type \(\tau ^{l,1}_{n} \) is populated by a single name, (i.e., n describes a constant or a non-replicated nonce) and \(\tau ^{l,\infty }_{n} \) is a special type, that is instantiated to \(\tau ^{l,1}_{n_j} \) in the jth replication of the process. Type \(\llbracket \tau ^{l,{a}}_{n} \,;\, \tau ^{l',{a}}_{m} \rrbracket \) is a refinement type that restricts the set of possible values of a message to values of type \(\tau ^{l,{a}}_{n} \) on the left and type \(\tau ^{l',{a}}_{m} \) on the right. For a refinement type \(\llbracket \tau ^{l,{a}}_{n} \,;\, \tau ^{l,{a}}_{n} \rrbracket \) with equal types on both sides we write \(\tau ^{l,{a}}_{n} \).

Keys can have three different types ranged over by KT, ordered by a subtyping relation (SEqKey, SSesKey): \(\mathrm {seskey}^{l,a}(T)<:\mathrm {eqkey}^{l}(T) <:\mathrm {key}^{l}(T)\). For all three types, l denotes the security label (SKey) of the key and T is the type of the payload that can be encrypted or signed with these keys. This allows us to transfer typing information from one process to another one: e.g. when encrypting, we check that the payload type is respected, so that we can be sure to get a value of the payload type upon decryption. The three different types encode different relations between the left and the right component of a bikey \((k,k')\). While type \(\mathrm {key}^{l}(T)\) can be given to bikeys with different components \(k \ne k'\), type \(\mathrm {eqkey}^{l}(T)\) ensures that the keys are equal on both sides in the specific typed instruction. Type \(\mathrm {seskey}^{l,a}(T)\) additionally guarantees that the key is always the same on the left and the right throughout the whole process. We allow for dynamic generation of keys of type \(\mathrm {seskey}^{l,a}(T)\) and use a label a to denote whether the key is generated under replication or not – just like for nonce types.

For a key of type T, we use types \(\mathrm {pkey}(T)\) and \(\mathrm {vkey}(T)\) for the corresponding public key and verification key, and types \({(T')}_{T}\) and \({\{T'\}}_{T}\) for symmetric and asymmetric encryptions of messages of type \(T'\) with this key. Public keys and verification keys can be treated as \(\mathtt {LL}\) if the corresponding keys are equal (SPubkey, SVkey) and subtyping on encryptions is directly induced by subtyping of the payload types (SEnc, SAenc) (Fig. 6).

Fig. 6.
figure 6

Selected rules for messages

Constraints. When typing messages, we generate constraints of the form \((M \sim N)\), meaning that the attacker may see M and N in the left and right process, respectively, and these two messages are thus required to be indistinguishable.

Due to space reasons we only present a few selected rules that are characteristic of the typing of branching protocols. The omitted rules are similar in spirit to the presented ones or are standard rules for equivalence typing [28].

4.1 Typing Messages

The typing judgement for messages is of the form \( \varGamma \vdash M \sim N : T \rightarrow c \) which reads as follows: under the environment \(\varGamma \), M and N are of type T and either this is a high confidentiality type (i.e., M and N are not disclosed to the attacker) or M and N are indistinguishable for the attacker assuming the set of constraints c is consistent.

Confidential nonces can be given their label from the typing environment in rule TNonce. Since their label prevents them from being released in clear, the attacker cannot observe them and we do not need to add constraints for them. They can however be output in encrypted form and will then appear in the constraints of the encryption. Public nonces (labeled as \(\mathtt {LL}\)) can be typed if they are equal on both sides (rule TNonceL). These are standard rules, as well as the rules TVar, TSub, TPair and THigh  [28].

A non-standard rule that is crucial for the typing of branching protocols is rule TKey. As the typing environment contains types for bikeys \((k,k')\) this rule allows us to type two potentially different keys with their type from the environment. With the standard rule TPubKeyL we can only type a public key of the same keys on both sides, while rule TPubKey allows us to type different public keys \(\mathtt {pk}(M),\mathtt {pk}(N)\), provided we can show that there exists a valid key type for the terms M and N. This highlights another important technical contribution of this work, as compared to existing type systems for equivalence: we do not only support a fixed set of keys, but also allow for the usage of keys in variables, that have been received from the network.

To show that a message is of type \({\{T\}}_{T'}\) – a message of type T encrypted asymmetrically with a key of type \(T'\), we have to show that the corresponding terms have exactly these types in rule TAenc. The generated constraints are simply propagated. In addition we need to show that \(T'\) is a valid type for a public key, or \(\mathtt {LL}\), which models untrusted keys received from the network. Note, that this rule allows us to encrypt messages with different keys in the two processes. For encryptions with honest keys (label \(\mathtt {HH}\)) we can use rule TAenc to give type \(\mathtt {LL}\) to the messages, if we can show that the payload type is respected. In this case we add the entire encryptions to the constraints, since the attacker can check different encryptions for equality, even if he cannot obtain the plaintext. Rule TAencL allows us to give type \(\mathtt {LL}\) to encryptions even if we do not respect the payload type, or if the key is corrupted. However, we then have to type the plaintexts with type \(\mathtt {LL}\) since we cannot guarantee their confidentiality. Additionally, we have to ensure that the same key is used in both processes, because the attacker might possess the corresponding private keys and test which decryption succeeds. Since we already add constraints for giving type \(\mathtt {LL}\) to the plaintext, we do not need to add any additional constraints.

4.2 Typing Processes

From now on, we assume that processes assign a type to freshly generated nonces and keys. That is, \(\mathtt {new}\; n.P\) is now of the form \(\mathtt {new}\; n:T.\ P\). This requires a (very light) type annotation from the user. The typing judgement for processes is of the form \( \varGamma \vdash P \sim Q \rightarrow C \) and can be interpreted as follows: If two processes P and Q can be typed in \(\varGamma \) and if the generated constraint set C is consistent, then P and Q are trace equivalent. We present selected rules in Fig. 7.

Fig. 7.
figure 7

Selected rules for processes

Rule POut states that we can output messages to the network if we can type them with type \(\mathtt {LL}\), i.e., they are indistinguishable to the attacker, provided that the generated set c of constraints is consistent. The constraints of c are then added to all constraints in the constraint set C. We define \(C{\cup _{\forall }}c' := \left\{ (c \cup c',\varGamma ) \;|\; (c,\varGamma ) \in C \right\} \). This rule, as well as the rules PZero, PIn, PNew, PPar, and PLet, are standard rules [28].

Rule PNewKey allows us to generate new session keys at runtime, which models security protocols more faithfully. It also allows us to generate infinitely many keys, by introducing new keys under replication.

Rule PLetAdecSame treats asymmetric decryptions where we use the same fixed honest key (label \(\mathtt {HH}\)) for decryptions in both processes. Standard type systems for equivalence have a simplifying (and restrictive) invariant that guarantees that encryptions are always performed using the same keys in both processes and hence guarantee that both processes always take the same branch in decryption (compare rule PLet). In our system however, we allow encryptions with potentially different keys, which requires cross-case validation in order to retain soundness. Still, the number of possible combinations of encryption keys is limited by the assignments in the typing environment \(\varGamma \). To cover all the possibilities, we type the following combinations of continuation processes:

Fig. 8.
figure 8

Selected destructor rules

  • Both \(\mathtt {then}\) branches: In this case we know that key k was used for encryption on both sides. Because of \(\varGamma (k,k)=\mathrm {key}^{\mathtt {HH}}(T)\), we know that in this case the payload type is T and we type the continuation with \(\varGamma ,x:T\).

    Because the message may also originate from the attacker (who also has access to the public key), we have to type the two \(\mathtt {then}\) branches also with \(\varGamma ,x:\mathtt {LL}\).

  • Both \(\mathtt {else}\) branches: If decryption fails on both sides, we type the two \(\mathtt {else}\) branches without introducing any new variables.

  • Left \(\mathtt {then}\), right \(\mathtt {else}\): The encryption may have been created with key k on the left side and another key \(k'\) on the right side. Hence, for each \(k' \ne k\), such that \(\varGamma (k,k')\) maps to a key type with label \(\mathtt {HH}\) and payload type \(T'\), we have to typecheck the left \(\mathtt {then}\) branch and the right \(\mathtt {else}\) branch with \(\varGamma ,x:T'\).

  • Left \(\mathtt {else}\), right \(\mathtt {then}\): This case is analogous to the previous one.

The generated set of constraints is simply the union of all generated constraints for the subprocesses. Rule PIfAll lets us typecheck any conditional by simply checking the four possible branch combinations. In contrast to the other rules for conditionals that we present in a companion technical report, this rule does not require any other preconditions or checks on the terms \(M,M',N,N'\).

Destructor Rules. The rule PLet requires that a destructor application succeeds or fails equally in the two processes. To ensure this property, it relies on additional rules for destructors. We present selected rules in Fig. 8. Rule DAdecL is a standard rule that states that a decryption of a variable of type \(\mathtt {LL}\) with an untrusted key (label \(\mathtt {LL}\)) yields a result of type \(\mathtt {LL}\). Decryption with a trusted (label \(\mathtt {HH}\)) session key gives us a value of the key’s payload type or type \(\mathtt {LL}\) in case the encryption was created by the attacker using the public key. Here it is important that the key is of type \(\mathrm {seskey}^{\mathtt {HH},a}(T)\), since this guarantees that the key is never used in combination with a different key and hence decryption will always equally succeed or fail in both processes. Rule DAdecL’ is similar to rule DAdecL except it uses a variable for decryption instead of a fixed key. Rule DAdecT treats the case in which we know that the variable x is an asymmetric encryption of a specific type. If the type of the key used for decryption matches the key type used for encryption, we know the exact type of the result of a successful decryption. DAdecT’ is similar to DAdecT, with a variable as key. In a companion technical report we present similar rules for symmetric decryption and verification of signatures.

Fig. 9.
figure 9

Type derivation for the response to A and the decoy message

4.3 Typing the Private Authentication Protocol

We now show how our type system can be applied to type the Private Authentication protocol presented in Sect. 2.3, by showing the most interesting parts of the derivation. We type the protocol using the initial environment \(\varGamma \) presented in Fig. 1.

We focus on the responder process \(P_b\) and start with the asymmetric decryption. As we use the same key \(k_b\) in both processes, we apply rule PLetAdecSame. We have \(\varGamma (x)=\mathtt {LL}\) by rule PIn and \(\varGamma (k_b,k_b) = \mathrm {key}^{\mathtt {HH}}(\mathtt {HH},\mathtt {LL})\). We do not have any other entry using key \(k_b\) in \(\varGamma \). We hence typecheck the two \(\mathtt {then}\) branches once with \(\varGamma ,y:(\mathtt {HH}* \mathtt {LL})\) and once with \(\varGamma ,y:\mathtt {LL}\), as well as the two \(\mathtt {else}\) branches (which are just \(\mathtt {0}\) in this case).

Typing the let expressions is straightforward using rule PLet. In the conditional we check \(y_2 = \mathtt {pk}(k_a)\) in the left process and \(y_2 = \mathtt {pk}(k_c)\) in the right process. Since we cannot guarantee which branches are taken or even if the same branch is taken in the two processes, we use rule PIfAll to typecheck all four possible combinations of branches. We now focus on the case where A is successfully authenticated in the left process and is rejected in the right process. We then have to typecheck B’s positive answer together with the decoy message: \(\varGamma \vdash \mathtt {aenc}(\langle y_1, \langle N_b, \mathtt {pk}(k_b) \rangle \rangle ,\mathtt {pk}(k_a)) \sim \mathtt {aenc}(N_c,\mathtt {pk}(k)) : \mathtt {LL}\).

Figure 9 presents the type derivation for this example. We apply rule TAenc to give type \(\mathtt {LL}\) to the two terms, adding the two encryptions to the constraint set. Using rule TAencH we can show that the encryptions are well-typed with type \({\{\mathtt {HL}\}}_{\mathrm {pkey}(\mathrm {key}^{\mathtt {HH}}(\mathtt {HL}))}\). The type of the payload is trivially shown with rule THigh. To type the public key, we use rule TPubKey followed by rule TKey, which looks up the type for the bikey \((k_a,k)\) in the typing environment \(\varGamma \).

5 Consistency

Our type system collects constraints that intuitively correspond to (symbolic) messages that the attacker may see (or deduce). Therefore, two processes are in trace equivalence only if the collected constraints are in static equivalence for any plausible instantiation.

However, checking static equivalence of symbolic frames for any instantiation corresponding to a real execution may be as hard as checking trace equivalence [24]. Conversely, checking static equivalence for any instantiation may be too strong and may prevent proving equivalence of processes. Instead, we use again the typing information gathered by our type system and we consider only instantiations that comply with the type. Actually, we even restrict our attention to instantiations where variables of type \(\mathtt {LL}\) are only replaced by deducible terms. This last part is a key ingredient for considering processes with dynamic keys. Hence, we define a constraint to be consistent if the corresponding two frames are in static equivalence for any instantiation that can be typed and produces constraints that are included in the original constraint.

Formally, we first introduce the following ingredients:

  • and denote the frames that are composed of the left and the right terms of the constraints respectively (in the same order).

  • \(\phi _\mathtt {LL}^{\varGamma }\) denotes the frame that is composed of all low confidentiality nonces and keys in \(\varGamma \), as well as all public encryption keys and verification keys in \(\varGamma \). This intuitively corresponds to the initial knowledge of the attacker.

  • Two ground substitutions \(\sigma , \sigma '\) are well-typed in \(\varGamma \) with constraint \(c_\sigma \) if they preserve the types for variables in \(\varGamma \), i.e., for all x, \(\varGamma \vdash \sigma (x) \sim \sigma '(x) : \varGamma (x) \rightarrow c_x\), and \(c_\sigma = \bigcup _{x\in \mathrm {dom}(\varGamma )}c_x\).

The instantiation of a constraint is defined as expected. If c is a set of constraints, and \(\sigma \), \(\sigma '\) are two substitutions, let be the instantiation of c by \(\sigma \) on the left and \(\sigma '\) on the right, that is, .

Definition 3

(Consistency). A set of constraints c is consistent in an environment \(\varGamma \) if for all substitutions \(\sigma \), \(\sigma '\) well-typed in \(\varGamma \) with a constraint \(c_\sigma \) such that , the frames \(\phi _\mathtt {LL}^{\varGamma } \cup \) \((c)\sigma \) and \(\phi _\mathtt {LL}^{\varGamma } \cup \) \((c)\sigma '\) are statically equivalent. We say that \((c,\varGamma )\) is consistent if c is consistent in \(\varGamma \) and that a constraint set C is consistent in \(\varGamma \) if each element \((c, \varGamma ) \in C\) is consistent.

Compared to [28], we now require . This means that instead of considering any (well typed) instantiations, we only consider instantiations that use fragments of the constraints. For example, this now imposes that low variables are instantiated by terms deducible from the constraint. This refinement of consistency provides a tighter definition and is needed for non fixed keys, as explained in the next section.

6 Soundness

In this section, we provide our main results. First, soundness of our type system: whenever two processes can be typed with consistent constraints, then they are in trace equivalence. Then we show how to automatically prove consistency. Finally, we explain how to lift these two first results from finite processes to processes with replication. But first, we discuss why we cannot directly apply the results from [28] developed for processes with long term keys.

6.1 Example

Consider the following example, typical for a key-exchange protocol: Alice receives some key and uses it to encrypt, e.g. a nonce. Here, we consider a semi-honest session, where an honest agent A is receiving a key from a dishonest agent D. Such sessions are typically considered in combination with honest sessions.

$$ \begin{array}{ccccl} C &{} \rightarrow &{} A &{} : &{} \mathtt {aenc}(\langle k, C \rangle ,\mathtt {pk}(A))\\ A &{} \rightarrow &{} C &{} : &{} \mathtt {aenc}(n,k)\\ \end{array} $$

The process modelling the role of Alice is as follows.

$$ \begin{array}{rl} P_A = &{} \mathtt {in}(x).~ ~ \mathtt {let}\; x' = \mathtt {adec}(x,k_A) \;\mathtt {in} ~ ~ \mathtt {let}\; y = \pi _1(x') \;\mathtt {in} ~ ~ \mathtt {let}\; z = \pi _2(x') \;\mathtt {in}\\ &{} \mathtt {if}\;z = C \;\mathtt {then} ~ ~ \mathtt {new}\; n.\; \mathtt {out}(\mathtt {enc}(n,y)) \end{array} $$

When type-checking \(P_A\sim P_A\) (as part as a more general process with honest sessions), we would collect the constraint \(\mathtt {enc}(n,y)\sim \mathtt {enc}(n,y)\) where y comes from the adversary and is therefore a low variable (that is, of type \(\mathtt {LL}\)). The approach of [28] consisted in opening messages as much as possible. In this example, this would yield the constraint \(y\sim y\) which typically renders the constraint inconsistent, as exemplified below.

When typechecking the private authentication protocol, we obtain constraints containing \(\mathtt {aenc}(\langle y_1, \langle N_b, \mathtt {pk}(k_b) \rangle \rangle ,\mathtt {pk}(k_a)) \sim \mathtt {aenc}(N_b,\mathtt {pk}(k))\) (as seen in Fig. 9), where \(y_1\) has type \(\mathtt {HL}\). Assume now that the constraint also contains \(y \sim y\) for some variable y of type \(\mathtt {LL}\) and consider the following instantiations of y and \(y_1\): \(\sigma (y_1)=\sigma '(y_1)=a\) for some constant a and \(\sigma (y)=\sigma '(y)=\mathtt {aenc}(N_b,\mathtt {pk}(k))\). Note that such an instantiation complies with the type since \(\varGamma \vdash \sigma (y) \sim \sigma '(y) : \mathtt {LL} \rightarrow c\) for some constraint c. The instantiated constraint would then contain

$$\begin{aligned} \{\mathtt {aenc}(\langle a, \langle N_b, \mathtt {pk}(k_b) \rangle \rangle ,\mathtt {pk}(k_a))&\sim \mathtt {aenc}(N_b,\mathtt {pk}(k)),\\ \mathtt {aenc}(N_b,\mathtt {pk}(k))&\sim \mathtt {aenc}(N_b,\mathtt {pk}(k))\} \end{aligned}$$

and the corresponding frames are not statically equivalent, which makes the constraint inconsistent for the consistency definition of [28].

Therefore, our first idea consists in proving that we only collect constraints that are saturated w.r.t. deduction: any deducible subterm can already be constructed from the terms of the constraint. Second, we show that for any execution, low variables are instantiated by terms deducible from the constraints. This guarantees that our new notion of consistency is sound. The two results are reflected in the next section.

6.2 Soundness

Our type system, together with consistency, implies trace equivalence.

Theorem 1

(Typing implies trace equivalence). For all P, Q, and C, for all \(\varGamma \) containing only keys, if \(\varGamma \vdash P \sim Q \rightarrow C\) and C is consistent, then \(P \approx _tQ\).

Example 3

We can typecheck PA, that is

$$\begin{aligned} \varGamma \vdash P_a(k_a,\mathtt {pk}(k_b)) \;~|~\; P_b(k_b,\mathtt {pk}(k_a)) \sim P_a(k_a,\mathtt {pk}(k_b)) \;~|~\; P_b(k_b,\mathtt {pk}(k_c)) \rightarrow C_{ PA } \end{aligned}$$

where \(\varGamma \) has been defined in Fig. 1 and assuming that nonce \(N_a\) of process \(P_a\) has been annotated with type \(\tau ^{\mathtt {HH},1}_{N_a} \) and nonce \(N_b\) of \(P_b\) has been annotated with type \(\tau ^{\mathtt {HH},1}_{N_b} \). The constraint set \(C_{ PA }\) can be proved to be consistent using the procedure presented in the next section. Therefore, we can conclude that

$$\begin{aligned} P_a(k_a,\mathtt {pk}(k_b)) \;~|~\; P_b(k_b,\mathtt {pk}(k_a))\;\approx _t\; P_a(k_a,\mathtt {pk}(k_b)) \;~|~\; P_b(k_b,\mathtt {pk}(k_c)) \end{aligned}$$

which shows anonymity of the private authentication protocol.

The first key ingredient in the proof of Theorem 1 is the fact that any well-typed low term is deducible from the constraint generated when typing it.

Lemma 1

(Low terms are recipes on their constraints). For all ground messages M, N, for all \(\varGamma \), c, if \(\varGamma \vdash M \sim N : \mathtt {LL} \rightarrow c\) then there exists an attacker recipe R without destructors such that \(M = R (\)\((c)\cup \phi _\mathtt {LL}^{\varGamma })\) and \(N = R (\)\((c)\cup \phi _\mathtt {LL}^{\varGamma })\).

The second key ingredient is a finer invariant on protocol executions: for any typable pair of processes PQ, any execution of P can be mimicked by an execution of Q such that low variables are instantiated by well-typed terms constructible from the constraint.

Lemma 2

For all processes P, Q, for all \(\phi \), \(\sigma \), for all multisets of processes \(\mathcal {P}\), constraint sets C, sequences s of actions, for all \(\varGamma \) containing only keys, if \(\varGamma \vdash P \sim Q \rightarrow C\), C is consistent, and \((\{P\},\emptyset ,\emptyset ) \xrightarrow {\;s\;}_* (\mathcal {P},\phi ,\sigma )\), then there exist a sequence \(s'\) of actions, a multiset \(\mathcal {Q}\), a frame \(\phi '\), a substitution \(\sigma '\), an environment \(\varGamma '\), a constraint c such that:

\((\{Q\},\emptyset ,\emptyset ) \xrightarrow {\;s'\;}_* (\mathcal {Q},\phi ',\sigma ')\), with \(s =_\tau s'\)

\(\varGamma ' \vdash \phi \sigma \sim \phi '\sigma ' : \mathtt {LL} \rightarrow c\), and for all \(x\in \mathrm {dom}(\sigma )\cap \mathrm {dom}(\sigma ')\), there exists \(c_x\) such that \(\varGamma ' \vdash \sigma (x) \sim \sigma (x) : \varGamma '(x) \rightarrow c_x\) and \(c_x\subseteq c\).

Note that this finer invariant guarantees that we can restrict our attention to the instantiations considered for defining consistency.

As a by-product, we obtain a finer type system for equivalence, even for processes with long term keys (as in [28]). For example, we can now prove equivalence of processes where some agent signs a low message that comes from the adversary. In such a case, we collect \(\mathtt {sign}(x,k)\sim \mathtt {sign}(x,k)\) in the constraint, where x has type \(\mathtt {LL}\), which we can now prove to be consistent (depending on how x is used in the rest of the constraint).

6.3 Procedure for Consistency

We devise a procedure \(\mathtt {check\_const}(C)\) for checking consistency of a constraint C, depicted in Fig. 10. Compared to [28], the procedure is actually simplified. Thanks to Lemmas 1 and 2, there is no need to open constraints anymore. The rest is very similar and works as follows:

  • First, variables of refined type \(\llbracket \tau ^{l,1}_{m} \,;\, \tau ^{l',1}_{n} \rrbracket \) are replaced by m on the left-hand-side of the constraint and n on the right-hand-side.

  • Second, we check that terms have the same shape (encryption, signature, hash) on the left and on the right and that asymmetric encryption and hashes cannot be reconstructed by the adversary (that is, they contain some fresh nonce).

  • The most important step consists in checking that the terms on the left satisfy the same equalities than the ones on the right. Whenever two left terms M and N are unifiable, their corresponding right terms \(M'\) and \(N'\) should be equal after applying a similar instantiation.

Fig. 10.
figure 10

Procedure for checking consistency.

For constraint sets without infinite nonce types, \(\mathtt {check\_const}\) entails consistency.

Theorem 2

Let C be a set of constraints such that

$$\forall (c, \varGamma )\in C.\; \forall l, l', m, p.\; \varGamma (x) \ne \llbracket \tau ^{l,\infty }_{m} \,;\, \tau ^{l',\infty }_{p} \rrbracket .$$

If \(\mathtt {check\_const}(C) = \mathtt {true}\), then C is consistent.

Example 4

Continuing Example 3, typechecking the PA protocol yields the set \(C_{ PA }\) of constraint sets. \(C_{ PA }\) contains in particular the set

$$\begin{aligned} \{\mathtt {aenc}(\langle N_a, \mathtt {pk}(k_a) \rangle ,\mathtt {pk}(k_b))&\sim \mathtt {aenc}(\langle N_a, \mathtt {pk}(k_a) \rangle ,\mathtt {pk}(k_b)),\\ \mathtt {aenc}(\langle y_1, \langle N_b, \mathtt {pk}(k_b) \rangle \rangle ,\mathtt {pk}(k_a))&\sim \mathtt {aenc}(N_b,\mathtt {pk}(k))\} \end{aligned}$$

where variable \(y_1\) has type \(\mathtt {HL}\) (we also have the same constraint but where \(y_1\) has type \(\mathtt {LL}\)). The other constraint sets of \(C_{ PA }\) are similar and correspond to the various cases (\(\mathtt {else}\) branch of \(P_a\) with \(\mathtt {then}\) branch of \(P_b\), etc.). The procedure \(\mathtt {check\_const}\) returns true since no two terms can be unified, which proves consistency. Similarly, the other constraints generated for PA can be proved to be consistent applying \(\mathtt {check\_const}\).

6.4 From Finite to Replicated Processes

The previous results apply to processes without replication only. In the spirit of [28], we lift our results to replicated processes. We proceed in two steps.

  1. 1.

    Whenever \(\varGamma \vdash P \sim Q \rightarrow C\), we show that: \({\left[ \,{\varGamma }\,\right] _{1}}\cup \cdots \cup {\left[ \,{\varGamma }\,\right] _{n}}\! \vdash \!{\left[ \,{P}\,\right] _{1}} |\dots |{\left[ \,{P}\,\right] _{n}} \sim {\left[ \,{Q}\,\right] _{1}} |\dots | {\left[ \,{Q}\,\right] _{n}} \rightarrow {\left[ \,{C}\,\right] _{1}}{\cup _{\times }}\cdots {\cup _{\times }}{\left[ \,{C}\,\right] _{n}}\), where \({\left[ \,{\varGamma }\,\right] _{i}}\) is intuitively a copy of \(\varGamma \), where variables x have been replaced by \(x_i\), and nonces or keys n of infinite type \(\tau ^{l,\infty }_{n} \) (or \(\mathrm {seskey}^{l,\infty }(T)\)) have been replaced by \(n_i\). The copies \({\left[ \,{P}\,\right] _{i}}\), \({\left[ \,{Q}\,\right] _{i}}\), and \({\left[ \,{C}\,\right] _{i}}\) are defined similarly.

  2. 2.

    We cannot directly check consistency of infinitely many constraints of the form \({\left[ \,{C}\,\right] _{1}}{\cup _{\times }}\cdots {\cup _{\times }}{\left[ \,{C}\,\right] _{n}}\). Instead, we show that it is sufficient to check consistency of two copies \({\left[ \,{C}\,\right] _{1}}{\cup _{\times }}{\left[ \,{C}\,\right] _{2}}\) only. The reason why we need two copies (and not just one) is to detect when messages from different sessions may become equal.

Formally, we can prove trace equivalence of replicated processes.

Theorem 3

Consider P, Q, \(P'\), \(Q'\), C, \(C'\), such that P, Q and \(P'\), \(Q'\) do not share any variable. Consider \(\varGamma \), containing only keys and nonces with finite types.

Assume that P and Q only bind nonces and keys with infinite nonce types, i.e. using \(\mathtt {new}\; m : \tau ^{l,\infty }_{m} \) and \(\mathtt {new}\; k : \mathrm {seskey}^{l,\infty }(T)\) for some label l and type T; while \(P'\) and \(Q'\) only bind nonces and keys with finite types, i.e. using \(\mathtt {new}\; m : \tau ^{l,1}_{m} \) and \(\mathtt {new}\; k : \mathrm {seskey}^{l,1}(T)\).

Let us abbreviate by \(\mathtt {new}\; \overline{n}\) the sequence of declarations of each nonce \(m\in \mathrm {dom}(\varGamma )\) and session key k such that \(\varGamma (k,k)=\mathrm {seskey}^{l,1}(T)\) for some l, T. If

  • \(\varGamma \vdash P \sim Q \rightarrow C\),

  • \(\varGamma \vdash P' \sim Q' \rightarrow C'\),

  • \(\mathtt {check\_const}({\left[ \,{C}\,\right] _{1}}{\cup _{\times }}{\left[ \,{C}\,\right] _{2}}{\cup _{\times }}{\left[ \,{C'}\,\right] _{1}}) = \mathtt {true}\),

then                   \(\mathtt {new}\; \overline{n}. \;((!P)~|~P') \approx _t\mathtt {new}\; \overline{n}. \;((!Q)~|~Q')\)

Interestingly, Theorem 3 allows to consider a mix of finite and replicated processes.

7 Experimental Results

We implemented our typechecker as well as our procedure for consistency in a prototype tool TypeEq. We adapted the original prototype of [28] to implement additional cases corresponding to the new typing rules. This also required to design new heuristics w.r.t. the order in which typing rules should be applied. Of course, we also had to support for the new bikey types, and for arbitrary terms as keys. This represented a change of about 40% of the code of the software. We ran our experiments on a single Intel Xeon E5-2687Wv3 3.10 GHz core, with 378 GB of RAM (shared with the 19 other cores). Actually, our own prototype does not require a large amount of RAM. However, some of the other tools we consider use more than 64 GB of RAM on some examples (at which point we stopped the experiment). More precise figures about our tool are provided in the table of Fig. 11. The corresponding files can be found at [27].

We tested TypeEq on two symmetric key protocols that include a handshake on the key (Yahalom-Lowe and Needham-Schroeder symmetric key protocols). In both cases, we prove key usability of the exchanged key. Intuitively, we show that an attacker cannot distinguish between two encryptions of public constants: \(P.\mathtt {out}(\mathtt {enc}(a,k))\;\approx _t\; P.\mathtt {out}(\mathtt {enc}(b,k))\). We also consider one standard asymmetric key protocol (Needham-Schroeder-Lowe protocol), showing strong secrecy of the exchanged nonce.

Helios [4] is a well known voting protocol. We show ballot privacy, in the presence of a dishonest board, assuming that voters do not revote (otherwise the protocol is subject to a copy attack [39], a variant of [30]). We consider a more precise model than the previous Helios models which assume that voters initially know the election public key. Here, we model the fact that voters actually receive the (signed) freshly generated election public key from the network. The BAC protocol is one of the protocols embedded in the biometric passport [1]. We show anonymity of the passport holder \(P(A)\approx _tP(B)\). Actually, the only data that distinguish P(A) from P(B) are the private keys. Therefore we consider an additional step where the passport sends the identity of the agent to the reader, encrypted with the exchanged key. Finally, we consider the private authentication protocol, as described in this paper.

Fig. 11.
figure 11

Experimental results for the bounded case

7.1 Bounded Number of Sessions

We first compare TypeEq with the tools for a bounded number of sessions. Namely, we consider Akiss [22], APTE [23] as well as its optimised variant with partial order reduction APTE-POR [10], SPEC [32], and SatEquiv [26]. We step by step increase the number of sessions until we reach a “complete” scenario where each role is instantiated by A talking to B, A talking to C, B talking to A, and B talking to C, where AB are honest while C is dishonest. This yields 14 sessions for symmetric-key protocols with two agents and one server, and 8 sessions for a protocol with two agents. In some cases, we further increase the number of sessions (replicating identical scenarios) to better compare tools performance. The results of our experiments are reported in Fig. 11. Note that SatEquiv fails to cover several cases because it does not handle asymmetric encryption nor else branches.

7.2 Unbounded Number of Sessions

We then compare TypeEq with Proverif. As shown in Fig. 12, the performances are similar except that ProVerif cannot prove Helios. The reason lies in the fact that Helios is actually subject to a copy attack if voters revote and ProVerif cannot properly handle processes that are executed only once. Similarly, Tamarin cannot properly handle the else branch of Helios (which models that the ballot box rejects duplicated ballots). Tamarin fails to prove that the underlying check either succeeds or fails on both sides.

Fig. 12.
figure 12

Experimental results for an unbounded number of sessions

8 Conclusion and Discussion

We devise a new type system to reason about keys in the context of equivalence properties. Our new type system significantly enhances the preliminary work of [28], covering a larger class of protocols that includes key-exchange protocols, protocols with setup phases, as well as protocols that branch differently depending on the decryption key.

Our type system requires a light type annotation that can be directly inferred from the structure of the messages. As future work, we plan to develop an automatic type inference system. In our case study, the only intricate case is the Helios protocol where the user has to write a refined type that corresponds to an over-approximation of any encrypted message. We plan to explore whether such types could be inferred automatically.

We also plan to study how to add phases to our framework, in order to cover more properties (such as unlinkability). This would require to generalize our type system to account for the fact that the type of a key may depend on the phase in which it is used.

Another limitation of our type system is that it does not address processes with too dissimilar structure. While our type system goes beyond diff-equivalence, e.g. allowing else branches to be matched with then branches, we cannot prove equivalence of processes where traces of P are dynamically mapped to traces of Q, depending on the attacker’s behaviour. Such cases occur for example when proving unlinkability of the biometric passport. We plan to explore how to enrich our type system with additional rules that could cover such cases, taking advantage of the modularity of the type system.

Conversely, the fact that our type system discards processes that are in equivalence shows that our type system proves something stronger than trace equivalence. Indeed, processes P and Q have to follow some form of uniformity. We could exploit this to prove stronger properties like oblivious execution, probably further restricting our typing rules, in order to prove e.g. the absence of side-channels of a certain form.