Academia.eduAcademia.edu

Output-Sensitive Information Flow Analysis

2019, Formal Techniques for Distributed Objects, Components, and Systems

Constant-time programming is a countermeasure to prevent cache based attacks where programs should not perform memory accesses that depend on secrets. In some cases this policy can be safely relaxed if one can prove that the program does not leak more information than the public outputs of the computation. We propose a novel approach for verifying constant-time programming based on a new information flow property, called output-sensitive noninterference. Noninterference states that a public observer cannot learn anything about the private data. Since real systems need to intentionally declassify some information, this property is too strong in practice. In order to take into account public outputs we proceed as follows: instead of using complex explicit declassification policies, we partition variables in three sets: input, output and leakage variables. Then, we propose a typing system to statically check that leakage variables do not leak more information about the secret inputs than the public normal output. The novelty of our approach is that we track the dependence of leakage variables with respect not only to the initial values of input variables (as in classical approaches for noninterference), but taking also into account the final values of output variables. We adapted this approach to LLVM IR and we developed a prototype to verify LLVM implementations.

Output-sensitive Information flow analysis Cristian Ene, Laurent Mounier, Marie-Laure Potet To cite this version: Cristian Ene, Laurent Mounier, Marie-Laure Potet. Output-sensitive Information flow analysis. 39th International Conference on Formal Techniques for Distributed Objects, Components, and Systems (FORTE), Jun 2019, Copenhagen, Denmark. pp.93-110, ฀10.1007/978-3-030-21759-4_6฀. ฀hal02303984฀ HAL Id: hal-02303984 https://hal.archives-ouvertes.fr/hal-02303984 Submitted on 2 Oct 2019 HAL is a multi-disciplinary open access archive for the deposit and dissemination of scientific research documents, whether they are published or not. The documents may come from teaching and research institutions in France or abroad, or from public or private research centers. L’archive ouverte pluridisciplinaire HAL, est destinée au dépôt et à la diffusion de documents scientifiques de niveau recherche, publiés ou non, émanant des établissements d’enseignement et de recherche français ou étrangers, des laboratoires publics ou privés. Distributed under a Creative Commons Attribution| 4.0 International License Output-sensitive Information flow analysis⋆ Cristian Ene[0000−0001−6322−0383] , Laurent Mounier[0000−0001−9925−098X] , and Marie-Laure Potet[0000−0002−7070−6290] Univ. Grenoble Alpes, CNRS, Grenoble INP, VERIMAG, 38000 Grenoble, France [email protected] Abstract. Constant-time programming is a countermeasure to prevent cache based attacks where programs should not perform memory accesses that depend on secrets. In some cases this policy can be safely relaxed if one can prove that the program does not leak more information than the public outputs of the computation. We propose a novel approach for verifying constant-time programming based on a new information flow property, called output-sensitive noninterference. Noninterference states that a public observer cannot learn anything about the private data. Since real systems need to intentionally declassify some information, this property is too strong in practice. In order to take into account public outputs we proceed as follows: instead of using complex explicit declassification policies, we partition variables in three sets: input, output and leakage variables. Then, we propose a typing system to statically check that leakage variables do not leak more information about the secret inputs than the public normal output. The novelty of our approach is that we track the dependence of leakage variables with respect not only to the initial values of input variables (as in classical approaches for noninterference), but taking also into account the final values of output variables. We adapted this approach to LLVM IR and we developed a prototype to verify LLVM implementations. Keywords: Information flow · Output-sensitive non-interference · Type system. 1 Introduction An important task of cryptographic research is to verify cryptographic implementations for security flaws, in particular to avoid so-called timing attacks. Such attacks consist in measuring the execution time of an implementation on its execution platform. For instance, Brumley and Boneh [12] showed that it was possible to mount remote timing attacks by against OpenSSL’s implementation of the RSA decryption operation and to recover the key. Albrecht and Paterson [3] showed that the two levels of protection offered against the Lucky 13 attack from [2] in the first release of the new implementation of TLS were imperfect. A ⋆ This work is supported by the French National Research Agency in the framework of the “Investissements d’ avenir” program (ANR-15-IDEX-02) 2 Cristian Ene, Laurent Mounier, and Marie-Laure Potet related class of attacks are cache-based attacks in which a malicious party is able to obtain memory-access addressses of the target program which may depend on secret data through observing cache accesses. Such attacks allow to recover the complete AES keys [17]. A possible countermeasure is to follow a very strict programming discipline called constant-time programming. Its principle is to avoid branchings controlled by secret data and memory load/store operations indexed by secret data. Recent secure C libraries such as NaCl [10] or mbedTLS1 follow this programming discipline. Until recently, there was no rigorous proof that constant-time algorithms are protected to cache-based attacks. Moreover, many cryptographic implementations such as PolarSSL AES, DES, and RC4 make array accesses that depend on secret keys and are not constant time. Recent works [6, 4, 11] fill this gap and develop the first formal analyzes that allow to verify if programs are correct with respect to the constant-time paradigm. An interesting extension was brought by Almeida et al. [4] who enriched the constant-time paradigm “distinguishing not only between public and private input values, but also between private and publicly observable output values”. This distinction raises interesting technical and theoretical challenges. Indeed, constant-time implementations in cryptographic libraries like OpenSSL include optimizations for which paths and addresses can depend not only on public input values, but also on publicly observable output values. Hence, considering only input values as non-secret information would thus incorrectly characterize those implementations as non-constant-time. [4] also develops a verification technique based on symbolic execution. Howvere, the soundness of their approach depends in practice on the soundness of the underlying symbolic execution engine, which is very difficult to guarantee for real-world programs with loops. Moreover, their product construction can be very expensive in the worst case. In this paper we deal with statically checking programs for output-sensitive constant-time correctness: programs can still do branchings or memory accesses controlled by secret data if the information that is leaked is subsumed by the normal output of the program. To give more intuition about the property that we want to deal with, let us consider the following example, where ct eq is a constant time function that allows to compare the arguments: good = 1; for (i=0; i<B_Size; i++){good = good & ct_eq(secret[i],in_p[i]);} if (!good) { for(i=0; i<B_Size; i++) secret[i] = 0; } return good; Let suppose that the array variable secret is secret, and all the other variables are public. Intuitively this a sort of one-time check password verifying that in p = secret and otherwise overwrites the array secret with zero. Obviously, this function is not constant-time as the variable good depends on secret, and hence branching on good violates the principles of constant-time programming. 1 mbed TLS (formerly known as PolarSSL). https://tls.mbed.org/ Output-sensitive Information flow analysis 3 It is easy to transform this program into an equivalent one which is constant time. For example one could replace if (!good) { for(i=0; i<B_Size; i++) secret[i] = 0; } by for (i=0; i<B_Size; i++) {secret[i] = secret[i] & ct_eq(good,1);} But branching on good is a benign optimization, since anyway, the value of good is the normal output of the program. Hence, even if the function is not constanttime, it should be considered output-sensitive constant time with respect to its specification. Such optimization opportunities arise whenever the interface of the target application specifies what are the publicly observable outputs, and this information is sufficient to classify the extra leakage as benign [4]. The objective of this work is to propose a static method to check if a program is output-sensitive constant time secure. We emphasize that our goal is not to verify that the legal output leaks “too much”, but rather to ensure that the unintended (side-channel) output does not leak more than this legal output. First, we propose a novel approach for verifying constant-time security based on a new information flow property, called output-sensitive noninterference. Information-flow security prevents confidential information to be leaked to public channels. Noninterference states that a public observer cannot learn anything about the private data. Since real systems need to intentionally declassify some information, this property is too strong. An alternative is relaxed noninterference which allows to specify explicit downgrading policies. In order to take into account public outputs while staying independent of how programs intentionally declassify information, we develop an alternative solution: instead of using complex explicit policies for functions, we partition variables in three sets: input, output and leakage variables. Hence we distinguish between the legal public output and the information that can leak through side-channels, expressed by adding fresh additional leakage variables. Then we propose a typing system that can statically check that leakage variables do not leak more secret information than the public normal output. The novelty of our approach is that we track the dependence of leakage variables with respect to both the initial value of input variables (as classically the case for noninterference) and final values of output variables. Then, we show how to verify that a program written in a high-level language is output-sensitive constant time secure by using this typing system. Since timed and cache-based attacks target the executions of programs, it is important to carry out this verification in a language close to the machineexecuted assembly code. Hence, we adapt our approach to a generic unstructured assembly language inspired from LLVM and we show how we can verify programs coded in LLVM. Finally, we developed a prototype tool implementing our type system and we show how it can be used to verify LLVM implementations. To summarize, this work makes the following contributions described above: - in section 2 we reformulate output-sensitive constant-time as a new interesting noninterference property and we provide a sound type system that guarantees 4 Cristian Ene, Laurent Mounier, and Marie-Laure Potet (x := e, σ) −→ σ[x 7→ σ(e)] (skip, σ) −→ σ (c1 , σ) −→ σ ′ (c1 , σ) −→ (c′1 , σ ′ ) (c1 ; c2 , σ) −→ (c2 , σ ′ ) (c1 ; c2 , σ) −→ (c′1 ; c2 , σ ′ ) σ(e) = 1 ? i = 1 : i = 2 (If e then c1 else c2 fi , σ) −→ (ci , σ) σ(e) 6= 1 (While e Do c oD , σ) −→ σ σ(e) = 1 (While e Do c oD , σ) −→ (c; While e Do c oD , σ) Fig. 1. Operational semantics of the While language that well-typed programs are output-sensitive noninterferent; - in section 3 we show that this general approach can be used to verify that programs written in a high-level language are output-sensitive constant time; - in section 4 we adapt our approach to the LLVM-IR language and we develop a prototype tool that can be used to verify LLVM implementations. An extended version of this paper, inluding all proofs and complete type systems is available on-line2 . 2 Output-sensitive non-interference 2.1 The While language and Output-sensitive noninterference In order to reason about the security of the code, we first develop our framework in While, a simple high-level structured programming language. In section 3 we shall enrich this simple language with arrays and in section 4 we adapt our approach to a generic unstructured assembly language. The syntax of While programs is listed below: c ::= x := e | skip | c1 ; c2 | If e then c1 else c2 fi | While e Do c oD Meta-variables x, e and c range over the sets of program variables V ar, expressions and programs, respectively. We leave the syntax of expressions unspecified, but we assume they are deterministic and side-effect free. The semantics is shown in Figure 1. The reflexive and transitive closure of −→ is denoted by =⇒. A state σ maps variables to values, and we write σ(e) to denote the value of expression e in state σ. A configuration (c, σ) is a program c to be executed along with the current state σ. Intuitively, if we want to model the security of some program c with respect to side-channel attacks, we can assume that there are three special 2 https://www-verimag.imag.fr/∼Cristian.Ene/OSNI/main.pdf Output-sensitive Information flow analysis 5 subsets of variables: XI the public input variables, XO the public output variables and XL the variables that leak information to some malicious adversary. Then, output sensitive nonintereference asks that every two complete executions starting with XI -equivalent states and ending with XO -equivalent final states must be indistinguishable with respect to the leakage variables XL . Definition 1. (adapted from [4]) Let XI , XO , XL ⊆ V ar be three sets of variables, intended to represent the input, the output and the leakage of a program. A program c is (XI , XO , XL )-secure when all its executions starting with XI equivalent stores and leading to XO -equivalent final stores, give XL -equivalent final stores. Formally, for all σ, σ ′ , ρ, ρ′ , if hc, σi =⇒ σ ′ and hc, ρi =⇒ ρ′ and σ =XI ρ and σ ′ =XO ρ′ , then σ ′ =XL ρ′ . 2.2 Typing rules This section introduces a type-based information flow analysis that allows to check whether a While program is output-sensitive noninterferent, i.e. the program does not leak more information about the secret inputs than the public normal output. As usual, we consider a flow lattice of security levels L. An element x of L is an atom if x 6= ⊥ and there exists no element y ∈ L such that ⊥ ⊏ y ⊏ x. A lattice is called atomistic if every element is the join of atoms below it. Assumption 2.21 Let (L, ⊓, ⊔, ⊥, ⊤) be an atomistic continuous bounded lattice. As usual, we denote t1 ⊑ t2 iff t2 = t1 ⊔ t2 . We assume that there exists a distinguished subset TO ⊆ L of atoms. Hence, from the above assumption, for any τo ∈ TO and for any t1 , t2 ∈ L: 1. τo ⊑ t1 ⊔ t2 implies τo ⊑ t1 or τo ⊑ t2 , 2. τo ⊑ t1 implies that there exists t ∈ L such that t1 = t ⊔ τo and τ0 6⊑ t. A type environment Γ : V ar 7→ L describes the security levels of variables and the dependency with respect to the current values of variables in XO . In order to catch dependencies with respect to current values of output variables, we associate to each output variable o ∈ XO a fixed and unique symbolic type α(o) ∈ TO . For example if some variable x ∈ V ar has the type Γ (x) = Low⊔α(o), it means that the value of x depends only on public input and the current value of the output variable o ∈ XO . Hence, we^assume that there is a fixed injective ^ mapping α : X0 7→ T0  α(o) ∈ TO . We extend o1 6= o2 ⇒ α(o1 ) 6= α(o2 ) ∧ such that o1 ,o2 ∈XO o∈XO mappings Γ and α to sets of variables in the usual way: given A ⊆ V ar and def G def G α(x). Γ (x) , α(B) = B ⊆ XO we note Γ (A) = x∈A x∈B Our type system aims to satisfy the following output sensitive non-interference condition: if the final values of output variables in XO remain the same, only changes to initial inputs with types ⊑ t should be visible to leakage outputs with type ⊑ t ⊔ α(XO ). More precisely, given a derivation ⊢α Γ {c}Γ ′ , the final value 6 Cristian Ene, Laurent Mounier, and Marie-Laure Potet of a variable x with final type Γ ′ (x) = t ⊔ α(A) for some t ∈ L and A ⊆ XO , should depend at most on the initial values of those variables y with initial types Γ (y) ⊑ t and on the final values of variables in A. We call “real dependencies” the dependencies with respect to initial values of variables and “symbolic dependencies” the dependencies with respect to the current values of output variables. Following [19] we formalize the non-interference condition satisfied by the typing system using reflexive and symmetric relations. We write =A0 for relation which relates mappings which are equal on all values in A0 i.e. for two mappings f1 , f2 : A 7→ B and A0 ⊆ A, f1 =A0 f2 iff ∀a ∈ A0 , f1 (a) = f2 (a). For any mappings f1 : A1 7→ B and f2 : A2 7→ B, we write f1 [f2 ] the operation which updates f1 according to f2 , namely (f1 [f2 ])(x) = if x ∈ A2 then f2 (x) else f1 (x). Given Γ : V ar 7→ L , X ⊆ V ar and t ∈ L, we write =Γ,X,t for the reflexive and symmetric relation which relates states that are equal on all variables having type v ⊑ t in environment Γ , provided that they are equal on all variables in X: σ =Γ,X,t σ ′ iff σ =X σ ′ ⇒ ∀x, (Γ (x) ⊑ t ⇒ σ(x) = σ ′ (x)) . When X = ∅, we omit it, hence we write =Γ,t instead of =Γ,∅,t . Definition 2. [20] Let R and S be reflexive and symmetric relations on states. We say that program c maps R into S, written c : R =⇒ S, iff ∀σ, ρ, if hc, σi =⇒ σ ′ and hc, ρi =⇒ ρ′ then σRρ ⇒ σ ′ Sρ′ . The type system we propose enjoys the following useful property: if ⊢α Γ {c}Γ ′ then c: =Γ,Γ (XI ) =⇒ =Γ ′ ,XO ,α(XO )⊔Γ (XI ) This property is an immediate consequence of Theorem 2. Hence, in order to prove that the above program c is output sensitive noninterferent according to Definition 1, it is enough to check that for all xl ∈ XL , Γ ′ (xl ) ⊑ α(XO ) ⊔ Γ (XI ). Two executions of the program c starting from initial states that coincide on input variables XI , and ending in final states that coincide on output variables XO , will coincide also on the leaking variables XL . We now formally introduce our typing system. Due to assignments, values and types of variables change dynamically. For example let us assume that at some point during the execution, the value of x depends on the initial value of some variable y and the current value of some output variable o (which itself depends on the initial value of some variable h), formally captured by an environment Γ where Γ (o) = Γ0 (h) and Γ (x) = Γ0 (y) ⊔ α(o), where Γ0 represents the initial environment. If the next to be executed instruction is some assignment to o, then the current value of o will change, so we have to mirror this in the new type of x: even if the value of x does not change, its new type will be Γ ′ (x) = Γ0 (y) ⊔ Γ0 (h) (assuming that α(o) 6⊑ Γ0 (y)). Hence Γ ′ (x) is obtained by replacing in Γ (x) the symbolic dependency α(o) with the real dependency Γ (o). Definition 3. If t0 ∈ TO is an atom and t′ , t ∈ L are arbitrary types, then we denote by t[t′ /t0 ] the type obtained by replacing (if any) the occurrence of t0 by t′ in the decomposition in atoms of t. Now we extend this definition to environments: def let x ∈ XO and p ∈ L. Then Γ1 = Γ ⊳α x represents the environment where the symbolic dependency on the last value of x of all variables is replaced by the real def def type of x: Γ1 (y) = (Γ (y))[Γ (x)/α(x)]. Similarly, (p, Γ ) ⊳α x = p[Γ (x)/α(x)]. Output-sensitive Information flow analysis As1 x 6∈ XO p ⊢α Γ {x := e}Γ [x 7→ p ⊔ Γ [α](f v(e))] Skip Seq p ⊢α Γ {skip}Γ p ⊢α Γ {c1 }Γ1 As3 p ⊢α Γ1 {c2 }Γ2 p ⊢α Γ {c1 ; c2 }Γ2 As2 x ∈ XO \ f v(e) 7 Γ1 = Γ ⊳α x p ⊢α Γ {x := e}Γ1 [x 7→ p ⊔ Γ1 [α](f v(e))] x ∈ XO ∩ f v(e) Γ1 = Γ ⊳α x p ⊢α Γ {x := e}Γ1 [x 7→ p ⊔ Γ (x) ⊔ Γ1 [α](f v(e) \ x)] Sub Γ ⊑ Γ′ p0 ⊑ p1 p1 ⊢α Γ ′ {c}Γ1′ Γ1′ ⊑ Γ1 p0 ⊢α Γ {c}Γ1 p′ = (Γ [α](f v(e)), Γ ) ⊳α (aff O (c1 ) ∪ aff O (c2 )) If Γ ′ = Γ1 ⊳α aff O (c2 ) ⊔ Γ2 ⊳α aff O (c1 ) p ⊔ p′ ⊢α Γ {ci }Γi p ⊢α Γ {If e then c1 else c2 fi }Γ ′ pe = (Γ [α](f v(e)), Γ ) ⊳α aff O (c) Wh p ⊔ pe ⊢α Γ {c}Γ ′ Γ ′ ⊔ (Γ ⊳α aff O (c)) ⊑ Γ p ⊢α Γ {While e Do c oD }Γ Fig. 2. Flow-sensitive typing rules for commands with output We want now to extend the above definition from a single output variable x to subsets X ⊆ XO . Our typing system will ensure that each generated environment Γ will not contain circular symbolic dependencies between output variables, i.e., there are no output variable o1 , o2 ∈ XO such that α(o1 ) ⊑ Γ (o2 ) and α(o2 ) ⊑ Γ (o1 ). We can associate a graph G(Γ ) = (XO , E) to an environment Γ , such that (o1 , o2 ) ∈ E iff α(o1 ) ⊑ Γ (o2 ). We say that Γ is well formed, denoted AC(Γ ), if G(Γ ) is an acyclic graph. For acyclic graphs G(Γ ) we extend Definition 3 to subsets X ⊆ XO , by first fixing an ordering X = {x1 , x2 , . . . xn } of variables in V compatible with the graph (i.e. j ≤ k implies that there is no def path from xk to xj ), and then (p, Γ ) ⊳α X = (((p, Γ ) ⊳α x1 ) ⊳α x2 ) . . . ⊳α xn . Let aff (c) be the set of assigned variables in a program c and let us denote def def aff I (c) = aff (c) ∩ (V ar \ XO ) and aff O (c) = aff (c) ∩ XO . We define the ^ def Γ1 (x) ⊑ Γ2 (x). For a command ordering over environments: Γ1 ⊑ Γ2 = x∈V ar c, judgements have the form p ⊢α Γ {c}Γ ′ where p ∈ L and Γ and Γ ′ are type environments well-formed. The inference rules are shown in Figure 2. The idea is that if Γ describes the security levels of variables which hold before execution of c, then Γ ′ will describe the security levels of those variables after execution of c. The type p represents the usual program counter level and serves to eliminate indirect information flows; the derivation rules ensure that all variables that can be changed by c will end up (in Γ ′ ) with types greater than or equal to p. As usual, whenever p = ⊥ we drop it and write ⊢α Γ {c}Γ ′ instead of ⊥ ⊢α Γ {c}Γ ′ . Throughout this paper the type of an expression e is defined simply by taking the lub of the types of its free variables Γ [α](f v(e)), for example the type of x+y +o 8 Cristian Ene, Laurent Mounier, and Marie-Laure Potet p = ⊥, Γ0 = [y → Y, z → Z, o1 → O1 , o2 → O2 ] (1) o1 := x + 1 Γ1 = [y → Y, z → Z, o1 → X, o2 → O2 ] (2) y := o1 + z Γ2 = [y → O1 ⊔ Z, z → Z, o1 → X, o2 → O2 ] (3) o1 := u Γ3 = [y → X ⊔ Z, z → Z, o1 → U, o2 → O2 ] (4) z := o1 + o3 Γ4 = [y → X ⊔ Z, z → O1 ⊔ O3 , o1 → U, o2 → O2 ] (5) If (o2 = o3 + x) p = O3 ⊔ O2 ⊔ X (6) then o1 := o2 Γ6 = [y → X ⊔ Z, z → U ⊔ O3 , o1 → O3 ⊔ O2 ⊔ X ⊔ O2 , o2 → O2 ] (7) else o2 := o1 Γ7 = [y → X ⊔ Z, z → O1 ⊔ O3 , o1 → U, o2 → O3 ⊔ O2 ⊔ X ⊔ O1 ] (8) fi Γ8 = (Γ6 ⊳α o2 ) ⊔ (Γ7 ⊳α o1 ) = [y → X ⊔ Z, z → U ⊔ O3 , o1 → O3 ⊔ O2 ⊔ X ⊔ U, o2 → O3 ⊔ O2 ⊔ X ⊔ U] Fig. 3. Example of application for our typing system where o is the only output variable is Γ (x) ⊔ Γ (y) ⊔ α(o). This is consistent with the typings used in many systems, though more sophisticated typing rules for expressions would be possible in principle. Notice that considering the type of an expression to be Γ [α](f v(e)) instead of Γ (f v(e)) allows to capture the dependencies with respect to the current values of output variables. In order to give some intuition about the rules, we present a simple example in Figure 3. Example 1. Let {x, y, z, u} ⊆ V ar \XO and {o1 , o2 , o3 } ⊆ XO be some variables, and let us assume that ∀i ∈ {1, 2, 3}, α(oi ) = Oi . We assume that the initial environment is Γ0 = [x → X, y → Y, z → Z, u → U, o1 → O1 , o2 → O2 , o3 → O3 ]. Since the types of variables x, u and o3 do not change, we omit them in the following. We highlighted the changes with respect to the previous environment. After the first assignment, the type of o1 becomes X, meaning that the current value of o1 depends on the initial value of x. After the assignment y := o1 + z, the type of y becomes O1 ⊔ Z, meaning that the current value of y depends on the initial value of z and the current value of o1 . After the assignment o1 = u, the type of y becomes X ⊔ Z as o1 changed and we have to mirror this in the dependencies of y, and the type of o1 becomes X. When we enter in the If , the program counter level changes to p = O3 ⊔ O2 ⊔ X as the expression o2 = o3 + x depends on the values of variables o2 , o3 , x, but o2 and o3 are output variables and o2 will be assigned by the If command, hence we replace the “symbolic” dependency α(o2 ) = O2 by its “real” dependency Γ (o2 ) = O2 . At the end of the If command, we do the join of the two environments obtained after the both branches, but in order to prevent cycles, we first replace the “symbolic” Output-sensitive Information flow analysis 9 dependencies by the corresponding “real” dependencies for each output variable that is assigned by the other branch. As already stated above, our type system aims to capture the following noninterference condition: given a derivation p ⊢α Γ {c}Γ ′ , the final value of a variable x with final type t ⊔ α(XO ), should depend at most on the initial values of those variables y with initial types Γ (y) ⊑ t and on the final values of variables in XO . Or otherwise said, executing a program c on two initial states σ and ρ such that σ(y) = ρ(y) for all y with Γ (y) ⊑ t which ends with two final states σ ′ and ρ′ such that σ ′ (o) = ρ′ (o) for all o ∈ XO will satisfy σ ′ (x) = ρ′ (x) for all x with Γ ′ (x) ⊑ t⊔α(XO ). In order to prove the soundness of the typing system, we need a stronger invariant denoted I(t, Γ ): intuitively, (σ, ρ) ∈ I(t, Γ ) means that for each variable x and A ⊆ XO , if σ =A ρ and Γ (x) ⊑ t ⊔ α(A), then σ(x) = ρ(x). def \ Formally, given t ∈ L and Γ : V ar 7→ L, we define I(t, Γ ) = =Γ,A,α(A)⊔t . A⊆XO The following theorem states the soundness of our typing system. Theorem 1. Let us assume that AC(Γ ) and ∀o ∈ XO , α(o) 6⊑ t. If p ⊢α Γ {c}Γ ′ then c : I(t, Γ ) =⇒ I(t, Γ ′ ). 2.3 Soundness w.r.t. to output-sensitive non-interference In this section we show how we can use the typing system in order to prove that a program c is output-sensitive noninterferent. Let V are = V ar ∪ {o | o ∈ XO }. def Let us define L = {τA | A ⊆ V are }. We denote ⊥ = τ∅ and ⊤ = τV are and we def consider the lattice (L, ⊥, ⊤, ⊑) with τA ⊔ τA′ = τA∪A′ and τA ⊑ τA′ iff A ⊆ A′ . The following Theorem is a consequence of the Definition 1 and Theorem 1. Theorem 2. Let L be the lattice described above. Let (Γ, α) be defined by Γ (x) = {τx }, for all x ∈ V ar and α(o) = {τo }, for all o ∈ XO . If ⊢α Γ {c}Γ ′ and for all xl ∈ XL , Γ ′ (xl ) ⊑ Γ (XI ) ⊔ α(XO ), then c is (XI , XO , XL )-secure. 3 Output-sensitive constant-time Following [1, 4], we consider two types of cache-based information leaks: 1) disclosures that happen when secret data determine which parts of the program are executed; 2) disclosures that arise when acces to memory is indexed by sensitive information. In order to model the latter category, we shall enrich the simple language from section 2.2 with arrays: c ::= x := e | x[e1 ] := e | skip | c1 ; c2 | If e then c1 else c2 fi | While e Do c oD To simplify notations, we assume that array indexes e1 are basic expressions (not referring to arrays) and that XO does not contain arrays. Moreover as in [4], a state or store σ maps array variables v and indices i ∈ to values σ(v, i). The labeled semantics of While programs are listed in Figure 4. In all rules, we ◆ 10 Cristian Ene, Laurent Mounier, and Marie-Laure Potet → − act ≡ r(σ( f )) → − act ≡ w(σ(e1 )) : r(σ( f )) act (x := e, σ) −→ σ[(x, 0) 7→ σ(e)] σ(e) 6= 1 act (x[e1 ] := e, σ) −→ σ[(x, σ(e1 )) 7→ σ(e)] → − act ≡ b(σ(e)) : r(σ( f )) act (While e Do c oD , σ) −→ σ σ(e) = 1 → − act ≡ b(σ(e)) : r(σ( f )) act (While e Do c oD , σ) −→ (c; While e Do c oD , σ) → − act ≡ b(σ(e)) : r(σ( f )) σ(e) = 1 ? i = 1 : i = 2 act (If e then c1 else c2 fi , σ) −→ (ci , σ) Fig. 4. Syntax and Labeled Operational semantics As1’ x 6∈ XO → − p ⊢ct Γ {x := e}Γ [x → 7 p ⊔ Γ [α](f v(e))][xl 7→ Γ (xl ) ⊔ Γ [α](f v( f )))] α x 6∈ XO As1” If p1 = (Γ [α](f v(e1 ), f v(e)) → − pl = (Γ [α](f v(e1 ), f v( f )) p ⊢ct α Γ {x[e1 ] := e}Γ [x 7→ p ⊔ Γ (x) ⊔ p1 ][xl 7→ Γ (xl ) ⊔ pl ] p′ = (Γ [α](f v(e)), Γ ) ⊳α aff O (c1 ; c2 ) → − pl = (Γ [α](f v( f )), Γ ) ⊳α aff O (c1 ; c2 ) p ⊢ct α p ⊔ p′ ⊢ct α Γ {ci }Γi Γ = Γ1 ⊳α aff O (c2 ) ⊔ Γ2 ⊳α aff O (c1 ) ′ Γ {If e then c1 else c2 fi }Γ ′ [xl 7→ Γ ′ (xl ) ⊔ pl ⊔ p′ ] Fig. 5. Typing Rules for Output Sensitive Constant Time (excerpts) → − denote f = (fi )i , where xi [fi ] are the indexed variables in e. The labels on the execution steps correspond to the information which is leaked to the environment (r() for a read access on memory, w() for a write access and b() for a branch operation). In the rules for (If) and (While) the valuations of branch conditions are leaked. Also, all indexes to program variables read and written at each statement are exposed. We give in Fig. 5 an excerpts of the new typing rules. As above, → − we denote f = (fi )i , where xi [fi ] are the indexed variables in e. We add a fresh variable xl , that is not used in programs, in order to capture the unintended leakage. Its type is always growing and it mirrors the information leaked by each command. In rule (As1”) we take a conservative approach and we consider that the type of an array variable is the lub of all its cells. The information leaked → − by the assignment x[e1 ] := e is the index e1 plus the set f = (fi )i of all indexes occurring in e. Moreover, the new type of the array variable x mirrors the fact that now the value of x depends also on the index e1 and the right side e. a a a 1 2 n Definition 4. An execution is a sequence of visible actions: −→ −→ . . . −→. A program c is (XI , XO )-constant time when all its executions starting with XI -equivalent stores that lead to finally XO -equivalent stores, are identical. Following [4], given a set X of program variables, two stores σ and ρ are Xequivalent when σ(x, i) = ρ(x, i) for all x ∈ X and i ∈ . Two executions a1 an b1 bm ◆ −→ . . . −→ and −→ . . . −→ are identical iff n = m and aj = bj for all 1 ≤ j ≤ n. We can reduce the (XI , XO )-constant time security of a command Output-sensitive Information flow analysis 11 ω(•) • − → x := e xl := xl : r( f ); x := e − → x[e1 ] := e xl := xl : w(e1 ) : r( f ); x[e1 ] := e skip skip c1 ; c2 ω(c1 ); ω(c2 ) − → If e then c1 else c2 fi xl := xl : b(e) : r( f ); If e then ω(c1 ) else ω(c2 ) fi − → − → While e Do c oD xl := xl : b(e) : r( f ); While e Do ω(c); xl := xl : b(e) : r( f ) oD Fig. 6. Instrumentation for ω(•) c to the (XI , XO , {xl })-security (see section 2.3) of a corresponding command ω(c), obtained by adding a fresh variable xl to the program variables f v(c), and then adding recursively before each assignment and each boolean condition predicate, a new assignment to the leakage variable xl that mirrors the leaked information. Let :, b(, )r(, )w() be some new abstract operators. The construction of the instrumentation ω(•) is shown in Fig. 6. As above, we denote → − f = (fi )i , where xi [fi ] are the indexed variables in e. Then, we extend, as in the rules As1′ , Ass1” from Fig 5, the typing system from section 2.2 to take into account the array variables. The following lemma holds. Lemma 1. Let c a command such that xl 6∈ f v(c), σ, σ ′ two stores , tr some execution trace and [] the empty trace. ′ ′ 1. p ⊢ct α Γ {c}Γ iff p ⊢α Γ {ω(c)}Γ . tr ∗ 2. (c, σ) −→ σ ′ iff (ω(c), σ[xl 7→ []]) −→∗ σ ′ [xl 7→ tr]. Now combining Theorem 2 and Lemma 1 we get the following Theorem. Theorem 3. Let L be the lattice defined in the section 2.3. Let (Γ, α) be defined by Γ (x) = {τx }, for all x ∈ V ar and α(o) = {τo }, for all o ∈ XO and Γ (xl ) = ⊥. ′ ′ If p ⊢ct α Γ {c}Γ and Γ (xl ) ⊑ Γ (XI )⊔α(XO ), then c is (XI , XO )- constant time. 4 Application to low-level code We show in this section how the type system we proposed to express outputsensitive constant-time non-interference on the While language can be lifted to a low-level program representation like the LLVM byte code [21]. 4.1 LLVM-IR We consider a simplified LLVM-IR representation with four instructions: assignments from a temporary expression (register or immediate value) or from a memory location (load), writing to a memory location (store) and (un)conditional jump instructions. We assume that the program control flow is represented by a control-flow graph (CFG) G = (B, →E , binit ) where B is the set of basic blocks, 12 Cristian Ene, Laurent Mounier, and Marie-Laure Potet → r ← op(Op, − v) r ← load(v) store(v1 , v2 ) cond(r, bthen , belse ) goto b → assign to r the result of Op applied to operands − v load in r the value stored at address v store at address v2 the value stored at address v1 branch to bthen if the value of r is true and to bf alse otherwise branch to b Fig. 7. Syntax and informal semantics of simplified LLVM-IR →E the set of edges connecting the basic blocks, and binit ∈ B the entry point. We denote by Reach(b, b′ ) the predicate indicating that there exists a path from b to b′ . A program is a (partial) map from control points (b, n) ∈ B × N to instructions. Each basic block is terminated by a jump instruction. The memory model consists in a set of registers R and the memory M (including the execution stack). V al is the set of values and memory addresses. The informal semantics of our simplified LLVM-IR is given in Figure 7, where r ∈ R and v ∈ R ∪ V al.We consider an operational semantics where execution steps are labelled with leaking data, i.e., addresses of store and load operations and branching conditions. 4.2 Type system For a CFG G = (B, →E , binit ): 1. Function dep : B → 2B associates to each basic block its set of “depending blocks”, i.e., b′ ∈ dep(b) iff b′ dominates b and there is no block b” between b′ and b such that b” post-dominates b′ . We recall that a node b1 dominates (resp. post-dominates) a node b2 iff every path from the entry node to b2 goes through b1 (resp. every path from b2 to the ending node goes through b1 ). 2. Partial function br : B → R returns the “branching register”, i.e., the register r used to compute the branching condition leading outside b (b is terminated by an instruction cond(r, bthen , belse )). Note that in LLVM branching registers are always fresh and assigned only once before to be used. 3. Function P ointsT o : (B×N)×V al → 2R returns the set of registers containing memory locations pointed to by a given address at a given control point. For example, for a given address v, r ∈ P ointsT o(b, n)(v) means that register r contains a memory address pointed to by v. We define a type system to express output-sensitive constant-time property on LLVM-IR. The main differences from the rules at the source level is that the control-flow is explicitly given by the CFG. For lack of space we describe only the rule for the Store instruction (Figure 8). It updates the type of v1 by adding the dependencies of all memory locations pointed to by v2. In addition, the type of the leakage variable xl is also updated with the dependencies of all these memory locations lying in Am (since these locations are read). Output-sensitive Information flow analysis p(b, n) = store(v1 , v2 ) G τ0 = Γ [α](x) St Am = P ointsT o(b, n)(v2 ) A0 = Am ∩ X 0 τ1 = Γ1 [α](v2 ) ⊔ τ0 13 Γ1 = (Γ, α) ⊳ A0 τ2 = Γ1 [α](v1 ) x∈br(dep(b)) ⊢α (b, n) : Γ ⇒ Γ1 [xl → Γ1 (xl ) ⊔ τ1 ][vs∈Am → Γ (vs ) ⊔ τ2 ⊔ τ0 ] Fig. 8. store instruction 4.3 Well typed LLVM programs are output-sensitive constant-time Definition 5. An LLVM-IR program p is well typed with respect to an initial environment Γ0 and final environment Γ ′ (written ⊢α p : Γ0 ⇒ Γ ′ ) , if there is a family of well-defined environments {(Γ )(b,n) | (b, n) ∈ (B, )}, such that for all nodes (b, n) and all its successors (b′ , n′ ), there exists a type environment γ and A ⊆ XO such that ⊢α (b, n) : Γ(b,n) ⇒ γ and (γ ⊳α A) ⊑ Γ(b′ ,n′ ) . ◆ In the above definition the set A is mandatory in order to prevent dependency cycles between variables in XO . The following Theorem shows the soundness of the typing system with respect to output-sensitive constant-time. Theorem 4. Let L be the lattice from the section 2.3. Let (Γ, α) be defined by Γ (x) = {τx }, for all x ∈ R ∪ M , α(o) = {τo }, for all o ∈ XO and Γ (xl ) = ⊥. If ⊢α p : Γ ⇒ Γ ′ and Γ ′ (xl ) ⊑ Γ (XI ) ⊔ α(XO ), then p is (XI , XO )- constant time. 4.4 Implementation We developed a prototype tool implementing the type system for LLVM programs. This type system consists in computing flow-sensitive dependency relations between program variables. Def. 5 provides the necessary conditions under which the obtained result is sound (Theorem 4). We give some technical indications regarding our implementation. Output variables XO are defined as function return values and global variables; we do not currently consider arrays nor pointers in XO . Control dependencies cannot be deduced from the syntactic LLVM level, we need to explicitly compute the dominance relation between basic blocks of the CFG (the dep function). Def. 5 requires the construction of a set A ⊆ XO to update the environment produced at each control locations in order to avoid circular dependencies (when output variable are assigned in alternative execution paths). To identify the set of basic blocks belonging to such alternative execution paths leading to a given block, we use the notion of Hammock regions [15]. More precisely, we compute function Reg : (B × B × (→E )) → 2B , returning the set of Hammock regions between a basic block b and its immediate dominator b′ with respect to an incoming edge ei of b. Thus, Reg(b′ , b, (c, b)) is the set of blocks belonging to CFG paths going from b′ to b without reaching edge ei = (c, b): Reg(b′ , b, (c, b)) = {bi | b′ →E b1 · · · →E bn →E b∧∀i ∈ [1, n−1]. ¬Reach(bi , c)}. 14 Cristian Ene, Laurent Mounier, and Marie-Laure Potet Fix-point computations are implemented using Kildall’s algorithm. To better handle real-life examples we are currently implementing the P ointsT o function, an inter-procedural analysis, and a more precise type analysis combining both over- and under-approximations of variable dependencies (see section 6). 5 Related Work Information flow. There is a large number of papers on language-based security aiming to prevent undesired information flows using type systems (see [26]). An information-flow security type system statically ensures noninterference, i.e. that sensitive data may not flow directly or indirectly to public channels [30, 24, 29, 28]. The typing system presented in section 2.2 builds on ideas from Hunt and Sands’ flow-sensitive static information-flow analysis [20]. As attractive as it is, noninterference is too strict to be useful in practice, as it prevents confidential data to have any influence on observable, public output: even a simple password checker function violates noninterference. Relaxed definitions of noninterference have been defined in order to support such intentional downward information flows [27]. Li and Zdancewic [22] proposed an expressive mechanism called relaxed noninterference for declassification policies that supports the extensional specification of secrets and their intended declassification. A declassification policy is a function that captures the precise information on a confidential value that can be declassified. For the password checker example, the following declassification policy λp.λx.h(p) == x, allows an equality comparison with the hash of password to be declassified (and made public), but disallows arbitrary declassifications such as revealing the password. The problem of information-flow security has been studied also for low level languages. Barthe and Rezk [8, 9] provide a flow sensitive type system for a sequential bytecode language. As it is the case for most analyses, implicit flows are forbidden, and hence, modifications of parts of the environment with lower security type than the current context are not allowed. Genaim and Spoto present in [16] a compositional information flow analysis for full Java bytecode. Information flow applied to detecting side-channel leakages. Informationflow analyses track the flow of information through the program but often ignore information flows through side channels. Side-channel attacks extract sensitive information about a program’s state through its observable use of resources such as time or memory. Several approaches in language-based security use security type systems to detect timing side-channels [1, 18]. Agat [1] presents a type system sensitive to timing for a small While-language which includes a transformation which takes a program and transforms it into an equivalent program without timing leaks. Molnar et al [23] introduce the program counter model, which is equivalent to path non-interference, and give a program transformation for making programs secure in this model. FlowTracker [25] allows to statically detect time-based side-channels in LLVM programs. Relying on the assumption that LLVM code is in SSA form, they compute control dependencies using a sparse analysis [13] without building the whole Output-sensitive Information flow analysis 15 Program Dependency Graph. Leakage at assembly-level is also considered in [6]. They propose a fine-grained information-flow analysis for checking that assembly programs generated by CompCert are constant-time. Moreover, they consider a stronger adversary which controls the scheduler and the cache. All the above works do not consider publicly observable outputs. The work that is closest to ours is [4], where the authors develop a formal model for constant-time programming policies. The novelty of their approach is that it is distinguishing not only between public and private input values, but also between private and publicly observable output values. As they state, this distinction poses interesting technical and theoretical challenges. Moreover, constant-time implementations in cryptographic libraries like OpenSSL include optimizations for which paths and addresses can depend not only on public input values, but also on publicly observable output values. Considering only input values as nonsecret information would thus incorrectly characterize those implementations as non-constant-time. They also develop a verification technique based on the self-composition based approach [7]. They reduce the constant time security of a program P to safety of a product program Q that simulates two parallel executions of P. The tool operates at the LLVM bytecode level. The obtained bytecode program is transformed into a product program which is verified by the Boogie verifier [5] and its SMT tool suite. Their approach is complete only if the public output is ignored. Otherwise, their construction relies on identifying the branches whose conditions can only be declared benign when public outputs are considered. For all such branches, the verifier needs to consider separate paths for the two simulated executions, rather than a single synchronized path and in the worst case this can deteriorate to an expensive product construction. 6 Conclusion and Perspectives In this paper we proposed a static approach to check if a program is outputsensitive constant-time, i.e., if the leakage induced through branchings and/or memory accesses do not overcome the information produced by (regular) observable outputs. Our verification technique is based on a so-called output-sensitive non-interference property, allowing to compute the dependencies of a leakage variable from both the initial values of the program inputs and the final values of its outputs. We developed a type system on a high-level While language, and we proved its soundness. Then we lifted this type system to a basic LLVM-IR and we developed a prototype tool operating on this intermediate representation, showing the applicability of our technique. This work could be continued in several directions. One limitation of our method arising in practice is that even if the two snippets xl = h; o = h and o = h; xl = o are equivalent, only the latter can be typed by our typing system. We are currently extending our approach by considering also an underapproximation β(•) of the dependencies between variables and using “symbolic dependencies” also for non-output variables. Then the safety condition from Theorem 2 can be improved to something like ”∃V such that (Γ ′ (xl ) ⊳α V ) ⊑ 16 Cristian Ene, Laurent Mounier, and Marie-Laure Potet (Γ (XI ) ⊳α V ) ⊔ (β ′ (XO ) ⊳α V ) ⊔ α(XO )”. In the above example, we would obtain Γ ′ (xl ) = α(h) = β ′ (o) ⊑ α(o) ⊔ β ′ (o), meaning that the unwanted maximal leakage Γ ′ (xl ) is less than the minimal leakage β ′ (o) due to the normal output. From the implementation point of view, further developments are needed in order to extend our prototype to a complete tool able to deal with real-life case studies. This may require to refine our notion of arrays and to take into account arrays and pointers as output variables. We could also consider applying a sparse analysis, as in FlowTracker [25]. It may happen that such a pure static analysis would be too strict, rejecting too much “correct” implementations. To solve this issue, a solution would be to combine it with the dynamic verification technique proposed in [4]. Thus, our analysis could be used to find automatically which branching conditions are benign in the output-sensitive sense, which could reduce the product construction of [4]. Finally, another interesting direction would be to adapt our work in the context of quantitative analysis for program leakage, like in [14]. References 1. Agat, J.: Transforming out timing leaks. In: Proceedings of the 27th ACM SIGPLAN-SIGACT symposium on Principles of programming languages. pp. 40– 53. ACM (2000) 2. Al Fardan, N.J., Paterson, K.G.: Lucky thirteen: Breaking the tls and dtls record protocols. In: Security and Privacy (SP), 2013 IEEE Symposium on. pp. 526–540. IEEE (2013) 3. Albrecht, M.R., Paterson, K.G.: Lucky microseconds: A timing attack on amazon’s s2n implementation of tls. In: Annual International Conference on the Theory and Applications of Cryptographic Techniques. pp. 622–643. Springer (2016) 4. Almeida, J.B., Barbosa, M., Barthe, G., Dupressoir, F., Emmi, M.: Verifying constant-time implementations. In: 25th USENIX Security Symposium (USENIX Security 16). pp. 53–70. USENIX Association, Austin, TX (2016), https://www.usenix.org/conference/usenixsecurity16/technicalsessions/presentation/almeida 5. Barnett, M., Chang, B.Y.E., DeLine, R., Jacobs, B., Leino, K.R.M.: Boogie: A modular reusable verifier for object-oriented programs. In: FMCO. vol. 5, pp. 364– 387. Springer (2005) 6. Barthe, G., Betarte, G., Campo, J., Luna, C., Pichardie, D.: System-level noninterference for constant-time cryptography. In: Proceedings of the 2014 ACM SIGSAC Conference on Computer and Communications Security. pp. 1267–1279. ACM (2014) 7. Barthe, G., D’Argenio, P.R., Rezk, T.: Secure information flow by self-composition. In: Computer Security Foundations Workshop, 2004. Proceedings. 17th IEEE. pp. 100–114. IEEE (2004) 8. Barthe, G., Rezk, T.: Secure information flow for a sequential java virtual machine. In: TLDI’05: Types in Language Design and Implementation. Citeseer (2003) 9. Barthe, G., Rezk, T., Basu, A.: Security types preserving compilation. Computer Languages, Systems & Structures 33(2), 35–59 (2007) 10. Bernstein, D., Lange, T., Schwabe, P.: The security impact of a new cryptographic library. Progress in Cryptology–LATINCRYPT 2012 pp. 159–176 (2012) Output-sensitive Information flow analysis 17 11. Blazy, S., Pichardie, D., Trieu, A.: Verifying constant-time implementations by abstract interpretation. In: European Symposium on Research in Computer Security. pp. 260–277. Springer (2017) 12. Brumley, D., Boneh, D.: Remote timing attacks are practical. Computer Networks 48(5), 701–716 (2005) 13. Choi, J.D., Cytron, R., Ferrante, J.: Automatic construction of sparse data flow evaluation graphs. In: Proceedings of the 18th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages. pp. 55–66. POPL ’91, ACM (1991) 14. Doychev, G., Köpf, B., Mauborgne, L., Reineke, J.: Cacheaudit: A tool for the static analysis of cache side channels. ACM Trans. Inf. Syst. Secur. 18(1), 4:1–4:32 (Jun 2015). https://doi.org/10.1145/2756550, http://doi.acm.org/10.1145/2756550 15. Ferrante, J., Ottenstein, K., Warren, J.: The program dependence graph and its use in optimization. TOPLAS 9(3), 319–349 (1987) 16. Genaim, S., Spoto, F.: Information flow analysis for java bytecode. In: Verification, Model Checking, and Abstract Interpretation. pp. 346–362. Springer (2005) 17. Gullasch, D., Bangerter, E., Krenn, S.: Cache games–bringing access-based cache attacks on aes to practice. In: Security and Privacy (SP), 2011 IEEE Symposium on. pp. 490–505. IEEE (2011) 18. Hedin, D., Sands, D.: Timing aware information flow security for a javacardlike bytecode. Electronic Notes in Theoretical Computer Science 141(1), 163–182 (2005) 19. Hunt, S., Sands, D.: Binding time analysis: A new perspective. In: In Proceedings of the ACM Symposium on Partial Evaluation and Semantics-Based Program Manipulation (PEPM’91). pp. 154–164. ACM Press (1991) 20. Hunt, S., Sands, D.: On flow-sensitive security types. In: ACM SIGPLAN Notices. vol. 41, pp. 79–90. ACM (2006) 21. Lattner, C., Adve, V.: Llvm: A compilation framework for lifelong program analysis & transformation. In: Proceedings of the International Symposium on Code Generation and Optimization: Feedback-directed and Runtime Optimization. CGO ’04, IEEE Computer Society, Washington, DC, USA (2004) 22. Li, P., Zdancewic, S.: Downgrading policies and relaxed noninterference. In: Proceedings of POPL. vol. 40, pp. 158–170. ACM (2005) 23. Molnar, D., Piotrowski, M., Schultz, D., Wagner, D.: The program counter security model: Automatic detection and removal of control-flow side channel attacks. In: ICISC. vol. 3935, pp. 156–168. Springer (2005) 24. Myers, A.C.: Jflow: Practical mostly-static information flow control. In: Proceedings of the 26th ACM SIGPLAN-SIGACT symposium on Principles of programming languages. pp. 228–241. ACM (1999) 25. Rodrigues, B., Quintão Pereira, F.M., Aranha, D.F.: Sparse representation of implicit flows with applications to side-channel detection. In: Proceedings of the 25th International Conference on Compiler Construction. pp. 110–120. ACM (2016) 26. Sabelfeld, A., Myers, A.C.: Language-based information-flow security. IEEE Journal on selected areas in communications 21(1), 5–19 (2003) 27. Sabelfeld, A., Sands, D.: Declassification: Dimensions and principles. Journal of Computer Security 17(5), 517–548 (2009) 28. Swamy, N., Chen, J., Chugh, R.: Enforcing stateful authorization and information flow policies in fine. In: ESOP. pp. 529–549. Springer (2010) 29. Vaughan, J.A., Zdancewic, S.: A cryptographic decentralized label model. In: Security and Privacy, 2007. SP’07. IEEE Symposium on. pp. 192–206. IEEE (2007) 30. Volpano, D., Irvine, C., Smith, G.: A sound type system for secure flow analysis. Journal of computer security 4(2-3), 167–187 (1996)