Welcome!

Welcome to the Dexios Book! Here you will find a lot of information about the project, including: how to use it, best practices, performance metrics and much more. Please view the list of pages to find what you're looking for!

What is Dexios?

Dexios is a command-line file encryption utility, suitable for encrypting files before uploading them to a cloud service. It is written entirely in rust and contains no unsafe code (some dependencies may contain unsafe code, but they have received the correct audits and are deemed secure).

It uses XChaCha20-Poly1305 encryption by default, or AES-256-GCM if you specify --aes.

By default, BLAKE3-Balloon is used for hashing your password. Alternatively, argon2id can be used to hash your key, by specifying --argon during encryption.

Security Notices

The AES-GCM crate has received a security audit by NCC group, with no significant findings. You can view the audit here.

The ChaCha20-Poly1305 crate has received a security audit by NCC group, with no significant findings. You can view the audit here

The deoxys crate does not have a formal audit, and it is still rather experimental. Due to how new Deoxys is, relatively speaking, it's extremely hard to find a good implementation. We opted to use one created by the same RustCrypto Team that almost all of our other cryptographic dependencies rely on. You will experience decreased encryption and decryption performance while using Deoxys. NOTE: As of v8.8.0, encryption using Deoxys has been temporarily disabled. We weren't happy with the performance and agreed it would be best to remove this functionality until it improves. You may still decrypt any files that were encrypted with Deoxys.

We use BLAKE3-Balloon hashing for passwords (as of v8.7.0), and older versions used argon2id with good, hard parameters. Both hashing algorithms are regarded as secure, and both APIs are provided by the RustCrypto Team.

All other cryptographic functions are deemed secure - but they don't protect your data, so any vulnerabilities in them would not have a detrimental effect on the security and integrity of said data.

The Defaults

The defaults used in Dexios are more than adequate for even the most paranoid of users.

By running the simple command dexios -e input.txt output.enc, you are using the following:

  • XChaCha20-Poly1305
  • BLAKE3-Balloon hashing
  • Sensitive data being completely erased from memory
  • A tamper-resistant header that is authenticated along with every block of encrypted data
  • LE31 STREAM encryption

Tested Operating Systems

OSWorking?
Void LinuxYes
Fedora 35Yes
Fedora 36Yes
Ubuntu 20.04Yes
FreeBSD 13Yes
FreeBSD 14Yes
Windows 10Yes
Windows 11Yes
Android 12Yes
MacOSYes

Donating

If you like my work, and want to help support the project, feel free to donate! This is not necessary by any means, so please don't feel obliged to do so.

XMR: 84zSGS18aHtT3CZjZUnnWpCsz1wmA5f65G6BXisbrvAiH7PxZpP8GorbdjAQYRtfeiANZywwUPjZcHu8eXJeWdafJQFK46G
BTC: bc1q8x0r7khrfj40qd0zr5xv3t9nl92rz2387pu48u
ETH: 0x9630f95F11dFa8703b71DbF746E5c83A31A3F2DD

Privacy

Dexios will never collect your data. No data is sent to any server, nor are any requests.

Everything is done locally on your hardware and your machine.

I encourage all able users to take a peek at the source code and see for themselves.

You may find the cryptographic functions in dexios-core

Thank you!

I'd like to give a huge "thank you" to the entire RustCrypto Team for their hard work within the Rust cryptography community. This program would not be possible without their countless hours of work and dedication.

Installing

To install Dexios, there are two main options.

