Tutorials

Learn how to encrypt files in Rust

CM-IV
#rust#file-encryption#how-to

This will be a quick overview demonstrating how to use a few of my favorite crates. The crates you’ll need to add to your config.toml are shown below:


...

[dependencies]
age = "0.9.2"
camino = "1.1.6"
dirs = "5.0.1"
inquire = { git = "https://github.com/mikaelmello/inquire", rev = "6b85740ef69009721f37cee1028c010421438215" }
owo-colors = "3.5.0"

# optimize for size
[profile.release]
strip = true
lto = true
opt-level = "z"
debug = 0
overflow-checks = false
panic = "abort"
codegen-units = 1

The age crate is the star of the show, it’s how we’ll actually encrypt the file that we provide the path to on our local drive. The camino and dirs crates make it easier dealing with those file paths, the /home/$USER/Downloads path in particular. The camino crate converts those PathBuf types into UTF-8 compatible ones for us. The inquire crate, which uses a git revision to pull into our program, gives us the terminal prompts we need to get the required data.

I’ll be encrypting an image of a zucchini plant today, here’s what that looks like before:

photo 1

The encrypt_file() function that does the heavy lifting takes in a age::x25519::Recipient public key in order to encrypt the file buffer. The private key age::x25519::Identity that was used to generate that public key can be later used to decrypt the file in a decrypt_file() function. Once the program is run by using cargo run, we get the following output in the terminal:

photo 2

Here’s what the encrypted result looks like afterwards, unreadable data basically:

photo 3

The encrypted file, as per the specification, is composed of two parts - a header with the file key and the encrypted binary payload. You can store this file on your cloud storage or wherever you backup your data for safe keeping, whoever has access to the private x25519 key that we generate within the main() function can decrypt the file.

Here is the code for the encrypt_file() function:

fn encrypt_file(public_key: Recipient) {
    let file: Utf8PathBuf = inquire::Text::new("Enter the path to the file for encryption")
        .with_validator(required!())
        .with_help_message("Enter the path to the file you want encrypted")
        .prompt()
        .unwrap()
        .into();

    if !file.is_file() {
        println!(
            "{}",
            "\nThe file either does not exist or this isn't a file\n".red()
        );

        return;
    }

    println!("{}", "Encrypting...".yellow());

    let encrypted = {
        let encryptor = age::Encryptor::with_recipients(vec![Box::new(public_key)])
            .expect("we provided a recipient");

        let f = File::open(file.as_path()).unwrap();
        let mut reader = BufReader::new(f);
        let mut buffer = Vec::new();
        reader.read_to_end(&mut buffer).unwrap();

        let mut encrypted = vec![];
        let mut writer = encryptor.wrap_output(&mut encrypted).unwrap();
        writer.write_all(&buffer).unwrap();
        writer.finish().unwrap();

        encrypted
    };

    let dir = dirs::download_dir().expect("Couldn't get downloads dir!");
    let path = Utf8PathBuf::from_path_buf(dir).unwrap();
    let dest = format!("{}/{}.age", path, file.file_name().unwrap());

    let mut writer = File::create(&dest).unwrap();

    writer.write_all(encrypted.as_slice()).unwrap();

    println!("{}", "\nFile successfully encrypted!\n".green());

}
← Back to Blog