1 / 20

Proving Computational Security of C Programs using VCC (and F7)

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.

brian
Download Presentation

Proving Computational Security of C Programs using VCC (and F7)

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 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)

  2. 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

  3. Prior Tools for Cryptographic Code Also, [O’Shea], [Pironti, Sisto et al.], [Backes, Hritcu et al.], [Jürjens et al.]…

  4. 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

  5. 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

  6. 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

  7. The Method

  8. 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…

  9. 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

  10. 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

  11. 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

  12. 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

  13. 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

  14. Going Through the Steps F7game-hopping Concrete F# Implementation Ideal F# Implementation VCCannotations VCCannotations Concrete C Implementation Ideal C Implementation

  15. Defining and Proving Simulation as a Contract

  16. 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));

  17. 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)));

  18. 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)));

  19. 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?

  20. 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

More Related