{'fraction_non_alphanumeric': 0.02499365643237757, 'fraction_numerical': 0.005423750317178381, 'mean_word_length': 5.20039331366765, 'pattern_counts': {'":': 0, '<': 0, '<?xml version=': 0, '>': 0, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
PrettyCat: Adaptive guarantee-controlled software partitioning of security protocols

One single error can result in a total compromise of all security in today's large, monolithic software. Partitioning of software can help simplify code-review and verification, whereas isolated execution of software-components limits the impact of incorrect implementations. However, existing application partitioning techniques are too expensive, too imprecise, or involve unsafe manual steps. An automatic, yet safe, approach to dissect security protocols into component-based systems is not available. We present a method and toolset to automatically segregate security related software into an indefinite number of partitions, based on the security guarantees required by the deployed cryptographic building blocks. As partitioning imposes communication overhead, we offer a range of sound performance optimizations. Furthermore, by applying our approach to the secure messaging protocol OTR, we demonstrate its applicability and achieve a significant reduction of the trusted computing base. Compared to a monolithic implementation, only 29% of the partitioned protocol requires confidentiality guarantees with a process overhead comparable to common sandboxing techniques.


I. INTRODUCTION
Mass surveillance is reality today. It is technically possible to record the Internet traffic of a whole country [14] or monitor complete Internet exchange points in real time [24]. Consequently, software vendors realized that encryption is key to protect the sensitive information of their customers. Strong security protocols gradually became a default [29], [3], [23], some of the protocols even got formally verified [6], [11], [21], which means that it can only be broken if an assumption is invalidated, or a mathematically hard problem is solved.
In practice, security protocols are always embedded into a very complex software system. Depending on their realization, they rely on operating system services, application runtimes or web browsers. If an attacker exploits a vulnerability in any of these dependencies, security protocols can be broken or bypassed, even when formally verified.
To achieve correctness, we must ensure an error-free implementation of the security protocol and all its dependencies.
However, the size of those software dependencies typically is in the range of some million lines of code. This, by far, exceeds the limits of formal verification and makes thorough manual review infeasible.
To enable complete verification, the fact that only small parts of a security protocol implementation are security critical can be leveraged. If software is segregated into multiple isolated components which interact only through well-defined interfaces, verification can be done on a per-component basis. While the small critical components are analyzed independently with manageable effort, large uncritical components can be ignored completely.
Component-based architectures realize this concept using for example a microkernel. This small software isolates components and allows access to resources and communication channels only if permitted explicitly. This default-deny policy and the limited functionality of the microkernel dramatically reduced the trusted computing base (TCB), i.e. the code that has to be correct to fulfill the objective of a security protocol.
While the component-based approach has been studied extensively in the past, its application to security protocol implementations poses a number of open problems: (a) Determining security critical and uncritical components systematically with minimum manual intervention and identifying data-flows between them is complex and error-prone. (b) Minimizing the TCBs of the resulting component-based system is unsolved. (c) There are no methods that guarantee the overall security of componentized implementations. (d) No systematic approach for reducing the introduced overhead is available.
To solve these problems, we model security protocol implementations as a graph of connected primitives annotated with predicates representing the required security guarantees. A constraint data flow analysis assigns valid guarantees to the model using an SMT solver. In a subsequent partitioning step, primitives with compatible guarantees are combined into components to optimize performance. The security-performance trade-off made during partitioning is configurable.

Our contribution
To the best of our knowledge, we are the first to present an automatic, yet sound partitioning algorithm that can minimize arXiv:1706.04759v1 [cs.CR] 15 Jun 2017 the required guarantees and can be tweaked regarding its security-performance trade-off. Our main contributions in the field of software partitioning are: • a methodology for predicate-based data-flow analysis for assigning security guarantees • combination of benefits from data-flow analysis and manual decomposition • trade-off between security and performance using optimization criteria.
Further results, like the component-based, minimal-TCB implementation of the OTR messaging protocol, as well as the combination of predicate logic and software-partitioning together with a set of predicate templates for common securityprimitives is of independent interest.
Section II discusses related work and distinguishes our method from existing approaches. Subsequently we present our approach, introduce necessary building blocks and demonstrate it's applicability using two well-known examples in Section III. We scale our method to larger protocols using our automated toolkit described in Section IV. Section V puts our results into context regarding the related work and discusses future research directions. A summary concludes our work in Section VI.

II. RELATED WORK
Our analysis relates to many well established research areas. We classify previous results into a) protocol proofs for cryptographic protocols based on presumably hard mathematical problem, b) software verification to show the correctness of an implementation, c) decomposition mechanisms for partitioning software into multiple components, and d) isolation mechanisms for securely executing mutually distrusted processes. In the following paragraphs we position the proposed analysis along these classes, clarify our contribution, highlight important results over previous works and remove ambiguity.
a) Protocol proofs: Some cryptographic protocols can be proven to be secure as long as a hard mathematical problems cannot be solved and the assumptions are not invalidated. As such, the well known textbook Diffie-Hellman key agreement protocol is secure as long as the discrete logarithm problem can not be solved easily, or the assumption that the adversary can not intercept and modify messages between both participating parties is invalidated. Relying on proven cryptographic protocols guarantees that the cryptographic building blocks are not easily attackable. Security does, however, still depend on a correct software implementation of the proven protocol. We use these results to systematically eliminate attacks on the theoretical basis of secure systems.
b) Software verification: Architecing secure software systems requires the correct implementation of software components. To find programming mistakes and software errors, methods for software verification were developed. Applying these to software implementations guarantees the absence of runtime errors, otherwise potentially nullifying the proven security properties. A formally verified reference implementation of the Transport Layer Security (TLS) protocol is presented in [6]. The authors show confidentiality and integrity of data sent over the protocol.
Domain specific languages can be used for writing cryptographic protocol code that can be checked afterwards. Pro-Script [22] is such an example that can be executed within JavaScript programs and used for symbolic analysis. The authors verify a variant of the Signal Protocol for secure messaging.
Even though the software implementing a cryptographic protocol is checked, errors are likely to be found in the surrounding runtimes, libraries and operating systems required to run the verified cryptographic protocol code. Verified implementations may serve as a correct input model for our approach, but do not solve the problem to find small trusted computing bases. c) Decomposition mechanisms: As verified software for proven cryptographic protocols is still vulnerable just by its huge trusted computing bases, isolated execution of decomposed software was proposed. There exist two main approaches for software decomposition in the relevant literature: (1) manual decomposition and (2) dataflow analysis.
We directly enhance the state-of-the-art by proposing a new mechanism to deduce components from existing software in a semiautomatic way. Security and soundness properties are composable and can be combined more easily than with any of the previously proposed methods.
(1) Manual decomposition of cryptographic protocols into trusted and untrusted components running on a microkernel have been done for IPsec [18] and IKE [30], [10], which required a lot of manual work and expert knowledge. Even though the results likely improve security, no guarantees exist that the partitioning was done correctly. For every new protocol or major software update, the partitioning must be redone.
(2) Dataflow analysis has been proposed to eliminate most of the manual effort. Several tools were developed to assist and partially automate program partitioning. PrivTrans [8] performs static analysis of source code to partition programs into core functionality and a monitor. ProgramCutter [36] automatically splits a program based on traces collected for multiple program runs. The authors of [28] performed taint analysis on Android applications to move critical portions into an isolated trusted execution environment. None of the tools exploit specifics of cryptographic operations. This results in an unnecessarily large TCB or requires unsafe manual removal of taints using expert knowledge.
d) Isolation mechanisms: Execution of components has strong isolation requirements, which can be met using different techniques. Hardware extensions have been introduced for isolating memory accesses [35], [25], by creating trusted execution environments [12], [4] and encrypting memory [19]. Microkernels [20], [15], micro hypervisors [32] and separation kernels [9] realize isolation by means of a trusted software layer, monolithic operating systems have been extended to enhance process isolation [34], [31], [1]. We assume component isolation and controlled interaction, but do not rely on any particular technology. These mechanisms can be used as building blocks for fulfilling our requirements.
Restricting privileges of software regarding their ability to perform system calls as can be found in the principle of least privileges literature is not applicable [17]. These results always assume a monolithic system that inherently comes with large trusted computing bases, contradicting our goal of having a small TCB that can be checked for correctness.

