Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove...

35
Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc ¸ois Dupressoir The Open University Andrew D. Gordon Microsoft Research Jan J¨ urjens TU Dortmund & Fraunhofer ISST David A. Naumann Stevens Institute of Technology May 2011 Technical Report MSR–TR–2011–50 Microsoft Research Roger Needham Building 7 J.J. Thomson Avenue Cambridge, CB3 0FB United Kingdom An abridged form of this report appears in the proceedings of the 24th IEEE Computer Security Foundations Symposium, June 27–29, 2011, Domaine de l’Abbaye des Vaux de Cernay, France.

Transcript of Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove...

Page 1: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

Guiding a General-Purpose C Verifier to ProveCryptographic Protocols

Francois DupressoirThe Open University

Andrew D. GordonMicrosoft Research

Jan JurjensTU Dortmund & Fraunhofer ISST

David A. NaumannStevens Institute of Technology

May 2011

Technical ReportMSR–TR–2011–50

Microsoft ResearchRoger Needham Building7 J.J. Thomson AvenueCambridge, CB3 0FB

United Kingdom

An abridged form of this report appears in the proceedings of the 24th IEEE Computer Security Foundations Symposium,June 27–29, 2011, Domaine de l’Abbaye des Vaux de Cernay, France.

Page 2: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols

Francois DupressoirThe Open University

Andrew D. GordonMicrosoft Research

Jan JurjensTU Dortmund & Fraunhofer ISST

David A. NaumannStevens Institute of Technology

Abstract—We describe how to verify security properties ofC code for cryptographic protocols by using a general-purposeverifier. We prove security theorems in the symbolic model ofcryptography. Our techniques include: use of ghost state toattach formal algebraic terms to concrete byte arrays and todetect collisions when two distinct terms map to the same bytearray; decoration of a crypto API with contracts based onsymbolic terms; and expression of the attacker model in termsof C programs. We rely on the general-purpose verifier VCC;we guide VCC to prove security simply by writing suitableheader files and annotations in implementation files, ratherthan by changing VCC itself. We formalize the symbolic modelin Coq in order to justify the addition of axioms to VCC.

I. INTRODUCTION

Economies of scale suggest that it is better, where possi-ble, to adapt an existing general-purpose tool to a specialistproblem, than to go to the expense of building a specialisttool for each niche application area.

Our particular concern is the specialist problem of veri-fying the implementation code of cryptographic protocols[28][27][10]. This code is mostly written in C, and isoften the first—and sometimes the only—completely pre-cise description of the message formats and invariants ofcryptographic protocols. Hence, reasoning about such codeoffers a way to find and prevent both the design andimplementation flaws that lead to expensive failures (forinstance, [15][39][37]).

Background: Proving Cryptographic Protocol Code:The prior work on verifying C code of security protocolsrelies on special-purpose tools. Csur [28] analyzes C codefor secrecy properties via a custom abstract interpretation,while ASPIER [16] relies on security-specific softwaremodel-checking techniques, obtaining good results on themain loop of OpenSSL. Both these tools use the symbolicmodel of cryptography introduced by Dolev and Yao [24].

Another line of work considers the problem of verifyingreference implementations written as functional programs.Initial approaches rely on security-specific analyzers. Thetools FS2PV [10] and FS2CV [26] translate functional pro-grams in F# to the process calculi accepted by the specialisedverifiers ProVerif [13] and CryptoVerif [14] for automaticverification in the symbolic and computational models; animplementation of TLS [11] is a substantial case study.

Instead of translating to a protocol verifier, the type-checker F7 [9] checks F# by using security-specific refine-ment types, types qualified with formulas, to express security

properties. The theory of F7 is based on the symbolic model,although in some circumstances it can be adapted to beprovably computationally sound [5][25].

A subsequent, more flexible, method of using refine-ment types, based on invariants for cryptographic struc-tures [12], relies on axiomatizations of cryptographic pred-icates (such as which data is public); first implemented forF7, the method works in principle with any general-purposerefinement-type checker. The method has been ported toF* [43], a recent functional language.

Our strategy is to port this method to a verifier for C.Background: General-Purpose C Verifiers: By now

there are several general-purpose and more-or-less auto-matic verification tools for C, including Frama-C [22],VeriFast [31], and VCC [18]. This paper describes ourtechniques for guiding one of these, VCC, to verify a rangeof security protocol implementations. Although we adoptVCC, we expect our method would port to other tools.

VCC verifies C code against specifications written asfunction contracts in the tradition of Floyd-Hoare logic. Ittranslates C to an intermediate verifier, Boogie [7], whichitself relies on an SMT solver, Z3 [23]. The translation toBoogie encodes an accurate low-level memory model forC. VCC supports concurrency, which we use to model dis-tributed execution of protocols as well as for multithreadedcode. Specifications use ghost state, that is, specially-markedprogram variables that may be mentioned in contracts, butthat are not allowed to affect ordinary state or control flow.We aim to scale to verify large amounts of off-the-shelfC code such as OpenSSL; VCC has already proved itselfcapable of verifying large pre-existing codebases [18].

A. Outline of our Techniques

We summarize the main aspects of our adaptation to Cand VCC of the method of invariants for cryptographicstructures [12]. Later on, we describe the differences fromthe prior work on F# and F7.

1) Language-independent definitional theory: We de-velop a theory of symbolic cryptography, independent ofany programming language, within the interactive proofassistant Coq. The theory is definitional in the sense that itis developed from sound definitional principles (on the basisof symbolic cryptography), with no additional assumptions.

As usual in the symbolic model [24], the core of ourtheory is an algebraic type with constructors corresponding

Page 3: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

to the following: the outcomes of cryptographic algorithmssuch as keyed hashing, encryptions, and signatures; literalbyte strings (which represent messages, principal names,keys, and nonces, etc); and reversible pairing (for messageformatting; implemented with a length field).

Our theory accounts for the time-dependent history ofprotocol execution by defining a log, L, to be a set of events,which records progress so far in a protocol run. Events arevalues of another algebraic type, for example:• Key generation is recorded by an event such as

New k (KeyAB a b), which records that the term krepresents a fresh key with usage KeyAB a b, that is,a key shared between principals a and b. Values of thealgebraic datatype usage represent the different sorts ofkeys used in a protocol.

• Progress through a protocol is tracked by events suchas Request a b t, meaning that a client a has started aprotocol run by sending a request t to a server b.

• Principal compromise is recorded using an event suchas Bad a, meaning that principal a, and its keys, areunder the control of the attacker.

Our theory also includes an inductive definition of confi-dentiality levels of terms, parameterized by the log of events.Terms may either be public (known to the attacker), orprivate (known only to the protocol participants). We needto parameterize by the log because once events such as aprincipal compromise are logged, more data becomes public(such as keys known to the principal).

2) Theory imported as first-order axioms: C is a low-level language and does not directly support abstractions foralgebraic types. Also, VCC cannot easily perform proofs byinduction, being based on first-order logic without inductionprinciples. Still, VCC does allow ghost state and ghostcommands to manipulate unbounded data—ghost data oftype mathint consists of arbitrary mathematical integers—and VCC allows us to assume arbitrary first-order axioms.Hence, we can import our Coq theory into VCC by (1) usingghost data of type mathint to represent algebraic types such assymbolic terms, and (2) importing Coq theorems about ourinductive definitions as first-order axioms. Results provedby VCC hold in all models of the axioms, including theintended one inductively defined in Coq.

3) A ghost table relates bytestrings and symbolic terms:Our cryptographic library manipulates byte arrays via a Cstruct bytes c, which contains a length field together witha pointer to a heap-allocated chunk of memory with thatlength. Additionally, the struct contains a mathint ghost field,encoding, satisfying an invariant that it encodes the actualbytestring stored in memory. In global ghost state, wemaintain a representation table, which holds a finite one-to-one correspondence between bytestrings and symbolicterms. These are the cryptographically significant bytestringsarising so far in the run. The predicate table .DefinedB[b] holdsjust if bytestring b exists in the table. If so, table .B2T[b] is

the corresponding term. Conversely, if term t is in the table,table .T2B[t] is the corresponding bytestring.

We rely on the table to write VCC function contracts thatspecify symbolic assumptions about concrete cryptographicroutines. For example, the contract for hmacsha1 follows.It enforces that tb can be MAC’ed with tk only when theprotocol-dependent precondition MACSays(tk, tb) holds.i n t hmacsha1 ( bytes c ∗k , bytes c ∗b , bytes c ∗ res )requires ( t ab l e . DefinedB [ k−>encoding ] )requires ( t ab l e . DefinedB [ b−>encoding ] )ensures ( ! resul t ==> t ab l e . DefinedB [ res−>encoding ] )requires

(MACSays( t ab l e . B2T [ k−>encoding ] , t ab l e . B2T [ b−>encoding ] ) )ensures ( ! resul t ==>

t ab l e . B2T [ res−>encoding ] ==Hmac( t ab l e . B2T [ k−>encoding ] , t ab l e . B2T [ b−>encoding ] ) ) ;

The contract’s first three lines express the precondition(using the requires keyword) that the two concrete argumentsare in the table, and the postcondition (using the ensureskeyword) that the concrete result is in the table. The fourthline requires the MACSays predicate is fulfilled. The final lineensures the term associated with the result is Hmac(tk, tb),where terms tk and tb are associated with the concreteinputs. (As these lines illustrate, we include the ghost fieldencoding in bytes c because it allows succinct access inspecifications to the contents of memory.)

The VCC-verified concrete implementation of hmacsha1,called a hybrid wrapper, simply calls a routine trusted tocompute the MAC of the inputs, and then in ghost codeupdates the table with the result, if it is new.

4) Protocol roles described as ordinary C code: Eachrole of a protocol is simply code in C, executed as normal,and verified for memory safety and security with VCC.We model distributed execution by multiple threads thatcommunicate concretely by message passing via a networkAPI, but that share a single representation table. The protocolcode can itself be multithreaded and use shared memory, butthat feature is not used in the simple examples presentedhere.

Throughout this article, we take as a running examplethe following simple authenticated RPC [12]. This two-partyprotocol uses a pre-shared secret key to authenticate requestsand responses and link responses to the correspondingrequest, using a keyed hash as MAC.Running example: an authenticated RPC protocola : Log(Request(a, b, payload))a → b: payload | hmac(kab, ”1” | payload)b : assert(Request(a, b, payload))b : Log(Response(a, b, payload, payload’))b → a: payload’ | hmac(kab, ”2” | payload | payload’)a : assert(Response(a, b, payload, payload’))

The narration logs events marking a’s intent to send arequest, and b’s intent to send a response. At these pointsin our code, ghost commands add events to the log. Thenarration also includes correspondence assertions markingb’s conclusion that a has sent a request, and a’s conclusion

3

Page 4: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

that b has sent a response. In our code, these correspondencesbecome assert statements, to be verified by VCC.

5) Attacker model expressed using C interface: We proveprotocol code secure against a network-based attacker [38],rather than against say local malware. We consider anattacker to be a C program consisting simply of a series ofcalls to functions in the attacker interface. In keeping withthe symbolic model, the attacker cannot directly manipulatebytestrings using the bitwise operators of C, but only viathis interface. It includes functions for cryptography, to sendand receive network messages, to create new principals (butwithout access to their keys), to create instances of protocolroles, and to cause the compromise of principals (after whichtheir keys are available). Since our security results hold inspite of an arbitrary attacker, we place no bound on thenumber of distinct principals or concurrent sessions.

6) Security theorems obtained by running a general-purpose verifier: By running VCC on the protocol imple-mentation, we prove both correspondence properties, ex-pressing authentication and integrity properties, and weaksecrecy properties. As mentioned, correspondences amountto embedded assert statements. Standard symbolic cryptog-raphy assumptions are expressed using local assume state-ments. Weak secrecy properties amount to consequences ofinvariants, respected by all verified code. A typical secrecyproperty can be explained as follows: if k is a key sharedbetween a and b and k is public, then either principal aor principal b is compromised. Proof with VCC is semi-automatic in that it relies on automatic deductive inference,but with the help of user-supplied annotations.

B. Contributions of the Paper

To the best of our knowledge, this is the first pub-lished verification methodology for C implementations ofcryptographic protocols that proves both memory safetyand security properties for unbounded sessions. Csur [28],[29] proves secrecy properties, but does not show memorysafety; in fact, verification succeeds despite the examplecode allowing accidental access to uninitialized memory.ASPIER [16] proves various security properties by softwaremodel-checking, but verification considers only a few con-current sessions, and relies on substantial abstractions.

Our work takes the idea of invariants for cryptographicstructures [12] away from strongly-typed functional pro-gramming in F# and F7, and recasts it in the setting ofa weakly-typed low-level imperative language. In C we canneither rely on abstract types nor escape from the difficultiesof reasoning about mutable memory and aliasing. These areprobably the main new difficulties we address compared tothe prior work with F7 [12]; verifying C is much harder thanverifying F#. In return, we enjoy vastly wider applicability,as the bulk of production cryptographic protocols is in C. Aless obvious and more technical benefit is that in F7, the logof events is implicit and its impact on inductively defined

predicates requires a bespoke notion of semantic safety forF#. By making the log explicit in ghost state, we can workwithin a completely standard semantics of assertions on Cprograms.

Our method forces the developer to precisely specifymemory safety and security properties. We verify themwith a scalable and practically reliable tool that has clearsemantics in terms of standard C, its compilers and hard-ware architecture. Since user interaction is by way of codeannotation, the verification effort may be expected to evolvewell as the code base evolves. We may also hope to reap thebenefit of ongoing improvements in automation for general-purpose verifiers for C. Although we prove our security-specific theory in Coq, we do trust the VCC/Boogie/Z3 toolchain and the C compiler.

We have validated our approach on implementations wedeveloped using our own cryptographic APIs. In futurework, we intend to apply our techniques to pre-existing code.

C. Structure of the Paper

We verify the following stack of C program files, listedin dependency order, which link to form an executable.

crypto.h/c library: crypto, malloc, etc (not verified)RPCdefs.h representation table, event logRPChybrids.h/c hybrid wrappersRPCprot.h/c protocol roles, setupRPCshim.h/c network attacker interfaceRPCattack 0.c sample attack / applicationSection II introduces features of VCC used in our treat-

ment of symbolic cryptography (the topic of Section III,file RPCdefs.h) and its connection to concrete data (thetopic of Section IV, files RPChybrids.h and RPChybrids.c).Section V states our assumptions about VCC. Section VImodels symbolic attacks as programs (e.g., RPCattack 0.c)using an API RPCshim.h. Section VII states and provessafety of an example protocol implementation (RPCprot.h andRPCprot.c). Section VIII summarizes our results includingverification of other examples. Section IX covers relatedwork. Section X concludes with remarks on limitations andfuture work.

II. BACKGROUND ON THE VCC VERIFIER

VCC uses an automatic theorem prover to statically checkcorrectness of C code with respect to specifications writtenas function contracts and other annotation comments. Thetool is based on a precise model of multithreaded, shared-memory executions of C programs. In order to verify richfunctional specifications without the need for interactivetheorem proving and yet scaling to industrial software usingidiomatic C, VCC relies on a somewhat intricate method-ology for specifications. This section sketches pertinentfeatures of the model and methodology. Section V describesmore precisely the way in which our security results rely onVCC. For details that are glossed over here, see [18], [19]and the tool documentation. We expect the reader is familiar

4

Page 5: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

