Subject: "Making of Fragments"

I didn't really talk much about Fragments because its scope as a project was much smaller but I decided to talk about it nonetheless. Fragments was a learning project to me that I spent about three or so working on. It was quite a fun project and it helped me become more clearer with how encryption and hashing works under the hood.

The Necessity

So it's important to ask. Why? I wanted this tool to securely store personal files I had. Granted, I don't share my laptop with anyone but I use lots of cloud storage and had reasonable suspicion that my personal data was being used to train a bunch of these AI models (looking at you Google Photos).

It's getting harder and harder to trust these large corporations especially with how invasive they have gotten so I decided to build a small app that can handle encryption and decryption of any file. I kept a few requirements for myself.

Quite a tall order. I know. However, it was something that I could solve easily. I've had some experience with both of these with my time in PhotoStory but really wanted to delve deeper into it.

The Implementation

Fragments is a shockingly simple utility. I didn't implement any of these cryptographic primitives myself in pure python because it would be too slow and memory inefficient. I could have also introduced my own suite of bugs and problems that can attenuate the security. These are complex algorithms that take lots of math knowledge that I simply didn't have.

Maybe in the future, I can make a toy crypto library but for now, I used Cryptography as an underlying basis. It offered two algorithms that were the most useful in building this applications. One being Argon2 and the other being AES-GCM. These are the industry gold standards for their intended purposes and I wanted to use them.

So when a user first specifies a file, the script first generates a 6-word diceware random password. The script doesn't allow for user-specified passwords for security reasons. It then generates a salt which will be used as part of the hashing process. It is then passed into Argon2id hashing algorithm along with the salt to produce a unique hash for the files.

Argon2id is also known as a key-derivation function as it can generate variable length hashes. The hash generated by it is constrained by the script to be 256-bits so it can easily be used as an AES key. Once the key has been derived, the file's content is split into multiple smaller parts.

Fragments uses AES-GCM as a means to encrypt data. AES-GCM allows for the addition of an associated data tag. This allows for the inclusion of metadata that can be verified to detect tampering. AES-GCM requires the use of a 96-bit nonce which is generated uniquely for each part, ensuring the usefulness of the algorithm.

Lastly, The nonce, salt, magic number and sequence number are all stored as part of a header. This is then prepended to the file which can be read by the script for decryption and restoration later on.

Threat Model

So there are only two attack surfaces that are available to a malicious actor. One being the files themselves and the other being the application. With the main goal being to ensure confidentiality and data integrity, the threat model tries to understand the various attacks that could take place and the necessary precautions in place to ensure the requirements are fulfilled.

Let's first talk about the fragments generated by the script. A .frgx file can easily be identified due to the magic numbers present in the header. The first 4 bytes can used to identify files generated by the script. Any attacker can perform a full file system search for files whose first 4-bytes match the one used by the application and then use it collect fragments.

However, none of other header data yield any useful information as the password itself needed to derive the key is completely unknown to an attacker. The first obvious idea would be to employ brute-force methodology to guess passwords. This would be the least expensive means to gain access to the file as guessing keys would have a far larger search space.

The only viable means to brute-forcing would be to guess the password of each file. For a file generated through fragments, this is very difficult for two reasons. Firstly, fragments doesn't allow users to specify their own passwords. It uses EFF's Large Wordlist to generate a password combination of 6 words offering upto 2.21 x 10^23 possible combinations to guess.

The Key Derivation Function used is also Argon2id which prevents GPU-based attacks by requiring 64 MB of memory per guess leading to impossible memory and bandwidth requirements to even crack such a password. It also prevents side-channel attacks that measure timing or voltage fluctuations, making it highly secure for offline or online data storage.

Each file that has been processed through Fragments has a unique salt assigned to it. This helps mitigate the use of a rainbow table which contains precomputed hashes allowing for easy guessing. This also provides a unique benefit of allowing for password reuse but as mentioned earlier, passwords are generated by the application so this doesn't have a huge bearing with regards to security.

There are several vulnerabilities that could arise from poor implementation of primitives. One such vulnerability is being Nonce reuse. This can compromise the Associated Tag Checking of AES-GCM. Fragments guarantees a new nonce for each part created from a file, mitigating harm to file integrity and associated data checking.

One shortcoming of Fragments is that it doesn't have the ability to check for the structural integrity of the restored file. This allows for the reordering of parts or their deletion entirely. Suppose I supply the script with a random file and slice it into 10 parts. The app will still successfully decrypt and output the contents despite any missing parts or them being out-of-order.

This doesn't impact the confidentiality of the data or make decryption easier but it could allow users to tamper with headers and make restoring the files to their original state far more difficult. Another minor issue is the use of flags to specify user passwords. This could end up in certain system logs depending on the operating system used.

The weakest link is how the password itself can be stored securely. It is imperative to store the passwords in a key-vault or a password manager. Storing the password in plaintext would nullify all of the meaningful benefit from the implemented security measures. The passwords themselves are created through common dictionary words making them more memorable.

Improvements That Can Be Made

There are two main issues that are still present that I plan on fixing in the near future. Firstly, to mitigate the structural integrity problem I discussed earlier, a simple hash chain system can be implemented. The previous hash value can be added to the associated data to ensure it is authenticated and spoofing headers doesn't lead to garbled output.

Secondly, a simple python library like getpass can be used to securely retrieve the passwords from users without the password getting logged. It also prevents anyone peeking the screen from knowing what the password is as it is hidden behind asterisks.

Reflection

Fragments is a practical solution to an age-old problem of needing to keep secrets. Whether it's a shared computer or an unencrypted chat app, it's important to keep our data solely in our hands. In an age of AI where everything is being consumed to train these new models, it is important to keep our data secure and prevent the complete erosion of our privacy.

Despite being able to complete it within the span of a couple days, I learnt a lot about the various nuances of encryption and the best practices regarding it. Hopefully, these will come in more handy as I'm tackling an interesting project that will put me deeper into the realm of security. I'll share more details about it in the future but that's it for now.