Rust command-line tool to encrypt and decrypt files or directories with age

Overview

Bottle

A Rust command-line tool that can compress and encrypt (and decrypt and extract) files or directories using age, gzip, and tar.

Bottle has no config options and optionally accepts two (simple) flags, in an attempt to follow age's philosophy of simplicity. It can take multiple files or directories.

This program is currently just a toy. I would not use it for real-world encryption/archiving at this time. As the age crate, which this tool uses, warns, "Caution: all crate versions prior to 1.0 are beta releases for testing purposes only."

Install

  1. Install Rust, if you haven't already. Recommend version 1.57+.
  2. Install Bottle by running: cargo install --git https://github.com/sts10/bottle-rs --branch main

Optional: While Bottle does not require age be installed, you may want to have it handy. Probably best to use age version 1.0+ when interacting with bottled files.

Bottle's executable command is bottle.

Usage

Things to know about Bottle

Bottle is hard-coded to use an Age Identity (basically a public/private key-pair) located at ~/.bottle/bottle_key.txt. If there isn't a file there, Bottle will create one the first time you use Bottle.

Bottle will always create the outputted file or directory in the current working directory. This outputted file will be named automatically based on the inputted file. If a file or directory with that name already exists, Bottle will throw an error and quit. Users can force an overwrite with the --force/-f flag.

The (informal) basics

  • Encrypt a file with bottle <path/to/file>
  • Compress and encrypt a directory with bottle <path/to/directory>
  • Decrypt an age-encrypted file with bottle <path/to/file.age>. (Must have a .age extension.)
  • Decrypt and extract a .tar.gz.age file with bottle <path/to/archive.tar.gz.age>. (Must have a .tar.gz.age extension.)

If given multiple "targets", bottle will act on them completely independently.

Note that when you encrypt a file, you must have access to the file at ~/.bottle/bottle_key.txt to decrypt it at a later time. So take your bottle_key with you, but keep it safe!

Help text

USAGE:
    bottle [FLAGS] [TARGETS]...

FLAGS:
    -f, --force         Force overwrite when creating a file or directory
    -h, --help          Prints help information
    -t, --time-stamp    If encrypting a file or directory, add a timestamp to the end of filename (but before file
                        extensions) of the resulting, encrypted file. Format is rfc3339, with colons replaced with
                        underscores. If decrypting a file, this flag is effectively ignored
    -V, --version       Prints version information

ARGS:
    <TARGETS>...    Files and/or directories to either encrypt or decrypt. If given a directory, will tar, then gzip
                    (compress), then encrypt, creating a file with the extension .tar.gz.age. If given a .tar.gz.age
                    file, will decrypt and extract contents. Can accept multiple targets. Bottle will act on each of
                    them separately. All outputted files are placed in the current working directory

Examples

Tar, compress and encrypt a folder of your journal entries with

cd ~/files-to-upload
bottle ~/Documents/journal_entries

This bottle command will create an encrypted file called journal_entries.tar.gz.age in ~/files-to-upload. You can then safely upload that journal_entries.tar.gz.age file to a cloud service.

Now let's say your on a new computer and want access to your journals again. To decrypt your journals to your new Documents folder, first place your bottle_key.txt file at ~/.bottle/bottle_key.txt. Then, download the journal_entries.tar.gz.age file to ~/Downloads and run:

cd ~/Documents
bottle ~/Downloads/journal_entries.tar.gz.age

This will place your journal_entries directory at ~/Documents/journal_entries.

If you'd like Bottle to timestamp your encrypted files, just add a -t flag when you encrypt: bottle -t ~/Documents/journal_entries. This will create a file called journal_entries__bottled_2022-01-10T22_49_12-05_00.tar.gz.age. Bottle of course can decrypt this file as well: bottle journal_entries__bottled_2022-01-10T22_49_12-05_00.tar.gz.age.

Multiple targets

Bottle can even encrypt multiple files and directories with one command: bottle file1.txt directory2 will create file1.txt.age and directory2.tar.gz.age. Note that for each target given, Bottle will create completely separate encrypted files.