with C syntax such as macro definitions (#define) and recorddeclarations (typedef struct).

VCC’s semantics of C is embodied in its verificationcondition generator (VCG). That works by translatingannotated C source code to a relatively simple intermediatelanguage (BoogiePL) which itself is equipped with a VCG.The translation is procedure-modular, i.e., each C functionbody b is verified separately; function calls are interpretedby inlining their specifications. The VCG reflects a reason-ing methodology that includes memory safety and locallychecked invariants. The VCG models preemptive multi-threading by interpreting code in terms of its atomic steps,between each of which there may be arbitrary interferenceon shared state, constrained only by invariants associatedwith data types declared in the program as explained later.Atomicity is with respect to sequentially consistent hardwareand data types like integers with atomic read and write. (Themethodology has been adapted to reasoning about a weakermemory model, Total Store Order, but that has not yet beenimplemented in VCC [20].)

Memory blocks are arrays of bytes, but a typed view isimposed in order to simplify reasoning while catering foridiomatic C and standard compilers. The verifier attemptsto associate a type with each pointer dereferenced by theprogram, and imposes the requirement that distinct point-ers reference separate parts of memory. For example twointegers cannot partially overlap. Structs may nest as fieldsinside other structs, in accord with the declared struct types,but distinct values do not otherwise overlap. Annotations canspecify, however, the re-interpretation of an int as an arrayof bytes, changing the typestate of a union, etc.

The declaration of a struct type can be annotated with aninvariant: a formula that refers to fields of an instance this.(We often say “invariant” for what are properly called “typeinvariants”.) Invariants need not hold of uninitialized objects,so there is a boolean ghost field that designates whether theobject is open or closed: in each reachable state, every closedobject should satisfy the invariant associated with its type.Ghost state is disjoint from the concrete state that exists atruntime; syntactic restrictions ensure that it cannot influenceconcrete state. This standard technique appears as “auxiliaryvariables” in [42], as in Assumption 2.

Useful invariants often refer to more than one object,but the point of associating invariants with objects is tofacilitate local reasoning: when a field is written, the verifieronly needs to check the invariants of relevant objects, ow-ing to admissibility conditions VCC imposes on invariants.Invariants and other specifications designate an ownershiphierarchy: if object o1 owns o2 then the invariant of o1may refer to the state of o2 and thus must be maintainedby updates of o2. The state of a thread is modeled by aghost object. An object is wrapped if it is owned by thecurrent thread (object) and closed. The owner of an objectis recorded in a ghost field. VCC provides notations unwrap

and wrap to open/close an object, with wrap also assertingthe invariant.

Ownership makes manifest that the invariant for oneobject o1 may depend on fields of another object o2, so theVCG can check o1’s invariant when o2 is updated. Sincehierarchical ownership is inadequate for shared objects likelocks, VCC provides another way to make manifest thato1 depends on the state of o2: it allows that o1 maintainsa claim on o2—a ghost object with no concrete state butan invariant that depends on o2. Declaring a type to beclaimable introduces implicit ghost state used by the VCGto track outstanding claims. The ghost code to create a claimor store it in a field is part of the annotation provided bythe programmer.

The term invariant encompasses two-state predicates forthe before and after states of a state transition. In this way,invariants serve as the rely conditions in a form of rely-guarantee reasoning (compare [33], an early formulation ofthis concept). Usually two-state invariants are written asordinary formulas, using the keyword old to designate ex-pressions interpreted in the before state. We say an invariantis one-state to mean that it does not depend on the beforestate. 1

A thread can update an object that it owns, using un-wrap/wrap. However, in many cases such as locks, havinga single owner is too restrictive, and another mechanismis needed to allow multiple threads to update the objectconcurrently. VCC interprets fields marked volatile as beingsusceptible to update by other threads, in accord with theinterpretation of the volatile keyword by C compilers. Anatomic step is allowed to update a volatile field withoutopening it, provided that the object is proved closed and theupdate maintains the object’s two-state invariant (that beingthe interference condition on which interleaved threads rely).The standard idiom for locks is that several threads eachmaintain a claim that the lock is closed, so they may relyon its invariant; outstanding claims prevent even the ownerfrom unwrapping the object. Atomic blocks are explicitlymarked as such. An atomic block may make at most oneconcrete update, to be sound for C semantics, but may updatemultiple ghost fields. We do not use assume statements inatomic blocks.

III. SYMBOLIC CRYPTOGRAPHY IN VCC

In this section, we introduce the inductive model ofsymbolic cryptography, implemented in Coq and shown infull in appendix A-A for the RPC protocol, and show howit is approximated in VCC by first-order logic axioms overuninterpreted function symbols and code manipulating ghoststate.

1To ensure that there is an appropriate interpretation of an invariant in asingle state, one of the admissibility conditions is that if an invariant holdsfor the state transition σ1 to σ2 it also holds for the stuttering transitionσ2, σ2 (which thus serves as the one-state interpretation in σ2).

5

Page 6: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

A. Term Algebra

We use a standard symbolic model of cryptography,where cryptographic primitives (and further operations suchas pairing) are modelled as constructors of an inductivedatatype. Some details are protocol-specific, so for clarity wefocus on a simple model adequate for our running example,the RPC protocol. We use ordinary mathematical notationfor the following definitions, which are formalized in ourCoq development.An algebra of cryptographic termsti, k,m, p, a, b ::= term

Literal bs with bs a byte arrayPair t1 t2Hmac k m

Automated verifiers like VCC support specifications infirst-order logic without inductive definitions. So we usean over-approximation of the term algebra given by un-interpreted function symbols and first-order axioms. Thereasoning performed by VCC thus holds for all models ofthe axioms, in particular for the intended inductive model.The following shows the first-order axioms correspondingto the algebra above.

The VCC spec() syntax indicates that all symbols declaredwithin are ghost objects. In particular, it allows the use ofthe mathint type of mathematical integers (in Z).A first-order model of cryptographic termsspec (

typedef math in t bytes ;typedef math in t term ;

ispure math in t tag term ( term ) ;

/ / Const ruc tors ( declared as un in te rp re ted fu n c t i o n s )ispure term L i t e r a l ( bytes bs ) ;ispure term Pa i r ( term t1 , term t2 ) ;ispure term Hmac( term k , term m) ;

/ / Theoremstheorem ( L i t e r a l I n j e c t i v e ,

f o r a l l ( bytes bs1 , bs2 ; L i t e r a l ( bs1 ) == L i t e r a l ( bs2 )==> bs1 == bs2 ) ) ;

theorem ( P a i r I n j e c t i v e ,f o r a l l ( term a1 , a2 , b1 , b2 ; Pa i r ( a1 , b1 ) == Pa i r ( a2 , b2 )

==> a1 == a2 && b1 == b2 ) ) ;theorem ( Hmac Inject ive ,

f o r a l l ( term k1 ,m1, k2 ,m2; Hmac( k1 ,m1) == Hmac( k2 ,m2)==> k1 == k2 && m1 == m2) ) ;

theorem ( L i t e r a l D i s j o i n t ,f o r a l l ( bytes bs ; tag term ( L i t e r a l ( bs ) ) == 0) ) ;

theorem ( P a i r D i s j o i n t ,f o r a l l ( term t1 , t2 ; tag term ( Pa i r ( t1 , t2 ) ) == 1) ) ;

theorem ( Hmac Disjoint ,f o r a l l ( term k ,m; tag term (Hmac( k ,m) ) == 2) ) ;

)

We use the bytes type to manipulate whole byte arraysas values, and assume a bijection between finite byte arraysand mathematical integers. (One such bijection interprets abyte array as an integer, pairs that with its length to accountfor leading zeroes, and injects the pair into Z.) We willdesignate the injection from byte arrays to type bytes as:Encode(unsigned char∗, unsigned long).

The ispure keyword is used to specify that a given functionis to be interpreted as a total function whose return valuedepends only on the value of its arguments and memorylocations listed in its reads() clauses. Only pure functionscan be used in function contracts and assertions, and purityneeds to be explicitly specified even for spec functions, asthey may update ghost state.

We use axioms to state properties of the declared functionsymbols that cannot easily be expressed using pre andpostconditions (injectivity and disjointness, in this case).2

Those axioms are separately proved in Coq to hold aboutthe intended, inductive model of cryptography. The theoremnotation is a simple macro that generates a VCC axiom;its first argument, ignored by VCC, is the name of thecorresponding Coq theorem.

B. Events and Log

Much as in prior work [12], we use a global log of eventsto express the wanted correspondence properties. Eventsthemselves are defined using a protocol-specific algebra (seebelow).

The hashkey usage and some top-level events are specificto the RPC protocol, but some constructors are of general useand are needed for most protocols. The top-level of eventsalways includes a New event, logging the intended usageof freshly generated bytestrings. In particular, the Attack-erGuess models that the corresponding term represents abytestring known to the attacker, because it was providedas a starting parameter, or because it represents a fixedbytestring appearing in the protocol specification (e.g. a tag,or a fixed format string).Algebra of events for RPChu ::= hashkey usage

KeyAB a bus ::= usage

AttackerGuessHashKey hu

ev ::= eventNew t us | Bad a | Request a b t | Response a b t1 t2

As with the cryptographic terms, a first-order approxi-mation of this algebra is given to VCC using uninterpretedfunctions and axioms. The log itself, intended as the set ofevents that have occurred so far, is defined next as a structurecontaining one set for each kind of event. We use booleanmaps to model sets. The VCC notation is similar to that ofarrays, e.g., bool B[mathint] declares B to be a boolean-valuedtotal function on the integers.

Logical formulas have type bool, e.g., the expressionRequest[a][b][ t ] can be used as an assertion saying that thisevent has occurred. We use two-state invariants to expressthat the log can only grow (see the “Stability” group of

2Injectivity could, in general, be expressed as a postcondition, but VCCimposes syntactic restrictions on the postconditions of pure functions toensure that they are total and computable.

6

Page 7: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

invariants in the code displayed below). We use a one-stateinvariant to express that bytestrings, and in particular keys,should only be given one usage. Other protocol-specific one-state invariants could be added. We call them “Miscellaneousconditions”, and will say that a log is good, or valid, whenits miscellaneous conditions hold.Encoding of the log in VCC (RPCdefs.h)#define s tab le ( F ) old ( F ) ==> F

spec (typedef struct log s {

v o l a t i l e bool New[ term ] [ usage ] ;v o l a t i l e bool Bad [ term ] ;v o l a t i l e bool Request [ term ] [ term ] [ term ] ;v o l a t i l e bool Response [ term ] [ term ] [ term ] [ term ] ;

/ / Misc . cond i t i onsinvar iant ( f o r a l l ( term t ; usage u1 , u2 ;

New[ t ] [ u1 ] && New[ t ] [ u2 ] ==> u1 == u2 ) )invar iant ( f o r a l l ( term t ; usage u ;

New[ t ] [ u ] ==> exists ( bytes b ; t == L i t e r a l ( b ) ) ) )

/ / S t a b i l i t yinvar iant ( f o r a l l ( term t ; usage u ; s tab le (New[ t ] [ u ] ) ) )invar iant ( f o r a l l ( term t ; s tab le (Bad [ t ] ) ) )invar iant ( f o r a l l ( term a , b , s ;

s tab le ( Request [ a ] [ b ] [ s ] ) ) )invar iant ( f o r a l l ( term a , b , s , t ;

s tab le ( Response [ a ] [ b ] [ s ] [ t ] ) ) )} Log ; )

spec ( Log log ; )

#define v a l i d l o g\f o r a l l ( term T ; usage U1,U2;\

log .New[ T ] [ U1 ] && log .New[ T ] [ U2 ] ==> U1 == U2) &&\f o r a l l ( term T ; usage U;\

log .New[ T ] [ U] ==> exists ( bytes B ; T == L i t e r a l (B) ) )

#define s tab le log\f o r a l l ( term T ; usage U; s tab le ( log .New[ T ] [ U ] ) ) &&\f o r a l l ( term T ; s tab le ( log . Bad [ T ] ) ) &&\f o r a l l ( term A,B,S ; s tab le ( log . Request [A ] [ B ] [ S ] ) ) &&\f o r a l l ( term A,B,S, T ; s tab le ( log . Response [A ] [ B ] [ S ] [ T ] ) )

We also introduce macros valid log and stable log, whichexpand to both invariant blocks, to simplify the expression ofcertain properties; valid log is used in the inversion principlefor the Pub() predicate.

C. Inductive Predicates for CryptographyWe use inductive predicates to express the correct usage of

cryptographic primitives, as specified by a given protocol. Inparticular, we define a predicate Pub() that holds on all termsthat can be published without compromising the protocol’sgoals. We also define a Bytes() predicate that holds on bytearrays an honest protocol participant is allowed to build. Weensure by definition that Bytes() holds for all terms on whichPub() holds. Both of these predicates, and all intermediatepredicates used in their definition, are actually functions ofthe log. We write L ` P to say P holds in log L. Thefollowing shows an excerpt of the inductive rules definingthe Pub() predicate. Those rules are very similar to thoseseen in previous work [12], in which they are described inmore detail. The main difference is that we make the logexplicit in the definitions, avoiding the need for customiseddefinitions of safety.

Some inductive predicates for cryptography(MACSays KeyAB Request)New k (HashKey (KeyAB a b)) ∈ L

Request req a b ∈ Lm = Pair (Literal TagRequest) req

L ` MACSays k m

(MACSays KeyAB Response)New k (HashKey (KeyAB a b)) ∈ L

Response req resp a b ∈ Lm = Pair (Literal TagResponse) (Pair req resp)

L ` MACSays k m

(Pub AttackerGuess)New (Literal b) AttackerGuess ∈ L

L ` Pub (Literal b)

(Pub HmacKey KeyAB)New (Literal bk) (HashKey (KeyAB a b)) ∈ L

Bad a ∈ L ∨ Bad b ∈ LL ` Pub (Literal bk)

(Pub Pair)L ` Pub t1 L ` Pub t2L ` Pub (Pair t1 t2)

(Pub Hmac)L ` MACSays k mL ` Bytes kL ` Pub m

L ` Pub (Hmac k m)

(Pub Hmac Pub)L ` Pub k L ` Pub m

L ` Pub (Hmac k m)

(Bytes Literal)

L ` Bytes (Literal b)

(Bytes Pair)L ` Bytes t1L ` Bytes t2

L ` Bytes (Pair t1 t2)

(Bytes Hmac)L ` MACSays k mL ` Bytes kL ` Bytes m

L ` Bytes (Hmac k m)

(Bytes Hmac Pub)L ` Pub k L ` Pub m

L ` Bytes (Hmac k m)

In order to simplify the notations in VCC, we do notmake the log an explicit argument in the predicates’ VCCdeclaration. Instead, we express that the functions intendedto model the predicates depend on the state, by markingthem with the reads(set universe()) contract, stating that thevalue returned by the function may depend on the set ofpointers that contains all addresses in the state. This makesthe program state an implicit parameter to the function; laterwe use axioms to frame the function’s dependency on thestate more precisely. We can then use axioms to give theintended meaning of these symbols. In particular, we provein Coq that our predicate symbols are monotonic functionsof the log and import this fact as an axiom in VCC (seethe theorem Pub Monotonic below). To that end we give the

7

Page 8: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

following definitions. The notation in state(S, e) denotes thevalue of expression e in the state S.Log stability between explicit states#define s s tab le (S1 , S2 , F )\

i n s t a t e (S1 , F ) ==> i n s t a t e (S2 , F )

#define s s tab le log (S1 , S2)\f o r a l l ( term T ; usage U;\

s s tab le (S1 , S2 , log .New[ T ] [ U ] ) ) &&\f o r a l l ( term T;\

s s tab le (S1 , S2 , log . Bad [ T ] ) ) &&\f o r a l l ( term A,B,S;\

s s tab le (S1 , S2 , log . Request [A ] [ B ] [ S ] ) ) &&\f o r a l l ( term A,B,S, T;\

s s tab le (S1 , S2 , log . Response [A ] [ B ] [ S ] [ T ] ) )

Here are the VCC axioms for the Pub() predicate and someexample theorems.VCC axiomatic definition for Pub() (excerpt)spec ( ispure bool Pub ( term t )

reads ( se t un iverse ( ) ) ; )

theorem ( Pub Monotonic ,f o r a l l ( s t a t e t s1 , s2 ; term x ;

s s tab le log ( s1 , s2 ) ==> s s tab le ( s1 , s2 , Pub ( x ) ) ) ) ;

rule ( Pub AttackerGuess ,f o r a l l ( bytes b ;

log .New[ L i t e r a l ( b ) ] [ AttackerGuess ( ) ] ==>Pub ( L i t e r a l ( b ) ) ) ) ;

Inversion theorems (excerpt)theorem ( KeyAB WeakSecrecy ,

f o r a l l ( term k , a , b ;v a l i d l o g &&log .New[ k ] [ HmacKey(KeyAB( a , b ) ) ] &&Pub ( k ) ==>

log . Bad [ a ] | | log . Bad [ b ] ) ) ;

theorem (MACSays RPC,f o r a l l ( term a , b ,m, k ;

v a l i d l o g && log .New[ k ] [ HmacKey(KeyAB( a , b ) ) ] &&MACSays( k ,m) ==>

( exists ( term s ; Requested (m, s ) &&log . Request [ a ] [ b ] [ s ] ) | |

exists ( term s , t ; Responded (m, s , t ) &&log . Response [ a ] [ b ] [ s ] [ t ] ) ) ) ) ;

theorem (Pub MACSays Pub ,f o r a l l ( term k ,m; Pub (Hmac( k ,m) ) ==>

MACSays( k ,m) | | Pub ( k ) ) ) ;

The axioms introduced using rule correspond to the in-ductive definition rules shown earlier. Again, the axiomsintroduced using theorem correspond to results that areproved to hold in the model inductively defined in Coq.Similar axioms are used to define Bytes() and MACSays(), aswell as theorems similar to those used in F7 [12].

We also separately prove in Coq, and import as axioms inVCC, some weak secrecy theorems, expressing under whichconditions a key or nonce can become known to the attacker.In the case of RPC, the weak secrecy theorem simply statesthat a key shared between principals a and b can only bepublic if either a or b is compromised, as follows.

