Simple secret-key encryption without the PGP/GPG/OpenSSL/OMG jungle. Secure by default by leveraging libsodium's power.
Crypto Box gives you two (filter) utilities:
have been developed with the Unix philosophy Do one thing and do it well in
mind. They are very simple to use, but that doesn't mean you can't do anything
wrong. As always, it's your responsibility to keep a secret key secret.
Reads plaintext from STDIN and writes ciphertext to STDOUT. Below are the different ways of specifying a key. The ciphertext will be sightly larger than the plaintext. See Internals.
Random key (no key file)
If no options are given, a random key is generated and printed on STDERR. It will not be automatically stored anywhere. This is only safe to use locally or over secure connections like SSH (and nobody looking over your shoulders).
$ echo foobar | lock_box > locked.box 4a5c4119c24b0db47bb4b4d8383c716ea390f04553f8877d0c94099e1ac12eb6 $ ls -l locked.box -rw-r--r--+ 1 user staff 48 Jun 18 12:20 locked.box
The output file in this case is 24+16+1+7=48 bytes long, for nonce, chunk MAC, chunk type, and ciphertext, respectively. See Internals for more details.
The long hex string is the randomly generated key. Store it somewhere safe and keep it secret.
To decrypt a box later, specify the key directly on the command line or use
--ask (see below).
Use the option
--key-file to specify a key file. Note: If the key
file doesn't exist yet, a randomly generated key will be used and stored into
that file. Only use this if your key file will be located on an encrypted
$ ls -l secret.key ls: secret.key: No such file or directory $ echo foobar | lock_box -k secret.key > locked.box $ ls -l locked.box secret.key -rw-r--r--+ 1 user staff 48 Jun 18 19:31 locked.box -r--------+ 1 user staff 48 Jun 18 19:31 secret.key
If the key file already exists, its first 32 bytes are used as the key. Note: Crypto Box will refuse to use a key file that permits access to anyone else but the owner.
$ ls -l *secret.key -rw-r--r--+ 1 user staff 32 Jun 18 19:29 not_so_secret.key $ echo foobar | lock_box -k not_so_secret.key > locked.box lock_box: Please specify a *secret* key file.
Keep in mind that, on a shared system, the administrator (
root) can still
read your key file. Hell, anybody is still able to read your key file if your
system sucks (security vulnerabilities, physical access but no disk
Key as command line argument
You can specify a key on the command line, using hex ASCII characters (
This can be useful if you let
lock_box generate a random key earlier without
storing it to a key file or you've kept it as a piece of information outside
If you do this, make sure your command won't get logged! Enable the option
hist_ignore_space in Zsh or
ignore_space in Bash.
# notice the additional space before the whole command, so it won't get logged $ echo foobar | lock_box abba0ff887ca6064622b30a47a2aa9980faa1f544b24a9991b14e948d7331728 > locked.box $ ls -l locked.box -rw-r--r--+ 1 user staff 48 Jun 18 12:22 locked.box
:) in the key are ignored, so you can also specify the key like this:
A key shorter than 32 byte (which would be at least 64 ASCII hex characters) will be repeated to make up a complete 32 byte key. This is not recommended, as it greatly decreases the information content of the key, which makes it easier to guess.
$ echo foobar | lock_box 6ea390f04553 > insecurely_locked.box lock_box: Warning: Reusing key material to make up a complete key. $ ls -l locked.box -rw-r--r--+ 1 user staff 48 Jun 18 12:24 insecurely_locked.box
Prompting for the key
Use the option
--ask to be prompted for a key. In this case, you have to
specify the input file with
--file, as STDIN is already used to get the
key. This can be useful if you're worried about your command being logged in
your shell's history.
$ echo foobar > secret.txt $ lock_box --ask --file secret.txt > secret.box Enter key:
Don't forget to delete your plaintext file after encrypting it! ;-) Or avoid creating a file altogether with some shell magic:
$ lock_box -af <(echo foobar) > secret.box Enter key:
Reads ciphertext from STDIN and writes plaintext to STDOUT. The
key can be given in the same ways as for
lock_box. Here's an example using a
$ open_box -k secret.key < locked.box foobar
In case the box has been tampered with, MAC verification will fail and the program will exit with a message on STDERR. Example:
$ echo foobar | lock_box -k secret.key > locked.box $ ls -l locked.box secret.key -rw-r--r--+ 1 user staff 48 Jun 18 12:27 locked.box -r--------+ 1 user staff 48 Jun 18 12:27 secret.key $ echo "baz" >> locked.box $ ls -l locked.box -rw-r--r--+ 1 user staff 52 Jun 18 12:27 locked.box $ open_box -k secret.key < locked.box open_box: Ciphertext couldn't be verified. It has been tampered with or you're using the wrong key.
As mentioned above, libsodium is used to do encryption/decryption/authentication. The cryptographic primitives used are XSalsa20 and Poly1305.
XSalsa20 (with its 24 byte nonces) is a good choice because it allows one to safely use randomly generated nonces. Of course, the full 20-rounds version of XSalsa20 is used.
Poly1305 will ensure the integrity of your data. Never use encryption without authentication to verify the integrity of the encrypted data. If you don't care if someone tampers with your data, you might as well just send plaintext.
The memory used for the secret key is locked before the key is stored in it and
zeroed out and unlocked before the programs exit. This applies to the randomly
generated key and the one given on STDIN (with
--ask). The key given as
a command line argument is zeroed out right after reading it (but not locked).
Encryption and decryption are done in chunks. This means that only a small amount of memory is used, no matter how big the input is. Each chunk is encrypted using a new nonce and authenticated with a MAC. However, only the first nonce will be output. All further nonces can be calculated from the first (simply increments).
Also, each chunk MAC will not only be computed over its ciphertext, but also over the previous chunk's MAC to avoid missing/reordered/replayed chunks going undetected. To know which chunks are the first and the last (to avoid truncation going undetected), each chunk gets an additional byte to identify its type. If there's only one chunk, it's marked as the last. This way, tail truncation of all but the first chunk can be detected.
That means that
lock_box will output 24 additional bytes for the nonce plus
16+1=17 additional bytes for each chunk. Versions before 0.4.0 would output a
fixed number of 24+16=40 additional bytes for the whole input. According to
libsodium's documentation, one single MAC isn't suited for huge files.
The chunk size is 64 KiB (or less for the last chunk, depending on input size). This makes the speed and size overheads negligible and still allows a tiny memory foot print.
Schematically, the output of
lock_box will look like this:
+-----------------------+----------------------------------------------------+ | nonce (24 bytes) | one or more chunks | +-----------------------+----------------------------------------------------+
Whereas each chunk will look like this:
+-------------------+------------+-------------------------------------------+ | MAC (16 bytes) | type (1 B) | ciphertext (up to 64 KiB - 17 B) | +-------------------+------------+-------------------------------------------+
Truncated ciphertext input produces incomplete plaintext output
It's possible that
open_box will output data before it
eventually finds out that the ciphertext has been truncated. However, it will
never ever output any unauthenticated data. And, of course, when it
eventually finds out the ciphertext has been truncated, it exits immediately
with an error code.
In other words: Don't use Crypto Box if you're redirecting the plaintext to
another command which must never ever read a single byte of truncated plaintext
(even before it will be terminated right after
open_box exits with an error
Reason: In a pipeline, it's impossible to know how long the data from STDIN is, unless you want to read everything into memory (or a temporary file) before starting to write to STDOUT. Starting with 0.4.0, Crypto Box works in chunks to keep a small memory footprint, no matter how large the input. That's why it works like this.
There's no padding involved, even when the plaintext input's length is 0. The plaintext's length can be calculated from the ciphertext's length.
Due to the filter nature and lack of a header that specifies the length of the data, there are no real limitations in the length of a file/data stream you can encrypt (at once). Of course, reasonable reuse of a key is advised.
To be precise: You could safely encrypt up to 3.347787592E35 YiB at once with one key. That's a whole lot more than all of the WWW. And more than ZFS can store.
Explanation: We have a 24 byte nonce. That nonce is used to encrypt one chunk of 64 KiB. After that, the nonce is incremented for the next chunk. 24 bytes are 192 bit, so we have 2^192 different nonces for one key. Each nonce can be used to encrypt up to 64 KiB. So:
2^192 * 64 KiB = 3.923188585E56 KiB = 3.831238852E53 MiB = 3.741444192E50 GiB = 3.653754093E47 TiB = 3.568119232E44 PiB = 3.484491437E41 EiB = 3.402823669E38 ZiB = 3.347787592E35 YiB