Disk Encryption in Redox OS
Total Page:16
File Type:pdf, Size:1020Kb
Masaryk University Faculty of Informatics Disk Encryption in Redox OS Master’s Thesis Tomáš Ritter Brno, Fall 2019 Masaryk University Faculty of Informatics Disk Encryption in Redox OS Master’s Thesis Tomáš Ritter Brno, Fall 2019 This is where a copy of the official signed thesis assignment and a copy ofthe Statement of an Author is located in the printed version of the document. Declaration Hereby I declare that this paper is my original authorial work, which I have worked out on my own. All sources, references, and literature used or excerpted during elaboration of this work are properly cited and listed in complete reference to the due source. Tomáš Ritter Advisor: RNDr. Petr Ročkai, Ph.D. i Acknowledgements I would like to thank Petr for his guidance during the process of writing of this thesis. I would also like to thank my parents for supporting me throughout my studies and not giving up on me, even though it took 6 and a half years. ii Abstract Rust is a fairly new programming language that is focused on mem- ory safety while still achieving high performance. Many open-source projects are using it, among those an operating system called Redox. The goal of this thesis is to study the architecture of the Redox operat- ing system, with the focus being on the filesystem, and implement a block-layer encryption that will work alongside it. This should result in a contribution to the Redox operating system and possibly other libraries. iii Keywords rust, redox, disk encryption ... iv Contents 1 Introduction 1 1.1 Rust Programming Language ................2 1.2 Organization of the thesis ...................5 2 Redox OS 6 2.1 Why write an operating system in Rust ...........6 2.2 URLs, Schemes and Resources ................6 2.3 Microkernel ..........................8 2.4 Filesystem ...........................9 3 Disk Encryption 11 3.1 Keys in encryption ...................... 11 3.1.1 Salt and key derivation function . 11 3.1.2 Key management . 12 3.2 Filesystem and block device encryption ............ 14 3.2.1 Stacked filesystem encryption . 15 3.2.2 Block device encryption . 15 3.2.3 Comparison . 15 3.3 Stream and block ciphers ................... 16 3.4 Block cipher modes of operation ................ 17 3.4.1 Electronic Code Book - ECB . 17 3.4.2 Cipher Block Chaining - CBC . 18 3.4.3 XEX-based tweaked-codebook mode with ci- phertext stealing – XTS . 19 3.5 Initialization vector ...................... 22 4 High-Level Design 24 5 Implementation 27 5.1 First iteration ......................... 27 5.1.1 Cipher trait . 27 5.1.2 The typenum and generic_array crates . 28 5.1.3 Crates which implement block cipher and block cipher modes of operation . 29 5.1.4 Initialization vector generation . 31 5.1.5 Putting all the parts together . 31 v 5.2 Second iteration ........................ 33 5.2.1 Volume metadata . 33 5.2.2 Creating the filesystem . 34 5.2.3 Mounting the disk . 36 5.3 XTS addition ......................... 37 5.4 Replacement of dynamic dispatch ............... 38 5.4.1 Enumerated types in Rust . 38 5.4.2 Using crate enum_dispatch ............ 40 5.5 Testing ............................. 41 6 Conclusions 43 Bibliography 44 Index 46 A Electronic attachment 46 vi List of Figures 2.1 Difference between the monolithic kernel and the microkernel, from [10] 8 2.2 High level structure of the Redox filesystem 10 3.1 Structure of a LUKS header, from [15] 13 3.2 Recovery process of the master key 14 3.3 ECB encryption, from [19] 17 3.4 ECB decryption, from [19] 18 3.5 Comparison of encryption between ECB and CBC, which results in seemingly pseudorandom output, from [19] 18 3.6 CBC encryption, from [19] 19 3.7 CBC decryption, from [19] 19 3.8 XTS encryption without ciphertext stealing, from [19] 20 3.9 XTS encryption showing ciphertext stealing, from [19] 21 3.10 Computation of the tweak value 22 4.1 Architecture of the block-layer encryption module 25 5.1 Disk trait from redoxfs without the size function 28 5.2 Cipher trait 28 5.3 Generic array within a structure using const generics 29 5.4 Generic array within a structure using GenericArray crate 29 5.5 C++ generic array within a structure 29 5.6 The BlockCipher trait used to generically implement block modes of operation in block_modes for block ciphers 30 5.7 Trait that generators of initialization vectors need to implement 31 5.8 Writing of a u128 number as big endian 31 5.9 Cipher implementation 32 5.10 Example use of static lifetime 32 5.11 BlockEncrypt structure with a dynamically dispatched Cipher implementation 33 vii 5.12 Definition of C-type packed header aligned to the sizeof the filesystem block 34 5.13 Generic serialization of a structure to a byte array 34 5.14 Rust enumerated type representing an IP address, from [6] 38 5.15 Rust enumerated type representing an IP address with enumerated type values, from [6] 39 5.16 Pattern matching on enumerated types with enum items 39 5.17 Usage of pattern matching on structures implementing Cipher trait 39 5.18 Usage of enum_dispatch 40 5.19 Example of enum_dispatch not changing the syntax of the call 41 5.20 Setup for unit testing in Rust 42 viii 1 Introduction The demand for new technologies with ever-increasing performance, safety and usability has never been higher. Every day there seems to be some new buzzword and associated technique, program or device aiming to solve the problems within a particular domain. Often, it dies before it gains any attention. Much less often, there comes a project that the early adopters are able to push into the mainstream, usually with the help of a major sponsor. In the area of programming languages, we have seen many technologies rise and fall, only to be used in legacy systems dependent upon it. Some languages try to have a very narrow focus and are unable to capture a substantial share of the market, while others aim too wide and lose focus of their overall goal. With the rapid pace of iteration in the software world, a lot of languages aim to "be the new" C or C++ while not providing enough benefits to abandon these time-tested tools. Even when a feature from a less known language is touted as useful, these older languages can incorporate it, rendering the new one obsolete. On the other hand, the need to keep the language backward compatible also means that a lot of features cannot be easily removed from the language. This creates friction in the language, leaving users confused about what subset of features it is recommended to use. That is where Rust comes along. With its unique focus on safety, while still having high performance, it has captured a significant user base in its relatively short existence. Major companies such as Google and Microsoft are starting to use it within their products, which shows that there is demand for such a language. [1] [2] Open-source projects ranging from simple web development, game engines and software for embedded devices are being actively developed. One of the projects is Redox OS, an experimental operating system built in Rust. It is still very much in its infancy, but provides a look into what is possible to create with features provided by this language. Redox contains many "reimplementations" of popular programs under Linux but disk encryption is currently not one of them. This thesis will focus on a simple design and implementation of block-layer disk encryption working with the currently implemented filesystem 1 1. Introduction in Redox. It will discuss the challenges stemming from the usage of this language and how to overcome them. In the end, it will present a fully functioning block-layer encryption working with the Redox filesystem. 1.1 Rust Programming Language Rust is a multi-paradigm programming language that supports object- oriented, functional, imperative and concurrent programming style. It aims to provide an alternative to system-level programming languages, such as C and C++, while achieving a high degree of safety and performance. Since a lot of Rust’s usage is aimed at a similar market compared to C++, many comparisons in this thesis will be made to the C++ language. Work on the language started in 2006 by Mozilla Foundation em- ployee Graydon Hoare. Mozilla became interested in the project and started sponsoring it in 2009. Work shifted to the compiler and in 2011 it became self-compiled. Rust compiler uses LLVM as its backend. The first stable version was released in 2015. Stable releases are currently released every 6 weeks. New features are developed in the nightly version, and then tested in the alpha and the beta phase for six weeks each. Syntax of Rust is somewhat similar to C and C++, but it takes a lot of its patterns from the ML family of languages and the Haskell lan- guage. Almost all parts of a function body are an expression, including the control flow operators. Return expressions are often superfluous since omitting the semicolon at the end of an expression automatically creates a return value. The Ruby language operates in the same way and its closure syntax is therefore very similar. Since Rust provides a high level of abstraction and safety but still tries to compete with C in low-level domains such as embedded and operating system development, it must provide a way to interact with hardware at a lower level. By putting the code in an unsafe block, one can for example use inline assembly or another language more suited to the domain to deal with the details.