(New k (KeyAB a b) ∈ L) ∧ (L ` Pub(k)) ⇒(Bad a ∈ L) ∨ (Bad b ∈ L)

The verification of correspondence assertions in VCCmay make use of this theorem, which holds in any system

that enforces the log invariants—and in particular of anysystem verified using VCC, to establish the correspondenceproperties only in terms of logged events.

IV. REPRESENTATION TABLE AND HYBRID WRAPPERS

A. The Representation Table

Symbolic models of cryptography generally assume thattwo distinct symbolic terms yield two distinct byte strings,and that fresh literals cannot be guessed by an attacker. Theintent is to use such a model with cryptographic operationsthat, in the computational model, have a negligible proba-bility of collision. Verification in the symbolic model is away of ruling out a well-defined class of attacks, which inpractice do not depend on collisions or lucky guesses.

Prior work on cryptographic software in F#, for example[10], [9], relies on type abstraction to verify protocol codewhen running with purely symbolic libraries, which satisfythese assumptions, instead of concrete libraries, which donot. In the absence of type abstraction in C, we must verifyprotocol code when running with concrete cryptographicalgorithms on byte strings. Our aim remains to verify againstan attacker in the symbolic model. To do so, we instrumentthe program with specification code that maintains a rep-resentation table, which tracks the correspondence betweenconcrete byte strings and symbolic terms. We intercept allcalls to cryptographic functions with ghost code to updatethe representation table. We say a collision occurs whenthe table associates a single byte string with two distinctsymbolic terms.

For example, suppose x and yare two distinct bytestrings thathave the same HMAC, h, undera key k. After the first call tohmac() the table looks like this:

Bytestring Termk Literal kx Literal xy Literal yh Hmac k x

When computing the second HMAC, our instrumentedhmac() function tries to insert the freshly computed h andthe corresponding term Hmac k y in the table, but detectsthat h is already associated with a distinct term Hmac k x.

We make the absence of such collisions an explicithypothesis in our specification by assuming, via an assumestatement in the ghost code updating the table, that acollision has not occurred. This removes from considerationany computation following a collision, as is made precisein Section V. We treat the event of the attacker guessinga non-public value in a similar way; we assume it doesnot happen, using an assume statement. In this way weprove symbolic security properties of the C code. A separateargument may be made that such collisions only happen withlow probability.

Like the log, the representation table, given next, is astructure containing maps. Its VCC definition is shownbelow.

8

Page 9: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

The representation table in VCCspec (

typedef vcc ( c la imab le ) struct tab le s {v o l a t i l e bool DefinedB [ bytes ] ;v o l a t i l e term B2T [ bytes ] ;

v o l a t i l e bool DefinedT [ term ] ;v o l a t i l e bytes T2B [ term ] ;

/ / B i j e c t i v i t yinvar iant ( f o r a l l ( bytes b ;

DefinedB [ b ] ==> T2B [ B2T [ b ] ] == b ) )invar iant ( f o r a l l ( term t ;

DefinedT [ t ] ==> B2T [ T2B [ t ] ] == t ) )invar iant ( f o r a l l ( bytes b ;

DefinedB [ b ] ==> DefinedT [ B2T [ b ] ] ) )invar iant ( f o r a l l ( term t ;

DefinedT [ t ] ==> DefinedB [ T2B [ t ] ] ) )

/ / S t a b i l i t yinvar iant ( f o r a l l ( bytes b ;

s tab le ( DefinedB [ b ] ) ) )invar iant ( f o r a l l ( bytes b ;

old ( DefinedB [ b ] ) ==> unchanged (B2T [ b ] ) ) )invar iant ( f o r a l l ( term t ;

s tab le ( DefinedT [ t ] ) ) )invar iant ( f o r a l l ( term t ;

old ( DefinedT [ t ] ) ==> unchanged (T2B [ t ] ) ) )

/ / V a l i d i t yinvar iant ( f o r a l l ( bytes b ;

DefinedT [ L i t e r a l ( b ) ] ==> T2B [ L i t e r a l ( b ) ] == b ) )invar iant ( f o r a l l ( term t ;

DefinedT [ t ] ==> Bytes ( t ) ) )

/ / Ownershipinvar iant ( keeps(& log ) )

} Rep ; )

spec (Rep tab le ; )

We use two maps to store the bijection between bytes,which are byte string values (not pointers), and terms (e.g.,the byte string b corresponds to the algebraic term B2T[b]).Two-state invariants express that the table can only grow.There is an ownership invariant: the representation tablealways owns the log. This means that whenever &rep isclosed, &log has to be closed itself, so the invariants forthe representation table can depend on values in the login accord with the VCC ownership methodology. Thisownership relation also enables us to make only the tableobject claimable (using the vcc(claimable) attribute on thestructure declaration), releasing some of the pressure on theprover in later stages. A thread holding a claim on the tablewill be able to show, through the ownership relation, thatthe log is also closed.

B. The Hybrid Wrappers

We want to ensure that all cryptographic operations areused in ways that preserve the representation table invariants.We provide hybrid wrappers around the concrete libraryfunctions; wrappers are not only verified to maintain thetable’s invariants but also serve to give symbolic contractsto a cryptographic interface working with concrete bytes.

For simplicity in this paper, the hybrid wrappers ma-nipulate a structure type bytes c containing all informationpertaining to a byte array.

A type for byte stringstypedef struct {

unsigned char ∗p t r ;unsigned long l en ;

spec ( bytes encoding ; )invar iant ( keeps ( as array ( p t r , len ) ) )invar iant ( encoding == Encode ( p t r , len ) )

} bytes c ;

In particular, we keep not only a pointer to the concretebyte array considered and its length, but we also add aghost field of type bytes, representing—as a mathematicalinteger—the byte string value contained by the len bytesat memory location ptr. For the invariants of bytes c to beadmissible, we also make sure, using the keeps keyword,that the heap-allocated byte array of length len pointed to byptr is always owned by the structure, ensuring in particularthat it is never modified while the structure is kept closed.Idiomatic C manipulates the beginning address and lengthseparately. Porting our method to a lower-level programmingstyle is only a matter of providing the necessary memory-safety annotations, which would in any event be neededto verify non-cryptographic properties of the code, such asmemory safety.

As an example, here is the contract of our hybrid wrapperfor the hmac sha1() cryptographic function.Hybrid interface for hmacsha1()

i n t hmacsha1 ( bytes c ∗k , bytes c ∗b , bytes c ∗ rescla imp ( c ) )

/ / S t a b i l i t y o f log and tab lealways ( c , c losed (& tab le ) &&

c s tab le log && c s tab le tab le )ensures ( s tab le log && s tab le tab l e )

/ / P rope r t i es o f i npu t byte s t r i n g smaintains ( wrapped ( k ) )maintains ( wrapped ( b ) )

/ / P rope r t i es o f out parameterwrites ( span ( res ) )ensures ( unchanged (emb( res ) ) )ensures ( ! resul t ==> wrapped dom ( res ) )

/ / Cryptographic con t rac trequires ( t ab l e . DefinedB [ k−>encoding ] )requires ( t ab l e . DefinedB [ b−>encoding ] )ensures ( ! resul t ==> t ab l e . DefinedB [ res−>encoding ] )

/ / Cryptographic p r o p e r t i e s on inpu t termsrequires (

MACSays( t ab l e . B2T [ k−>encoding ] , t ab l e . B2T [ b−>encoding ] )| | (Pub ( t ab l e . B2T [ k−>encoding ] ) &&

Pub ( t ab l e . B2T [ b−>encoding ] ) ) )/ / Cryptographic p r o p e r t i e s on output term

ensures ( ! resul t ==>t ab l e . B2T [ res−>encoding ] ==

Hmac( t ab l e . B2T [ k−>encoding ] , t ab l e . B2T [ b−>encoding ] ) ) ;

For log and table stability, as well as concurrent access,there is a ghost parameter containing a claim c (representedby the claimp macro). The function’s contract says, using thealways(c ,...) construction, that the claim parameter shouldensure that the table object is closed, and that both table andlog have only grown since the claim was created (which isexpressed using the c stable variant of the s stable macro).Additionally, the function should ensure that the log andtable only grow during its execution. The next lines ofthe contract concern memory-safety, e.g., the arguments

9

Page 10: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

used to pass values in are wrapped both at call-site andat return-site (maintains(F) expands to requires(F) ensures(F)),and the third argument is used as an output parameter, toreturn the function’s result. The latter is specified by statingthat the function is allowed to write the set of memorylocations contained within the span of the out-parameter.Additionally, we also specify that the embedding (emb) ofthe out-parameter is left unchanged by a call to the function,meaning that the pointer refers to the same typed memorylocation. It is then specified that a call to this function maywrite all fields of the structure passed in the res argument,and that this memory update has to wrap the structure whenthe function call is successful. These are enough to let VCCknow that k and b are not written to during the function’sexecution. They are both wrapped at call site: their span isthus not writable, and they therefore have to be distinct fromres.

The first three lines under “Cryptographic contract” dealonly with the table, stating that the input byte strings shouldappear in the table, and that, upon successful return fromthe function, the output byte string appears in the table.Then comes an important cryptographic precondition: thateither MACSays() holds on the terms associated with theinput byte strings (modelling an honest participant’s callingconditions), or they are both in Pub() (modelling a call bythe attacker or a compromised principal). The postconditionstates that, upon successful return, the output byte string isassociated with the term obtained by applying the Hmacconstructor to the terms associated with the input bytestrings.

An honest client, when calling a function with this con-tract, needs to establish the first disjunct of the precondition:that MACSays() holds on the terms associated with the inputbyte arrays. For this, to hold, the term associated with thekey k must be given, in the log, a certain usage HashKey hufor some hashkey usage hu, and the term associated withthe message to authenticate b must be formatted correctly, asspecified by the corresponding inference rule (here (MAC-Says KeyAB Request) or (MACSays KeyAB Response)).

A typical hybrid wrapper implementation first performsthe concrete operation on byte strings (e.g., by calling acryptographic library) before performing updates on theghost state to ensure the cryptographic postconditions, whilstmaintaining the log and table invariants. To do so, it firstcomputes the expected cryptographic term by looking up,in the table, the terms associated with the input byte stringsand applying the suitable constructor. Once both the concretebyte string and the corresponding terms are computed, theimplementation can check for collisions, and in case thereare none, update the table (and the log) as expected. In casea collision happens, an assume statement expresses that oursymbolic cryptography assumptions have been violated.

A hybrid wrapper for hmacsha1()

i n t hmacsha1 ( bytes c ∗k , bytes c ∗b , bytes c ∗ rescla imp ( c ) )

{ spec ( term tb , tk , th ; )spec ( bool c o l l i s i o n = fa lse ; )

res−>l en = 20;res−>p t r = mal loc ( res−>l en ) ;i f ( res−>p t r == NULL)

return 1;sha1 hmac ( k−>p t r , unchecked ( ( i n t ) k−>l en ) , b−>p t r ,

unchecked ( ( i n t ) b−>l en ) , res−>p t r ) ;

spec (res−>encoding = Encode ( res−>p t r , res−>len ) ;wrap ( as array ( res−>p t r , res−>len ) ) ;wrap ( res ) ; )

spec (atomic ( c , &tab l e ){

tb = tab le . B2T [ b−>encoding ] ;t k = tab l e . B2T [ k−>encoding ] ;th = Hmac( tk , tb ) ; / / Compute the symbol ic term

i f ( ( t ab l e . DefinedB [ res−>encoding ] &&tab le . B2T [ res−>encoding ] != th ) | |

( t ab l e . DefinedT [ th ] &&tab le . T2B [ th ] != res−>encoding ) )

c o l l i s i o n = true ;else{

t ab l e . DefinedT [ th ] = true ;t ab l e . T2B [ th ] = res−>encoding ;t ab l e . DefinedB [ res−>encoding ] = true ;t ab l e . B2T [ res−>encoding ] = th ;

}})

assume ( ! c o l l i s i o n ) ; / / Our symbol ic c ryp to assumptionreturn 0; }

Our implementation of an HMAC SHA1 wrapper, shownabove, uses the PolarSSL project’s sha1 hmac() func-tion ([40]).

The unchecked keyword is used to let VCC ignore thepotential arithmetic overflow due to the type casts.

Since the table is shared and its fields marked volatile, allreads and writes from and to it need to occur in an atomicblock guarded by a claim c ensuring, among other things,that the global table object is closed.

We also provide a function toString converting an ordinarystring pointer to a bytes c, the input type for functionslike hmacsha1. It logs a New event with usage AttackerGuessand assumes the guessed literal does not collide with anyother term already in the table. We also provide a functionbytescmp, that compares two bytes c objects.

That completes the groundwork needed to specify andverify the RPC protocol code (RPCprot.c). The followingshows a slightly simplified version of the annotated codefor the client role, where the Request event is logged by theatomic assignment and the final correspondence is assertedas a disjunction of events taking into account the potentialcompromise of one of the principals involved. Each of thefunction calls is verified to happen in a state where thefunction’s precondition holds. In particular, the call to thechannel write() function yields a proof obligation that Pub()holds on the term corresponding to the second argument.The return statements are for various kinds of failure.

10

Page 11: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

Annotated RPC client code

void c l i e n t ( bytes c ∗a l i ce , bytes c ∗bob , bytes c ∗kab ,bytes c ∗req , channel∗ chan claimp ( c ) )

maintains ( wrapped ( a l i c e ) && wrapped ( bob ) &&wrapped ( kab ) && wrapped ( req ) )

always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c )requires ( t ab l e . DefinedB [ a l i ce−>encoding ] &&

tab le . DefinedB [ bob−>encoding ] &&tab le . DefinedB [ kab−>encoding ] &&tab le . DefinedB [ req−>encoding ] )

requires (Pub ( t ab l e . B2T [ a l i ce−>encoding ] ) &&Pub ( t ab l e . B2T [ bob−>encoding ] ) &&Pub ( t ab l e . B2T [ req−>encoding ] ) &&Bytes ( t ab l e . B2T [ kab−>encoding ] ) )

requires ( t ab l e . B2T [ kab−>encoding ] ==L i t e r a l ( kab−>encoding ) )

requires (log .New[ t ab l e . B2T [ kab−>encoding ] ]

[ HmacKey(KeyAB( tab l e . B2T [ a l i ce−>encoding ] ,t ab l e . B2T [ bob−>encoding ] ) ) ] ) ;

{spec ( c la im t tmp ; )bytes c ∗toMAC1 , ∗mac1 , ∗msg1 ;bytes c ∗msg2 , ∗resp , ∗toMAC2 , ∗mac2 ;/ / Eventspec ( atomic ( c ,& tab le ,& log ) {

log . Request [ t ab l e . B2T [ a−>encoding ] ][ t ab l e . B2T [ b−>encoding ] ][ t ab l e . B2T [ req−>encoding ] ] = true ;} )

/ / Bu i l d and send request messagei f ( ( toMAC1 = mal loc ( sizeof (∗ toMAC1) ) ) == NULL) return ;i f ( request ( req , toMAC1 spec ( c ) ) ) return ;

i f ( ( mac1 = mal loc ( sizeof (∗mac1) ) ) == NULL) return ;i f ( hmacsha1 ( kab , toMAC1 , mac1 spec ( c ) ) ) return ;

i f ( ( msg1 = mal loc ( sizeof (∗msg1) ) ) == NULL) return ;i f ( p a i r ( req , mac1 , msg1 spec ( c ) ) ) return ;

i f ( channel wr i te ( chan , msg1 spec ( c ) ) ) return ;

/ / Receive and check response messagei f ( ( msg2 = mal loc ( sizeof (∗msg2) ) ) == NULL) return ;i f ( channel read ( chan , msg2 spec ( c ) ) ) return ;

i f ( ( resp = mal loc ( sizeof (∗ resp ) ) ) == NULL) return ;i f ( ( mac2 = mal loc ( sizeof (∗mac2) ) ) == NULL) return ;i f ( des t r uc t (msg2 , resp , mac2 spec ( c ) ) ) return ;

i f ( ( toMAC2 = mal loc ( sizeof (∗ toMAC2) ) ) == NULL) return ;i f ( response ( req , resp , toMAC2 spec ( c ) ) ) return ;

i f ( ! hmacsha1Verify ( kab , toMAC2 , mac2 spec ( c ) ) ) return ;

/ / Correspondence asse r t i onassert ( log . Response [ t ab l e . B2T [ a l i ce−>encoding ] ]

[ t ab l e . B2T [ bob−>encoding ] ][ t ab l e . B2T [ req−>encoding ] ][ t ab l e . B2T [ resp−>encoding ] ]

| | log . Bad [ t ab l e . B2T [ a−>encoding ] ]| | log . Bad [ t ab l e . B2T [ b−>encoding ] ] ) ;

}

To prove that the correspondence assertion holds, VCCwill use the postconditions of hmacsha1Verify() stating that azero return value implies that the third argument is indeed avalid hmac, the fact that the byte array toMAC2 is knownto have a correct response format as it is the result ofa succesful call to the response() function, and the factthat Pub() holds on the response message, as it was readfrom the network. Using these facts, VCC can use theinversion theorems shown in Section III-C and prove thecorrespondence assertion.

V. ASSUMPTIONS CONCERNING THE C VERIFIER

Several research papers [18], [19] document the VCCsystem but there is no formal model of its semantics ofprograms and specifications aside from the VCG itself. To beable to formulate a precise specification of the program prop-erties (in particular security properties) verified by VCC,we sketch a conventional operational semantics, in terms ofwhich we specify what we assume about the verifier. Themodel sketched here has been formalized as part of our Coqdevelopment. The model idealizes from low level features ofC, using instead a simple Java-like heap model (following[19]), but please keep in mind that VCC reasons soundlyabout the gory details of low level C code.

Leaving aside annotations, a program consists of type andfunction declarations. The body of a function is a sequenceof commands including concurrency primitives: thread fork,send, and receive on named channels (all channels being vis-ible to the attacker). Local variables and function parameters(and returns) have declared types and in our model there areno type casts.

An execution environment consists of a self-containedcollection of type and function declarations. For a givenexecution environment, a runtime configuration takes theform (h, ts, qs) where h is the heap, ts is the threadpool, and qs is a map from channel names to messagequeues. A thread state consists of a command (its currentcontinuation) and a local store (i.e., a mapping of localsand parameters to their current values); a thread pool isa finite list of thread states. Thus threads share the heapand the message queues (which hold messages sent but notyet received). A run is a series of configurations that aresuccessors in the transition relation. The transition relationallows nondeterministic selection of any thread that is notblocked waiting to receive on an empty channel. A singlestep (transition) may be an assignment, the test of a branchcondition, creation of a new thread, etc. Nondeterministicscheduling models all interleavings including ones that maybe preferred by an attacker.

A state predicate is a predicate on a heap together with astore. The store is used for function parameters and results,which are thread local. The precondition of a functioncontract is a state predicate; its postcondition is a two-state predicate that refers to the initial and final state ofthe function’s invocation. An invariant is a predicate on apointer together with a pair of heaps, as described earlier.

The only unusual feature of the semantics is our treatmentof assumptions, which are usually only given an axiomaticsemantics. If there is any thread poised to execute thecommand assume p, and the condition p does not hold inthe current configuration, then there is no transition—wesay there is an assumption failure. If all current assumptionshold, then some thread takes a step. Thus some runs end witha “stuck” configuration from which there are no successors.

11

Page 12: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

The only other stuck configurations are those where everythread is blocked waiting on an empty channel. 3 Executionof assume p takes a single step with no effect on state.Execution of assert p also has no effect on the state—nordoes it have an enabling condition. An assertion is effectivelya labelled skip, in terms of which we formulate correctness.

Definition 1 (safe command): An assertion failure is arun in which there is a configuration where some thread’sactive command is assert p for some p that does not holdin that configuration, or some object’s invariant fails tohold, and there is no assumption failure at that point. Aconfiguration is safe if none of its runs are assertion failures.A command c is safe under precondition p if for statessatisfying p, the configuration with that initial state and thesingle thread c is a safe configuration.

Given our treatment of assumptions, safety means thatthere is no assertion failure unless and until there is anassumption failure.

VCC works in a procedure-modular way: it verifies thateach function implementation satisfies its contract, under theassumption of specified contracts for all functions directlycalled in the body.

Definition 2 (verifiable): We write api.h ` p.c q.h tomean there exists p′.c that instruments p.c with additionalghost code (but no assumptions, and no other changes),and q′.h that may extend q.h with contracts for additionalfunctions (but not alter those in q.h) and type invariants, suchthat VCC successfully verifies the implementation of eachfunction f in p′.c against the contract for f in q′.h, underhypotheses api.h and q′.h; moreover admissibility holds forall the type invariants.

We use names ending in .c or .h for code or interfacetexts, as mnemonic for usual file names, but these may becatenations of multiple files.

The most common additions to p.c are assertions thatserve as hints to guide the prover, but claims and other ghostcode can be added; assumptions would subvert the intendedspecifications and could even be unsound. The most commonadditions to q.h are contracts, as q.h may only providecontracts for functions of interest such as main, whereas thecode p.c may include other functions, which for modularreasoning must have contracts.

Note that VCC is never successful unless p.c compilesagainst p.h with api.h, which in particular means that p.h+api.h are a closed collection of declarations. An immediateconsequence of Definition 2 is the following, where the +operator stands for catenation.

Lemma 1 (VCC Modularity): If p.h ` q.c q.h andp.h+ q.h ` r.c r.h then p.h ` q.c+ r.c q.h+ r.h.

The VCC methodology supports verification conditionsfor sound modular reasoning, but it is not easy to give a

3A divergent atomic block would also be stuck in our semantics, but weonly use manifestly terminating atomic blocks. We also disallow assumestatements in atomic blocks.

VCG-independent semantics for the verifiability judgementp.h ` q.c q.h. Fortunately, for our purposes it is enoughto consider soundness for complete programs. A completeprogram is verified as ∅ ` m.c main.h.main.h

void main ( )requires ( program entry point ( ) )writes ( se t un iverse ( ) ) ;

The program entry point() precondition means that allglobal objects exist and are owned by the current threadat the beginning of this function, as it is the first functionthat is called when the process is started.

Assumption 1 (VCC Soundness): If ∅ ` m.c main.hthen the body of function main in m.c is safe for theprecondition in main.h.

VCC checks that ghost state is used in ways that are soundfor reasoning about actual observations; i.e., it has no influ-ence on non-ghost state except for introducing additionalsteps that do not change non-ghost state.

Assumption 2 (VCC Ghost): Let m.c be any completeprogram, which may include ghost state and ghost code,and let m.c be the program with ghost code (includingdeclarations) removed. ¡ For any run R of m.c, let Rbe the sequence of configurations obtained from R byremoving ghost code, ghost variables, ghost fields, objectsreachable only from ghost variables on the current stack,and configurations reached by steps of ghost code. Weassume (a) Ghost code does not introduce new observablebehaviours: For any run R of m.c, R is a run of m.c. (b)Ghost code does not remove observable behaviours exceptat assumption failures: For any run R of m.c, there is a runS of m.c such that R is a prefix of S and either R = S orR ends at an assumption failure.

VI. ATTACK PROGRAMS

An attacker in the symbolic model can intercept messageson unprotected communication links (such as the Internet)and send messages constructed from parts of interceptedmessages, as specified by a term algebra. We model the setof all possible attacks, each attack being represented by anattack program (or just “attack”). In this section we sketchthe formal definition of attack program, relative to a suitableinterface, and give an example. Attack programs are whatenable us, in Section VII, to use an ordinary program verifierto reason about active attackers.

An attack program is a straight-line C program thatcompiles against an attacker interface. Such an interface pro-vides some “opaque” type declarations together with somefunction signatures; these include message send/receive,standard cryptographic operations, and protocol-specific ac-tions like creating sessions and initiating roles. For anannotated interface p.h, we let erase(p.h) be the attackerinterface obtained by deleting annotations and the bodies oftype declarations.

12

Page 13: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

Attacker interfacesT ::= type

bool | unsigned char∗ | X∗µ ::= entry in an interface

typedef X; type declarationT f(T1 x1, . . . , Tn xn) function prototype (n ≥ 0)void f(T1 x1, . . . , Tn xn) procedure prototype (n ≥ 0)

I ::= µ1 . . . µn interface (n ≥ 0)

Recall the software stack shown in Section I-C; the fileRPCshim.h provides a network attacker interface includinggeneric cryptography and network operations as well asprotocol specific functions.An attacker interface: erase(RPCshim.h)typedef bytespub ;

bytespub∗ at t toBytespub ( unsigned char∗ p t r ,unsigned long l en ) ;

bytespub∗ a t t p a i r ( bytespub∗ b1 , bytespub∗ b2 ) ;bytespub∗ a t t f s t ( bytespub∗ b ) ;bytespub∗ at t snd ( bytespub∗ b ) ;

bytespub∗ att hmacsha1 ( bytespub∗ k , bytespub∗ b ) ;bool att hmacsha1Ver i fy ( bytespub∗ k ,

bytespub∗ b ,bytespub∗ m) ;

void a t t channe l wr i t e ( channel∗ chan , bytespub∗ b ) ;bytespub∗ at t channel read ( channel∗ chan ) ;

typedef session ;

session∗ a t t se tup ( bytespub∗ c l , bytespub∗ se ) ;

void a t t r u n c l i e n t ( session∗ s , bytespub∗ request ) ;void a t t run serve r ( session∗ s ) ;

bytespub∗ at t compromise c l ien t ( session∗s ) ;bytespub∗ att compromise server ( session∗s ) ;

channel∗ a t t ge tChanne l c l i en t ( session∗ s ) ;channel∗ at t getChannel server ( session∗ s ) ;

Type bytespub is critical: its invariant constrains its valuesto be concrete byte arrays that correspond to terms thatsatisfy the Pub() predicate. Verifying the implementation ofthis attacker interface therefore provides a proof that Pub()is closed under attacker actions. The function contracts inRPCshim.h and code in RPCshim.c are similar to the hybridwrappers in Section IV-B but oriented to Pub data. Theyare more complicated, due to memory safety annotationsdealing with thread fork and messaging, though that ismostly protocol-independent. An example contract appearsin Section VII.Attack program for given interface IAn attack program for a given interface I has the form:void main ( ) { D C }where D is a sequence of local variable declarationsand C a sequence of commands, such that:1) Each of the declarations in D has the form T x;, where T iseither bool, unsigned char∗, or T∗ where T is declared in I.2) Each command in the sequence C is either (a) a function callassignment with variables as arguments, x = f (y ...) ;(b) a procedure call with variables as arguments f (y ...) ;or (c) an assignment x = s; where s is a string literal.3) A variable is assigned at most once and every variable

