Saltybox is a tool for encrypting files with a passphrase.
Its primary intended use-case is for encrypting small amounts of personal data for safe keeping on untrusted media (physical or otherwise).
brew install scode/dist-tap/saltybox
Requires Rust.
$ cargo install --path .
Assuming ~/.cargo/bin is in your $PATH, saltybox is now ready to use.
Alternatively, you can build and run directly from the repository:
$ cargo build --release
$ ./target/release/saltybox --help
If you decide to use saltybox for anything important, please review guidance for use.
Here's how to encrypt a file (you will be interactively prompted for a passphrase):
saltybox encrypt -i allmysecrets.txt -o allmysecrets.txt.saltybox
And here is how to decrypt it afterwards (again, you will be interactively prompted for a passphrase):
saltybox decrypt -i allmysecrets.txt.saltybox -o allmysecrets.txt
And here is how to update a previously encrypted file in a manner that ensures the passphrase is not accidentally changed:
saltybox update -i allmysecrets-updated.txt -o allmysecrets.txt.saltybox
update requires -i and -o to reference different files. Using the same file (or a path alias that resolves to the
same file) is rejected.
- Files must fit comfortably in memory and there is no support for encrypting a stream in an incremental fashion.
- There is no attempt to lock the passphrase or derived key into memory. The passphrase may be paged to disk by the operating system. You are responsible for the security of the device on which you run this program.
- File writes use atomic same-directory temporary files named
.saltybox-*.tmp. On Unix, these temporary files are written with mode0600and then atomically renamed into place. In the event of a crash, stale.saltybox-*.tmpfiles may remain and should still be private (0600). - The format is based upon well known algorithms with exceedingly simple layering on top. scrypt is used for key stretching, nacl is used for encryption, and a base64 variant is used for encoding.
- The amount of code is relatively small and light on dependencies.
Always use update when updating an existing encrypted file. This avoids the possibility of accidentally changing the
passphrase by providing a different passphrase than what was used to encrypt the existing file. If you manually decrypt
and re-encrypt, you lose this protection.
It is important to consider the possibility that saltybox disappears from github, or stops building because a dependency has changed or becomes unavailable. In order to ensure that you are able to decrypt your data if such a thing were to happen, the following steps are recommended:
- Store a copy of the binary for your platform(s) in a safe place.
- In a copy of the source code, run
cargo vendorto download all necessary dependencies into thevendordirectory. Ensure saltybox builds withcargo build --frozen --offline. Then store a copy of the complete source tree (includingvendorandCargo.lock) in a safe place.
In an emergency need to decrypt data, this should maximize your chances of being able to do so without relying on external projects/people aside from the Rust language tools themselves remaining available.
- Future versions if any will remain able to decrypt data encrypted by older versions.
- The command line interface may change at any time. It is currently not intended for automated scripting (for this reason and others).
- The code in this project is not meant to be consumed as a library and may be refactored or changed at will. It's possible this changes in the future, but if so it will be explicitly made clear.
I am not a cryptographer and the code has not been reviewed by any cryptographers. Are you one? Please send me feedback ([email protected]).
Although I certainly did not attempt to invent any new cryptographic primitives and rather use well established trusted primitives, there is generally a risk that cryptographic primitives are used incorrectly when composed into a larger program.
Unfortunately, I have not been able to find a tool like this that satisfies my personal criteria for what I want to depend on for emergency life recovery media.
Saltybox encrypts files using a passphrase-based encryption scheme. The output is a text file containing an armored (ASCII text) string that represents the encrypted data. This section documents the format.
- The contents of the file starts with the string
saltybox1:which identifies the format. - This is followed by a base64url encoded (RFC 4648 Section 5, no padding) payload whose format is described below
("binary format").
- Uses URL-safe base64 alphabet:
-and_instead of+and/
- Uses URL-safe base64 alphabet:
- Example:
saltybox1:RF0qX8mpCMXVBq6zxHfamdiT64s6Pwvb99Qj9gV61sMAAAAAAAAAFE6RVTWMhBCMJGL0MmgdDUBHoJaW- The
1in the prefix indicates the format version. Future versions would use a different version number (e.g.,saltybox2:).
- The
The binary format contains the following, in order:
- Salt (8 bytes): Random salt used for key derivation.
- Nonce (24 bytes): Random nonce for the NaCl secretbox encryption.
- Length (8 bytes): Big-endian encoded signed 64-bit integer (int64) indicating the number of bytes in the sealed box that follows. This value must be non-negative and must not exceed the remaining length of the input data after the salt, nonce, and length fields. During decryption, invalid lengths (negative, too large, or causing truncation) are rejected as format errors.
- Sealed Box (variable length, as specified by the length field): The encrypted payload, sealed using NaCl's
secretbox(XSalsa20 stream cipher with Poly1305 MAC). The sealed box contains the user's plaintext exactly - without any padding or additional metadata.
The encryption key is derived from the user-provided passphrase and the salt using scrypt with parameters:
- N = 32768
- r = 8
- p = 1
- Key length = 32 bytes
The repository includes comprehensive test vectors in testdata/golden-vectors.json for purpose of
correctness/compatibility testing.
Run golden vector tests:
cargo test --test golden_vectors -- --ignored
(The arguments are required because by default not all golden vectors are tested in order to avoid adding multiple seconds to test time.)
The testdata/golden-vectors.json file contains a JSON array of test vectors. Each vector is an object with these
fields:
- plaintext: Base64-encoded plaintext data to encrypt
- ciphertext: Armored (text format) expected ciphertext (e.g.,
saltybox1:...) - nonce: Base64-encoded nonce used for encryption
- salt: Base64-encoded salt used for encryption
- passphrase: Base64-encoded passphrase used for encryption
- comment: Human-readable description of what the test case exercises
All binary data (including passphrases that may contain arbitrary bytes) is base64-encoded for safe JSON storage. The ciphertext is in the final armored text format that would be written to a file.