My undergraduate thesis on a capability based security system for a data-centric operating system.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

holy finally finished all of daniels comments

+158 -175
+67 -66
1-introduction.typ
··· 4 4 5 5 #mol-chapter("Introduction") 6 6 7 + Twizzler is a research operating system focused on new programming paradigms 8 + possible via NVM (Non-Volatile Memory)@twizzler. It gives programmers direct 9 + access to underlying data by removing the kernel from the datapath, which 10 + results in huge performance gains, while using NVM to make that data persistent 11 + across power cycles. However this reimagining of an operating system leaves many 12 + questions for how security is undertaken. 13 + 7 14 // talk about the standard unix abstractions 8 - In mainstream operating systems, an 9 - omnicient and all-powerful kernel enforces security policy at runtime. 10 - // what am i trying to say here. 11 - It acts as the bodyguard, holding all I/O and data hostage unless the 12 - requesting party has the authorization to access some resource. This tight 13 - coupling of security policy and access mechanisms works well since any access 14 - must be done through the kernel, so why not perform security checks 15 - alongside accesses? 16 15 16 + In mainstream operating systems, an omnicient and all-powerful kernel enforces 17 + security policy at runtime. It acts as the bodyguard, holding all I/O and data 18 + hostage unless the requesting party has the authorization to access some 19 + resource. This tight coupling of security policy and access mechanisms works 20 + well since any access must be done through the kernel, so why not perform 21 + security checks alongside accesses? This coupling gets challenged as soon as one 22 + tries to decouple access mechanisms from the kernel, as we see in Twizzler. 17 23 18 - This coupling gets challenged as soon as one tries to decouple access mechanisms 19 - from the kernel, as we see in Twizzler. 20 - Twizzler is a research operating system focused on new programming paradigms 24 + == Data-Centric Operating Systems 21 25 22 - However, 23 - the enforcement of security policy starts getting complicated when we try 24 - to separate the access mechanisms from the kernel. 26 + Twizzler defines itself as a data-centric operating system, meaning 27 + it is built upon two key principles @twizzler: 25 28 26 - // TODO: maybe give a brief introduction to twizzler? 29 + + Providing direct, kernel-free, access to data. 27 30 31 + + Pointers are tied to the data they represent. 28 32 29 - 30 - 31 - //TODO: explain why this happens? 32 - == Data-Centric Operating Systems 33 - 34 - 35 - //TODO: daniel feedback 36 33 // I would add a paragraph motivating the data-centric approach 37 34 // like why would i want the kernel out of the way? 35 + These principiles emerge from treating persistent data as a first class citizen. 36 + Since NVM removes the necessity of the kernel to serialize and deserialize 37 + data from storage devices and memory, it only makes sense for it to be removed 38 + from the access path. If applications want to utilize memory as truly 39 + persisent, they require a persistent way to access that memory, leading to a 40 + notion of persistent pointers. 38 41 39 - Data-centric operating systems are defined by two principles @twizzler: 40 - 41 - + They provide direct, kernel-free, access to data. 42 - 43 - + They have a notion of pointers that are tied to the data they represent. 42 + // this talks about why this kind of access rights is a secuirty issue. 43 + With the decoupling of the kernel and access methods, we have to rethink 44 + how security policy for objects is enforced. While the kernel doesn't manage 45 + the connection between applications and data, its still responsible for 46 + creating that connection. This provides one area of enforcement, where 47 + the kernel can check access rights before granting the application access 48 + to the object, and then stay out of the way after. Twizzler programs 49 + the MMU, per thread, to grant access rights, allowing for a point of enforcement; more 50 + detail can be found in section 4.2. Now we have to build the underlying system that 51 + must be enforced. 44 52 45 - By defenition, they require the removal of the kernel from the access path. 46 - This is desirable since it would remove the kernel overhead for data accesses, 47 - resulting in pure performance gains for heavy I/O applications. 48 - 49 - // then this paragrapch can focus on why removing the kernel 50 - // from the access path is a security probjem 51 - // maybe worth discussing how the access is tied to the mmu as well. 52 - // 53 - Mainstream operating systems fail to classify as data-centric operating 54 - systems, as they rely on the kernel for all data access, and use virtualized 55 - pointers per process to represent underlying data. The benefit of this "class" 56 - of operating systems comes from the low overhead for data manipulation, due to the lack 57 - of kernel involvement. However, the mainstream security model fails to operate 58 - here as, by definition, the kernel cannot be in front of access to data. So, 59 - something new must be investigated. 60 53 61 54 == Capability Based Security Systems 62 - 63 - 64 - 65 - Capability-based security systems have a rich history in research, and offer 66 - an alternative approach to security, in opposition to the Access Control Lists of prevalent OS's @linux_security. 67 - Boiled down, a capability is a token of authority, holding at minimum some 68 - permissions and a unique identifier to which "thing" those permissions apply 69 - to @cap-book. This simple approach of having a "token", allows for a separation 70 - of the kernel's involvement in the creation and management of security policy. 71 - In a well-designed system, as we see in @twizsec and described later, this allows 72 - users to completely create and manage security policy while the kernel is left to enforce 73 - it. 55 + Capability-based security systems have a rich history in research, and offer an 56 + alternative approach to security, in opposition to the Access Control Lists of 57 + prevalent OS's @linux_security. Boiled down, a capability is a token of 58 + authority, holding at minimum some permissions and a unique identifier to which 59 + "thing" those permissions apply to @cap-book. There are some 60 + additions we make to this basic defenition in order to apply capabilities in Twizzler, 61 + most notablity the addition of a cryptograhic signature. Since capabilities 62 + are stored on-disk, the kernel needs a way to ensure the policy its enforcing 63 + is coming from a trusted entity. If this weren't the case, it would be trivial 64 + for a bad actor to manipulate capabilties, and the kernel would be 65 + none-the-wiser as it goes to enforce it. Thus we have this construciton of an 66 + cryptographically signed capability, in which the kernel only enforces after it 67 + verifies the signature to be authentic. 74 68 75 - //TODO: how? (via mmu + pagetable mappings programmed via the kernel) (which is still kernel-free for most data accesses) 76 69 This paradigm permits kernel-free access of data, while also guaranteeing 77 - security. 78 - 79 - //TODO: maybe add a bit about "unforgeable" tokens, cryptographic operations, why they are needed 80 - 81 - 82 - 83 - 70 + security by enforcing it right before the point of access through the MMU. 84 71 85 72 == Our Contributions 86 - 87 73 In this thesis, I detail the fundamentals of security in the Twizzler 88 74 operating system, and discuss how I implement and refine some of the high 89 75 level ideas described in Twizzler @twizzler and an early draft of a Twizzler security 90 76 paper @twizsec. Additionally, we evaluate these systems inside kernel and user space, using 91 77 Alice/Bob scenarios and microbenchmarks. 92 - Code can be found in this 78 + 79 + A list of merged PR's to Twizzler: 80 + + #link("https://github.com/twizzler-operating-system/twizzler/pull/267")[Old Security Port to Main] 81 + - Implementation of SigningKey and VerifyingKey mentioned in seciton 2. 82 + - Implementation of Capabilities mentioned in section 3. 83 + - Support to compile twizzler-security for the kernel and userspace 84 + 85 + + #link("https://github.com/twizzler-operating-system/twizzler/pull/273")[Adds creation of SigningKey / Verifying object pairs.] 86 + - Implementation of the keypair objects containing singing and verifying keys, mentioned in section 2. 87 + - Userspace tests for keypair creation and usage of signing / verifying keys. 88 + 89 + + #link("https://github.com/twizzler-operating-system/twizzler/pull/275")[Security Contexts and Benchmarking] 90 + - Implements Security Contexts for kernel and userspace, as described in section 4. 91 + - A benchmarking framework for the kernel. 92 + - Benchmarks for cryptographic operations inside the kernel, and can be viewed in seciton 5. 93 + - Userspace benchmarks of security policy creation, as shown in section 5. 94 + 95 + More details can be found in this 93 96 #link("https://github.com/twizzler-operating-system/twizzler/issues/268")[Github 94 97 tracking issue]. 95 - 96 - //TODO: list pr's with a short summary of what each one accomplishes? 97 98 98 99 #load-bib(read("refs.bib"))
+22 -24
2-keypair.typ
··· 4 4 #mol-chapter("Key Pairs") 5 5 6 6 // what are keypair objects ? 7 - // TODO: fix this fuckin sentence 8 - Key pairs in Twizzler are representation of the cryptographic signing 9 - schemes used to create a signed capability, as discussed in 3.1. 7 + Key-Pairs in Twizzler are two objects, one containing a signing key, and the 8 + other having a verifying key. The signing key is used to form the signature when 9 + creating a capability, while the verifying key is used by the kernel to validate 10 + a capability before granting access rights. More detail can be found about 11 + capability signatures in section 3.1. 10 12 11 13 12 - We design 13 - the keypair objects to be agnostic towards the underlying scheme to allow for 14 - multiple schemes, as described in @twizzler. This also helps with backwards 15 - compatibilty when adding new, more secure schemes, in the future. The keys 16 - are stored inside of objects, allowing for persistent or volatile 17 - storage depending on object specification, and allows for keys themselves to 18 - be treated as any other object and have security policy applied to them. This 19 - allows for powerful primitives and rich expressiveness for describing secruity 20 - //NOTE: elaborate or point forward to later 21 - policy, while also being intuitive enough to construct basic policy easily. 14 + They keys are represented as follows: 22 15 16 + ```rust 17 + struct Key { 18 + key: [u8; MAX_KEY_SIZE], 19 + len: u64, 20 + scheme: SigningScheme, 21 + } 22 + ``` 23 + Since the underlying data is just a byte array, the keys themselves are 24 + scheme-agnostic, enabling support for multiple cryptograhic schemes, as 25 + described in @twizzler. This also makes backward compatibility trivial when 26 + adding new signing schemes.The keys are stored inside of objects, allowing for 27 + persistent or volatile storage depending on object specification, and allows for 28 + keys themselves to be treated as any other object and have security policy 29 + applied to them. 23 30 24 31 == Abstraction 25 - 26 - The `SigningKey` struct is a fixed length byte array with a length field 27 - and an enum specifying what algorithm that key should be interpreted as. 28 32 Currently we use the Elliptic Curve Digital Signature Algorithm (ECDSA) @ecdsa 29 - //TODO: why are we talkin about the simplistic data representation without 30 - // actually explaining the representation. maybe having a diagram would be useful? 31 33 to sign capabilities and verify them, but the simplistic data representation 32 34 allows for any arbitrary algorithm to be used as long as the key can be 33 35 represented as bytes. 34 36 35 - Additionally this specification allows for backward compatibility, allowing 36 - for an outdated signing scheme to be used in support of older programs / 37 - files. An existing drawback for backward compatibility is the maximum size 37 + An existing drawback for backward compatibility is the maximum size 38 38 of the buffer we store the key in. Currently we set the maximum size as 256 39 39 bytes, meaning if a future cryptographic signing scheme was to be created with 40 40 a key size larger than 256 bytes, we would have to drop backwards 41 41 compatibility. While this can be prevented now by setting the maximum size to 42 42 something larger, it ends up being tradeoff between possible cryptographic schemes 43 - vs the real on-disk cost of larger buffers. 43 + vs the real on-disk cost of larger buffers, something we plan to investigate in future work. 44 44 45 45 == Compartmentalization 46 46 // how they can be used to sign multiple objects (compartmentalization) ··· 62 62 the involvement of the kernel. 63 63 64 64 // nice, we should talk more about this 65 - 66 - 67 65 #load-bib(read("refs.bib"))
+5 -1
3-cap.typ
··· 60 60 We hope for future work to develop more expressive ways of using capabilities, i.e. Decentralized Information Flow Control, as specified in 61 61 6.1. 62 62 63 - //TODO: maybe worth discussing delegations if only to describe how they could be 63 + // maybe worth discussing delegations if only to describe how they could be 64 64 // extended from capabilities (as a future work ofc) 65 + // 66 + // lowkey tuah lazy to do dis rn, so maybe later, plus i havent talked about 67 + // them at all so im sure daniel is just recomending this as a way to add 68 + // content here, so ill see. 65 69 66 70 67 71 #load-bib(read("refs.bib"))
+17 -12
4-secctx.typ
··· 28 28 the relevant security context implementations for kernel and userspace to 29 29 parse security context objects. Implicitly, the kernel uses 30 30 this map for lookup while the user interacts with this map to indicate the insertion, removal, or modification of 31 - a capability. 31 + a capability. The `Map` type here and for `masks` is a flat data-structure, and stores 32 + offsets into the object where capabilities can be found for a target object. 32 33 33 - //TODO: talk about the map in memory, and about how its flat, might be worth discussing in the context 34 - // of not storing virtual address pointers. 35 34 36 35 === Masks 37 36 Masks act as a restraint on the permissions this context can provide for some targeted object. ··· 61 60 they switch @twizzler. To manage these threads, the kernel assigns a Security Context Manager, 62 61 which holds onto security context references that a thread has. 63 62 64 - The enforcement of security policy in Twizzler happens on page fault when trying to access 65 - a new object @twizzler. Upon fault, the kernel inspects the target object and identifies the 63 + There exists only 1 point of enforcement for security policy if we wish 64 + to keep the kernel out of the access path; the creation of the path itself! 65 + On page fault, the point in which a process requests the kernel to map an object in is 66 + when we have access to the security policy we seek to enforce (the signed capabilities inside the security context), the 67 + target object, and most importantly, kernel execution! Its the only time 68 + we can program the mmu according to the desired protections, and transfer control 69 + of enforcement to the hardware @twizzler. 70 + 71 + Upon page fault, the kernel inspects the target object and identifies the 66 72 default permissions of that object. Then the kernel checks if the currently active 67 73 security context for the accessing thread has either cached or capabilities that provide 68 74 permissions. If default permissions + the active context permissions arent enough to ··· 72 78 was found. If it fails all of these, then the kernel terminates the process, citing inadequate 73 79 permissions. 74 80 75 - Since the security context can have a mask per object, while also having a global_mask to 76 - the protections it can grant, the kernel also takes this into account while determining if 77 - a process has the permissions for access. 78 81 79 - The original Twizzler paper @twizzler, and the following security paper 80 - go into more detail about the philosophy behind why enforcement works this way, such as the 81 - performance benefits of letting programs access objects directly without kernel involvement, etc. 82 82 83 - //TODO: may be worth summarizing a few more bits here 83 + 84 + 85 + 86 + // why should enforcement work this way? 87 + 88 + // may be worth summarizing a few more bits here 84 89 // doesnt have to be super detailed or anything but its better to havea ... thing tie together 85 90 // than and a parathere with "etc" 86 91 //
+11 -13
5-results.typ
··· 1 1 #import "template.typ": * 2 2 3 + #show link: it => underline(text(fill:blue)[#it]) 4 + 3 5 #import "@preview/unify:0.7.1" 4 6 #mol-chapter("Results") 5 7 ··· 32 34 == Micro Benchmarks 33 35 Additionally, we have microbenchmarks of core security operations in Twizzler. All 34 36 benchmarks were run with a Ryzen 5 2600, with Twizzler virtualized in QEMU. Unfortunately 35 - //TODO: do not say they they should be the same :sob:, instead say that finding 36 - // the difference between virtualized performance and actual performance is future work 37 - I ran out of time to perform benchmarks on bare metal, but they should be the same, if 38 - not more, performant. 37 + I ran out of time to perform benchmarks on bare metal, but hope to find 38 + any discrepencies between virtualized and actual performance in future work. 39 39 40 40 === Kernel 41 + 42 + The kernel benchmarking framework takes a closure ( a block of code we want to benchmark ), 43 + runs it atleast 100 times, and scales the number of iterations to reach 2 seconds of total runtime, and stores 44 + the time it takes for each run. Then it computes the average, and the standard deviation from the timings. 45 + 41 46 There a couple of things we benchmark inside the kernel, including core cryptographic 42 47 operations like signature generation and verification, as well as the total time it takes 43 48 to verify a capability. 44 49 45 - //TODO: is this with SIMD in kernel? maybe worth discussing this nuance 46 - // 47 - // how many times did you run the experimnt and how were the stats calculated. 48 - // 49 - // could be interesting to compare signature verification cost as the amount of data 50 - // to verify goes up 51 - // my_note: (wouldnt this only be applicable towards delegations though since others are always 52 - // done properly) 53 - // 54 50 #figure( 55 51 table( 56 52 columns: (auto, auto), ··· 104 100 capability creation only takes place in user space. 105 101 106 102 === UserSpace 103 + Userspace benchmarks were calculated using rust's built in 104 + #link("https://doc.rust-lang.org/cargo/commands/cargo-bench.html")[benchmarking tool]. 107 105 108 106 In userspace, we benchmark keypair and capability creation, as these operations are core to 109 107 creating a security policy.
+35 -9
6-conclusion.typ
··· 2 2 3 3 #mol-chapter("Conclusion") 4 4 5 - //TODO: So, this is more a summary. Which is good to have here, but 5 + //So, this is more a summary. Which is good to have here, but 6 6 // you'll want to have some conclusions -- e.g. What did you learn (e.g. about the 7 7 // cost of the operations) 8 8 In short we provide a general overview of the critical security ··· 11 11 security policy can be expressed and verifies that the kernel is enforcing as 12 12 programmed. Lastly we go over microbenchmarks to show and explain the cost of these operations. 13 13 14 + The results affirm our intuition that performance would be greatly improved via 15 + caching. The cost of verifying a signature everytime a new page from an object 16 + had to be mapped into a process's memory space would be redundant. Additionally, 17 + the performance of the kernel verifying signatures is bottlenecked by the 18 + performance of the cryptograhpic scheme, meaning its a good plan to allow for 19 + the addition of new schemes while allowing for backwards compatibility since 20 + adopting a more performant scheme would lead to pure performance gains. 14 21 15 - == Future Works 22 + == Future Work 16 23 17 - // TODO: Maybe go into more detail here. There's a number of things that are discussed 24 + // Maybe go into more detail here. There's a number of things that are discussed 18 25 // as future work throughout that could use a couple sentences each here. 19 - In the future I hope to take the primitives created during my thesis, and apply them towards 20 - the implementation of Decentralized Information Flow Control, as described in @flume, into 21 - the Twizzler security model. Additionally I would love to see how the current security model 22 - evolves once we start adding distributed computing support to Twizzler, as described in 23 - the orignal paper @twizzler. 26 + 27 + There are a number of things I hope to achieve in future work, listed as follows. 28 + 29 + - Perform a cost-benefit analysis between key sizes and performance, trying 30 + to optimimze for a future proof key size in order to maximize backwards 31 + compatibility. 32 + 33 + - Program the kernel to perform access rights checks with a processes secuirty 34 + context 35 + during a page fault. I was hoping to get this completed before the end of this 36 + quarter, but we ran into some bugs and were unable to resolve them in time. 37 + Once this is hooked up, we plan to design scenarios that test the degress of 38 + expressivity allowed by our secuirty model to ensure it operates as expected. 39 + 40 + - Investigate areas of the secuirty model that could be extended to support 41 + Decentralized 42 + Information Flow Control, inspired by the work done in FLUME @flume. 43 + 44 + - Create a onboarding process that allows new students to learn the essentials 45 + of the Twizzler operating system, to foster an environment for increased 46 + student contributions to the project. 47 + 48 + - Clear code documentation so that users wanting to interface with the library 49 + have an easier time integrating it with their applications. 24 50 25 51 26 52 == Acknowledgements 27 53 I couldn't have done the work for this thesis and for Twizzler if it wasn't for the 28 54 support I've recieved from my advisor Owen Arden and my technical mentor Daniel Bittman! I 29 - owe both of you so much, not just for the class credit but also for how much I've learned in 55 + owe both of you so much, not just for this thesis but also for how much I've learned in 30 56 this endeavor. Thanks guys! 31 57 32 58
+1 -49
refs.bib
··· 71 71 @inproceedings{twizsec, 72 72 author = {Daniel Bittman and Peter Alvaro and Pankaj Mehra and Darrell D. E. 73 73 Long and Ethan L. Miller}, 74 - title = {Twizzler: a {Data-Centric} {OS} for {Non-Volatile} Memory}, 75 - booktitle = {2020 USENIX Annual Technical Conference (USENIX ATC 20)}, 76 - year = {2020}, 74 + title = {A Data Centric Model for OS Security}, 77 75 isbn = {978-1-939133-14-4}, 78 76 pages = {65--80}, 79 77 url = {https://www.usenix.org/conference/atc20/presentation/bittman}, ··· 128 126 129 127 130 128 131 - @inproceedings{10.1145/1294261.1294293, 132 - author = {Krohn, Maxwell and Yip, Alexander and Brodsky, Micah and Cliffer, 133 - Natan and Kaashoek, M. Frans and Kohler, Eddie and Morris, Robert}, 134 - title = {Information flow control for standard OS abstractions}, 135 - year = {2007}, 136 - isbn = {9781595935915}, 137 - publisher = {Association for Computing Machinery}, 138 - address = {New York, NY, USA}, 139 - url = {https://doi.org/10.1145/1294261.1294293}, 140 - doi = {10.1145/1294261.1294293}, 141 - abstract = {Decentralized Information Flow Control (DIFC) is an approach to 142 - security that allows application writers to control how data 143 - flows between the pieces of an application and the outside world. 144 - As applied to privacy, DIFC allows untrusted software to compute 145 - with private data while trusted security code controls the 146 - release of that data. As applied to integrity, DIFC allows 147 - trusted code to protect untrusted software from unexpected 148 - malicious inputs. In either case, only bugs in the trusted code, 149 - which tends to be small and isolated, can lead to security 150 - violations.We present Flume, a new DIFC model that applies at the 151 - granularity of operating system processes and standard OS 152 - abstractions (e.g., pipes and file descriptors). Flume was 153 - designed for simplicity of mechanism, to ease DIFC's use in 154 - existing applications, and to allow safe interaction between 155 - conventional and DIFC-aware processes. Flume runs as a user-level 156 - reference monitor onLinux. A process confined by Flume cannot 157 - perform most system calls directly; instead, an interposition 158 - layer replaces system calls with IPCto the reference monitor, 159 - which enforces data flowpolicies and performs safe operations on 160 - the process's behalf. We ported a complex web application 161 - (MoinMoin Wiki) to Flume, changingonly 2\% of the original code. 162 - Performance measurements show a 43\% slowdown on read 163 - workloadsand a 34\% slowdown on write workloads, which aremostly 164 - due to Flume's user-level implementation.}, 165 - booktitle = {Proceedings of Twenty-First ACM SIGOPS Symposium on Operating 166 - Systems Principles}, 167 - pages = {321–334}, 168 - numpages = {14}, 169 - keywords = {web services, system call interposition, reference monitor, 170 - endpoints, decentralized information flow control, DIFC}, 171 - location = {Stevenson, Washington, USA}, 172 - series = {SOSP '07}, 173 - } 174 - 175 - 176 -
thesis.pdf

This is a binary file and will not be displayed.

-1
thesis.typ
··· 15 15 /* Only one supervisor? The singleton array ("Dr Jack Smith",) needs the 16 16 trailing comma. */ 17 17 supervisors: ("Owen B. Arden",), 18 - //TODO: fix these 19 18 committee: ( 20 19 "Dr. Peter Alvaro", 21 20 "Dr. Andi Quinn",