Notably, this is different than how the tar command works, which would group all given files into a single archive. If you want Bottle to put multiple files or directories into one "bottle", first put them all in a single directory, then run bottle on that directory.

Limitations

Bottle currently reads entire files (or, for directories, entire tar files) into memory. This means the size of the file/directory you bottle is limited by the amount of RAM on your machine.

This is... obviously not good. I've created an issue to focus my work on solving it. But in the meantime, I would avoid bottling any files or directories over a few GBs... Sorry!

Troubleshooting

Let's say you have a .tar.gz.age file that you encrypted with Bottle, but now you can't install or get the bottle tool to work. Here's a procedure for decrypting and extracting it without using Bottle (though you still need you bottle_key.txt file).

With age installed, try the following two commands to decrypt and extract your archive file:

age --decrypt -i ~/.bottle/bottle_key.txt my_archive.tar.gz.age > compressed_decrypted_archive.tar.gz
mkdir my_archive
tar -xf compressed_decrypted_archive.tar.gz -C ./my_archive

Non-goals of the project (and recommended tools)

Bottle is not, at this point, aiming to be a tool for backing-up your entire HOME directory, or even a multi-gigabyte code or Documents or Pictures directory. For large, repeated back-ups like that, I'd recommend Restic, which I use myself.

And while this may sound obvious, Bottle is not a re-write of the age or rage command-line tools. If you want to encrypt files for other people, use age!

Other notes

This project is not affiliated with the similarly named bitbottle project, nor are the archive file formats compatible, to my knowledge. That said, it looks much more sophisticated than my tool, so it might fit your needs better. Also, sorry about the name conflict... worried I subconsciously copied it. Open an issue if you have a suggestion for a new name for this project!