III. DESIGN
In this section we introduce our methodology to partition a given software into components in a structured way. It is a combination of manual decomposition and dataflow analysis based on predicate logic. We naturally combine the benefits of both methods, namely we inherit the specificity of manual decomposition and the efficiency of dataflow analysis. However, instead of analyzing the software implementation directly, we create an annotated data flow representation termed model as basis for all further analysis. As a last step we group together parts of the model with similar properties into partitions.
Accordingly, there are four main steps involved to apply our methodology.
A. Generate an abstract model of the software B. Annotate parts of it with predicates C. Perform a constraint dataflow analysis D. Run an adaptive partitioning algorithm The following sections introduce and describe the involved steps in more detail.

A. Model
A software implementation of a security protocol inherently requires guarantees from its environment. However, as stated in the previous sections, not all parts of the software require the same guarantees. To calculate the actually required guarantees for any part of the software we model it as a graph of interconnected primitives. All primitives have a set of input and output ports with associated guarantees. These guarantees are represented using variables in a predicate.
Some primitives might have different guarantee requirements on its input and output ports. For example, a symmetric encryption algorithm could have the requirement to guarantee confidentiality on its input ports (data and key), while not requiring confidentiality on its output. To capture such a relation, predicates are assigned to primitives describing the relation of guarantees between ports of a primitive and security goals.
Initially, only a few model variables are assigned manually to capture the assumptions of the environment. To derive the required guarantees for all parts of the model, a system of equations consisting of all predicates needs to be solved. If a solution is found, then the assigned variable values represent the necessary guarantees of the protocol.
As a next step, primitives that require similar guarantees are identified. The identified primitives may then be realized as an isolated component with well-defined interfaces.

