This is the encryption docs for file structure version 2.
Files are encrypted using the ChaCha20/NONE/NoPadding
cipher in Android. See Android Ciphers for details.
The key algorithm is PBKDF2withHmacSHA512
with 50000 iterations (default, can be changed) and a 256-bit key length.
The salt is 16 bytes and the IV is 12 bytes. An additional 12 bytes is used to check if the supplied password can decrypt the file, see details below.
The following filename suffixes are used:
-i.valv
for image files-g.valv
for GIF files-v.valv
for video files-x.valv
for text files-n.valv
for note files-t.valv
for thumbnail files
The number represents the file structure version (for future expansion/changes).
Filenames are generated randomly and are 32 chars + SUFFIX_LENGTH
long.
Every media file has a corresponding thumbnail with the same name. For example, an image file named
aLFshh71iywWo7HXtEcOtZNVJe-Ot7iQ-i.valv
has a thumbnail
aLFshh71iywWo7HXtEcOtZNVJe-Ot7iQ-t.valv
.
Similarly, a note has the same name as its media file.
All text and strings are encoded as UTF-8.
The app creates the encrypted files in the following way:
- Generate a random 16 byte salt, a 12 byte IV and 12 check bytes.
- Create an unencrypted output stream.
- Write the encrypted file structure version (4 bytes, integer)
- Write the salt.
- Write the IV.
- Write the iteration count used for key generation (4 bytes, integer)
- Write the check bytes.
- Pass the output stream into a cipher (encrypted) output stream. Everything below is encrypted.
- Write the check bytes.
- Write a newline character followed by a JSON object as an string containing the original filename and another newline character (
'\n' + "{\"originalName\":\"file.jpg\"}" + '\n'
). - Write the file data.
The app reads the encrypted files in the following way:
- Create an unencrypted input stream.
- Read the encrypted file structure version (4 bytes, integer)
- Read the 16 byte salt.
- Read the 12 byte IV.
- Read the iteration count used for key generation (4 bytes, integer)
- Read the 12 check bytes.
- Pass the input stream into a cipher (encrypted) input stream. Everything below is read from encrypted data.
- Read the check bytes. If the unencrypted check bytes does not equal the check bytes in the encrypted part, the given password is invalid.
- Read a newline character (
0x0A
) followed by the JSON object string and another newline character. - Read the file data.