mentioned is declared in D.4) For each function or procedure call, each argument variable isassigned earlier in the sequence of commands.5) In each call to a function or procedure f , there is a declarationof f in I and each argument variable in the call has declared typeidentical to that of the corresponding parameter of f .6) In a function call assignment x = f (y ...) ; , the declared type ofx is the result type of f . In a string assignment x = s; the declaredtype of x is char∗.

Owing to item 2, an attack program does not directlyassign any object field, nor any global variable. Nor does itdirectly invoke any operations except functions and proce-dures in I (item 5).An attack program for RPCshim.h (from RPCattack 0.c)void main ( ){ unsigned char ∗a ,∗b ,∗ r ;

bytespub ∗a l i ce ,∗bob ,∗ arg ,∗ req ,∗ resp ;channel ∗c l ien tC ,∗ serverC ;session ∗s ;

a = ” A l i ce ” ; a l i c e = at t toBytespub ( a , 5 ) ;b = ”Bob ” ; bob = at t toBytespub ( b , 3 ) ;r = ” Request ” ; arg = at t toBytespub ( r , 7 ) ;s = a t t se tup ( a l i ce , bob ) ;c l i e n t C = a t t ge tChanne l c l i en t ( s ) ;serverC = at t getChannel server ( s ) ;a t t run serve r ( s ) ;a t t r u n c l i e n t ( s , arg ) ;req = at t channel read ( c l i e n t C ) ;a t t channe l wr i t e ( serverC , req ) ;resp = at t channel read ( serverC ) ;a t t channe l wr i t e ( c l i en tC , resp ) ;}

An attack program for RPCshim.h (from RPCattack 0.c)void main ( ){

unsigned char ∗a , ∗b , ∗r1 , ∗ r2 ;bytespub ∗a l i ce , ∗bob , ∗arg1 , ∗req1 , ∗resp1 , ∗arg2 , ∗

req2 ;session ∗s ;channel ∗c l ien tC , ∗serverC ;

/ / Setup phasea = ” A l i ce ” ;a l i c e = at t toBytespub ( a ) ;b = ”Bob ” ;bob = at t toBytespub ( b ) ;r1 = ” Request1 ” ;arg1 = at t toBytespub ( r1 ) ;r2 = ” Request2 ” ;arg2 = at t toBytespub ( r2 ) ;s = a t t se tup ( a l i ce , bob ) ;c l i e n t C = a t t ge tChanne l c l i en t ( s ) ;serverC = at t getChannel server ( s ) ;

/ / F i r s t run through of the pro toco l , the a t t acke robserves

a t t run serve r ( s ) ;a t t r u n c l i e n t ( s , arg1 ) ;req1 = at t channel read ( c l i e n t C ) ;a t t channe l wr i t e ( serverC , req1 ) ;resp1 = at t channel read ( serverC ) ;a t t channe l wr i t e ( c l i en tC , resp1 ) ;

/ / Run only the c l i e n t , w i th a d i f f e r e n t request , andrespond wi th the f i r s t run ’ s response

a t t r u n c l i e n t ( s , arg2 ) ;req2 = at t channel read ( c l i e n t C ) ;a t t channe l wr i t e ( serverC , resp1 ) ;

}

This happens to model an attack that would be successfulagainst a flawed RPC protocol where the request is not

13

Page 14: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

included in the response’s MAC:

A symbolic attack

A → B: payload | hmac(”request” | payload)B → A: payload’ | hmac(”response” | payload’)

It would break the correspondence property by making theclient accept a response that does not correspond to itsrequest.

VII. AN EXAMPLE SECURITY THEOREM

An attack program for the RPC protocol is a programthat relies only on RPCshim.h. To form an executable, itneeds to be combined with System which we define tobe the catenation crypto.c + RPChybrids.c + RPCprot.c. Herecrypto.c is the library of cryptographic algorithms (and welet it subsume OS libraries, e.g., for memory allocation andsockets), which is used in RPChybrids.c and RPCprot.c.

Before providing the formal results, we informally de-scribe a key property on which soundness of our approachrests. Consider any attack program M.c and any run ofthe program System + RPCshim.c + M.c. It is an invariant thatat every step of the run, the representation table holdsevery term that has arisen by cryptographic computation orby invocation of the toBytesPub function which an attackmust use to convert guessed bytestrings to type bytespub asneeded to invoke the other functions of RPCshim. This isnot an invariant that we state in the program annotations;its only role is to justify our use of assumptions. The onlyassumptions used are in RPCshim.c and RPChybrids.c wherecollisions are detected. In light of the key invariant, thismeans that in any run that reaches an assumption failure,the sequence of terms computed includes a hash collision oran attacker guess of a term that is not public according tothe symbolic model of cryptography. In short, assumptionsare used only to express the Dolev-Yao assumption.

The contracts in RPCshim.h all follow a similar pattern;we give one for reference in the following proof.

Example contract from RPCshim.h

bytespub∗ att hmacsha1 ( bytespub∗ k , bytespub∗ b claimp ( c ) )maintains ( wrapped ( k ) )maintains ( wrapped ( b ) )writes ( k , b )always ( c , c losed (& tab le )&&c s tab le log&&c s tab le tab le )writes ( c )ensures ( wrapped ( resul t ) ) ;

Attack programs were defined in order to show thattheir behaviours are among those of interfering threadsencompassed by the verification conditions VCC imposes onprotocol code. This is formalized by way of the following.

Lemma 2: If M.c is an attack program forerase(RPCshim.h), then RPCshim.h ` M.c main.h.

Proof: (Sketch) According to Definition 2 we have toshow admissibility of the type invariants in RPCshim.h, whichwe have checked using VCC. It remains to prove verifiability

of the attack program against main.h. A general argument isneeded since there are infinitely many attacks. (Here weidealize: VCC’s resources can be taxed in many ways, e.g.,M.c could declare a vast number of variables.)

Because the contract in main.h does not impose a postcon-dition and its write specification is vacuous, we just needto show that invariants are established and maintained. LetM.c be void main(){D C}. In accord with Definition 2 we willshow verifiability of code C′ which augments the statementsof C with two sorts of instrumentation. The first is simplyto prefix C with ghost code that initializes the representationtable and log. This code is defined as macro init () , shownin the code sample below, where maps are defined usingVCC’s lambda notation, and the constants tagRequest andtagResponse are separately defined to be the integer encodingof the 1-byte-long strings ”1” and ”2”, respectively.The init () macro#define i n i t ( . . . ) \

spec (\/∗ Log ∗ /\log .New = lambda ( term t ;\

lambda ( usage u ; fa lse ) ) ;\log . Request = lambda ( term a ;\

lambda ( term b ;\lambda ( term s ; fa lse ) ) ) ;\

log . Response = lambda ( term a ;\lambda ( term b ;\

lambda ( term s ;\lambda ( term t ; fa lse ) ) ) ) ;\

wrap(& log ) ;\/∗ Representat ion tab l e . I t i n i t i a l l y conta ins

tagRequest and tagResponse . ∗ /\t ab l e . DefinedB =\

lambda ( bytes b ; b == tagRequest\| | b == tagResponse ) ;\

t ab l e . B2T [ tagRequest ] = L i t e r a l ( tagRequest ) ;\t ab l e . B2T [ tagResponse ] = L i t e r a l ( tagResponse ) ;\t ab l e . DefinedT =\

lambda ( term t ; t == L i t e r a l ( tagRequest )\| | t == L i t e r a l ( tagResponse ) ) ;\

t ab l e . T2B [ L i t e r a l ( tagRequest ) ] = tagRequest ;\t ab l e . T2B [ L i t e r a l ( tagResponse ) ] = tagResponse ;\wrap(& tab l e ) ;\c = cla im (& tab le , c losed (& tab le ) &&\

c s tab le log && c s tab le tab le ) ; )

We verified a sample attack using init () , which servesto prove that init () establishes the log and table invariants,as the invariants are proved to hold when the objects arewrapped. Furthermore, init () creates a claim c on the table(which owns the log) that says they remain wrapped andstable. Owing to the contracts in RPCshim.h, this claimwill be maintained, which ensures from that point on thatthe log and table can never be opened so their invariantsare maintained even in the presence of interference frominterleaved threads. Thus the second sort of instrumentationin C′ passes the claim c as ghost parameter to each functionand procedure call in C, in accord with their contracts inRPCshim.h. For example:

a t t run serve r ( s spec ( f reshCla im ( c ) ) ) ;

We also add an assertion before each function and procedurecall. (Though in fact these assertions are not needed forVCC to verify the example attack.) Let us say “pointer

14

Page 15: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

variable” for the variables declared in D with pointertype. Preceding each procedure call f(y) and function callx = f(y) in C′ we can assert a conjunction of the formwrapped(x0)&& . . . &&wrapped(xj) where x0, . . . , xj are thepointer variables that have been assigned up to this point.(We gloss over memory safety assertions needed for strings.)By induction on the length of C, we argue that each ofthese assertions holds, and moreover the type invariants aremaintained. An assignment, say x = f(y, z, w);, satisfies thepreconditions of f owing to the added claim, the requirementthat y, z, w were previously assigned, and the assertion thaty, z, w are all wrapped. By inspection of the contracts foreach f in RPCshim.h (e.g., att hmacsha1() given above), thatis all that is needed. The postcondition of f ensures thatresults are wrapped, so in particular x is wrapped at thenext assertion (and the claim maintained).

Running VCC on RPCattack 0.c not only served to checkthe init () code used in the proof but also as a sanity checkon this Lemma.

Theorem 1: Assume ∅ ` crypto.c crypto.h. For anyattack program M.c against the interface erase(RPCshim.h),the program System + RPCshim.c +M.c is safe.

Proof: We have verified with VCC that:crypto.h ` (RPChybrids.c+RPCprot.c+RPCshim.c) RPCshim.hBy assumption ∅ ` crypto.c crypto.h and Lemma 1 we get` (crypto.c+RPChybrids.c+RPCprot.c+RPCshim.c) RPCshim.hi.e., we have ∅ ` (System + RPCshim.c) RPCshim.h bydefinition of System. By Lemma 2, since M.c is an attackprogram for erase(RPCshim.h), we get RPCshim.h ` M.c main.h and thus by Lemma 1 we get: ∅ ` (System +RPCshim.c + M.c) main.h. So by Assumption 1 theprogram System + RPCshim.c +M.c is safe.

Informal corollary: For all applications A verified againstRPCprot.h and the rest of the API (excluding RPCshim.h),A+RPCprot.c+RPChybrids.c+crypto.c is safe in the presenceof any active network attacker (under the symbolic modelof cryptography). The software stack shown in Section I-Cis executable but its real purpose is to show security for adifferent software stack, without RPCshim.c and RPCattack 0.cbut with additional application code that is verified to bememory safe and conform to the protocol API RPCprot.h.

VIII. SUMMARY OF EMPIRICAL RESULTS

In this section, we summarize our experimental results onimplementations of RPC and the variant of the Otway-Reesprotocol presented by Abadi and Needham [1].

A. Results

We prove authentication properties of the implementationsusing non-injective correspondences, expressed as assertionson a log of events, by relying on weak secrecy properties,which we prove formally as invariants of the log. Theattacker controls the network, can instantiate an unboundednumber of principals, and can run unbounded instances of

each protocol role —but can never cause a correspondenceassertion to fail and can never break the secrecy invariants,unless the Dolev-Yao assumption (no collisions or luckyguesses) has already been violated In particular, we provethe following properties about our sample protocol imple-mentations.

1) RPC: Our implementation of RPC does not let theserver reply to unwanted requests, and does not let the clientaccept a reply that is not related to a previously sent request.Moreover, their shared key remains secret unless either theclient or the server is compromised by the attacker.

2) Otway-Rees: The initiator and responder only acceptreplies from the trusted server that contain a freshly gener-ated key for their specific usage, and this key remains secretunless either the initiator or the responder is compromised.

As both a side-effect and a requirement to use a generalpurpose verifier, we also prove memory safety propertiesof our implementations. This can significantly slow ver-ification, especially in parts of the code that handle thebuilding of messages by catenation, and is a large part ofthe annotation burden.

B. Performance

The following table shows verification times, as wellas lines of code (LoC) and lines of annotation (LoA)estimations for various implementation files. Times are givenas over-approximations of the verification time in minutes(on a mid-end laptop). The number of lines of annotationincludes the function contracts, but not earlier definitions.For example, when verifying a function in hybrids.c, alldefinitions from symcrypt.h can be used but are not countedtowards the total. The shim and sample attack programs areverified, as part of the proof of the Theorem, but they are notpart of the protocol verification and so are omitted here. TheOtway-Rees shim available online assumes, for simplicity,a special semantics for some function calls, as the threadsrunning the initiator and responder role should be able toreturn a value to the attacker, which requires some moreglue code in C. It is possible to write and verify this gluecode using VCC, but it makes the code that much morecomplex to understand and is not relevant to the protocol’ssecurity.

File/Function LoC LoA Time (mins)symcrypt.h - 50 ≤ 1table .h - 50 ≤ 1RPCdefs.h - 250 ≤ 1ORdefs.h - 250 ≤ 1

hybrids.c 150 300 ≤ 5destruct () 20 40 ≤ 5hmacsha1() 20 20 ≤ 1

RPCprot.c 130 80 ≤ 15client () 40 20 ≤ 5server() 40 10 ≤ 10

ORprot.c 300 100 ∼ 100initiator () 40 15 ≤ 5

responder() 100 100 ∼ 60server() 40 15 ∼ 30

15

Page 16: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

These two case studies confirm previous observationson the annotation burden that comes with general purposeverifiers (and VCC in particular), in “order of one line ofannotation per line of code” [19], which was also similar inthe F7 implementation of the RPC protocol [12], where thetrusted libraries correspond to our hybrid wrappers. For theRPC protocol, our verification times are a lot higher thanthose of F7 (which were all under 30 seconds), becauseVCC verifies the program’s memory safety simultaneously,whereas F7 relies on F#’s underlying type system andmemory structure to do so.

In order to focus on verifying security properties, we sim-plified several aspects of the implementation that were notrelevant to symbolic security and usually require extensiveannotations: details of network operations are ignored by theverifier (in particular, each principal is only given one singlechannel to the attacker), and memory is not freed after use.

IX. RELATED WORK ON PROTOCOL CODE

We discussed the closely related tools Csur and ASPIERfor C and some tools for F# in Section I. We discuss otherwork on verifying executable code of security protocols.

Pistachio [44] verifies compliance of a C code with arule-based specification of the communication steps of aprotocol. It proves conformance rather than specific securityproperties. DYC [32] is a C API for symbolic cryptographicprotocol messages which can be used to generate executableprotocol implementations, and also to generate constraintswhich can be fed to a constraint solver to search for attacks.Code is checked by model-checking a finite state spacerather than being fully verified.

In this paper, we present how a high-level security modelcan be expressed as part of a C program. Conversely, onecan extract a high-level model of the implemented protocol.Symbolic execution of C code is a promising technique forthis purpose. Corin and Manzano [21] extend the KLEEsymbolic execution engine to represent the outcome ofcryptographic algorithms symbolically, but do not considerprotocol code. Other recent work [2] extracts verifiableProVerif models by symbolic execution of C protocol code,on code similar to that of this paper.

There are approaches for verifying implementations ofsecurity protocols in other languages. Jurjens [34] describesa specialist tool to transform a Java program’s control-flowgraph to a Dolev-Yao formalization in FOL which is verifiedfor security properties with automated theorem provers suchas SPASS. O’Shea [41] translates Java implementations intoformal models to the LySa process calculus so as to per-form a security verification. The VerifiCard project uses theESC/Java2 static verifier to check conformance of JavaCardapplications to protocol models (e.g., [30]). Mukhamedov,Gordon, and Ryan [36] perform a formal analysis of theimplementation code of a reference implementation of the

TPM’s authorization and encrypted transport session proto-cols in F#, and automatically translate it into executable Ccode.

Work on RCF, the concurrent lambda calculus underpin-ning F7, is directly related. [5] provides conditions underwhich symbolic security of programs in RCF using cryp-tographic idealizations implies computational security usingcryptographic algorithms. [4] enhances RCF with union andintersection types for the verification of the source code ofcryptographic-protocol implementations in F#.

X. CONCLUSION