1) Primitives and channels:
A primitive is a finite state machine reacting to stimuli from the environment or from other primitives. Stimuli are asynchronous messages received through the input ports of a primitive. It may send messages through its output ports. Primitives are loosely coupled and make no assumptions about connected primitives.
The Env primitive is special, as it denotes the boundary of our model. Messages received by this primitive from the environment are forwarded to its output port. Messages sent to its input port are forwarded to the environment, respectively. A networking component with an integrated TCP/IP stack can for example be represented as an Env primitive.

Input1
Input2 Output1 Figure 1: Example: Instance of primitive Prim with two input ports Input1 and Input2 and one output port Output.
Primitives are templates denoting a type of a node within the protocol model. They carry specific semantics, input ports and output ports. To realize a protocol, primitives are instantiated and their ports are connected to other instances through channels to form a graph. While instances are unique, the same primitive might be instantiated to multiple instances. A channel is a connection between exactly one output port of an instance to exactly one input port of another instance.

B. Predicates
An instance of a symmetric decryption primitive transforms ciphertext into plaintext using a secret key. Intuitively, no confidentiality needs to be guaranteed for the original ciphertext when sent to the decryption primitive, as confidentiality is achieved by means of cryptography. But once the data has been decrypted and is sent to an output port, confidentiality is  For a hypothetical primitive P P rim (Figure 1) with input ports Input1 and Input2 and an output port −−−−→ Output, a predicate R, representing required guarantees, is expressed as R Prim = Input1 I ∧ Input2 C ∧ −−−−→ Output I . This states, that the primitive Prim assumes that the input channel connected to Input1 guarantees integrity (denoted by the I in the subscript). For the Channels must provide at least the guarantees required by the ports they connect. This implies that for two instances A and B where A's output port − − → Out is connected to B's input port In through a channel Chan the predicate R Chan = −→ Out I ⇔ In I ∧ −→ Out C ⇔ In C holds. For ease of understanding, channel predicates are not given explicitly, but represented by identical port names of connected ports throughout the remaining document.

