End-to-End Encrypted File Sharing System
This is a design project of CS 161 at UC Berkeley. The goal is designing an end-to-end encrypted file sharing system by using cryptos we learned in class. I designed this system by myself.
In order to design this system, we have to find ways to secure three parts of this system. The first part is how to secure users’ credentials, the second part is how to secure files content with efficient appending, and the third part is how to share securely. There are eight methods I have to finish, \(InitUser\), \(GetUser\), \(StoreFile\), \(AppendFile\), \(LoadFile\), \(ShareFile\), \(ReceiveFile\), and \(RevokeFile\).
\(InitUser\) - this method create an user account and store all necessary information of an user on database.
\(GetUser\) - this method can be called while logging in an user, and it will retrieve and decrypt all necessary information of an user.
\(StoreFile\) - this method is called when an user is trying to store a file.
\(AppendFile\) - this method is called when an user is trying to append something to a file.
\(LoadFile\) - this method is called when an user is trying to access a file’s content.
\(ShareFile\) - this method shares a file from a user who has access to that file.
\(ReceiveFile\) - this method is called by the file sharing receiver which will give the receiver full access to a file.
\(RevokeFile\) - this method can only be called by the file owner to revoke a user’s access to a file, and all users who got access from this revoked user will no longer can access this file.
There are two databases provided. One is \(DataStore\) and the other is \(KeyStore\). Each one is a dictionary structured database with
uuids as keys. \(DataStore\) can be compromised, but \(KeyStore\) is always assumed secure.
The crypto methods we have access to include RSA, Digital Signature, Hash, HMAC, and Symmetric Encryption. We also have a Passward Key Derivation Function which generates one key, a Hash Based Key Derivation Function which generates multiple keys, and a Random Bte Generator.
The system designed by me fully relied on users’ passwords. Since each password is only known to that user, all of my keys can be assumed as secure if I derive them from the password. Below I will address how I make all three parts of this system secure.
The first part is storing users’ credential securely. Each user would have a
User structure which contains its
file_key is derived from the user’s password and will work as an universal file
Ini symmetric enc / dec key, and this key is only known to the user. The
User structure will be encrypted using another key derived from user’s password and sign.
User’s password is stored with salting and slow hashing seperately from the
User structure, so one would verify the password before touching the
User structure. The password will be signed before storing.
When storing a file, I store three parts of information. The first part is the file
Ini, the second part is the file
metadata, and the third one is the file content block.
Ini contains a
uuid points to the file
metadata, the file symmetric key, the file HMAC, and the file owner who initially created this file. This is encrypted by user’s own file key which is derived from user’s password, and the file
Ini can be only encrypt/decrypt/signed by this user. The
uuid of file
Ini is generated from username and filename.
metadata contains a list of
uuids point to each file content block, the access tree of this file, and a
Share map which maps from each sharing user’s username to his own file
Ini’s uuid. The uuid of file
metadata is randomly generated.
File content block is an encrypted and signed using file HMAC file data block. The uuid of each file content block is also randomly generated.
metadata and file content block will be encrypted using the file symmetric key stored in file
Ini, and signed using the HMAC.
Besides, in order to support a fast append, my design uses multiple storage blocks on file content instead of just one block for each whole file. Everytime
AppendFile() is called, the file
Ini will be first retrieved and then the file
metadata. The signature will be verified first and everything will be decrypted after verifying. Then
metadata will be updated with the appended block
uuid, re-signed, and re-encrypted after finishing the appending.
When appending the file content, the only thing required to do is creating a new file block structure for the new appending content. Then encrypt the new content and signed, then only push this block and the new file
metadata block to the server. So, each time a user is loading a file, he is loading a list of
uuids stored in file
metadata. All these
uuids point to each file content block, and combining them together will give a user the whole file.
General idea on sharing in my design is passing the file
Ini which contains all information of a file to the recipient. When a user shares, he first retrieve his own file
Ini, verify and decrypt, then re-encrypt using the recipient’s RSA public key, next signed and send this as the magic string.
After the recipient receives the magic string, what he actually receives is the file
Ini which contains information about where to find the file
metadata, how to encrypt/decrypt/sign the file, and who is the original owner. The recipient would have the power to find, append, and load the file with these information.
When a user tries to revoke, he first retrieve the file
Ini and check whether he is the owner of this file. After checking, if he is indeed the owner, he would retrieve the file
metadata block. Next, he will get a list of users he has to revoke from the access tree, and remove their entries from the sharing list.
Now, in file
Ini, he re-generates new uuid for the file
metadata, new random file symmetric key, and new random file HMAC. Next, I have a wrapping struct built for this part outside of file
Ini contains an
Update section. The owner would now put the new file
Ini encrypted using each still sharing user’s RSA public key at this update section by looping through the updated sharing list. Additionally, the owner can also re-generate new uuid for all file content block and update the uuids list in the file
metadata to make sure the user who is revoked can never access any part of this file.
In order to make this update section work, here is the logic. The file
Ini can only be encrypted/decrypted/signed by that user only, so the user would always first verify and decrypt the file
Ini, then use the file owner stored in file
Ini to get the owner’s DS verify key to verify the
Update section if it is not nil. Only the owner can revoke, so anyone put and signed something at the
Update section would be wrong.
After the revoken, every user still on the sharing list would still have the power of editing this file as before, but revoked users would lose the information of the file
metadata, even all file content blocks.