We describe a method for guiding a general-purpose Cverifier to prove both memory safety and authenticationand weak secrecy properties of security protocols and theirimplementations. Still, our use of VCC leaves clear roomfor improvement in terms of reducing verification timesand numbers of user-supplied annotations. Our strategy ofbuilding on a general-purpose C verifier aims to benefitfrom economies of scale, and in particular to benefit fromfuture improvements in C verification in general. This paperestablishes a workable method and a baseline. We encourageverification specialists to take up the challenge.

We plan, for example, to investigate whether we can im-prove performance by adopting cryptographic invariants inthe style of TAPS [17]. Our use of separately-proved secrecyinvariants resembles the first-order approach followed inTAPS. We are aware of unpublished work by Cohen onadapting this approach to use with VCC. The TAPS stylerelies less on axioms, which may or may not place morestrain on the first-order prover.

Some of our security annotations can be re-used. Inparticular, the hybrid wrappers and their contracts need onlybe written once per cryptographic library, and can be used toverify multiple protocol implementations, as we have donefor RPC and Otway-Rees. The representation table is alsoentirely re-usable. Moreover, we believe that some of theannotations (for example, the log and inductive predicatedefinitions) may be automatically generated from a high-level description of the protocol.

In future work we intend to adapt our foundations toobtain provably computationally sound results with VCC.We have designed our contracts to correspond to crypto-graphic assumptions; for example, encryptions give onlyconfidentiality and MACs give only integrity. The tablestructure and hybrid wrappers introduced in Section IVresemble some standard methods for computational sound-ness, such as the the dual interpretation of the interface inBPW [6], or the hybrid wrappers used in [25]. We may alsotry more direct methods to obtain computational securityresults, for example by using the idealized interface from[25] and assuming a computationally sound implementation(or linking the C code against the F# implementation), orby extending the verifier with probabilistic semantics for C,

16

Page 17: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

similar to the probabilistic semantics given to the PWHILElanguage in CertiCrypt [8]. Another needed extension toprove computational security properties would be to verifystrong secrecy by observational equivalence. This wouldrequire extending our attacker model slightly to allow attackprograms to branch on, for example, MAC verificationresults.

Verification of symbolic security properties remains rel-evant, even without a computational soundness result, asrecent attacks on prominent protocols and implementationscould have been found by symbolic protocol verification.Moreover, some standard features of security protocols (suchas sending encrypted keys over the network) are hard toprove secure in the computational model, but may be studiedin symbolic models.

For highest assurance, the underlying libraries (crypto.c)would be verified, as would any application code using theprotocol library. Moreover, the C verifier would be provedsound with respect to a semantics for which the compiler isproved to be correct (as in the Verified Software Toolchain[3] built on the C semantics of CompCert [35]).

Acknowledgements: Discussions with Misha Aizatulin,Karthik Bhargavan, Josh Berdine, Ernie Cohen, Cedric Four-net, Bashar Nuseibeh, and Thomas Santen were useful.Mark Hillebrand and Michał Moskal helped with VCCmethodology, and Stephan Tobies helped with understandingVCC internals. Naumann acknowledges partial support fromMicrosoft and NSF awards CRI-0708330, CCF-0915611.

REFERENCES

[1] M. Abadi and R. M. Needham, “Prudent engineering practicefor cryptographic protocols,” IEEE Trans. Software Eng.,vol. 22, no. 1, pp. 6–15, 1996.

[2] M. Aizatulin, A. D. Gordon, and J. Jurjens, “Extractingand verifying cryptographic models from C protocolcode by symbolic execution,” 2011, unpublished draft.[Online]. Available: http://users.mct.open.ac.uk/ma4962/files/paper-full.pdf

[3] A. W. Appel, “Verified software toolchain,” in ESOP, ser.LNCS, vol. 6602, 2011, pp. 1–17.

[4] M. Backes, C. Hritcu, and M. Maffei, “Union and intersectiontypes for secure protocol implementations,” in TOSCA, 2011.

[5] M. Backes, M. Maffei, and D. Unruh, “Computationallysound verification of source code,” in ACM CCS, 2010, pp.387–398.

[6] M. Backes, B. Pfitzmann, and M. Waidner, “A composablecryptographic library with nested operations,” in ACM CCS,2003, pp. 220–230.

[7] M. Barnett, B.-Y. E. Chang, R. DeLine, B. Jacobs, andK. R. M. Leino, “Boogie: A modular reusable verifier forobject-oriented programs,” in FMCO, ser. LNCS, vol. 4111,2005, pp. 364–387.

[8] G. Barthe, B. Gregoire, and S. Z. Beguelin, “Formal certifi-cation of code-based cryptographic proofs,” in POPL, 2009,pp. 90–101.

[9] J. Bengtson, K. Bhargavan, C. Fournet, A. D. Gordon, andS. Maffeis, “Refinement types for secure implementations,”ACM TOPLAS, vol. 33, no. 2, p. 8, 2011.

[10] K. Bhargavan, C. Fournet, A. D. Gordon, and S. Tse, “Verifiedinteroperable implementations of security protocols,” ACMTOPLAS, vol. 31, pp. 5:1–5:61, December 2008.

[11] K. Bhargavan, C. Fournet, R. Corin, and E. Zalinescu, “Cryp-tographically verified implementations for tls,” in ACM CCS,2008, pp. 459–468.

[12] K. Bhargavan, C. Fournet, and A. D. Gordon, “Modularverification of security protocol code by typing,” in POPL,2010, pp. 445–456.

[13] B. Blanchet, “An efficient cryptographic protocol verifierbased on prolog rules,” in CSFW, 2001, pp. 82–96.

[14] ——, “A computationally sound mechanized prover for secu-rity protocols,” in IEEE Symposium on Security and Privacy,2006, pp. 140–154.

[15] I. Cervesato, A. D. Jaggard, A. Scedrov, J.-K. Tsay, andC. Walstad, “Breaking and fixing public-key Kerberos,” inASIAN, ser. LNCS, vol. 4435, 2006, pp. 167–181.

[16] S. Chaki and A. Datta, “ASPIER: an automated frameworkfor verifying security protocol implementations,” CyLab,Carnegie Mellon University, Technical CMU-CyLab-08-012,2008.

[17] E. Cohen, “First-order verification of cryptographic proto-cols,” Journal of Computer Security, vol. 11, no. 2, pp. 189–216, 2003.

[18] E. Cohen, M. Dahlweid, M. A. Hillebrand, D. Leinenbach,M. Moskal, T. Santen, W. Schulte, and S. Tobies, “VCC: Apractical system for verifying concurrent C,” in TPHOLs, ser.LNCS, vol. 5674, 2009, pp. 23–42.

[19] E. Cohen, M. Moskal, W. Schulte, and S. Tobies, “Localverification of global invariants in concurrent programs,” inCAV, ser. LNCS, vol. 6174, 2010, pp. 480–494.

[20] E. Cohen and B. Schirmer, “From total store order to sequen-tial consistency: A practical reduction theorem,” in InteractiveTheorem Proving, ser. LNCS, vol. 6172, 2010, pp. 403–418.

[21] R. Corin and F. A. Manzano, “Efficient symbolic executionfor analysing cryptographic protocol implementations,” inESSoS, ser. LNCS, vol. 6542, 2011, pp. 58–72.

[22] L. Correnson, P. Cuoq, A. Puccetti, and J. Signoles,Frama-C User Manual. [Online]. Available: http://frama-c.com/download/frama-c-user-manual.pdf

[23] L. M. de Moura and N. Bjørner, “Z3: An efficient SMTsolver,” in TACAS, ser. LNCS, vol. 4963, 2008, pp. 337–340.

17

Page 18: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

[24] D. Dolev and A. C.-C. Yao, “On the security of publickey protocols,” IEEE Transactions on Information Theory,vol. 29, no. 2, pp. 198–207, 1983.

[25] C. Fournet, “Cryptographic soundness for program verifica-tion by typing,” 2011, unpublished draft.

[26] “Crypto-verifying protocol implementations in ML,” projectwebsite at http://msr-inria.inria.fr/projects/sec/fs2cv/.

[27] A. D. Gordon, “Provable implementations of security proto-cols,” in 21th IEEE Symposium on Logic in Computer Science(LICS 2006), 2006, pp. 345–346.

[28] J. Goubault-Larrecq and F. Parrennes, “Cryptographic proto-col analysis on real c code,” in VMCAI, ser. LNCS, vol. 3385,2005, pp. 363–379.

[29] ——, “Cryptographic protocol analysis on real c code,” LSV- ENS Cachan, Tech. Rep. LSV-09-18, 2009.

[30] E. Hubbers, M. Oostdijk, and E. Poll, “Implementing aformally verifiable security protocol in Java Card,” in Securityin Pervasive Computing, ser. LNCS, vol. 2802, 2004, pp. 213–226.

[31] B. Jacobs and F. Piessens, “The VeriFast program verifier,”Katholieke Universiteit Leuven, Report CS 520, Aug. 2008.

[32] A. S. A. Jeffrey and R. Ley-Wild, “Dynamic model check-ing of C cryptographic protocol implementations,” in FCS-ARSPA, 2006.

[33] C. B. Jones, “Development methods for computer programsincluding a notion of interference,” Ph.D. dissertation, OxfordUniversity, June 1981, printed as: Programming ResearchGroup, Technical Monograph 25.

[34] J. Jurjens, “Security analysis of crypto-based java programsusing automated theorem provers,” in ASE, 2006, pp. 167–176.

[35] X. Leroy, “Formal certification of a compiler back-end or:programming a compiler with a proof assistant,” in POPL,2006, pp. 42–54.

[36] A. Mukhamedov, A. D. Gordon, and M. Ryan, “Towardsa verified reference implementation of the trusted platformmodule,” in 17th International Workshop on Security Proto-cols (2009), ser. LNCS. Springer, 2011, to appear.

[37] S. J. Murdoch, S. Drimer, R. J. Anderson, and M. Bond,“Chip and PIN is broken,” in IEEE Symposium on Securityand Privacy, 2010, pp. 433–446.

[38] R. M. Needham and M. D. Schroeder, “Using encryptionfor authentication in large networks of computers,” Commun.ACM, vol. 21, no. 12, pp. 993–999, 1978.

[39] oCERT, “oCERT advisory #2008-16 multiple OpenSSLsignature verification API misuse,” 2009. [Online]. Available:http://www.ocert.org/advisories/ocert-2008-016.html

[40] Offspark, “Polarssl,” 2008. [Online]. Available: http://polarssl.org

[41] N. O’Shea, “Using Elyjah to analyse Java implementationsof cryptographic protocols,” in FCS-ARSPA-WITS, 2008, pp.211–223.

[42] S. S. Owicki and D. Gries, “An axiomatic proof techniquefor parallel programs i,” Acta Inf., vol. 6, pp. 319–340, 1976.

[43] N. Swamy, J. Chen, C. Fournet, P.-Y. Strub, K. Bharagavan,and J. Yang, “Secure distributed programming with value-dependent types,” Tech. Rep. MSR–TR–2011–37, 2011.

[44] O. Udrea, C. Lumezanu, and J. S. Foster, “Rule-based staticanalysis of network protocol implementations,” USENIX Se-curity Symposium, pp. 193–208, 2006.

18

Page 19: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

APPENDIX A.FILES

For convenience, all VCC and Coq source files listed here, along with the files for the mentioned Otway-Rees example,are made available in a stand-alone archive, available at http://fdupress.net/files/confs/CSF11/guiding-source.tar.gz.

A. RPCdefs.v with proofs elided (using coqdoc -g)

All the Coq propositions marked as Definition, Inductive, and Theorem are imported into VCC header files, as describedin Section III, but those marked Lemma are not.

Require Import List.Require Import ListSet.Module BYTELISTS. (* Does not get exported *)(* Single byte *)Definition byte := (bool × bool × bool × bool × bool × bool × bool × bool)%type.(* Bytestrings *)Definition bytes := list byte.

End BYTELISTS.Export ByteLists.Module SYMCRYPT. (* Facts get exported into symcrypt.h *)(* Terms *)Inductive term: Type :=| Literal: bytes → term| Pair: term → term → term| Hmac: term → term → term| SEnc: term → term → term.

Definition tag term t: nat :=match t with| Literal ⇒ 1| Pair ⇒ 2| Hmac ⇒ 3| SEnc ⇒ 4end.

Theorem Literal Injective: ∀ b b’, Literal b = Literal b’ → b = b’.Theorem Pair Injective: ∀ t1 t2 t1’ t2’, Pair t1 t2 = Pair t1’ t2’ → t1 = t1’ ∧ t2 = t2’.Theorem Hmac Injective: ∀ k k’ m m’, Hmac k m = Hmac k’ m’ → k = k’ ∧ m = m’.Theorem SEnc Injective: ∀ k k’ m m’, SEnc k m = SEnc k’ m’ → k = k’ ∧ m = m’.Theorem Literal Disjoint: ∀ b, tag term (Literal b) = 1.Theorem Pair Disjoint: ∀ t1 t2, tag term (Pair t1 t2) = 2.Theorem Hmac Disjoint: ∀ k m, tag term (Hmac k m) = 3.Theorem SEnc Disjoint: ∀ k p, tag term (SEnc k p) = 4.(* Structural Predicates *)Definition IsLiteral l b := l = Literal b.Definition IsPair ab a b := ab = Pair a b.Definition IsHmac h k m := h = Hmac k m.Definition IsSEnc c k p := c = SEnc k p.

End SYMCRYPT.Export SymCrypt.Module RPCDEFS. (* Facts get imported into RPCdefs.h *)(* Hash Key Usage *)Inductive hash usage: Type :=| KeyAB: term → term → hash usage.Theorem KeyAB Injective: ∀ a b a’ b’, KeyAB a b = KeyAB a’ b’ → a = a’ ∧ b = b’.(* SEnc Key Usage *)Inductive senc usage: Type := .(* Usage *)Inductive usage: Type :=| AttackerGuess: usage

19

Page 20: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

| HmacKey: hash usage → usage| SEncKey: senc usage → usage.Theorem HmacKey Injective: ∀ u u’, HmacKey u = HmacKey u’ → u = u’.Theorem SEncKey Injective: ∀ u u’, SEncKey u = SEncKey u’ → u = u’.(* Events *)Inductive event: Type :=| New: term → usage → event| Bad: term → event

| Request: term → term → term → event| Response: term → term → term → term → event.(* Log *)Definition log: Type := set event.(* History Predicate *)Definition Logged (e: event) (l: log): Prop := set In e l.Definition leq log (l l’: log) := ∀ e, Logged e l → Logged e l’.(* Log Validity *)Definition valid log (l: log): Prop :=∀ t u u’, Logged (New t u) l → Logged (New t u’) l → u = u’.(* General Usage *)

Definition IsHmacKey log k := ∃ u, Logged (New k (HmacKey u)) log.Definition IsSEncKey log k := ∃ u, Logged (New k (SEncKey u)) log.Definition IsAttackerGuess log g := Logged (New g AttackerGuess) log.(* Precise Usage *)

Definition IsKeyAB log k a b := Logged (New k (HmacKey (KeyAB a b))) log.(* Other Events *)

Definition IsBad log p := Logged (Bad p) log.Definition IsRequest log s a b := Logged (Request a b s) log.Definition IsResponse log t a b s := Logged (Response a b s t) log.(* RPC Tags and Abbreviations *)Definition TagRequest: bytes := (false,false,false,false,false,false,false,true)::nil.Definition TagResponse: bytes := (false,false,false,false,false,false,true,false)::nil.Lemma Tags Distinct: TagRequest 6= TagResponse.Definition Requested m req := IsPair m (Literal TagRequest) req.Definition Responded m req resp := IsPair m (Literal TagResponse) (Pair req resp).Theorem Requested Injective: ∀ m r r’, Requested m r → Requested m r’ → r = r’.Theorem Responded Injective: ∀ m s s’ t t’, Responded m s t → Responded m s’ t’ → s = s’ ∧ t = t’.Theorem Requested Responded Disjoint: ∀ m m’ s s’ t’, Requested m s → Responded m’ s’ t’ → m 6= m’.(* Real inductive definitions *)Inductive MACSays: log → term → term → Prop :=| MACSays KeyAB Request: ∀ l k m a b req,

IsKeyAB l k a b →Requested m req →IsRequest l req a b →MACSays l k m

| MACSays KeyAB Response: ∀ l k m a b req resp,IsKeyAB l k a b →Responded m req resp →IsResponse l resp a b req →MACSays l k m.

Theorem MACSays Monotonic: ∀ l l’ k m, leq log l l’ → MACSays l k m → MACSays l’ k m.Inductive SEncSays: log → term → term → Prop := .Theorem SEncSays Monotonic: ∀ l l’ k p, leq log l l’ → SEncSays l k p → SEncSays l’ k p.Inductive Pub: log → term → Prop :=| Pub AttackerGuess: ∀ log b, IsAttackerGuess log (Literal b) → Pub log (Literal b)| Pub HmacKey KeyAB: ∀ log b’ a b, IsKeyAB log (Literal b’) a b → Logged (Bad a) log ∨ Logged (Bad b) log → Pub log (Literal

b’)| Pub Pair: ∀ log t1 t2, Pub log t1 → Pub log t2 → Pub log (Pair t1 t2)

20

Page 21: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

| Pub Hmac: ∀ log k m, MACSays log k m → Bytes log k → Pub log m → Pub log (Hmac k m)| Pub Hmac Pub: ∀ log k m, Pub log k → Pub log m → Pub log (Hmac k m)| Pub SEnc: ∀ log k p, SEncSays log k p → Bytes log k → Bytes log p → Pub log (SEnc k p)| Pub SEnc Pub: ∀ log k p, Pub log k → Pub log p → Pub log (SEnc k p)

with Bytes: log → term → Prop :=| Bytes Literal: ∀ log b, Bytes log (Literal b)| Bytes Pair: ∀ log t1 t2, Bytes log t1 → Bytes log t2 → Bytes log (Pair t1 t2)| Bytes Hmac: ∀ log k m, MACSays log k m → Bytes log k → Bytes log m → Bytes log (Hmac k m)| Bytes Hmac Pub: ∀ log k m, Pub log k → Pub log m → Bytes log (Hmac k m)| Bytes SEnc: ∀ log k p, SEncSays log k p → Bytes log k → Bytes log p → Bytes log (SEnc k p)| Bytes SEnc Pub: ∀ log k p, Pub log k → Pub log p → Bytes log (SEnc k p).

Theorem Pub Bytes: ∀ l t, Pub l t → Bytes l t.

Theorem Pub Monotonic: ∀ l1 l2 t, leq log l1 l2 → Pub l1 t → Pub l2 twith Bytes Monotonic: ∀ l1 l2 t, leq log l1 l2 → Bytes l1 t → Bytes l2 t.

Theorem Pair Pub: ∀ log t1 t2, Pub log (Pair t1 t2) → Pub log t1 ∧ Pub log t2.

Theorem Pair Bytes: ∀ log t1 t2, Bytes log (Pair t1 t2) → Bytes log t1 ∧ Bytes log t2.

Theorem MACSays RPC: ∀ log a b m k,valid log log → IsKeyAB log k a b → MACSays log k m →(∃ s, Requested m s ∧ IsRequest log s a b) ∨(∃ s, ∃ t, Responded m s t ∧ IsResponse log t a b s).

Theorem Pub MACSays Pub: ∀ log m k,Pub log (Hmac k m) →MACSays log k m ∨ Pub log k.

End RPCDEFS.

B. symcrypt.h

#include <vcc . h>

#define rule (name , . . . ) axiom ( VA ARGS )#define theorem (name , . . . ) axiom ( VA ARGS )

/ / Begin DolevYaospec (

typedef math in t bytes ;typedef math in t term ;

ispure math in t tag term ( term ) ;

/ / Const ruc tors ( declared as un in te rp re ted fu n c t i o n s )ispure term L i t e r a l ( bytes bs ) ;ispure term Pa i r ( term t1 , term t2 ) ;ispure term Hmac( term k , term m) ;

/ / Theoremstheorem ( L i t e r a l I n j e c t i v e ,

f o r a l l ( bytes bs1 , bs2 ; L i t e r a l ( bs1 ) == L i t e r a l ( bs2 )==> bs1 == bs2 ) ) ;

theorem ( P a i r I n j e c t i v e ,f o r a l l ( term a1 , a2 , b1 , b2 ; Pa i r ( a1 , b1 ) == Pa i r ( a2 , b2 )

==> a1 == a2 && b1 == b2 ) ) ;theorem ( Hmac Inject ive ,

f o r a l l ( term k1 ,m1, k2 ,m2; Hmac( k1 ,m1) == Hmac( k2 ,m2)==> k1 == k2 && m1 == m2) ) ;

theorem ( L i t e r a l D i s j o i n t ,f o r a l l ( bytes bs ; tag term ( L i t e r a l ( bs ) ) == 0) ) ;

theorem ( P a i r D i s j o i n t ,f o r a l l ( term t1 , t2 ; tag term ( Pa i r ( t1 , t2 ) ) == 1) ) ;

theorem ( Hmac Disjoint ,f o r a l l ( term k ,m; tag term (Hmac( k ,m) ) == 2) ) ;

)/ / End DolevYao

spec (ispure term SEnc ( term k , term b ) ;

theorem ( SEnc In ject ive ,f o r a l l ( term k1 , b1 , k2 , b2 ; SEnc ( k1 , b1 ) == SEnc ( k2 , b2 )

==> k1 == k2 && b1 == b2 ) ) ;

21

Page 22: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

theorem ( SEnc Dis jo int ,f o r a l l ( term k ,m; tag term (SEnc ( k ,m) ) == 3) ) ;

)

/ / Begin Encodespec (

ispure bytes Encode ( unsigned char ∗bytearray , unsigned long l eng th )requires ( i s a r ray ( bytearray , leng th ) )reads ( se t un iverse ( ) ) ;

/ / ” S t a t e f u l i n j e c t i v i t y ” axiom . . .axiom ( f o r a l l ( s t a t e t s1 , s2 ; unsigned char ∗b1 , b2 ; unsigned long l1 , l 2 ;

i n s t a t e ( s1 , Encode ( b1 , l 1 ) ) == i n s t a t e ( s2 , Encode ( b2 , l 2 ) ) <==>l 1 == l 2 && ( l 1 == l 2 ==> f o r a l l ( unsigned long i ; i < l 1 ==> i n s t a t e ( s1 , b1 [ i ] ) == i n s t a t e ( s2 , b2 [ i ] ) ) ) ) ) ;

ispure bool SameEncoding ( unsigned char ∗b1 , unsigned char ∗b2 , s t a t e t s1 , s t a t e t s2 , unsigned long l )requires ( i n s t a t e ( s1 , i s a r ray ( b1 , l ) ) )requires ( i n s t a t e ( s2 , i s a r ray ( b2 , l ) ) )requires ( f o r a l l ( math in t i ; 0 <= i && i < l ==> i n s t a t e ( s1 , b1 [ i ] ) == i n s t a t e ( s2 , b2 [ i ] ) ) )reads ( se t un iverse ( ) )ensures ( resul t == ( i n s t a t e ( s1 , Encode ( b1 , l ) ) == i n s t a t e ( s2 , Encode ( b2 , l ) ) ) )ensures ( resul t == true ){

return ( i n s t a t e ( s1 , Encode ( b1 , l ) ) == i n s t a t e ( s2 , Encode ( b2 , l ) ) ) ;}

)/ / End Encode

C. RPCdefs.h

#include <vcc . h>

/ / Begin Usagespec (

typedef math in t hmackey usage ;ispure math in t Tag hash usage ( hmackey usage ) ;

/ / Const ruc torsispure hmackey usage KeyAB( term a , term b ) ;

/ / Theoremstheorem ( KeyAB Inject ive , f o r a l l ( term a1 , b1 , a2 , b2 ; KeyAB( a1 , b1 ) == KeyAB( a2 , b2 ) ==> a1 == a2 && b1 == b2 ) ) ; )

spec (typedef math in t usage ;ispure math in t Tag usage ( usage ) ;

/ / Const ruc torsispure usage AttackerGuess ( ) ;ispure usage HmacKey( hmackey usage u ) ;

/ / Theoremstheorem ( HmacKey Injective , f o r a l l ( hmackey usage u1 , u2 ; HmacKey( u1 ) == HmacKey( u2 ) ==> u1 == u2 ) ) ; )

/ / End Usage

spec (typedef math in t senckey usage ;ispure usage SEncKey ( senckey usage u ) ;theorem ( SEncKey Inject ive , f o r a l l ( senckey usage u1 , u2 ; SEncKey ( u1 ) == SEncKey ( u2 ) ==> u1 == u2 ) ) ; )

/ / Begin Log#define s tab le ( F ) old ( F ) ==> F

spec (typedef struct log s {

v o l a t i l e bool New[ term ] [ usage ] ;v o l a t i l e bool Bad [ term ] ;v o l a t i l e bool Request [ term ] [ term ] [ term ] ;v o l a t i l e bool Response [ term ] [ term ] [ term ] [ term ] ;

/ / Misc . cond i t i onsinvar iant ( f o r a l l ( term t ; usage u1 , u2 ;

New[ t ] [ u1 ] && New[ t ] [ u2 ] ==> u1 == u2 ) )invar iant ( f o r a l l ( term t ; usage u ;

New[ t ] [ u ] ==> exists ( bytes b ; t == L i t e r a l ( b ) ) ) )

/ / S t a b i l i t yinvar iant ( f o r a l l ( term t ; usage u ; s tab le (New[ t ] [ u ] ) ) )invar iant ( f o r a l l ( term t ; s tab le (Bad [ t ] ) ) )invar iant ( f o r a l l ( term a , b , s ;

22

Page 23: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

s tab le ( Request [ a ] [ b ] [ s ] ) ) )invar iant ( f o r a l l ( term a , b , s , t ;

s tab le ( Response [ a ] [ b ] [ s ] [ t ] ) ) )} Log ; )

spec ( Log log ; )

#define v a l i d l o g\f o r a l l ( term T ; usage U1,U2;\

log .New[ T ] [ U1 ] && log .New[ T ] [ U2 ] ==> U1 == U2) &&\f o r a l l ( term T ; usage U;\

log .New[ T ] [ U] ==> exists ( bytes B ; T == L i t e r a l (B) ) )