C. Dataflow analysis
Using the introduced building blocks, a simple example for counter-mode encryption is evaluated as well as a text-book Diffie-Hellman key agreement protocol.

1) Simple encryption:
The counter mode encryption primitive Enc ctr in Figure 3 has three input ports: Plaintext, Key and Ctr for receiving plaintext, key and an initial counter, respectively. The result of the encryption is sent to the − −−−−−− → Ciphertext port.
The guarantees it requires from its runtime environment to fulfill its security objective are defined in Figure 3: (1) If integrity is required for − −−−−−− → Ciphertext then integrity is also required for the plaintext input port, as an attacker controlling the plaintext can influence the ciphertext. Counter mode encryption does not achieve integrity cryptographically, hence, integrity for the ciphertext is only achieved by guaranteeing integrity for the plaintext.
(2) Integrity and confidentiality of the encryption key must always be guaranteed or an attacker could learn or choose the key. (3) The integrity of the counter is essential to counter mode, as using the same key/counter combination twice is fatal.
2) Diffie-Hellman: We use the Diffie-Hellman key agreement as a more sophisticated example (Figure 4). Two connections to the environment exist in this model: Keystore for holding keys and Network for sending and receiving messages over an untrusted channel. As outlined in Section III-A we formalize the assumptions about the environment in (4) and (5). For brevity, we leave out channel predicates and relieve the Keystore from the necessary integrity assumption.
x C ∧ l I (8) Figure 6: Assumptions for the environment and predicates for cryptographic elements gi I ∨ gr I → g I (13) x C → (xi C ∧ xr C )  We can derive the complete guarantees for the model given in Figure 4 by using the guarantees of the environment and a couple of generic primitive predicates. The DHpub and DHsec primitives require integrity for their parameters and secret key, so that an attacker cannot choose an own value (6). The secret key x for both primitives must be confidential, just like the resulting key s (7). s I , g y I , l C , g C , m C , g  Figure 8: Solution for the above Diffie-Hellman model The result x of RNG must have confidentiality guaranteed and key length l is public, but it's integrity must be protected to prevent an attacker from choosing a too short key (8). We over-approximate the relationship between inputs and outputs of Transform: Whenever any input requires confidentiality, all outputs require confidentiality. Whenever an output requires integrity, all inputs must guarantee it. This implies the relevant guarantees shown in Figure 7.
To minimize the requirements for the implementation, we set variables lacking explicit guarantees to False (21). Together with the predicates above, we can determine a valid assignment for all variables in the model as shown in Figure 8 and visualized in Figure 5.
As stated above, our model does not assume integrity for the Keystore. This is a consequence of the Diffie-Hellman key agreement scheme, which does not achieve integrity by cryptographic means. If we had chosen the Keystore guarantees realistically, our method would have yield a conflict between the absence of integrity stated for the network environment and the system of equations given by our model. Specifically the assumption s I together with the predicates, (9), (17), (5) result in the contradiction γr yr I ∧ ¬γr yr I .

D. Adaptive Partitioning
The naïve transformation of our model into an implementation would create a single component for every instance in the model. As communication between components is expensive, this results in a large communication overhead, compared to a monolithic implementation. To reduce this overhead we introduce an algorithm to trade-off communication cost against TCB size. Instances with compatible guarantees can be merged into a single component. Due to efficient communication within a component, the total overhead is reduced. However, merging instances into fewer, but larger components comes at the cost of increasing the size of the TCB.
We instantiate three simple partitioning algorithms to demon-strate the described trade-off capabilities. One algorithm that only merges identical guarantees and two that take the semantics of the primitives into account. By adopting an appropriate cost function, more powerful, adaptive partitioning schemes can be realized.

