There is no easy way for users to transparently protect files. Security systems such as Pretty Good Privacy (PGP) require a lot of application-specific support, consume a lot of CPU cycles, are often incompatible with other systems, and ask the user to be directly involved with their setup and maintenance. Furthermore, many applications are not well integrated (or at all) with security systems. Today's users find themselves in the ironic position of having powerful workstations and fast networks, yet they are unable to reasonably protect their data with minimal effort and a small performance impact.
If the file system is kernel resident, it benefits from increased performance because of the reduced number of context switches and from running in privileged mode. A kernel based file system can offer better security than user-level file systems and encryption tools because information that is kernel resident is harder to get at, and the kernel has better access to private resources not available elsewhere. Encryption as part of the file system automatically offers a uniform encryption for all applications that access files, and reduces the user's involvement.
A file system that transparently allows access to encrypted data is an appealing idea. The idea has long been in existence and was implemented first by Blaze (CFS) and later by Cattaneo and Persiano (TCFS). However, these prior realizations have suffered from poor performance and they are harder to use; TCFS also suffers from limited availability. Consequently, transparent cryptographic file systems have not received wide use. We have remedied these problems in Cryptfs.
Cryptfs is implemented as a kernel-resident file system, and can be mounted on any directory and on top of any other file system such as UFS and NFS -- without requiring other daemons running. Users authenticate themselves by using a tool that prompts them for a passphrase which is cryptographically hashed using MD5 to form a key which is then passed to and stored in memory by Cryptfs. No information related to encryption is stored permanently, making Cryptfs both easier to use and more secure. Cryptfs uses the Blowfish encryption algorithm and determines key access in one of two modes. In the first mode, it looks up the key based on the real user ID (UID) of the accessing process; this allows a user on one machine to provide a key only once throughout the lifetime of the mount. In the second mode, Cryptfs uses both the UID and the process session ID to lookup keys. This mode offers greater security because only the process authenticated to Cryptfs and all future child processes will have access to the key -- as they all share the same session ID. Attackers who break the user's account or can become that UID will not be able to easily decrypt that user's data because they could not join the same session. Cryptfs also achieves greater performance by running in the kernel and by avoiding the NFS (V.2) protocol overheads such as asynchronous writes.
Cryptfs is implemented as a stackable vnode interface. A Virtual Node or ``vnode'' is a data structure used within Unix-based operating systems to represent an open file, directory, device, or other entity (e.g., socket) that can appear in the file system name-space. A vnode does not expose what type of physical file system it implements. The ``vnode interface'' allows higher level operating system modules to perform operations on vnodes uniformly.
One notable improvement to the vnode concept is ``vnode stacking,''[5,15,19] a technique for modularizing file system functions by allowing one vnode interface to call another. Before stacking existed, there was only a single vnode interface; higher level operating systems code called the vnode interface which in turn called code for a specific file system. With vnode stacking, several vnode interfaces may exist and may call each other in sequence: the code for a certain operation at stack level N typically calls the corresponding operation at level N-1, and so on.
Figure 1 shows the structure for a simple, single-level stackable encryption file system. In this one, system calls are translated into vnode level calls, and those invoke their Cryptfs equivalents. Cryptfs again invokes generic vnode operations, and the latter call their respective ``lower level'' file system specific operations such as UFS.
The rest of this paper is divided as follows. Next, Section 2 provides discussion on the design of Cryptfs. Following, Section 3 details the implementation. Section 4 evaluates Cryptfs' performance and security among others. We survey related file systems in Section 5 and conclude with a summary and future directions in Section 6.