#define s tab le log\f o r a l l ( term T ; usage U; s tab le ( log .New[ T ] [ U ] ) ) &&\f o r a l l ( term T ; s tab le ( log . Bad [ T ] ) ) &&\f o r a l l ( term A,B,S ; s tab le ( log . Request [A ] [ B ] [ S ] ) ) &&\f o r a l l ( term A,B,S, T ; s tab le ( log . Response [A ] [ B ] [ S ] [ T ] ) )

/ / End Log

/ / Begin L o g S t a b i l i t y V a r i a n t s#define c s tab le ( F ) when claimed ( F ) ==> F#define c s tab le log\

f o r a l l ( term T ; usage U; c s tab le ( log .New[ T ] [ U ] ) ) &&\f o r a l l ( term T ; c s tab le ( log . Bad [ T ] ) ) &&\f o r a l l ( term A,B,S ; c s tab le ( log . Request [A ] [ B ] [ S ] ) ) &&\f o r a l l ( term A,B,S, T ; c s tab le ( log . Response [A ] [ B ] [ S ] [ T ] ) )

/ / End L o g S t a b i l i t y V a r i a n t s

#define Hint ( F ) assert ( F )

spec (const bytes tagRequest = 1 ;axiom ( f o r a l l ( s t a t e t s ; unsigned char∗ p ;

i n s t a t e ( s , Encode ( p , 1 ) ) == i n s t a t e ( s , tagRequest ) ==>i n s t a t e ( s , p [ 0 ] ) == ’ 1 ’ ) ) ;

axiom ( f o r a l l ( s t a t e t s ; unsigned char∗ p ;i n s t a t e ( s , p [ 0 ] ) == ’ 1 ’ ==>

i n s t a t e ( s , Encode ( p , 1 ) ) == i n s t a t e ( s , tagRequest ) ) ) ;axiom ( f o r a l l ( s t a t e t s1 , s2 ; i n s t a t e ( s1 , tagRequest ) == i n s t a t e ( s2 , tagRequest ) ) ) ;ispure void Lemma EncodingTagRequest ( unsigned char∗ p )

reads ( array range ( p , 1 ) )requires ( p [ 0 ] == ’ 1 ’ )ensures ( Encode ( p , 1 ) == tagRequest ){

return ;}

const bytes tagResponse = 2;axiom ( f o r a l l ( s t a t e t s ; unsigned char∗ p ;

i n s t a t e ( s , Encode ( p , 1 ) ) == i n s t a t e ( s , tagResponse ) ==>i n s t a t e ( s , p [ 0 ] ) == ’ 2 ’ ) ) ;

axiom ( f o r a l l ( s t a t e t s ; unsigned char∗ p ;i n s t a t e ( s , p [ 0 ] ) == ’ 2 ’ ==>

i n s t a t e ( s , Encode ( p , 1 ) ) == i n s t a t e ( s , tagResponse ) ) ) ;axiom ( f o r a l l ( s t a t e t s1 , s2 ; i n s t a t e ( s1 , tagResponse ) == i n s t a t e ( s2 , tagResponse ) ) ) ;ispure void Lemma EncodingTagResponse ( unsigned char∗ p )

reads ( array range ( p , 1 ) )requires ( p [ 0 ] == ’ 2 ’ )ensures ( Encode ( p , 1 ) == tagResponse ){

return ;}

)

spec (ispure bool Requested ( term M, term S)reads ( se t un iverse ( ) )ensures ( resul t == ( M == Pa i r ( L i t e r a l ( tagRequest ) , S ) ) ) ;

ispure bool Responded ( term M, term S , term T )reads ( se t un iverse ( ) )ensures ( resul t == ( M == Pa i r ( L i t e r a l ( tagResponse ) , Pa i r ( S , T ) ) ) ) ; )

/ / I n j e c t i v i t y theoremstheorem ( Requested Responded Disjoint , f o r a l l ( term v , v1 , s , s1 , t1 ; Requested ( v , s ) && Responded ( v1 , s1 , t1 ) ==> v != v1 ) ) ;theorem ( Requested In ject ive , f o r a l l ( term v , v1 , s , s1 ; Requested ( v , s ) && Requested ( v1 , s1 ) && v==v1 ==> s==s1 ) ) ;theorem ( Responded Inject ive , f o r a l l ( term v , v1 , s , s1 , t , t1 ; Responded ( v , s , t ) && Responded ( v1 , s1 , t1 ) && v==v1 ==> s==s1 && t

== t1 ) ) ;

23

Page 24: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

spec ( ispure bool Bytes ( term t )reads ( se t un iverse ( ) ) ; )

spec ( ispure bool MACSays( term k , term b )reads ( se t un iverse ( ) ) ; )

spec ( ispure bool SEncSays ( term k , term p )reads ( se t un iverse ( ) ) ; )

/ / Begin L o g S t a t e S t a b i l i t y#define s s tab le (S1 , S2 , F )\

i n s t a t e (S1 , F ) ==> i n s t a t e (S2 , F )

#define s s tab le log (S1 , S2)\f o r a l l ( term T ; usage U;\

s s tab le (S1 , S2 , log .New[ T ] [ U ] ) ) &&\f o r a l l ( term T;\

s s tab le (S1 , S2 , log . Bad [ T ] ) ) &&\f o r a l l ( term A,B,S;\

s s tab le (S1 , S2 , log . Request [A ] [ B ] [ S ] ) ) &&\f o r a l l ( term A,B,S, T;\

s s tab le (S1 , S2 , log . Response [A ] [ B ] [ S ] [ T ] ) )/ / End L o g S t a t e S t a b i l i t y

/ / Begin PubDefsspec ( ispure bool Pub ( term t )

reads ( se t un iverse ( ) ) ; )

theorem ( Pub Monotonic ,f o r a l l ( s t a t e t s1 , s2 ; term x ;

s s tab le log ( s1 , s2 ) ==> s s tab le ( s1 , s2 , Pub ( x ) ) ) ) ;

rule ( Pub AttackerGuess ,f o r a l l ( bytes b ;

log .New[ L i t e r a l ( b ) ] [ AttackerGuess ( ) ] ==>Pub ( L i t e r a l ( b ) ) ) ) ;

/ / End PubDefs

rule (Pub HmacKey KeyAB ,f o r a l l ( bytes bk ; term a , b ;

log .New[ L i t e r a l ( bk ) ] [ HmacKey(KeyAB( a , b ) ) ] &&( log . Bad [ a ] | |

l og . Bad [ b ] ) ==>Pub ( L i t e r a l ( bk ) ) ) ) ;

rule ( Pub Pair ,f o r a l l ( term t1 , t2 ;

Pub ( t1 ) && Pub ( t2 ) ==> Pub ( Pa i r ( t1 , t2 ) ) ) ) ;

rule (Pub Hmac ,f o r a l l ( term k ,m;

MACSays( k ,m) &&Bytes ( k ) &&Pub (m) ==>Pub (Hmac( k ,m) ) ) ) ;

rule (Pub Hmac Pub ,f o r a l l ( term k ,m;

Pub ( k ) && Pub (m) ==> Pub (Hmac( k ,m) ) ) ) ;

theorem ( Pair Pub ,f o r a l l ( term t1 , t2 ;

Pub ( Pa i r ( t1 , t2 ) ) ==> Pub ( t1 ) && Pub ( t2 ) ) ) ;/ / End PubDefs

rule ( Pub SEnc ,f o r a l l ( term k , p ;

SEncSays ( k , p ) &&Bytes ( k ) &&Bytes ( p ) ==>Pub (SEnc ( k , p ) ) ) ) ;

rule ( Pub SEnc Pub ,f o r a l l ( term k , p ;

Pub ( k ) &&Pub ( p ) ==>Pub (SEnc ( k , p ) ) ) ) ;

/ / Begin BytesDefs

24

Page 25: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

rule ( By tes L i t e ra l ,f o r a l l ( bytes b ;

Bytes ( L i t e r a l ( b ) ) ) ) ;

rule ( Bytes Pair ,f o r a l l ( term b1 , b2 ;

Bytes ( b1 ) && Bytes ( b2 ) ==>Bytes ( Pa i r ( b1 , b2 ) ) ) ) ;

rule ( Bytes Hmac ,f o r a l l ( term k ,m;

MACSays( k ,m) && Bytes ( k ) && Bytes (m) ==>Bytes (Hmac( k ,m) ) ) ) ;

rule ( Bytes Hmac Pub ,f o r a l l ( term k , b ;

Pub ( k ) && Pub ( b ) ==> Bytes (Hmac( k , b ) ) ) ) ;

theorem ( Bytes Monotonic ,f o r a l l ( s t a t e t s1 , s2 ; term x ;

s s tab le log ( s1 , s2 ) ==>s s tab le ( s1 , s2 , Bytes ( x ) ) ) ) ;

theorem ( Pair Bytes ,f o r a l l ( term t1 , t2 ;

Bytes ( Pa i r ( t1 , t2 ) ) ==>Bytes ( t1 ) && Bytes ( t2 ) ) ) ;

theorem ( Pub Bytes ,f o r a l l ( term t ;

Pub ( t ) ==> Bytes ( t ) ) ) ;/ / End BytesDefs

rule ( Bytes SEnc ,f o r a l l ( term k , p ;

SEncSays ( k , p ) && Bytes ( k ) && Bytes ( p ) ==>Bytes (SEnc ( k , p ) ) ) ) ;

rule ( Bytes SEnc Pub ,f o r a l l ( term k , p ;

Pub ( k ) && Pub ( p ) ==> Bytes (SEnc ( k , p ) ) ) ) ;

/ / Begin MACSaysDefsrule ( MACSays KeyAB Request ,

f o r a l l ( term a , b ,m, k , req ;log .New[ k ] [ HmacKey(KeyAB( a , b ) ) ] &&Requested (m, req ) &&log . Request [ a ] [ b ] [ req ] ==>MACSays( k ,m) ) ) ;

/ / End MACSaysDefsrule (MACSays KeyAB Response ,

f o r a l l ( term a , b ,m, k , req , resp ;log .New[ k ] [ HmacKey(KeyAB( a , b ) ) ] &&Responded (m, req , resp ) &&log . Response [ a ] [ b ] [ req ] [ resp ] ==>

MACSays( k ,m) ) ) ;

theorem ( MACSays Monotonic ,f o r a l l ( s t a t e t s1 , s2 ; term k , b ; s s tab le log ( s1 , s2 ) ==> s s tab le ( s1 , s2 ,MACSays( k , b ) ) ) ) ;

/ / Begin Addit ionalRPCDefstheorem ( KeyAB WeakSecrecy ,

f o r a l l ( term k , a , b ;v a l i d l o g &&log .New[ k ] [ HmacKey(KeyAB( a , b ) ) ] &&Pub ( k ) ==>

log . Bad [ a ] | | log . Bad [ b ] ) ) ;

theorem (MACSays RPC,f o r a l l ( term a , b ,m, k ;

v a l i d l o g && log .New[ k ] [ HmacKey(KeyAB( a , b ) ) ] &&MACSays( k ,m) ==>

( exists ( term s ; Requested (m, s ) &&log . Request [ a ] [ b ] [ s ] ) | |

exists ( term s , t ; Responded (m, s , t ) &&log . Response [ a ] [ b ] [ s ] [ t ] ) ) ) ) ;

theorem (Pub MACSays Pub ,f o r a l l ( term k ,m; Pub (Hmac( k ,m) ) ==>

MACSays( k ,m) | | Pub ( k ) ) ) ;/ / End Addit ionalRPCDefs

25

Page 26: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

/ / End MACSaysDefs

D. table.h

#include <vcc . h>

