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
OS | Working? |
---|---|
Void Linux | Yes |
Fedora 35 | Yes |
Fedora 36 | Yes |
Ubuntu 20.04 | Yes |
FreeBSD 13 | Yes |
FreeBSD 14 | Yes |
Windows 10 | Yes |
Windows 11 | Yes |
Android 12 | Yes |
MacOS | Yes |
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 | -dHyk | Stream? |
---|---|---|---|
3.2.8 | 44.37s | 40.91s | No |
4.0.0 | 23.70s | 30.43s | No |
5.0.0 | 22.48s | 28.66s | No |
5.0.2 | 20.14s | 21.26s | No |
5.0.9 | 19.31s | 18.92s | No |
6.0.0 | 11.74s | 11.59s | No |
6.4.8 | 5.61s | 5.35s | Yes |
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 String
s. 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
- A 2-byte identifier (
- 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
andsha3
. 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 decryptionsrc/cipher.rs
- this handles "memory" initialisation, encryption and decryptionsrc/key.rs
- this handles salt generation andargon2id
hashingsrc/primitives.rs
- these are the constants used, and it contains a function for generating the noncesrc/header.rs
- this contains functions for serialising/deserialising/getting the AAD of headerssrc/protected.rs
- this contains ourProtected<>
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