Merge Basic
The simplest approach is to put connected instances with identical guarantees into the same partition.
The algorithm iterates over all instances in the model. For each instance, it checks whether it was assigned to a partition already. Otherwise, it assigns it to a new partition. For each new partition it traverses all connected components with the same guarantees and tries to assign them to the newly generated partition. Merge Const Additional to the basic partitioning scheme, this algorithm merges all Const instances with their connected partition if that partition provides sufficient guarantees. This eliminates single instances of the Const primitive representing an own partition as seen in P 1 of Figure 5. As constants are simple, merging them results only in a minor increase of the TCB. Merge Branch The third algorithm is similar to Merge Const in that it joins Const primitives, as well as additional, simple instances into the same partition. To define simple instances an arbitrary metric can be used, for examples the lines of code to implement an instance. If the metric used relates to the TCB size, it can be argued that the TCB is not increased significantly. As an example refer to the merging of partitions P 4 and P 5 into P 2 in Figure 5.

IV. APPLICATION OF OUR FRAMEWORK
As seen in the previous Diffie-Hellman example, the number of predicates to be checked for a model quickly grows to an unmanageable size. In the following section we describe our automatic PrettyCat toolset for partitioning and asserting of protocol models and apply it to the OTR protocol. It consists of four main phases: Analysis, Assertion, Partitioning, and Execution (cf. Figure 9). The last phase of the PrettyCat toolset Execution is not part of our methodical framework as described in Section III, but included to perform functional validation.

A. PrettyCat Toolset
In the analysis phase, model constraints are derived from primitive predicates, channel predicates and the guarantees assumed for the environment. The resulting constraint set is passed to an SMT solver, e.g. Z3 [13], to find a valid assignment for all guarantees. It may not be satisfiable, in which case the solver may produce an unsatisfiability core. If available, this minimum set of constraints that lead to the contradiction is mapped to the graph by our tool. The conflict can then be inspected visually and used to find conflicts in the model or the assumptions.
The assertion phase checks assertions associated with the model based on independent expert knowledge. Guarantees are derived automatically in the partitioning phase, solely using primitive predicates, channel predicates and assumption about the environment. However, missing guarantees may render a previously correct algorithm insecure. As assertions are not used in partitioning, they serve as an independent sanity check for predicates and assumptions.
In the partitioning phase, partitions are derived using the basic recursive algorithm described in Section III-D. Optionally, constants and branches are merged to reduce IPC overhead.
Lastly, models may be executed out of the PrettyCat toolset directly in the execution phase. We provide a library of primitives for HMAC, DSA, Diffie-Hellman and more. Connections to the environment are realized using TCP/IP, files or the console. Own implementations can be added easily by implementing a Python class. Being solely a validation tool, all primitives execute in the same Python process with no isolation enforced.

B. Study: Off-the-record messaging
We applied our approach to the Off-the-Record Messaging (OTR) protocol [7] for secure instant messaging. As the Socialist Millionaire Protocol is optional for the security of OTR, we leave this part out.
From the protocol specification we derived a component model. OTR's goal to serve as a plugin-solution for existing messaging protocols, turned out to be helpful identifying the Env primitives representing the boundaries of the model: User Data, Keystore and Fingerprint. They all require confidentiality and integrity guarantees as they handle user message, longterm keys or the identity of the remote party. The Network environment used to interface OTR with the Internet has no guarantees whatsoever.
The model for the full OTR protocol consists of 186 primitive instances connected by 285 channels. Only the four instances denoting the environment of the model are annotated explicitly with guarantees. Based on that information our optimized Merge Branch algorithm (cf. Section III-D) automatically splits the protocol into the 7 partitions shown in Figure 10.
In a step independent of the model creation, we revisited the OTR specification and augmented the model by 46 assertions which are validated correct by the PrettyCat toolset. The partitioned model runs against a minimal test client built with the OTR reference implementation of the protocol authors [2]. We successfully negotiate keys and send messages between both implementations.

V. DISCUSSION
To assess the quality of our approach, we need to consider the overhead caused by additional inter-process communication,  Figure 11: Process and IPC overhead in OTR the partitioning introduced and the security advantage of partitioning a protocol.

