Refinement Types for Secure Implementations
Total Page:16
File Type:pdf, Size:1020Kb
Refinement Types for Secure Implementations Jesper Bengtson Karthikeyan Bhargavan Cedric´ Fournet Andrew D. Gordon Uppsala University Microsoft Research Microsoft Research Microsoft Research Sergio Maffeis Imperial College London and University of California at Santa Cruz Abstract automatically extract models from F# code and, after We present the design and implementation of a typechecker for applying various program transformations, pass them to verifying security properties of the source code of cryptographic ProVerif, a cryptographic analyzer [Blanchet, 2001, Abadi protocols and access control mechanisms. The underlying type and Blanchet, 2005]. Their approach yields verified secu- theory is a l-calculus equipped with refinement types for express- rity for very detailed models, but also demands considerable ing pre- and post-conditions within first-order logic. We derive care in programming, in order to control the complexity of formal cryptographic primitives and represent active adversaries global cryptographic analysis for giant protocols. Even if within the type theory. Well-typed programs enjoy assertion-based ProVerif scales up remarkably well in practice, beyond a security properties, with respect to a realistic threat model includ- few message exchanges, or a few hundred lines of F#, veri- ing key compromise. The implementation amounts to an enhanced fication becomes long (up to a few days) and unpredictable typechecker for the general purpose functional language F#; type- checking generates verification conditions that are passed to an (with trivial code changes leading to divergence). SMT solver. We describe a series of checked examples. This is the first tool to verify authentication properties of cryptographic Cryptographic Verification meets Program Verification protocols by typechecking their source code. In parallel with specialist tools for cryptography, verifica- tion tools in general are also making rapid progress, and can deal with much larger programs [see for example Flana- 1 Introduction gan et al., 2002, Filliatre,ˆ 2003, Barnett et al., 2005, Pot- tier and Regis-Gianas,´ 2007]. To verify the security of The goal of this work is to verify the security of imple- programs with some cryptography, we would like to com- mentation code by typing. Here we are concerned particu- bine both kinds of tools. However, this integration is del- larly with authentication and authorization properties. icate: the underlying assumptions of cryptographic mod- We develop an extended typechecker for code written els to account for active adversaries typically differ from in F# (a variant of ML) [Syme et al., 2007] and annotated those made for general-purpose program verification. On with refinement types that embed logical formulas. We use the other hand, modern applications involve a large amount these dependent types to specify access-control and crypto- of (non-cryptographic) code and extensive libraries, some- graphic properties, as well as desired security goals. Type- times already verified; we’d rather benefit from this effort. checking then ensures that the code is secure. We evaluate our approach on code implementing autho- Authorization by Typing Logic is now a well established rization decisions and on reference implementations of se- tool for expressing and reasoning about authorization poli- curity protocols. Our typechecker verifies security proper- cies. Although many systems rely on dynamic authorization ties for a realistic threat model that includes a symbolic at- engines that evaluate logical queries against local stores of tacker, in the style of Dolev and Yao [1983], who is able facts and rules, it is sometimes possible to enforce policies to create arbitrarily many principals, create arbitrarily many statically. Thus, Fournet et al. [2007a,b] treat policy en- instances of each protocol roles, send and receive network forcement as a type discipline; they develop their approach traffic, and compromise arbitrarily many principals. for typed pi calculi, supplemented with cryptographic prim- Verifying Cryptographic Implementations In earlier itives. Relying on a “says” modality in the logic, they also work, Bhargavan et al. [2007] advocate the cryptographic account for partial trust (in logic specification) in the face of verification of reference implementations of protocols, partial compromise (in their implementations). The present rather than their handwritten models, in order to mini- work is an attempt to develop, apply, and evaluate this ap- mize the gap between executable and verified code. They proach for a general-purpose programming language. Outline of the Implementation Our prototype tool takes Our F# code is written in a functional style, so pre- and as input module interfaces (similar to F# module interfaces post-conditions concern data values and events represented but with extended types) and module implementations (in by logical formulas; our type system does not (and need not plain F#). It typechecks implementations against interfaces, for our purposes) directly support reasoning about mutable and also generates plain F# interfaces by erasure. Using the state, such as heap-allocated structures. F# compiler, generated interfaces and verified implementa- tions can then be compiled as usual. Contributions First, we formalize our approach within Our tool performs typechecking and partial type infer- a typed concurrent l-calculus. We develop a type system ence, relying on an external theorem prover for discharging with refinement types that carry logical formulas, building the logical conditions generated by typing. We currently use on standard techniques for dependent types, and establish plain first-order logic (rather than an authorization-specific its soundness. logic) and delegate its proofs to Z3 [de Moura and Bjørner, Second, we adapt our type system to account for active 2008], a solver for Satisfiability Modulo Theories (SMT). (untyped) adversaries, by extending subtyping so that all Thus, in comparison with previous work, we still rely on an values manipulated by the adversary can be given a spe- external prover, but this prover is being developed for gen- cial universal type (Un). Our calculus has no built-in cryp- eral program verification, not for cryptography; also, we use tographic primitives. Instead, we show how a wide range this prover locally, to discharge proof obligations at various of cryptographic primitives can be coded (and typed) in program locations, rather than rely on a global translation the calculus, using a seal abstraction, in a generalization of to a cryptographic model. the symbolic Dolev-Yao model. The corresponding robust Reflecting our assumptions on cryptography and other safety properties then follow as a corollary of type safety. system libraries, some modules have two implementations: Third, experimentally, we implement our approach as an a symbolic implementation used for extended typing and extension of F#, and develop a new typechecker (with par- symbolic execution, and a concrete implementation used tial type inference) based on Z3 (a fast, incomplete, first- for plain typing and distributed execution. We have ac- order logic prover). cess to a collection of F# test programs already analyzed us- Fourth, we evaluate our approach on a series of program- ing dual implementations of cryptography [Bhargavan et al., ming examples, involving authentication and authorization 2007], so we can compare our new approach to prior work properties of protocols and applications; this indicates that on model extraction to ProVerif. Unlike ProVerif, type- our use of refinement types is an interesting alternative to checking requires annotations that include pre- and post- global verification tools for cryptography, especially for the conditions. On the other hand, these annotations can ex- verification of executable reference implementations. press general authorization policies, and their use makes An online technical report provides details, proofs, and typechecking more compositional and predictable than the examples omitted from this version of the paper. global analysis performed by ProVerif. Moreover, type- checking succeeds even on code involving recursion and 2 A Language with Refinement Types complex data structures. Our calculus is an assembly of standard parts: call- Outline of the Theory We justify our extended type- by-value dependent functions, dependent pairs, sums, iso- checker by developing a formal type theory for a core of F#: recursive types, message-passing concurrency, refinement a concurrent call-by-value l-calculus. types, subtyping, and a universal type Un to model at- To represent pre- and post-conditions, our calculus has tacker knowledge. This is essentially the Fixpoint Calculus standard dependent types and pairs, and a form of refine- (FPC) [Gunter, 1992], augmented with concurrency and re- ment types [Freeman and Pfenning, 1991, Xi and Pfenning, finement types. Hence, we adopt the name Refined Concur- 1999]. A refinement type takes the form fx : T j Cg; a rent FPC, or RCF for short. This section introduces its syn- value M of this type is a value of type T such that the for- tax, semantics, and type system (apart from Un), together mula CfM=xg holds. (Another name for the construction is with an example application. Section 3 introduces Un and predicate subtyping [Rushby et al., 1998]; fx : T j Cg is the applications to cryptographic protocols. (Any ambiguities subtype of T characterized by the predicate C.) in the informal presentation should be clarified by