You can not select more than 25 topics
Topics must start with a letter or number, can include dashes (‘-‘) and can be up to 35 characters long.

WARNING: This is a personal project, unsafe for real data. Don’t use it.

Encrypt files using a random keyfile, like enchive without public keys. Plain
256-bit encryption satisfies even the most paranoid quantum computing fears.

Each file’s encryption key derives from a common keyfile plus a 256-bit random
seed. Keep the keyfile secret. Its passphrase discourages only casual snooping,
given that passphrases can be discovered by brute-force or hidden camera.

Currently uses Argon2id, BLAKE2b, and ChaCha20-Poly1305 from libsodium.

License

Copyright 2022-2023 Mark Dascher


Licensed under the ISC License.

Specification

WARNING: All details are still subject to change.

Argon2id makes two passes of 1 GiB by default. The memory limit is adjustable
between 1 MiB and 512 GiB, but only in even powers of two. Therefore, there are
only 20 possibilities to try when the correct value is inevitably forgotten.

ChaCha20-Poly1305 refers to the IETF/TLS variant, having a 96-bit nonce. Here
the nonce is treated as a single little-endian integer consisting of 12 bytes.

salt = 16 random bytes
keyP = Argon2id(salt, "ADIOS MOOCHACHA" || 0 || password)
keyW = 32 random bytes
wrap = ChaCha20-Poly1305(keyP, nonce=0, msg=keyW)
keyfile = (salt, wrap)

After creating or unlocking the keyfile, encryption of plaintext can begin. The
maximum file size is 2^108 + 8191 = 324,518,553,658,426,726,783,156,020,584,447
bytes which…ought to be enough for anybody?

A sequential 96-bit ChaCha20 nonce is exactly as safe as a random 192-bit
XChaCha20 nonce, even without overflow checking. Still, go ahead and check for
nonce overflow if you want, since it’s mathematically the weakest point.

plaintext = (p0, p1, ..., pN); where p0 is 12288 bytes,
                               each inner p is 4096 bytes,
                               and pN is 0-4095 bytes.
         or (p0); where p0 is 0-12287 bytes.

seed = 32 random bytes
keyF = BLAKE2b-256(keyW, msg=seed)

c0 = ChaCha20-Poly1305(keyF, nonce=0, msg=p0)
c1 = ChaCha20-Poly1305(keyF, nonce=1, msg=p1)
...
cN = ChaCha20-Poly1305(keyF, nonce=N, msg=pN)

ciphertext = (seed, c0, c1, ..., cN); where c0 is 12304 bytes,
                                      each inner c is 4112 bytes,
                                      and cN is 16-4111 bytes.
          or (seed, c0); where c0 is 16-12303 bytes.

When decrypting, the sequential nonce protects against reordering. To protect
against truncation, confirm that pN < 4096 bytes or p0 < 12288 bytes.

Threat model

Suppose a cloud provider, criminal organization, or government quietly retains
encrypted files for decades, waiting until decryption is feasible. Can we
encrypt uploads safely enough to resist theoretical quantum attacks?

Of course, a government has plenty of old-fashioned tools to capture the common
keyfile from your computer, but those require actual effort and target specific
individuals. The goal is merely to protect against automated mass surveillance.
The main exception involves malware, which can efficiently steal data and keys.

The 128-bit Poly1305 MAC might not resist future attacks, but that’s not fatal
as long as those attacks don’t reveal or weaken the encryption key. If Poly1305
is broken, simply delete this program and never use it again.

Weaknesses

An adversary who manages to unlock the keyfile can decrypt every file ever
encrypted. That’s unavoidable for offline storage. An online protocol should
rather exchange keys interactively, to achieve backward and forward secrecy.

Avoiding public-key cryptography is a tradeoff, since there’s no way to take a
private key offline. On the other hand, that’s not always as useful as it
sounds. For example, encrypted backups should be tested periodically to make
sure the decryption key works. If there’s a dedicated machine to validate
backups, then why not encrypt the backups on that same machine?

Still, definitely consider that there may be a better tool for the job.

Static analysis and testing

All C code passes analysis using Cppcheck, Frama-C’s Eva plugin, and Infer.
There’s also a custom test suite with full branch coverage. For details, see
checker.sh, frama.log, and the Makefile.

TODO

This tool is incomplete and unsafe. A partial list of what’s still needed:

  • Add password change feature.
  • Add typical command-line arguments.
  • Finish tweaking file format. Future revisions won’t be backward-compatible.
  • Revisit obsolete or unusual techniques.
  • Windows handles CTRL+C inconsistently until bug 334 is fixed.
  • Windows only supports ASCII passwords until bug 4551 is fixed.

The code is full of global variables and other anti-patterns, just for fun.

Usage

Build moochacha

The Makefile supports GCC and MSVC directly. To use Clang instead, delete
-fanalyzer and -flto-partition=none from CFLAGS.

Before building on Linux, install libsodium-dev (Debian/Ubuntu),
libsodium-devel (Fedora/RHEL), or a similar package.

## Linux
make lint
make
make check

Before building on Windows, download and extract the libsodium MSVC
pre-built library.
Place the extracted include folder and dynamic libsodium.lib for your MSVC
version into this project’s root folder, along with libsodium.dll.

:: Windows
nmake

Encrypt a file

Encrypting uses an existing keyfile if available. Provide the passphrase when
prompted. The default keyfile is moochacha.key.

Otherwise it creates a new keyfile and prompts for a passphrase. Every future
encryption and decryption will ask for the same passphrase.

## Linux
moochacha secret.txt.moo && rm secret.txt
:: Windows
moochacha <secret.txt >secret.txt.moo && del secret.txt

Decrypt a file

Decrypting requires the same keyfile used during encryption. Provide the
passphrase when prompted.

## Linux
moochacha -d secret.txt && rm secret.txt.moo
:: Windows
moochacha -d <secret.txt.moo >secret.txt && del secret.txt.moo

Options

The -k option selects a keyfile other than the default moochacha.key.

## Linux
moochacha -k ~/moochacha.key >secret.txt.moo
moochacha -dk ~/moochacha.key 
:: Windows
moochacha -k "%USERPROFILE%moochacha.key" >secret.txt.moo
moochacha -dk "%USERPROFILE%moochacha.key" <secret.txt.moo

The -m option adjusts the Argon2 memory limit, 1 GiB by default. Stick with
the default unless it needs to run on less RAM. A longer passphrase does more
for security than any Argon2 settings. There are 20 possibilities:

1M 2M 4M 8M 16M 32M 64M 128M 256M 512M
1G 2G 4G 8G 16G 32G 64G 128G 256G 512G

Padding

No padding is added by default. To conceal file sizes, add padding first. The
included padmoo proof-of-concept adds a specific amount of padding
to a plaintext file. Then, when that padded file is encrypted, the encrypted
output matches one of the legal Padmé sizes.

This could be built into moochacha itself, but keeping it separate may protect
against timing attacks. Besides, Padmé is just one possible padding scheme.

# Run padmoo before encrypting.
padmoo secret.txt
moochacha secret.txt.moo && rm secret.txt

# Run padmoo after successfully decrypting, to remove the padding.
moochacha -d secret.txt && rm secret.txt.moo
padmoo secret.txt

Read More