A. Overhead
In a partitioned protocol, security is improved by isolating code with different security guarantees. That security gain comes at an added cost of inter-process communication. Our baseline is a monolithic implementation with one single process and therefore efficient communication.
As shown in Figure 11, simply implementing every instance within its own component (Merge none), expectedly leads to a prohibitively large number of processes and inter-process communication for our OTR model 1 . To reduce the overhead we partition the model such that primitives with the same guarantees are merged into a single component. That basic partitioning schema Merge Basic reduces the required number of processes by 87% and the amount of inter-process channels by 62%. Performance optimizations can be applied to further reduce the overhead. As such, very small primitives like constants can be merged into adjacent components using Merge Const. This further reduces processes and communication channels by 54% and 16%, respectively. Similarly, Merge Branch merges even more primitives. This leads to the architecture depicted in Figure 10 with the minimum number of processes possible without significantly increasing the TCB. A description of the partitioning schemes can be found in Section III-D.
Partitioning software may significantly increases the total number of processes, thus consuming additional system resources. A large number of processes is for example used to improve fault-isolation in web browsers, with acceptable overhead [27]. Further overhead is imposed by additional interprocess communication, which is performed very efficiently on modern microkernels [32], typically orders of magnitude faster than common cryptographic primitives.

B. Security gain
The security gain achieved by partitioning a security protocol can be measured by the size of the resulting trusted computing  base. Monolithic software must guarantee confidentiality and integrity for their whole implementation, as an error in a part unrelated to the security protocol can completely defeat security.
Our approach reduces the trusted computing base by assigning parts of the protocol to components without security guarantees. Those parts become untrusted and can be implemented in the most convenient way. Additionally, we treat confidentiality and integrity independently, which results in components where only integrity needs to be guaranteed. While this does not change the size of the overall TCB, it does in fact simplify assumptions for the respective components.
In the lack of a full component-based implementation, we estimate the TCB size using the code in our model execution framework. As it implements primitives as Python classes, we chose a python OTR implementation to compare against. 2 The TCB calculation includes cryptographic and standard python classes used by the implementations, but excludes the python runtime an everything beneath. TCB size is represented by measuring the source lines of code (SLOC 3 ).
By partitioning the OTR protocol, we reduce the size of the OTR-related TCB to 53% compared to a monolithic Python implementation using the same cryptographic libraries. Our approach of treating confidentiality and integrity independently, specifically reduced the part of the TCB for which confidentiality is to be guaranteed to only 29% of the original size.

C. Future work
We automatically partition a security protocol, determine the guarantees required for the partitions and derive a communication policy between them. The user of our PrettyCat toolset still needs to provide the input model, which involves manual work and presents an additional source of errors. Also, while the result of our analysis is a machine-readable, partitioned model, the transformation into a component-based system is left as a manual step.
As suggested in Figure 12, we are going to extend our tools to extract the input model from existing source code using data flow analysis, e.g. FlowDroid [5]. Preliminary results suggest that most primitives can be matched against the source code automatically by annotating cryptographic interfaces.
Additionally, we are implementing automatic synthesis of partitioned security protocols for different target platforms, e.g. the Genode OS framework [16], including the system structure, communication policy and instantiated primitives. Even though implementations for common primitives will be provided, reusing source code from model extraction during system synthesis will be investigated further. The proposed extensions will reduce user interaction to manually annotating Env primitives.
A mostly automatic partitioning process opens an opportunity to study other and more complex security protocols. The widespread TLS protocol is an obvious and worthwhile candidate, as are modern widely adopted secure messaging protocols like Signal [26] or OMEMO [33].

VI. SUMMARY & CONCLUSION
Partitioning security protocols into component-based implementations with a minimal TCB facilitates code review and verification. Traditionally this has been done in a manual adhoc manner which is costly and error-prone. Existing software partitioning tools are too imprecise, yield a large TCB and often require unsafe manual steps.
We present a systematic approach to automatically partition security protocols. Our method models them as primitives connected by channels and derives required security guarantees using a constraint solver. A number of performance optimizations help to trade-off between IPC overhead and TCB size without sacrificing soundness.