200 likes | 332 Views
Proving Computational Security of C Programs using VCC (and F7). FCC/ASA Harvard University - 28 th June 2012 F. Dupressoir (Open University & MSRC) C. Fournet (MSRC) A. Gordon (MSRC & University of Edinburgh). Cryptographic Code in C.
E N D
Proving Computational Security of C Programs using VCC (and F7) FCC/ASAHarvard University - 28th June 2012 F. Dupressoir (Open University & MSRC)C. Fournet (MSRC)A. Gordon (MSRC & University of Edinburgh)
Cryptographic Code in C • The security of much criticalinfrastructure depends in part oncrypto code in C, yet vulnerabilitiescontinue to be found. • Designers starting to appreciate reference implementations • eg, TCG: “It has taken years to achieve good TPM interoperability”, need for “more precise language for describing TPM data structures and behaviors” • New TPM standard will come with a reference implementation in C
Prior Tools for Cryptographic Code Also, [O’Shea], [Pironti, Sisto et al.], [Backes, Hritcu et al.], [Jürjens et al.]…
Create(1001<id0>14) = 0001Create(1000<id1>14) = 0002Export(02) = 00<template1>14<key1>16Export(01) = FECreate(1001<id2>14) = FFEncrypt(01<plaintext0>16) = 00<ciphertext0>(16+16+20)Unload(01) = 00<blob0>(16+16+16+20)Clear(01) = 00Create(1001<id3>14) = 0001Decrypt(01<ciphertext0>(16+16+20)) = FBClear(03) = 00Load(<blob0>(16+16+16+20)) = 0003 Decrypt(03<ciphertext0>(16+16+20)) = 00<plaintext0>16 The Device
The Device Input/Output Buffer • Buffer to write in parameters and read out results • Create: template → handle • Uses a KDF • Import: template * bytes → handle • Export: handle → template * key • Clear: handle → unit • Unload: handle → bytes • Uses Encrypt-then-MAC • Load: bytes → handle • Encrypt/Decrypt: handle * bytes → bytes Internal State Primary Seed Root Storage Key Slot 0 Slot N Template … Template Key Key Slot i Template Key
Security • Ideal Functionality-Based Assumptions • Concrete KDF is indistinguishable from ideal PRF • Concrete (Encode-then-)Encrypt-then-MAC is indistinguishable from ideal Authenticated Encryption • Ideal Functionality-Based Theorem:Device is indistinguishable from an ideal functionality with the following features: • When the object involved is not sensitive, run the C code • Create samples a key and stores it in a table, marks an empty slot as taken, stores the template, and returns the chosen index • Import/Export fail with “wrong attributes” error • Unload/Load, Encrypt/Decrypt encrypt zeroes; use a table • Clear marks the selected handle as empty
Challenges • Probabilistic C Programs • Tool usability, maintaining probabilistic features • Probabilistic VS under-specified behaviours • Equivalence-Based Properties in C • F7: perfect secrecy approximated by parametricity • C: memory reused without being cleared • Clear sets a bit to 0, but may not overwrite the slot • Slots shared by sensitive and public objects with variable size • Fine-grained information-flow is hard for C • Stack pointer may depend on secret • Unspecified behaviours…
Total Functional Correctness andSpecification Security • VCC can prove functional correctness • Soundly deals with unspecified behaviours • Can prove termination • F7 can prove computational security • VCC and F7 have a coinciding core: • Pure inductive VCC specifications • First-order deterministic F7 expressions • Probabilistic F7 modelled by derandomizing
The Method in a Picture Reference (Pure) F7game-hopping Concrete F# Implementation Ideal F# Implementation VCCannotations VCCannotations Language Concrete C Implementation Ideal C Implementation System (Imperative) Cryptography Concrete Ideal
Going Through the Steps F7game-hopping Concrete F# Implementation Ideal F# Implementation • Type-checking the concrete code to prove that game switching steps can be applied ( is a concrete bound) • Unload/Load encryption (+ formatting) becomes ideal AE • KDF becomes 2 lazy-sampling RFs (sensitive/non-sensitive) • Encrypt/Decrypt encryption becomes ideal AE • by inlining crypto primitives • More complex examples may need more complex proofs • See Cédric’s ideal interface for TLS • secure by standard type-checking
Going Through the Steps Concrete F# Implementation • Assume that computes the same functions as • Observation function may need to be fine-tuned to crypto and device code • Write VCC contracts expressing observational equivalence • Observation function should fit the adversary model • Fiddle with VCC until success • Some work for loops/recursion • Unexplored potential for modularity VCCannotations Concrete C Implementation
Going Through the Steps Ideal F# Implementation • Do the same on the other side • Same contracts can be used: • VCC gives function-level abstraction • A single run of VCC proves both vertical sides of the square • On observation functions • At adversary interface, capture everything the adversary can do • Internally, can be very vague: any unspecified behaviour causes verification failure at some levelif it flows into the final output VCCannotations Ideal C Implementation
Going Through the Steps F7game-hopping Concrete F# Implementation Ideal F# Implementation VCCannotations VCCannotations Concrete C Implementation Ideal C Implementation
Defining and Proving Simulation as a Contract
Assumptions • Assume existence of reference KDF In practice, slightly more verbose and distant from F7 _(abstract (\kdfState*\key) KDF_R(\kdfStateS, \bytes t, \seed s)) • Assume the C implementation is equivalent (up to some observation) void _(assume_correct) KDF(BYTE* t, UINT8 t_len, _(ghost \template tmpl) BYTE* s, UINT8 s_len, _(ghost \seed seed) BYTE* buf, UINT8* buf_len _(ghost out \key key)) _(decreases 0) // Termination _(maintains \thread_local_array(t, t_len)) // Memory Safety _(maintains \thread_local_array(s, s_len)) // Memory Safety _(ensures \mutable_array(buf, *buf_len)) // Memory Safety _(writes \array_range(buf,(size_t) keylength(tmpl)), buf_len) // Writes the buffer and the length // Simulation on inputs _(requires observeState(state) == refState) _(requires from_array(t,t_len) == tmpl && from_array(s, s_len) == seedRepr(seed)) // Simulation on outputs _(ensures observeState(state) == refState) _(ensures from_array(buf,*buf_len) == keyRepr(tmpl,key)) _(ensures \old(KDF_R(refState,tmpl,seed)) == (refState,key));
Theorems • Write reference implementation in VCC _(def(\state*\iobuffer) Create_R((\state*\iobuffer) in) { switch (unmarshal_Create_IN_R(snd(in))) { case None(): return (fst(in), cons(RC_UNMARSHAL, substring(snd(in),1,IOBLEN - 1)) }; case Some(tmpl): Return<(\state*\handle)> res = Create_cmd_R(fst(in),tmpl); switch (res) { case Error(rc): return (fst(in), cons(rc,substring(snd(in),1,IOBLEN - 1))); case Success(S,h): return (S, cons(RC_SUCCESS,cons(h,substring(snd(in),2,IOBLEN - 2))); } } }) • Prove the C implementation is equivalent(up to adversary observations) void Create(void) _(decreases 0) // Termination _(writes \array_range(IOB,IOBLEN)) // Writes clause _(ensures \mutable_array(IOB,IOBLEN)) // Memory-safety _(maintains observe(store) == store.S) // State simulation invariant // Output simulation _(ensures \old(Create_R(store.S,from_array(IOB,IOBLEN))) == (store.S, from_array(IOB,IOBLEN)));
Theorems • Translate reference implementation from F7 let Create_R (S: state * buf: iobuffer): state * iobuffer = match (unmarshal_Create_IN_R(buf)) | None() -> (S, cons RC_UNMARSHAL (substring buf 1 (IOBLEN - 1))) | Some(tmpl) -> match Create_cmd_R S tmplwith | Error(rc) ->(S, cons rc (substring buf 1 (IOBLEN - 1))) | Success(S,h) -> (S, cons RC_SUCCESS (cons h (substring buf 2 (IOBLEN - 2)))) • Prove the C implementation is equivalent(up to adversary observation) void Create(void) _(decreases 0) // Termination _(writes \array_range(IOB,IOBLEN)) // Writes clause _(ensures \mutable_array(IOB,IOBLEN)) // Memory-safety _(maintains observe(store) == store.S) // State simulation invariant // Output simulation _(ensures \old(Create_R(store.S,from_array(IOB,IOBLEN))) == (store.S, from_array(IOB,IOBLEN)));
Conclusion • You can prove computational security of C programs using existing tools • And a lot of work: specification needs to be more formal than just the C code • Improve C verification tools? • Add probabilities and relational contracts • Politely ask for formal specifications?
Preliminary Conclusions on the TPM • Need automation (F7 to VCC, memory safety) • Very tedious work, concerning generated code • Why not generate it from a formal description on which security can be proved? • First convince ourselves that the proof would work: several important bugs found and fixed • Agile object loading allowed modifying public part • Adaptability of proof to other implementation choices is not obvious • Minor subsystems (slot/context allocation): easy • General (1.2 compatibility): major changes in formal spec