/ / Begin Tablespec (

typedef vcc ( c la imab le ) struct tab le s {v o l a t i l e bool DefinedB [ bytes ] ;v o l a t i l e term B2T [ bytes ] ;

v o l a t i l e bool DefinedT [ term ] ;v o l a t i l e bytes T2B [ term ] ;

/ / B i j e c t i v i t yinvar iant ( f o r a l l ( bytes b ;

DefinedB [ b ] ==> T2B [ B2T [ b ] ] == b ) )invar iant ( f o r a l l ( term t ;

DefinedT [ t ] ==> B2T [ T2B [ t ] ] == t ) )invar iant ( f o r a l l ( bytes b ;

DefinedB [ b ] ==> DefinedT [ B2T [ b ] ] ) )invar iant ( f o r a l l ( term t ;

DefinedT [ t ] ==> DefinedB [ T2B [ t ] ] ) )

/ / S t a b i l i t yinvar iant ( f o r a l l ( bytes b ;

s tab le ( DefinedB [ b ] ) ) )invar iant ( f o r a l l ( bytes b ;

old ( DefinedB [ b ] ) ==> unchanged (B2T [ b ] ) ) )invar iant ( f o r a l l ( term t ;

s tab le ( DefinedT [ t ] ) ) )invar iant ( f o r a l l ( term t ;

old ( DefinedT [ t ] ) ==> unchanged (T2B [ t ] ) ) )

/ / V a l i d i t yinvar iant ( f o r a l l ( bytes b ;

DefinedT [ L i t e r a l ( b ) ] ==> T2B [ L i t e r a l ( b ) ] == b ) )invar iant ( f o r a l l ( term t ;

DefinedT [ t ] ==> Bytes ( t ) ) )

/ / Ownershipinvar iant ( keeps(& log ) )

} Rep ; )

spec (Rep tab le ; )/ / End Table

#define v a l i d t a b l e\f o r a l l ( bytes B;\

t ab l e . DefinedT [ L i t e r a l (B) ] ==> t ab l e . T2B [ L i t e r a l (B) ] == B)

#define s tab l e t ab l e\f o r a l l ( bytes b ; s tab le ( t ab l e . DefinedB [ b ] ) ) &&\f o r a l l ( bytes b ; old ( t ab l e . DefinedB [ b ] ) ==> unchanged ( t ab l e . B2T [ b ] ) ) &&\f o r a l l ( term t ; s tab le ( t ab l e . DefinedT [ t ] ) ) &&\f o r a l l ( term t ; old ( t ab l e . DefinedT [ t ] ) ==> unchanged ( t ab l e . T2B [ t ] ) )

#define s s tab le tab le ( S1 , S2 )\f o r a l l ( bytes b ; s s tab le ( S1 , S2 , t ab l e . DefinedB [ b ] ) ) &&\f o r a l l ( bytes b ; i n s t a t e ( S1 , t ab l e . DefinedB [ b ] ) ==> ( i n s t a t e ( S1 , t ab l e . B2T [ b ] ) == i n s t a t e ( S2 , t ab l e . B2T [ b ] ) ) ) &&\f o r a l l ( term t ; s s tab le ( S1 , S2 , t ab l e . DefinedT [ t ] ) ) &&\f o r a l l ( term t ; i n s t a t e ( S1 , t ab l e . DefinedT [ t ] ) ==> ( i n s t a t e ( S1 , t ab l e . T2B [ t ] ) == i n s t a t e ( S2 , t ab l e . T2B [ t ] ) ) )

#define c s tab le tab le\f o r a l l ( bytes b ; c s tab le ( t ab l e . DefinedB [ b ] ) ) &&\f o r a l l ( bytes b ; when claimed ( t ab l e . DefinedB [ b ] ) ==> ( when claimed ( t ab l e . B2T [ b ] ) == tab l e . B2T [ b ] ) ) &&\f o r a l l ( term t ; c s tab le ( t ab l e . DefinedT [ t ] ) ) &&\f o r a l l ( term t ; when claimed ( t ab l e . DefinedT [ t ] ) ==> ( when claimed ( t ab l e . T2B [ t ] ) == tab l e . T2B [ t ] ) )

#define f reshCla im ( O)\c la im ( O, c s tab le log && c s tab le tab le )

/ / Begin C o l l i d e#define C o l l i d e ( B , T )\

( ( t ab l e . DefinedB [ B ] && tab le . B2T [ B ] != T ) | |\( t ab l e . DefinedT [ T ] && tab le . T2B [ T ] != B) )

/ / End C o l l i d e

E. hybrids.h

26

Page 27: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

#include <vcc . h>

/ / Begin BytesCtypedef struct bytes s {

unsigned char ∗p t r ;unsigned long l en ;

spec ( bytes encoding ; )invar iant ( keeps ( as array ( p t r , len ) ) )invar iant ( encoding == Encode ( p t r , len ) )

} bytes c ;/ / End BytesC

