How To Build CLI App In Rust Using Clap - Part 3

How To Build CLI App In Rust Using Clap - Part 3

Encrypt Command

Earlier in this series, we completed our first cli command that generates a secret to an output file which is provided as a flag. In this post, we will be working on our subsequent command encrypt that encrypts the input file given using the secret that we generated using the generate command. We proceeding with the Advanced encryption standard for the encryption and decryption of our file.

If you're new to cryptographic concepts do check out my series of posts on cyrptography.

Command Encrypt

Let's start writing logic to our command by adding dependencies to the Cargo.toml file and importing the same in the main.rs file.

aes = "0.7.4"
block-modes = "0.8.1"
use aes::Aes256;
use block_modes::{block_padding::Pkcs7, Cbc};

To understand more about Cbc(Cipher Block Chaining) and Aes follow the link

pub type AES = Cbc<Aes128, Pkcs7>;

This creates a type alias for Cipher Block Chaining on Aes128 with Padding.

Let's implement logic which does the encryption.

First thing first let's read the contents of the files that need to be encrypted along with the secret file that contains the key and IV for encryption.

pub fn encrypt(file_path: String, key_path: String) {
    let to_encrypt = read(file_path.clone()).unwrap();
    let secret = read(key_path).unwrap();
}

We are using the standard filesystem library to read the file from the file path. After getting the contents of the secret file we need to deserialize to Secret struct to get the key and initial value.

pub fn encrypt(file_path: String, key_path: String) {
    let to_encrypt = read(file_path.clone()).unwrap();
    let secret = read(key_path).unwrap();
    let secret: Secret = serde_json::from_slice(&secret).unwrap();
}

Next thing we need to create an instance of the cipher by adding the below line.

pub fn encrypt(file_path: String, key_path: String) {
    let to_encrypt = read(file_path.clone()).unwrap();
    let secret = read(key_path).unwrap();
    let cipher = AES::new_from_slices(&secret.decode_key(), &secret.decode_iv()).unwrap();
}

Create a buffer of having a size twice the length of the file content. This step stores the padding bits for encryption.

Finally, encrypt the file contents and write them back to the file.

pub fn encrypt(file_path: String, key_path: String) {
    let to_encrypt = read(file_path.clone()).unwrap();
    let secret = read(key_path).unwrap();

    let secret: Secret = serde_json::from_slice(&secret).unwrap();
    let cipher = AES::new_from_slices(&secret.decode_key(), &secret.decode_iv()).unwrap();

    let pos = to_encrypt.len();

    let mut buffer = vec![0u8; pos + pos];
    buffer[..pos].copy_from_slice(&to_encrypt);

    let encrypted_data = cipher.encrypt(&mut buffer, pos).unwrap();

    write(file_path, base64::encode(encrypted_data)).unwrap();
}

Complete the main function by adding the following lines.

fn main() {
    let cryptifer = Cryptifer::parse();

    match cryptifer.command {
        commands::Commands::Generate { output_path } => {
            Secret::new(output_path);
        }
        commands::Commands::Encrypt {
            file_path,
            key_path,
        } => {
            encrypt(file_path, key_path);
        }
        commands::Commands::Decrypt {
            encrypted_file: _,
            key_path: _,
        } => todo!(),
    }
}

Running Command Encrypt

cargo run encrypt  -f file.txt -k Keystore.secret

Screenshot 2022-09-30 at 4.52.07 PM.png Contents in the file.txt will be encrypted using the secret.

Wrapping Things Up

Hurray!! We have completed two commands in our CLI application. In this post, we covered the encryption command and configured the main function to handle the parsed arguments for the encrypt command. If you run into any issues with any part of this tutorial, please leave a comment so that I can update the content to be more clear. If you like this post, please follow me on Hashnode and subscribe to my newsletter for future updates.

Did you find this article valuable?

Support The Missing Semicolon by becoming a sponsor. Any amount is appreciated!