To do

  • Add ability to generate a key file for the user. This would eliminate the need to have age and age-keygen installed in order to use Bottle!
  • Have it be way more cautious when potentially overwriting a file or directory.
  • Consider a flag to add a timestamp to the file name of encrypted files.
  • Ability to encrypt a directory with only access to a public key. (Like age's -R flag.)
  • Ability to print public key of key-pair at ~/.bottle/bottle_key.txt
  • Consider adding an option to NOT to compress directory before encrypting it. Would need to be able to unbottle .tar.age files.
  • An option to use your ssh key instead (which age supports)
  • Might be neat if could read file from stdin and/or output to stdout, so could be used in a shell-command chain.

Ports of Bottle

Before writing this Rust tool, I tried to do something similar using a shell script. I'd say stick with this Rust version, though!

You might also like...
Like wc, but unicode-aware, and with per-line mode
Like wc, but unicode-aware, and with per-line mode

Like wc, but unicode-aware, and with per-line mode

This is a lightweight audio-video player built in Rust using FFmpeg libraries. It demonstrates the usage of FFmpeg with Rust to play back video files.

FFmpeg Rust Video Player This is a lightweight audio-video player built in Rust using FFmpeg libraries. It demonstrates the usage of FFmpeg with Rust

CLI program written in Rust to anonymize DICOM files

dicom-anonymizer CLI program written in Rust to anonymize DICOM files anonymizer 0.1.0 Domenic Melcher USAGE: anonymizer [OPTIONS] FILE ARGS:

A simple path traversal checker made with Rust. Useful for APIs that serve dynamic files.

Path trav A simple path traversal checker made with Rust. Useful for APIs that serve dynamic files. Note: this is a security tool. If you see somethin

Generate rust structs & query functions from diesel schema files

dsync A utility to generate database structs and querying code from diesel schema files. Primarily built for create-rust-app. Currently, it's more adv

A benchmark of Rust/serde deserializers on configuration files

This program compares the time some serde deserializers take to deserialize some string into a configuration-like struct deriving Deserialize. The ben

Create archives of files within Garry's Mod

gm_zip Create archives of files within Garry's Mod. Note: The scope of this module only works accross the gmod installation files e.g from GarrysMod/g

Send Youtube videos as audio podcasts to your personal Pocket Casts files section.

yttopocketcasts Send Youtube videos as audio podcasts to your personal Pocket Casts files section. Quick Start Prerequisites: Docker and Make must be

Code sample for "Reading files the hard way Part 3"

read-raw-ext4 Rust code sample to read an ext4 partition from Rust, for: https://fasterthanli.me/series/reading-files-the-hard-way/part-3 Usage Don't.

Comments
  • Explore other compression settings or even other formats

    Explore other compression settings or even other formats

    opened by sts10 0
  • Don't write entire tar file in to memory

    Don't write entire tar file in to memory

    To speed up performance and lower memory consumption, Bottle should archive, compress and encrypt the contents of a file (tar or otherwise) in chunks when encrypting/decrypting.

    Currently, when encrypting a directory, Bottle writes the entire directory's contents to tar file on the file system, then read that tar file back into memory in order to compress it.

    https://github.com/sts10/bottle-rs/blob/main/src/lib.rs#L187-L200

    Not great! I think attacking this part of the pipeline would be best.

    Thankfully, the Builder of the tar crate we're using takes a Writer, so we should be able to make our own Writer impl that directs the tar stream into compression.[1] I'm not sure how to do that yet, so any comments or PRs welcome!

    If done right, this will also eliminate the need to write the (unencrypted) tar file to the file system, removing a potential security issue.

    After that, we'll have to figure out how to "chunk" the compress-to-encryption pipeline.

    (1) Relevant Rust book pages: 178 and in O'Reilly: 428?

    opened by sts10 2
Owner
Sam Schlinkert
Sam Schlinkert
A gitweb/cgit-like interface for the modern age

rgit See it in action! A gitweb/cgit-like interface for the modern age. Written in Rust using Axum, git2, Askama and Sled. Sled is used to store all m

jordan 14 Apr 8, 2023
An open source WCH-Link library/command line tool written in Rust.

wlink - WCH-Link command line tool NOTE: This tool is still in development and not ready for production use. Known Issue: Only support binary firmware

WCH MCU for Rust 22 Mar 7, 2023
Utilities for converting Vega-Lite specs from the command line and Python

VlConvert VlConvert provides a Rust library, CLI utility, and Python library for converting Vega-Lite chart specifications into static images (SVG or

Vega 24 Feb 13, 2023
A tool and library to losslessly join multiple .mp4 files shot with same camera and settings

mp4-merge A tool and library to losslessly join multiple .mp4 files shot with same camera and settings. This is useful to merge multiple files that ar

Gyroflow 7 Jan 2, 2023
repl / scripting language / namespaced command line aliases

Adana Toy project with the following goals in mind: Making something concrete with rust Learning more about parser combinator Use the minimum amount o

Nordine Bittich 10 Aug 28, 2022
A tool that helps you to turn in one command a Rust crate into a Haskell Cabal library!

cabal-pack A tool that helps you to turn in one command a Rust crate into a Haskell Cabal library! To generate bindings, you need to annotate the Rust

Yvan Sraka 18 Dec 31, 2022
Rust library for concurrent data access, using memory-mapped files, zero-copy deserialization, and wait-free synchronization.

mmap-sync mmap-sync is a Rust crate designed to manage high-performance, concurrent data access between a single writer process and multiple reader pr

Cloudflare 97 Jun 26, 2023
Salty and Sweet one-line Rust Runtime Optimization Library

SAS SAS (Salty-And-Sweet) is an one-line Rust runtime optimization library. Features NUMA-aware rayon: numa feature should be enabled If you have 1 NU

UlagBulag 3 Feb 21, 2024
Checks Crusader Kings 3 user mod files for common mistakes and warns about them.

ck3-tiger Pounces on bugs. Checks Crusader Kings 3 user mod files for common mistakes and warns about them. For example: missing localizations, or usi

Richard Braakman 8 Jan 5, 2023
A tiny service that downloads files over HTTP links, with resume and restart support.

Http Drogue Http Drogue is a tiny service that downloads files over HTTP from links you provide. It can restart and resume interrupted downloads. Http

Kaan Barmore-Genç 4 Feb 27, 2023