/ / S t r i n g encodings/ / Begin t o S t r i n gi n t t o S t r i n g ( unsigned char ∗ in , unsigned long i n l , bytes c ∗ res cla imp ( c ) )maintains ( i s t h read loca l a r ray ( in , i n l ) )maintains ( f o r a l l ( math in t i ; 0 <= i && i < i n l ; typed ( i n + i ) ) )ensures ( f o r a l l ( math in t i ; 0 <= i && i < i n l ; unchanged (emb( i n + i ) ) ) )ensures ( f o r a l l ( math in t i ; 0 <= i && i < i n l ; unchanged ( i n [ i ] ) ) )writes ( span ( res ) )ensures ( unchanged (emb( res ) ) )ensures ( resul t ==> mutable ( res ) ) / / f o r f r e e i n g up memory on f a i l u r e ( can be cut out )ensures ( ! resul t ==> wrapped dom ( res ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )ensures ( s tab le log && s tab le tab l e )ensures ( ! resul t ==> t ab l e . DefinedB [ res−>encoding ] )ensures ( ! resul t ==> Pub ( t ab l e . B2T [ res−>encoding ] ) )ensures ( ! resul t ==> res−>encoding == Encode ( in , i n l ) )ensures ( ! resul t ==> t ab l e . B2T [ res−>encoding ] == L i t e r a l ( res−>encoding ) ) ;/ / End t o S t r i n g

/ / Comparisoni n t bytescmp ( bytes c∗ b1 , bytes c∗ b2 claimp ( c ) )maintains ( wrapped ( b1 ) )maintains ( wrapped ( b2 ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )ensures ( ! resul t <==> b1−>encoding == b2−>encoding )ensures ( unchanged ( b1−>encoding ) && unchanged ( b2−>encoding ) ) ;

/ / Concatenat ioni n t p a i r ( bytes c ∗b1 , bytes c ∗b2 , bytes c ∗ res cla imp ( c ) )maintains ( wrapped ( b1 ) )maintains ( wrapped ( b2 ) )writes ( span ( res ) )ensures ( unchanged (emb( res ) ) )ensures ( ! resul t ==> wrapped dom ( res ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )ensures ( s tab le log && s tab le tab l e )requires ( t ab l e . DefinedB [ b1−>encoding ] )requires ( t ab l e . DefinedB [ b2−>encoding ] )ensures ( ! resul t ==> t ab l e . DefinedB [ res−>encoding ] )ensures ( ! resul t ==> t ab l e . B2T [ res−>encoding ] == Pa i r ( t ab l e . B2T [ b1−>encoding ] , t ab l e . B2T [ b2−>encoding ] ) ) ;

i n t des t r uc t ( bytes c ∗ab , bytes c ∗a , bytes c ∗b claimp ( c ) )maintains ( wrapped ( ab ) )ensures ( ! resul t ==> wrapped dom ( a ) )ensures ( ! resul t ==> wrapped dom ( b ) )writes ( span ( a ) )writes ( span ( b ) )ensures ( unchanged (emb( a ) ) )ensures ( unchanged (emb( b ) ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )ensures ( s tab le log && s tab le tab l e )requires ( t ab l e . DefinedB [ ab−>encoding ] )ensures ( ! resul t ==> t ab l e . DefinedB [ a−>encoding ] )ensures ( ! resul t ==> t ab l e . DefinedB [ b−>encoding ] )ensures ( ! resul t ==> t ab l e . B2T [ ab−>encoding ] == Pa i r ( t ab l e . B2T [ a−>encoding ] , t ab l e . B2T [ b−>encoding ] ) ) ;

/ / Key Manipu la t ioni n t Mkeygen ( spec ( hmackey usage hu ) bytes c ∗ res cla imp ( c ) )writes ( span ( res ) )ensures ( unchanged (emb( res ) ) )ensures ( ! resul t ==> wrapped dom ( res ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )ensures ( s tab le log && s tab le tab l e )ensures ( ! resul t ==> t ab l e . DefinedB [ res−>encoding ] )ensures ( ! resul t ==> t ab l e . B2T [ res−>encoding ] == L i t e r a l ( res−>encoding ) )ensures ( ! resul t ==> log .New[ t ab l e . B2T [ res−>encoding ] ] [ HmacKey( hu ) ] ) ;

27

Page 28: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

i n t Skeygen ( spec ( senckey usage su ) bytes c ∗ res cla imp ( c ) )writes ( span ( res ) )ensures ( unchanged (emb( res ) ) )ensures ( ! resul t ==> wrapped dom ( res ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )ensures ( s tab le log && s tab le tab l e )ensures ( ! resul t ==> t ab l e . DefinedB [ res−>encoding ] )ensures ( ! resul t ==> t ab l e . B2T [ res−>encoding ] == L i t e r a l ( res−>encoding ) )ensures ( ! resul t ==> log .New[ t ab l e . B2T [ res−>encoding ] ] [ SEncKey ( su ) ] ) ;

/ / Generate p u b l i c randomnessi n t pub rand ( bytes c ∗ res cla imp ( c ) )writes ( span ( res ) )ensures ( unchanged (emb( res ) ) )ensures ( ! resul t ==> wrapped dom ( res ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )ensures ( s tab le log && s tab le tab l e )ensures ( ! resul t ==> t ab l e . DefinedB [ res−>encoding ] )ensures ( ! resul t ==> t ab l e . B2T [ res−>encoding ] == L i t e r a l ( res−>encoding ) )ensures ( ! resul t ==> log .New[ t ab l e . B2T [ res−>encoding ] ] [ AttackerGuess ( ) ] ) ;

/ / Message Au then t i ca t i on Codes/ / Begin hmaci n t hmacsha1 ( bytes c ∗k , bytes c ∗b , bytes c ∗ res

cla imp ( c ) )/ / S t a b i l i t y o f log and tab le

always ( c , c losed (& tab le ) &&c s tab le log && c s tab le tab le )

ensures ( s tab le log && s tab le tab l e )/ / P rope r t i es o f i npu t byte s t r i n g s

maintains ( wrapped ( k ) )maintains ( wrapped ( b ) )

/ / P rope r t i es o f out parameterwrites ( span ( res ) )ensures ( unchanged (emb( res ) ) )ensures ( ! resul t ==> wrapped dom ( res ) )

/ / Cryptographic con t rac trequires ( t ab l e . DefinedB [ k−>encoding ] )requires ( t ab l e . DefinedB [ b−>encoding ] )ensures ( ! resul t ==> t ab l e . DefinedB [ res−>encoding ] )

/ / Cryptographic p r o p e r t i e s on inpu t termsrequires (

MACSays( t ab l e . B2T [ k−>encoding ] , t ab l e . B2T [ b−>encoding ] )| | (Pub ( t ab l e . B2T [ k−>encoding ] ) &&

Pub ( t ab l e . B2T [ b−>encoding ] ) ) )/ / Cryptographic p r o p e r t i e s on output term

ensures ( ! resul t ==>t ab l e . B2T [ res−>encoding ] ==

Hmac( t ab l e . B2T [ k−>encoding ] , t ab l e . B2T [ b−>encoding ] ) ) ;/ / End hmac

i n t hmacsha1Verify ( bytes c ∗k , bytes c ∗b , bytes c ∗h claimp ( c ) )maintains ( wrapped ( k ) )maintains ( wrapped ( b ) )maintains ( wrapped ( h ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )ensures ( s tab le log && s tab le tab l e )requires ( t ab l e . DefinedB [ h−>encoding ] )requires ( t ab l e . DefinedB [ b−>encoding ] )requires ( t ab l e . DefinedB [ k−>encoding ] )requires ( exists ( hmackey usage hu ; log .New[ tab le . B2T [ k−>encoding ] ] [ HmacKey( hu ) ] ) | | Pub ( t ab l e . B2T [ k−>encoding ] ) )ensures ( resul t ==> t ab l e . B2T [ h−>encoding ] == Hmac( t ab l e . B2T [ k−>encoding ] , t ab l e . B2T [ b−>encoding ] ) ) ;

/ / Non−Authent ica ted Symmetric Encrypt ioni n t senc AES ( bytes c ∗k , bytes c ∗p , bytes c ∗ res cla imp ( c ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )ensures ( s tab le log && s tab le tab l e )maintains ( wrapped ( k ) )maintains ( wrapped ( p ) )writes ( span ( res ) )ensures ( unchanged (emb( res ) ) )ensures ( ! resul t ==> wrapped dom ( res ) )requires ( t ab l e . DefinedB [ k−>encoding ] )requires ( t ab l e . DefinedB [ p−>encoding ] )ensures ( ! resul t ==> t ab l e . DefinedB [ res−>encoding ] )requires ( SEncSays ( t ab l e . B2T [ k−>encoding ] , t ab l e . B2T [ p−>encoding ] ) | | (Pub ( t ab l e . B2T [ k−>encoding ] ) && Pub ( t ab l e . B2T [ p−>

encoding ] ) ) )ensures ( ! resul t ==> t ab l e . B2T [ res−>encoding ] == SEnc ( t ab l e . B2T [ k−>encoding ] , t ab l e . B2T [ p−>encoding ] ) ) ;

i n t sdec AES ( bytes c ∗k , bytes c ∗c i , bytes c ∗ res cla imp ( c ) )

28

Page 29: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )ensures ( s tab le log && s tab le tab l e )maintains ( wrapped ( k ) )maintains ( wrapped ( c i ) )writes ( span ( res ) )ensures ( unchanged (emb( res ) ) )ensures ( ! resul t ==> wrapped dom ( res ) )requires ( t ab l e . DefinedB [ k−>encoding ] )requires ( t ab l e . DefinedB [ c i−>encoding ] )ensures ( ! resul t ==> t ab l e . DefinedB [ res−>encoding ] )requires ( exists ( senckey usage su ; log .New[ tab le . B2T [ k−>encoding ] ] [ SEncKey ( su ) ] ) | | Pub ( t ab l e . B2T [ k−>encoding ] ) )ensures ( ! resul t ==> t ab l e . B2T [ c i−>encoding ] == SEnc ( t ab l e . B2T [ k−>encoding ] , t ab l e . B2T [ res−>encoding ] ) ) ;

/ / Networktypedef struct chan s {

bytes c∗∗ read queue ;i n t∗ r e a d f i r s t ;i n t∗ read number ;

bytes c∗∗ write queue ;i n t∗ w r i t e f i r s t ;i n t∗ write number ;

} channel ;/ / There should be a lock somewhere , but we ’ re not a c t u a l l y p lann ing on running t h i s f o r r e a l anyway . . .

i n t create channel ( channel∗∗ l , channel∗∗ c )writes ( l , c ) ;

i n t channel wr i te ( channel∗ chan , bytes c ∗b claimp ( c ) )maintains ( wrapped ( b ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )ensures ( s tab le log && s tab le tab l e )requires ( t ab l e . DefinedB [ b−>encoding ] )requires (Pub ( t ab l e . B2T [ b−>encoding ] ) ) ;

i n t channel read ( channel∗ chan , bytes c ∗ res cla imp ( c ) )writes ( span ( res ) )ensures ( unchanged (emb( res ) ) )ensures ( ! resul t ==> wrapped dom ( res ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )ensures ( s tab le log && s tab le tab l e )ensures ( ! resul t ==> t ab l e . DefinedB [ res−>encoding ] )ensures ( ! resul t ==> Pub ( t ab l e . B2T [ res−>encoding ] ) )ensures ( ! resul t ==> Bytes ( t ab l e . B2T [ res−>encoding ] ) ) ;

F. hybrids.c

i n t hmacsha1 ( bytes c ∗k , bytes c ∗b , bytes c ∗ rescla imp ( c ) )

{ spec ( term tb , tk , th ; )spec ( bool c o l l i s i o n = fa lse ; )

res−>l en = 20;res−>p t r = mal loc ( res−>l en ) ;i f ( res−>p t r == NULL)

return 1;sha1 hmac ( k−>p t r , unchecked ( ( i n t ) k−>l en ) , b−>p t r , unchecked ( ( i n t ) b−>len ) , res−>p t r ) ;

spec (res−>encoding = Encode ( res−>p t r , res−>len ) ;wrap ( as array ( res−>p t r , res−>len ) ) ;wrap ( res ) ; )

spec (atomic ( c , &tab l e ){

tb = tab le . B2T [ b−>encoding ] ;t k = tab l e . B2T [ k−>encoding ] ;th = Hmac( tk , tb ) ; / / Compute the symbol ic term

i f ( ( t ab l e . DefinedB [ res−>encoding ] &&tab le . B2T [ res−>encoding ] != th ) | |

( t ab l e . DefinedT [ th ] &&tab le . T2B [ th ] != res−>encoding ) )

c o l l i s i o n = true ;else{

t ab l e . DefinedT [ th ] = true ;t ab l e . T2B [ th ] = res−>encoding ;t ab l e . DefinedB [ res−>encoding ] = true ;t ab l e . B2T [ res−>encoding ] = th ;

29

Page 30: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

}})

assume ( ! c o l l i s i o n ) ; / / Our symbol ic c ryp to assumptionreturn 0; }

G. RPCprot.h

#define Event ( C, EPred )\atomic ( C, &tab le , &log ){ EPred = true ; }

/ / Tool f u n c t i o n si n t request ( bytes c ∗payload , bytes c ∗ res cla imp ( c ) )maintains ( wrapped ( payload ) )writes ( span ( res ) )ensures ( unchanged (emb( res ) ) )ensures ( ! resul t ==> wrapped dom ( res ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c )requires ( t ab l e . DefinedB [ payload−>encoding ] )requires (Pub ( t ab l e . B2T [ payload−>encoding ] ) )ensures ( ! resul t ==> t ab l e . DefinedB [ res−>encoding ] )ensures ( ! resul t ==> Pub ( t ab l e . B2T [ res−>encoding ] ) )ensures ( ! resul t ==> Requested ( t ab l e . B2T [ res−>encoding ] , t ab l e . B2T [ payload−>encoding ] ) ) ;

i n t response ( bytes c ∗req , bytes c ∗payload , bytes c ∗ res cla imp ( c ) )maintains ( wrapped ( req ) )maintains ( wrapped ( payload ) )writes ( span ( res ) )ensures ( unchanged (emb( res ) ) )ensures ( ! resul t ==> wrapped dom ( res ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c )requires ( t ab l e . DefinedB [ req−>encoding ] )requires ( t ab l e . DefinedB [ payload−>encoding ] )requires (Pub ( t ab l e . B2T [ req−>encoding ] ) )requires (Pub ( t ab l e . B2T [ payload−>encoding ] ) )ensures ( ! resul t ==> t ab l e . DefinedB [ res−>encoding ] )ensures ( ! resul t ==> Pub ( t ab l e . B2T [ res−>encoding ] ) )ensures ( ! resul t ==> Responded ( t ab l e . B2T [ res−>encoding ] , t ab l e . B2T [ req−>encoding ] , t ab l e . B2T [ payload−>encoding ] ) ) ;

i n t serv i ce ( bytes c ∗req , bytes c ∗ res cla imp ( c ) )maintains ( wrapped ( req ) )writes ( span ( res ) )ensures ( unchanged (emb( res ) ) )ensures ( ! resul t ==> wrapped dom ( res ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c )requires ( t ab l e . DefinedB [ req−>encoding ] )requires (Pub ( t ab l e . B2T [ req−>encoding ] ) )ensures ( ! resul t ==> t ab l e . DefinedB [ res−>encoding ] )ensures ( ! resul t ==> Pub ( t ab l e . B2T [ res−>encoding ] ) ) ;

/ / P ro toco l Rolesvoid c l i e n t ( bytes c ∗a l i ce , bytes c ∗bob , bytes c ∗kab , bytes c ∗req , channel∗ chan claimp ( c ) )maintains ( wrapped ( a l i c e ) )maintains ( wrapped ( bob ) )maintains ( wrapped ( kab ) )maintains ( wrapped ( req ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c )requires ( t ab l e . DefinedB [ a l i ce−>encoding ] )requires ( t ab l e . DefinedB [ bob−>encoding ] )requires ( t ab l e . DefinedB [ kab−>encoding ] )requires ( t ab l e . DefinedB [ req−>encoding ] )requires (Pub ( t ab l e . B2T [ a l i ce−>encoding ] ) )requires (Pub ( t ab l e . B2T [ bob−>encoding ] ) )requires (Pub ( t ab l e . B2T [ req−>encoding ] ) )requires ( Bytes ( t ab l e . B2T [ kab−>encoding ] ) )requires ( log .New[ t ab l e . B2T [ kab−>encoding ] ] [ HmacKey(KeyAB( tab le . B2T [ a l i ce−>encoding ] , t ab l e . B2T [ bob−>encoding ] ) ) ] ) ;

H. RPCprot.c

i n t request ( bytes c ∗payload , bytes c ∗ res cla imp ( c ) ){

bytes c ∗ tag ;unsigned char ∗st rTag = ( unsigned char∗) ” 1 ” ; assume ( s t rTag [ 0 ] == ’ 1 ’ ) ;spec ( s t a t e t s ; )

Hint ( i s a r ray ( strTag , 1 ) ) ;spec ( Lemma EncodingTagRequest ( s t rTag ) ; )

30

Page 31: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

Hint ( Encode ( strTag , 1 ) == tagRequest ) ;

i f ( res == NULL)return 1;

Hint ( Encode ( strTag , 1 ) == tagRequest ) ;

spec ( s = c u r r e n t s t a t e ( ) ; )i f ( ( tag = mal loc ( sizeof (∗ tag ) ) ) == NULL)

return 1;Hint ( SameEncoding ( strTag , strTag , s , c u r r e n t s t a t e ( ) , 1) ) ;Hint ( Encode ( strTag , 1 ) == tagRequest ) ;

i f ( t o S t r i n g ( strTag , 1 , tag spec ( f reshCla im ( c ) ) ) )return 1;

Hint ( SameEncoding ( strTag , strTag , s , c u r r e n t s t a t e ( ) , 1) ) ;Hint ( tag−>encoding == tagRequest ) ;

i f ( p a i r ( tag , payload , res spec ( f reshCla im ( c ) ) ) ){

spec ( unwrap ( tag ) ; )f r ee ( tag ) ;return 1;

}Hint ( t ab l e . B2T [ res−>encoding ] == Pa i r ( t ab l e . B2T [ tagRequest ] , t ab l e . B2T [ payload−>encoding ] ) ) ;

return 0;}

i n t response ( bytes c ∗ request , bytes c ∗payload , bytes c ∗ res cla imp ( c ) ){

bytes c ∗ tag , ∗ i n te rm ;unsigned char ∗st rTag ;spec ( s t a t e t s ; )

s t rTag = ( unsigned char∗) ” 2 ” ; assume ( s t rTag [ 0 ] == ’ 2 ’ ) ;

Hint ( i s a r ray ( strTag , 1 ) ) ;spec ( Lemma EncodingTagResponse ( st rTag ) ; )Hint ( Encode ( strTag , 1 ) == tagResponse ) ;

i f ( res == NULL)return 1;

spec ( s = c u r r e n t s t a t e ( ) ; )i f ( ( tag = mal loc ( sizeof (∗ tag ) ) ) == NULL)

return 1;i f ( ( in term = mal loc ( sizeof (∗ i n te rm ) ) ) == NULL)

return 1;Hint ( SameEncoding ( strTag , strTag , s , c u r r e n t s t a t e ( ) , 1) ) ;

i f ( t o S t r i n g ( strTag , 1 , tag spec ( f reshCla im ( c ) ) ) )return 1;

Hint ( SameEncoding ( strTag , strTag , s , c u r r e n t s t a t e ( ) , 1) ) ;Hint ( tag−>encoding == tagResponse ) ;

i f ( p a i r ( request , payload , in term spec ( f reshCla im ( c ) ) ) ){

spec ( unwrap ( tag ) ; )f r ee ( tag ) ;return 1;

}Hint ( t ab l e . B2T [ interm−>encoding ] == Pa i r ( t ab l e . B2T [ request−>encoding ] , t ab l e . B2T [ payload−>encoding ] ) ) ;Hint ( tag−>encoding == tagResponse ) ;

i f ( p a i r ( tag , interm , res spec ( f reshCla im ( c ) ) ) ){

spec ( unwrap ( tag ) ; )f r ee ( tag ) ;spec ( unwrap ( in te rm ) ; )f r ee ( in term ) ;return 1;

}Hint ( tag−>encoding == tagResponse ) ;Hint ( t ab l e . B2T [ interm−>encoding ] == Pa i r ( t ab l e . B2T [ request−>encoding ] , t ab l e . B2T [ payload−>encoding ] ) ) ;Hint ( t ab l e . B2T [ res−>encoding ] == Pa i r ( t ab l e . B2T [ tag−>encoding ] , t ab l e . B2T [ interm−>encoding ] ) ) ;

return 0;}

void c l i e n t ( bytes c ∗a l i ce , bytes c ∗bob , bytes c ∗kab , bytes c ∗req , channel∗ chan claimp ( c ) )

31

Page 32: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

{spec ( c la im t tmp ; )bytes c ∗toMAC1 , ∗mac1 , ∗msg1 ;bytes c ∗msg2 , ∗resp , ∗toMAC2 , ∗mac2 ;

spec (tmp = freshCla im ( c ) ;Event ( tmp , log . Request [ t ab l e . B2T [ a l i ce−>encoding ] ] [ t ab l e . B2T [ bob−>encoding ] ] [ t ab l e . B2T [ req−>encoding ] ] ) ; )

i f ( ( toMAC1 = mal loc ( sizeof (∗ toMAC1) ) ) == NULL)return ;

i f ( request ( req , toMAC1 spec ( f reshCla im ( c ) ) ) )return ;

Hint ( log .New[ t ab l e . B2T [ kab−>encoding ] ] [ HmacKey(KeyAB( tab l e . B2T [ a l i ce−>encoding ] , t ab l e . B2T [ bob−>encoding ] ) ) ] ) ;Hint ( Requested ( t ab l e . B2T [ toMAC1−>encoding ] , t ab l e . B2T [ req−>encoding ] ) ) ;Hint ( log . Request [ t ab l e . B2T [ a l i ce−>encoding ] ] [ t ab l e . B2T [ bob−>encoding ] ] [ t ab l e . B2T [ req−>encoding ] ] ) ;Hint (MACSays( t ab l e . B2T [ kab−>encoding ] , t ab l e . B2T [ toMAC1−>encoding ] ) ) ;

i f ( ( mac1 = mal loc ( sizeof (∗mac1) ) ) == NULL)return ;

i f ( hmacsha1 ( kab , toMAC1 , mac1 spec ( f reshCla im ( c ) ) ) )return ;

i f ( ( msg1 = mal loc ( sizeof (∗msg1) ) ) == NULL)return ;

i f ( p a i r ( req , mac1 , msg1 spec ( f reshCla im ( c ) ) ) )return ;

i f ( channel wr i te ( chan , msg1 spec ( f reshCla im ( c ) ) ) )return ;

i f ( ( msg2 = mal loc ( sizeof (∗msg2) ) ) == NULL)return ;

i f ( channel read ( chan , msg2 spec ( f reshCla im ( c ) ) ) )return ;

i f ( ( resp = mal loc ( sizeof (∗ resp ) ) ) == NULL)return ;

i f ( ( mac2 = mal loc ( sizeof (∗mac2) ) ) == NULL)return ;

i f ( des t r uc t (msg2 , resp , mac2 spec ( f reshCla im ( c ) ) ) )return ;

i f ( ( toMAC2 = mal loc ( sizeof (∗ toMAC2) ) ) == NULL)return ;

i f ( response ( req , resp , toMAC2 spec ( f reshCla im ( c ) ) ) )return ;

Hint ( Responded ( t ab l e . B2T [ toMAC2−>encoding ] , t ab l e . B2T [ req−>encoding ] , t ab l e . B2T [ resp−>encoding ] ) ) ;

i f ( ! hmacsha1Verify ( kab , toMAC2 , mac2 spec ( f reshCla im ( c ) ) ) )return ;

Hint (MACSays( t ab l e . B2T [ kab−>encoding ] , t ab l e . B2T [ toMAC2−>encoding ] ) | |Pub ( t ab l e . B2T [ kab−>encoding ] ) ) ;

Hint ( log .New[ t ab l e . B2T [ kab−>encoding ] ] [ HmacKey(KeyAB( tab l e . B2T [ a l i ce−>encoding ] , t ab l e . B2T [ bob−>encoding ] ) ) ] ) ;Hint ( Responded ( t ab l e . B2T [ toMAC2−>encoding ] , t ab l e . B2T [ req−>encoding ] , t ab l e . B2T [ resp−>encoding ] ) ) ;

Hint ( i nv (& log ) ) ;assert ( log . Response [ t ab l e . B2T [ a l i ce−>encoding ] ] [ t ab l e . B2T [ bob−>encoding ] ] [ t ab l e . B2T [ req−>encoding ] ] [ t ab l e . B2T [ resp−>

encoding ] ] | |Pub ( t ab l e . B2T [ kab−>encoding ] ) ) ;

assert ( log . Response [ t ab l e . B2T [ a l i ce−>encoding ] ] [ t ab l e . B2T [ bob−>encoding ] ] [ t ab l e . B2T [ req−>encoding ] ] [ t ab l e . B2T [ resp−>encoding ] ] | |

log . Bad [ t ab l e . B2T [ a l i ce−>encoding ] ] | | l og . Bad [ t ab l e . B2T [ bob−>encoding ] ] ) ;}

I. RPCshim.h

#include <vcc . h>

#include ” RPCprot . h ”

/ / Begin RPCInit#define i n i t ( . . . ) \

spec (\/∗ Log ∗ /\log .New = lambda ( term t ;\

lambda ( usage u ; fa lse ) ) ;\log . Request = lambda ( term a ;\

lambda ( term b ;\

32

Page 33: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

lambda ( term s ; fa lse ) ) ) ;\log . Response = lambda ( term a ;\

lambda ( term b ;\lambda ( term s ;\

lambda ( term t ; fa lse ) ) ) ) ;\wrap(& log ) ;\/∗ Representat ion tab l e . I t i n i t i a l l y conta ins tagRequest and tagResponse . ∗ /\t ab l e . DefinedB =\

lambda ( bytes b ; b == tagRequest\| | b == tagResponse ) ;\

t ab l e . B2T [ tagRequest ] = L i t e r a l ( tagRequest ) ;\t ab l e . B2T [ tagResponse ] = L i t e r a l ( tagResponse ) ;\t ab l e . DefinedT =\

lambda ( term t ; t == L i t e r a l ( tagRequest )\| | t == L i t e r a l ( tagResponse ) ) ;\

t ab l e . T2B [ L i t e r a l ( tagRequest ) ] = tagRequest ;\t ab l e . T2B [ L i t e r a l ( tagResponse ) ] = tagResponse ;\wrap(& tab l e ) ;\c = cla im (& tab le , c losed (& tab le ) &&\

c s tab le log && c s tab le tab le ) ; )/ / End RPCInit

/ / Begin Genera lA t tack In te r face/ / Begin Genera lA t tack In ter faceTypedeftypedef struct{

bytes c∗ bp ;spec ( c la im t c ; )

invar iant ( keeps ( c ) )invar iant ( keeps ( bp ) )invar iant ( c la ims ( c , c losed (& tab le ) ) )invar iant ( va l i d c l a im ( c ) ==> t ab l e . DefinedB [ bp−>encoding ] && Pub ( t ab l e . B2T [ bp−>encoding ] ) )

} bytespub ;/ / End Genera lAt tack In ter faceTypedef

/ / Begin Genera lAt tackInter faceToBytespubbytespub∗ at t toBytespub ( unsigned char∗ p t r , unsigned long l en spec ( c la im t c ) )maintains ( i s t h read loca l a r ray ( p t r , len ) )maintains ( f o r a l l ( math in t i ; 0 <= i && i < l en ; typed ( p t r + i ) ) )ensures ( f o r a l l ( math in t i ; 0 <= i && i < len ; unchanged (emb( p t r + i ) ) ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c )ensures ( wrapped ( resul t ) ) ;/ / End Genera lAt tackInter faceToBytespub

bytespub∗ a t t p a i r ( bytespub∗ b1 , bytespub∗ b2 claimp ( c ) )maintains ( wrapped ( b1 ) )maintains ( wrapped ( b2 ) )writes ( b1 , b2 )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c )ensures ( wrapped ( resul t ) ) ;

bytespub∗ a t t f s t ( bytespub∗ b claimp ( c ) )maintains ( wrapped ( b ) )writes ( b )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c )ensures ( wrapped ( resul t ) ) ;

bytespub∗ at t snd ( bytespub∗ b claimp ( c ) )maintains ( wrapped ( b ) )writes ( b )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c )ensures ( wrapped ( resul t ) ) ;

/ / Begin GeneralAt tackInter faceHmacbytespub∗ att hmacsha1 ( bytespub∗ k , bytespub∗ b claimp ( c ) )maintains ( wrapped ( k ) )maintains ( wrapped ( b ) )writes ( k , b )always ( c , c losed (& tab le )&&c s tab le log&&c s tab le tab le )writes ( c )ensures ( wrapped ( resul t ) ) ;/ / End GeneralAt tackInter faceHmac

bool att hmacsha1Ver i fy ( bytespub∗ k , bytespub∗ b , bytespub∗ m claimp ( c ) )maintains ( wrapped ( k ) )

33

Page 34: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

maintains ( wrapped ( b ) )maintains ( wrapped (m) )writes ( k , b ,m)always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c ) ;

void a t t channe l wr i t e ( channel∗ chan , bytespub∗ b claimp ( c ) )maintains ( wrapped ( b ) )writes ( b )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c ) ;

bytespub∗ at t channel read ( channel∗ chan claimp ( c ) )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c )ensures ( wrapped ( resul t ) ) ;/ / End Genera lA t tack In te r face

/ / Begin RPCSpec i f i cA t tack In te r facetypedef struct{

bytes c∗ c l i e n t ;bytes c∗ server ;bytes c∗ key ;channel∗ c chan ;channel∗ att c chan ;channel∗ s chan ;channel∗ att s chan ;spec ( c la im t c ; )

invar iant ( keeps ( c l i e n t ) )invar iant ( keeps ( server ) )invar iant ( keeps ( key ) )invar iant ( keeps ( c ) )invar iant ( c la ims ( c , c losed (& tab le ) ) )invar iant ( va l i d c l a im ( c ) ==>

t ab l e . DefinedB [ c l i e n t−>encoding ]&& tab le . DefinedB [ server−>encoding ]&& tab le . DefinedB [ key−>encoding ]&& Pub ( t ab l e . B2T [ c l i e n t−>encoding ] )&& Pub ( t ab l e . B2T [ server−>encoding ] )&& tab le . B2T [ key−>encoding ] == L i t e r a l ( key−>encoding )&& log .New[ L i t e r a l ( key−>encoding ) ] [ HmacKey(KeyAB( tab le . B2T [ c l i e n t−>encoding ] , t ab l e . B2T [ server−>encoding ] ) )

] )} session ;

session∗ a t t se tup ( bytespub∗ c l , bytespub∗ se claimp ( c ) )maintains ( wrapped ( c l ) )maintains ( wrapped ( se ) )writes ( c l , se )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c )ensures ( wrapped ( resul t ) ) ;

void a t t r u n c l i e n t ( session∗ s , bytespub∗ request c la imp ( c ) )maintains ( wrapped ( s ) )maintains ( wrapped ( request ) )writes ( s , request )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c ) ;

void a t t run serve r ( session∗ s claimp ( c ) )maintains ( wrapped ( s ) )writes ( s )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c ) ;

bytespub∗ at t compromise c l ien t ( session∗s claimp ( c ) )maintains ( wrapped ( s ) )writes ( s )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c )ensures ( wrapped ( resul t ) ) ;

bytespub∗ att compromise server ( session∗s claimp ( c ) )maintains ( wrapped ( s ) )writes ( s )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c )ensures ( wrapped ( resul t ) ) ;

34

Page 35: Guiding a General-Purpose C Verifier to Prove ... · Guiding a General-Purpose C Verifier to Prove Cryptographic Protocols Franc¸ois Dupressoir The Open University Andrew D. Gordon

channel∗ a t t ge tChanne l c l i en t ( session∗ s claimp ( c ) )maintains ( wrapped ( s ) )writes ( s )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c ) ;

channel∗ at t getChannel server ( session∗ s claimp ( c ) )maintains ( wrapped ( s ) )writes ( s )always ( c , c losed (& tab le ) && c s tab le log && c s tab le tab le )writes ( c ) ;/ / End RPCSpec i f i cA t tack In te r face

/∗/ / Begin EraseAt tack In te r facetypedef bytespub ;

bytespub∗ at t toBytespub ( unsigned char∗ p t r ,unsigned long len ) ;

bytespub∗ a t t p a i r ( bytespub∗ b1 , bytespub∗ b2 ) ;bytespub∗ a t t f s t ( bytespub∗ b ) ;bytespub∗ at t snd ( bytespub∗ b ) ;

bytespub∗ att hmacsha1 ( bytespub∗ k , bytespub∗ b ) ;bool at t hmacsha1Ver i fy ( bytespub∗ k ,

bytespub∗ b ,bytespub∗ m) ;

vo id a t t channe l wr i t e ( channel∗ chan , bytespub∗ b ) ;bytespub∗ at t channel read ( channel∗ chan ) ;

typedef session ;

session∗ a t t se tup ( bytespub∗ c l , bytespub∗ se ) ;

vo id a t t r u n c l i e n t ( session∗ s , bytespub∗ request ) ;vo id a t t run serve r ( session∗ s ) ;

bytespub∗ at t compromise c l ien t ( session∗s ) ;bytespub∗ att compromise server ( session∗s ) ;

channel∗ a t t ge tChanne l c l i en t ( session∗ s ) ;channel∗ at t getChannel server ( session∗ s ) ;/ / End EraseAt tack In te r face∗ /

35