0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【学習メモ】Rustで作るCLIツール

Last updated at Posted at 2024-08-18

お盆休み最終日!
今日は、ここまでのRust学習のまとめとして、CLIツールを作ってみたいと思います!

仕様

  • AES256およびその他のアルゴリズムに対応した暗号化ツール
  • コマンドライン引数から、入力ファイル名、出力ファイル名、アルゴリズムを指定できる
  • 同じプログラムで暗号化と復号化の両方ができる

依存関係

以下のような依存関係になります。

Cargo.toml
[dependencies]
aes = "0.8.4"
block-padding = "0.3.3"
cbc = { version = "0.1.2", features = ["std"] }
clap = { version = "4.5.16", features = ["derive"] }

実装

暗号化・復号化部分。
複数のアルゴリズムに対応できるように、トレイトを使っています。

AESのキーや初期ベクトルは適当な値を入れています。
くれぐれも実案件ではこのコードをコピペしないでくださいねー!

encrypt.rs
use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
use block_padding::{Pkcs7, UnpadError};

type Aes256Enc = cbc::Encryptor<aes::Aes256>;
type Aes256Dec = cbc::Decryptor<aes::Aes256>;

pub trait Encryptor {
    fn encrypt(&self, data: &Vec<u8>) -> Vec<u8>;
    fn decrypt(&self, data: &Vec<u8>) -> Result<Vec<u8>, UnpadError>;
}

pub struct Aes256Encryptor {
    key: Vec<u8>,
    iv: Vec<u8>
}

impl Aes256Encryptor {
    pub fn new() -> Self {
        let key = b"tekitou na password desu 1234567".to_vec();
        let iv = b"tekitou na vec 1".to_vec();
        Self {key, iv}
    }
}

impl Encryptor for Aes256Encryptor {
    fn encrypt(&self, data: &Vec<u8>) -> Vec<u8> {
        Aes256Enc::new_from_slices(&self.key, &self.iv)
            .unwrap()
            .encrypt_padded_vec_mut::<Pkcs7>(data)
    }

    fn decrypt(&self, data: &Vec<u8>) -> Result<Vec<u8>, UnpadError> {
        Aes256Dec::new_from_slices(&self.key, &self.iv)
            .unwrap()
            .decrypt_padded_vec_mut::<Pkcs7>(data)
    }
}

メイン関数。
コマンドライン引数をパースし、ひとつひとつ処理を進めています。
matchによって、エラーハンドルなどスッキリ書けた気がする!

main.rs
mod encrypt;

use clap::Parser;
use std::fs;
use crate::encrypt::{Aes256Encryptor, Encryptor};

#[derive(Parser, Debug)]
struct Args {
    /// モード(1が暗号化、2が復号化)
    #[arg(short, long, default_value_t = 1)]
    mode: u32,

    /// 入力ファイルのパス
    #[arg(short, long)]
    input: String,

    /// 出力ファイルのパス
    #[arg(short, long)]
    output: String,

    /// 暗号化アルゴリズム
    #[arg(short, long, default_value_t = String::from("AES"))]
    algorithm: String
}

fn main() {
    let args = Args::parse();

    println!("モード: {}", args.mode);
    println!("入力ファイル名: {}", args.input);
    println!("出力ファイル名: {}", args.output);
    println!("暗号化アルゴリズム: {}", args.algorithm);

    let encryptor = match args.algorithm.as_str() {
        "AES" => Aes256Encryptor::new(),
        _ => {
            println!("対応していない暗号モードです");
            return;
        }
    };

    let input_file = match fs::read(&args.input) {
        Ok(data) => data,
        Err(e) => {
            println!("入力ファイルの読み取りに失敗しました: {}", e.to_string());
            return;
        }
    };

    let crypted = match args.mode {
        1 => encryptor.encrypt(&input_file),
        2 => encryptor.decrypt(&input_file).unwrap(),
        _ => {
            println!("無効なモードです");
            return;
        }
    };

    match fs::write(&args.output, &crypted) {
        Ok(_) => (),
        Err(e) => {
            println!("出力ファイルの書き出しに失敗しました: {}", e.to_string());
            return;
        }
    }

    println!("完了しました")
}

使い方

cargo run -- -m 1 -i ./sample.txt -o ./output

暗号化したい時は -m を1に、復号したい時は2にします。
-i に入力ファイル名、-o に出力ファイル名を入れます。
アルゴリズムは -a で指定でき、デフォルトはAESです。

まとめ

Result型のエラーの部分の型など、コンパイル時にたくさんエラーが出て苦しみました...。
とっても難しい、でも楽しい言語ですね、Rust。
コンパイルさえ通れば、安全で高速な動作が保証されるので、根気よく頑張りたいところです。
それでは!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?