Firstly, you can install via cargo with cargo install dexios, or you may download a binary from the release page (make sure you mark it as executable, otherwise it won't run!).

Linux/FreeBSD

To use cargo for installing, ensure you have gcc installed on your system.

You may install via cargo with the command cargo install dexios

Windows

You may also use cargo for installing, just run the command:

cargo install dexios

Android

Dexios can be installed on Android devices, via Termux. All features have been confirmed to work, but if you find any, please open a Github issue.

Dexios was tested on Android 12, with Termux v0.118.0, but it should work on a wide range of devices.

To install, ensure you have cargo installed. You may install Dexios with the command:

AR=llvm-ar cargo install dexios

Building Notes

gcc is required for building on Linux and FreeBSD.

Manually setting RUSTFLAGS is no longer required, as the AEAD crates we use from the RustCrypto Team automatically detect, and take advantage of hardware cryptography primitives.

Downloading and running a pre-compiled binary

The Github Releases page contains pre-compiled binaries, generated with Github Actions. These are ideal if you don't want to go through the hassle of building, and they should run on any system (provided the architecture matches). They are the exact same binaries from Github Actions, so we can ensure they haven't been tampered with.

We encourage users to check the hash provided in the GA Workflow, and compare with the file you have downloaded.

Choosing a Key

Use a strong password, if you're planning to use an entered one.

dd can provide a suitable keyfile if you'd prefer a higher level of security:

dd if=/dev/urandom of=keyfile bs=1 count=4096 for a 4kb random keyfile

If you lose your file's key/keyfile, there is no recovering the encrypted data. This is not unique to Dexios - it's the nature of encryption as a whole.

Options for providing a key

You may provide keys in a few ways:

  • -k for giving path to a keyfile
  • --auto to tell Dexios to auto-generate a key
  • The DEXIOS_KEY environment variable
  • A user-provided password (this can be chosen by just running Dexios, and providing none of the above)

Checksums

Hashing mode uses BLAKE3 for verification, due to it's speed, security and regular updates. (very ideal for this use case).

Checksums are not used by Dexios for anything security related - they are for your peace of mind.

We hash the encrypted file (after encryption and before/during decryption). This is to ensure that your file wasn't tampered with between encrypting it and decrypting it. If the hash isn't the same then something very bad has happened.

Since v8.5.0, checksums are calculated after the main process has been executed. This allows for us to keep our encryption/decryption functions cleaner, and less cluttered, which can help us maintain the core functionality. It would be hard to keep on-top of these critical functions otherwise.

This was originally sha3-512 in versions 3.x.x and below, and was KangarooTwelve in 4.x.x (via the tiny_keccak crate) but since v5 it has been changed to BLAKE3 for a number of reasons. We have no plans to change BLAKE3 at this moment in time - it's fast, secure, and does the job very well.

Standalone Hashing Mode

You can use this by running dexios hash test.enc. It can also be ran on any file you'd like - encrypted or not.

You may even hash multiple files at once, with a command such as dexios hash test1.enc test2.enc

Performance

Tests were ran on a system with a Ryzen 7 3700x and 16gb of 3000MHz RAM - running Void Linux. The file used was originally 3.5GiB, and it was stored on a Cruicial MX500 SSD.

Version 6 removed JSON entirely, and dropped base64, which really shows in the performance metrics.

This is using AES-256-GCM.

The time was determined via /usr/bin/time -f "%e"

Version-eHyk-dHykStream?
3.2.844.37s40.91sNo
4.0.023.70s30.43sNo
5.0.022.48s28.66sNo
5.0.220.14s21.26sNo
5.0.919.31s18.92sNo
6.0.011.74s11.59sNo
6.4.85.61s5.35sYes

Environment Variables

Dexios can read your key from an environment variable! Just set DEXIOS_KEY and it will automatically be detected and used. Due to using different salts and nonces for every encryption, there is no inherent risk in reusing keys - although it's not a good security practice.

To just encrypt a file
dexios -e test.txt test.enc

To just decrypt a file
dexios -d test.enc test.txt

To just erase a file
dexios erase test.txt

To encrypt a file using AES-256-GCM (note: you do not need to specify --aes when decrypting)
dexios -e --aes test.txt test.enc

To encrypt a file, and show the hash of the encrypted (output) file for verification later on
dexios -eH test.txt test.enc

To decrypt a file, and show the hash of the encrypted file (to compare with the hash generated above)
dexios -dH test.enc test.txt

To encrypt a file, and erase the original file
dexios -e --erase test.txt test.enc

To use a keyfile for encryption
dexios -ek keyfile test.txt test.enc

To encrypt all .mp4 files in a directory (using find)
Remove `-maxdepth 1` to make this run recursively

find *.mp4 -type f -maxdepth 1 -exec dexios -eyk keyfile {} {}.enc \;

To decrypt all .mp4.enc files in a directory, and remove the .enc suffix
find . -type f -iname "*.mp4.enc" -exec sh -c 'dexios -dk keyfile "$0" "${0%.enc}"' {} \;

Technical Details

Here you will find all relevant technical details for Dexios.

This page will be updated whenever changes are made and released - if anything looks out of date, please open a Github Issue.

Obtaining the Key

All keys are wrapped in Protected<>. This ensures that data does not leak, is not copied and is zeroed on drop. It only stays in memory for as long as it needs to.

The key is checked, once, to ensure that it is not empty.

Once the length has been validated, keys are then safely transported to the appropriate hashing function. See Password Hashing for more information.

Autogenerating a Key (v8.7.0+)

The option to autogenerate passphrases has been added. It uses the EFF Large Wordlist to generate 3 completely random words, and then 6 digits for the end. The words are all capitalised, and are separated with -. This should provide more than enough protection, due to the absurd amount of possible combinations there are.

Reading from the Terminal

While reading from the terminal, the passwords are stored as Strings. We use rpassword to handle password entry, this way your input is hidden across multiple platforms.

On encryption, where you need to enter the password twice, they are compared. The String used for validation is safely zeroed out, and the original is consumed into a Vec<u8>.

On decryption, the password is only entered once, and it is consumed into a Vec<u8>.

The Vec<u8> containing the key is wrapped into a Protected<Vec>, which takes ownership of the value.

Reading from a Keyfile

The keyfile is opened, and it's contents are read into a Vec<u8>. The Vec<u8> is then wrapped into a Protected<Vec>, which takes ownership of the value.

Reading from Environment Variables

The DEXIOS_KEY environment variable is checked (to see if it exists) - if so, the contents are read into a String. It is then consumed into a Vec<u8>, which finally gets wrapped into a Protected<Vec>. This may seem cumbersome, but there are no copies or clones of the data, meaning it is safe.

Secure Erase

Due to the nature of SSDs and their controllers (wear levelling and such), it's almost impossible to fully erase a file - we can try our best though. It's not recommended to use the erase function on a large file (1gb+) on flash storage, as it will wear the drive down unneccessarily.

First we generate enough random bytes to fill the file, seeded by the system. Those random bytes are then written over the file, covering all of the previous data. This is repeated twice (more if specified) to ensure the data is thoroughly gone. This may panic due to a lack of entropy - there's not much we can do in this case.

Once the random bytes have been overwritten, we then overwrite all of the data one final time with just zeroes, before truncating the file length to 0. Afterwards, we remove the file using the system's methods (unlink on Unix and DeleteFile on Windows).

If you pass a directory to erase mode, it will prompt you to erase every file within the directory, before deleting it. This functionality was added in v8.6.0, and it allows you to securely erase many files at once. Use with extreme caution!

Directory "Packing"

After using the pack feature to create an archive, it is just a zip file. You may decrypt this as normal and open it for yourself, if you so wish.

Folders encrypted with this mode have all of their information hidden. There is no way for anyone to deduce how large each file was, or their names/contents, once it has been encrypted.

This mode does not preserve metadata, file attributes or permissions. It also does not follow symbolic links.

Usage Examples

To pack an entire directory (recursively):

dexios pack -r dir/ dir.enc

To unpack the directory (and output it to your current path):

dexios unpack dir.enc .

Creating an Archive

First, the files and directory structure are both indexed. While indexing, if any files/directories match an "excluded" pattern, they will be ignored.

The zip file is then initialised with a random 8 character alphanumeric extension, this is to prevent possible endless-loops (the buffer kept getting filled with the data that was just written).

The directory structure is recreated within the zip file (if in recursive mode, this will include other directories and not just the source one), and all of the files are added.

Stream reading is implemented if the file is larger than 1MB - if not then the file is just read into memory.

Once the zip file has been created, it is encrypted. If the zip file is larger than 1mb, stream encryption will be used (unless specified manually).

We then erase the temporary zip file, using 1 pass of the secure erase feature.

Unpacking an Archive

First, the zip file is decrypted as a temporary file with a random 8 character alphanumeric extension. This will use memory mode if it's less than 1MB, or if specified - otherwise stream mode will be used.

We then open the temporary zip file for reading, recreate the directory structure within the output directory, and extract each file to the correct path within the output directory.

Once all of the files have been successfully extracted, we securely erase the temporary zip file. This again uses 1 pass of the secure erase feature.

Zip-slip prevention measures have been taken, although it is hard to cover them all. Ensure you only unpack files from trusted sources.

Performance Notes

Please note that performance is heavily dependent on your hardware - mostly disk speed. Dexios has had a lot of performance optimisations applied throughout the versions, but we're getting to the point where the main factor is disk speed.

We will continue to optimise Dexios where possible.

Dexios-Core

This chapter contains all of the information regarding dexios-core, the core library that Dexios uses.

This core library was created in order to separate the cryptographic functions away from the end-user application. Not only does this make Dexios more maintainable, but it means that the core functionality can be used in many other projects as well. It also means that the core library can expand and offer different functionality that we don't feel is a good fit for Dexios itself.

The core library handles password hashing, encryption/decryption, and management of Dexios headers. Dexios-Core also holds the Protected value wrapper.

You may find the dexios-core repo on Github

Headers

Structure

The full header is 64 bytes - it contains relevant information about the encrypted data.

V1 Headers (v8.0.0 - v8.2.0):

  • The first two bytes of each header should contain 0xDE - this signifies that it is indeed a Dexios file.
  • The next two bytes signify the header version - as of v8.0.0, this is 0x01
  • The next two bytes contain the encryption algorithm, such as XChaCha20-Poly1305
  • The next two bytes contain the encryption mode, which is either stream or memory
  • The next 16 bytes contain the salt used for argon2id
  • We then have 16 bytes of empty space, this has the potential to store more information in later versions
  • We then have the n byte nonce - this is determined by the encryption algorithm and encryption mode
  • We finally have enough zeroes to reach 64 bytes - this is extra empty space that may be used in later revisions

V2 Headers (v8.3.0):

  • The first two bytes of each header should contain 0xDE - this signifies that it is indeed a Dexios file.
  • The next two bytes signify the header version - as of v8.3.0, this is 0x02
  • The next two bytes contain the encryption algorithm, such as XChaCha20-Poly1305
  • The next two bytes contain the encryption mode, which is either stream or memory
  • The next 16 bytes contain the salt used for argon2id
  • We then have the n byte nonce - this is determined by the encryption algorithm and encryption mode
  • We then pad the headers with zeroes until we reach a total of 48 bytes
  • The last 16 bytes contain a truncated SHA3-512 HMAC signature

V3 Headers (v8.4.0-v8.6.0):

  • The first two bytes of each header should contain 0xDE - this signifies that it is indeed a Dexios file.
  • The next two bytes signify the header version - as of v8.4.0, this is 0x03
  • The next two bytes contain the encryption algorithm, such as XChaCha20-Poly1305
  • The next two bytes contain the encryption mode, which is either stream or memory
  • The next 16 bytes contain the salt used for argon2id
  • We then have 16 bytes of empty space, this has the potential to store more information in later versions
  • We then have the n byte nonce - this is determined by the encryption algorithm and encryption mode
  • We finally have enough zeroes to reach 64 bytes - this is extra empty space that may be used in later revisions

V4 Headers (v8.7.0+):

  • The first two bytes of each header should contain 0xDE - this signifies that it is indeed a Dexios file.
  • The next two bytes signify the header version - as of v8.7.0, this is 0x04
  • The next two bytes contain the encryption algorithm, such as XChaCha20-Poly1305
  • The next two bytes contain the encryption mode, which is either stream or memory
  • The next 16 bytes contain the salt used for balloon hashing
  • We then have the n byte nonce used for encrypting the data - this is determined by the encryption algorithm (in LE31 STREAM mode)
  • Next we have enough padding to reach 48 bytes into the header
  • We then have the encrypted master key, which is 48 bytes (32 for the data, 16 for the AEAD tag)
  • We then have the n byte nonce used for encrypting the master key - this is determined by the encryption algorithm (in standard cipher/memory mode)
  • We finally have enough padding to reach a total of 128 bytes in length

V5 Headers (v8.8.0+):

  • The first two bytes of each header should contain 0xDE - this signifies that it is indeed a Dexios file.
  • The next two bytes signify the header version - as of v8.8.0, this is 0x05
  • The next two bytes contain the encryption algorithm, such as XChaCha20-Poly1305
  • The next two bytes contain the encryption mode, which is either stream or memory
  • We then have the n byte nonce used for encrypting the data - this is determined by the encryption algorithm (in LE31 STREAM mode)
  • We then have enough padding to reach 32 bytes. This padding, and the above, is used to form the AAD
  • Next is the potential for 4 keyslots, at 96 bytes each
  • A keyslot contains:
    • A 2-byte identifier (0xDF)
    • The encrypted master key (48 bytes in length)
    • The nonce used for encrypting the master key (n bytes)
    • Padding until we reach 74 bytes total for the keyslot
    • The 16-byte salt
    • Padding until we reach 96 bytes in total
  • We finally have enough padding to reach a total of 416 bytes in length

Authenticating the Header with AAD (v8.4.0+)

Headers are calculated as a byte array (including the padding), and provided as AAD (additional authenticated data) to every block in stream mode, and the whole block in memory mode.

On decryption, the whole header is read (including the padding), and it is provided as AAD to every block in stream mode, and the whole block in memory mode. The data will not decrypt if the header does not match, even if an empty byte is altered. This fully protects your file from tampering, as even a single change will result in the user being made aware.

We have decided to use AAD as opposed to SHA3-512 HMAC due to few things:

  • HMAC cluttered up the codebase quite significantly. This is not good for a tool such as Dexios, as it'd become harder and harder to maintain
  • HMAC required two additional dependencies, hmac and sha3. This could potentially increase the attack surface (although unlikely)
  • HMAC required additional computational power - this is something a lot of older systems do not have

On the other hand, AAD requires hardly any further computational power, or cluttering of the codebase - it's already implemented by the AEAD crates that we use.

For V4 Headers, the AAD is generated based on the first 48 bytes (for salt, nonce, version, algorithm, mode information, padding) and then the padding from the end. Authenticating the padding isn't necessary, but it could help prevent some forms of tampering.

For V5 headers, the AAD is generated based on the first 32 bytes of the header. This includes the header version, encryption algorithm, encryption mode, and the nonce for the data. The keyslots may change, so they can't be included with the AAD.

Adding a Key (v8.8.0+, V5 Headers+)

You can provide keyfiles with the -k and -n switches (for old and new keyfiles respectively). You may provide just an old keyfile, and --auto for autogenerating a new passphrase. The combinations are there to allow for you to easily change any key.

The file is first opened, and the header is deserialized. Both keys are then obtained, and are subsequently hashed.

Once both keys have been hashed successfully, the master key stored within the header is decrypted, using the old key. The master key is then encrypted with your new key, before being written back to the file.

Please view the notes about key manipulation.

Changing a Key (v8.8.0+, V5 Headers+)

You can provide keyfiles with the -k and -n switches (for old and new keyfiles respectively). You may provide just an old keyfile, and --auto for autogenerating a new passphrase. The combinations are there to allow for you to easily change any key.

The file is first opened, and the header is deserialized. Both keys are then obtained, and are subsequently hashed.

Once both keys have been hashed successfully, the master key stored within the header is decrypted, using the old key. It is then re-encrypted with your new key, and a new nonce, before being written back to the file.

Please view the notes about key manipulation.

Deleting a Key (v8.8.0+, V5 Headers+)

You can provide a keyfile with the -k switch, if required.

Your key is needed in order to identify which key you'd like to remove from the header. If this is not the functionality you're after, you may be interested in stripping the header.

Notes about Header-Key manipulation

With V5 headers, we now have the option to change/delete a key. It's extremely important to note that key change and key del will only act upon the first match that they find - not all matches.

If you have multiple keyslots with the password password, and you go to delete that key, you will need to run the action multiple times in order to remove them all. This is a design decision in order to make these key manipulation functions less-time consuming when multiple keyslots are present (it requires hashing the provided key up to 4 times with different salts). You may open an issue on Github if you oppose this choice.

Stripping

Headers are stripped by zeroing out the first n1 bytes of the file - this is where the header is stored. Headers are first deserialized to ensure you are stripping a valid Dexios header.

1 This is dependent on the header version

Dumping

Headers are dumped by reading/deserializing the header from the input file, and writing them to the output file. This is a protective measure to ensure that you are dumping a valid Dexios header.

Restoring

Headers are restored by deserializing the header provided in the input file, and writing the header to the output file.

Signing (v8.3.0 - replaced by AAD)

Headers are signed using your hashed encryption key.

On encryption, the headers are signed when the ciphers/stream ciphers are initialised, this way we only have to hash your key once. The key is never cloned within memory, and it is zeroed out (using zeroize and our protected wrapper) once the header has been signed.

The signature is generated from the first 48 bytes of the header - this is where all of the important information is enclosed.

Verifying (v8.3.0 - replaced by AAD)

On decryption, the headers are verified when the ciphers/stream ciphers are initialised. Once again, the key is zeroed out once the header has been verified.

If a single byte of the header changes, decryption will not continue as the signature will not match. This is to prevent a malicious actor from tampering with the header.

If the signature doesn't match, but you're positive your key is correct, it is best to assume the header was tampered with.

Password Hashing

Password hashing was done with argon2id in all versions prior to v8.7.0. In versions v8.7.0 and above, BLAKE3-Balloon hashing is used. Both modes use a 16-byte salt to derive the hashed key. Once a key has been hashed, it is dropped and zeroed out via the zeroize crate to ensure it stays in memory no longer than required.

You are able to use argon2id for hashing as of v8.8.0, via the --argon switch.

Handling the Hash

The hash is wrapped in Protected<>, which means it can only be exposed when called for in the program. It is only ever exposed to the cryptographic ciphers, and it is dropped right after. We use a custom Protected<> wrapper which implements zeroize-on-drop, to ensure that all sensitive information is removed from memory once it is no longer required.

Encryption

Encryption is done by default with XChaCha20-Poly1305, but Dexios also has options for AES-256-GCM and Deoxys-II-256. Encryption uses a cryptographically-secure randomly generated 32-byte value. This is then encrypted using the key you provided (once it has been hashed). Once the master key is encrypted, it is stored within the header. You may read more about this below.

Stream Mode

The XChaCha20-Poly1305 nonce is 20 bytes long, and the AES-256-GCM nonce is 8 bytes long when stored. The stream encryptor dynamically changes the nonce, based on a 31-bit little endian counter and a 1-bit "last block" flag. Those last 32 bits (4 bytes) are appended to the end of the nonce - making the total nonce length 24/12 bytes respectively. This is done by the stream encryptor to ensure that identical data will not be encrypted with the same nonce.

On encryption (as of v8.7.0), a completely random 32-byte "master key" is generated. The user's key is then used to encrypt the master key, and that is included within the header (along with the random nonce). This was implemented as it will allow us to add features such as changing a key, or allowing multiple keys to decrypt the same file.

On encryption, the header is serialised and written to the start of the file. Then, Dexios reads the source file in 1MiB blocks (1048576 bytes), and encrypts them using an LE31 stream encryptor. Each "block" is read, encrypted, written. The header is always the same size, so we can avoid having to serialise/deserialise the data with something such as JSON. This had a lot of performance benefits.

For decryption, the header is is read and deserialised. Next, Dexios reads the remaining part of the encrypted file in 1MiB blocks + 16 bytes, to account for the AEAD tag. Each "block" is read, decrypted, and then written.

Stream mode - Edge Cases

Edge cases regardigng very specific block sizes have been checked and confirmed to be working.

The major concern was when a file contains bytes exactly divisible by 1048576, but this is not an issue.

To test this, I created a file with exactly 3145728 bytes (3x streaming blocks). I then instructed Dexios to show me the read_count when encrypting, and it read the file 4 times - 3 times for each of the 3 blocks, and one containing 0 bytes. The 0 bytes were encrypted using the encrypt_last function, adding 16 bytes to the end of the file.

The total encrypted file size was 3145856 bytes - let's break it down.

  • 64 bytes are for the Dexios header
  • (16*3) are for the AEAD tag, 16 bytes per block

This brought our file size down to 3145744 bytes, which is a difference of 16 bytes compared to our source file. This confirms that the source data is encrypted completely, even in rather specific edge cases.

Using dexios hash, we can confirm that the file hashes are exactly the same before encryption, and after decryption.

Memory Mode

Memory mode should only be used in very specific cases. Streaming mode works on files less than the BLOCK_SIZE (1MiB) and should be favoured.

In memory mode, the nonce is static and 24/12 bytes (XChaCha20-Poly1305/AES-256-GCM) long, as the data is encrypted all in one pass.

On encryption, the data is read from the source file and placed into a Vec<u8> before being encrypted. The header is written, and then the encrypted data is written to the file - in that order.

On decryption, the 16 byte salt (used for hashing) and the 24/12 byte nonce are read - in that order. Dexios reads the remainder of the file into a Vec<u8>, before decrypting it and writing the plaintext bytes to the output file.

Protected Wrapper

We have implemented our own Protected<> wrapper/type, that implements zeroize-on-drop. This ensures all sensitive information is securely zeroed-out once it is no longer required, and that the sensitive informatiton is only accessed when explicit calls are made. Our implementation was inspired by the secrecy crate's functionality, so I'd like to provide a huge thanks to the original creators.

Protected values do not implement any copying. This is to prevent possible leaks of data.

Protected values may be explicitly cloned, but this requires .clone() or similar - you can easily find these within a codebase.

The only way to access the information held within this type, is to call .expose() - this makes things very manageable. With this, we are able to quickly and easily see wherever protected information is accessed. Not only does this allow us to detect potentially malicious pull requests and such, but it also makes auditing a lot easier.

Protected values are also redcated from debug logs and such, and they will show "[REDACTED]" whenever they are printed. They do not implement fmt::display.

You may find all of the information about how to use dexios-core on the docs.rs page.

This documentation covers just about everything that can be done, and is a great starting point to implementing Dexios within your own program!

Auditing

Official Audit

We currently have no plans to purchase an official audit from a company, as we just do not have the money. They're expensive, and they're only "valid" for a snapshot of your code, so it's probably not worth it for us.

We do use audited encryption libraries, so we're probably covered on that front. Rust doesn't allow for most issues thanks to it's compiler and lints, and with a bit of thought, we can prevent incorrect usage of these libraries.

Auditing Yourself

If anyone would like to take a dive into the code, and ensure that cryptography-related functions are written correctly, you may find items of interest within these directories:

  • src/stream.rs - this handles STREAM initialisation, encryption and decryption
  • src/cipher.rs - this handles "memory" initialisation, encryption and decryption
  • src/key.rs - this handles salt generation and argon2id hashing
  • src/primitives.rs - these are the constants used, and it contains a function for generating the nonce
  • src/header.rs - this contains functions for serialising/deserialising/getting the AAD of headers
  • src/protected.rs - this contains our Protected<> wrapper that implements zeroize-on-drop, to erase secrets from memory once they're no longer in use

I'd highly recommend taking a peek into key.rs, stream.rs, and cipher.rs - these are the main cryptographic files.

If you happen to find anything problematic, please open a Github issue or email brxken128@tutanota.com. This may be done anonymously.

Dexios-Domain

You may find rather high-level and basic documentation on docs.rs.

Dexios-Domain doesn't need much of an introduction, as it's usage is rather self explanatory. It is a layer between Dexios front-ends, and the core library.

It does offer some functionality of it's own - namely the key manipulation functions and the erase directory logic.

You craft a Request, and then execute the correct function, and your action is completed.

This library was made to allow easier integration of the Dexios format into different applications, such as a GUI front-end (or any other apps that will benefit from at-rest full file encryption).

Reporting a Vulnerability

Please report any vulnerabilities as a Github issue - we believe all issues should be known, and they are likely to get resolved very quickly this way. Thank you.

As an alternative, you may contact brxken128@tutanota.com

If you find any vulnerabilities within Dexios, and can provide steps/pointers to reproduce, please report them. You may do this anonymously via the email above. I'm afraid I cannot offer any money in return, but I can add you to the list of contributors (at your request).

Supporting Dexios

If you like my work, and would like to support the project, feel free to donate! This is not mandatory, so please don't feel obliged to do so.

I will continue to work on Dexios either way for the forseeable future.

XMR: 84zSGS18aHtT3CZjZUnnWpCsz1wmA5f65G6BXisbrvAiH7PxZpP8GorbdjAQYRtfeiANZywwUPjZcHu8eXJeWdafJQFK46G
BTC: bc1q8x0r7khrfj40qd0zr5xv3t9nl92rz2387pu48u
ETH: 0x9630f95F11dFa8703b71DbF746E5c83A31A3F2DD