RustでAES暗号化をやってみた


はじめに

Advent Calendar 2020の7日目です!
昨日に記事を投稿している人がいないという悲しいことが起きているわけですが、今後ほかの方が書いてくださることを信じて、私は表題の通りの内容の記事を投稿させてもらいます。

本記事に入る前に

この記事は、プログラミング言語であるRustを用い、ファイルを暗号化してみるという試みを行うものですので、その性質上Rustの知識があった方がわかりやすいかもしれません。

Rustって?

Rustとは、最近(筆者の中で)話題になっている言語で、安全なメモリ管理を売りにしているプログラミング言語です。firefoxを作っているmozilla社によって生み出されたこの言語は主に海外で使われることが多く日本国内でのシェアが伸び悩んでいるので皆さんも是非使ってみて欲しいところですね!!

AES暗号化って?

AESとは、[ Advanced Encryption Standard ] の略で、高度暗号化標準と日本語訳されるようです。これは米国で標準的な暗号化技術として採用されているらしく、今後様々なところで標準的な暗号化技術として採用されていくことが期待されています。
そして、AESには鍵の長さが128ビット, 192ビット, 256ビットの三種類があり、これに応じて中身の処理を変える必要があります。

やったこと

今回のアドベントカレンダーに際して、筆者はこの暗号化技術を使ってみたいということを動機として開発をしたのですが、「単に文字列の暗号化みたいな単純なものではつまらない!」と思い、せっかくなのでファイルを暗号化することを試しました。

機能について

機能は大きく分けて、暗号化復号化(暗号化されたデータを元に戻す)に分類されます。

暗号化

  1. 暗号化対象のファイルを指定
  2. ファイルをバイト情報として読み込み
  3. バイトデータを全て暗号化アルゴリズムでデータ変換
  4. 変換されたバイトデータを別のファイルに書き込み

復号化

  1. 復号化対象のファイルを指定
  2. ファイルをバイト情報として読み込み
  3. 得られたバイトデータを複合かアルゴリズムでデータ変換
  4. 変換されたバイトデータを別ファイルに書き込み

実装

実装をするにあたり、必要な機能をパッケージで取得する必要があります。

extern crate crypto;
extern crate aesstream;

use std::io::{Result, BufReader, Read, BufWriter, Write};
use aesstream::{AesWriter, AesReader};
use crypto::aessafe::{AesSafe256Encryptor, AesSafe256Decryptor};
use std::fs::File;

また、実装中に使われている定数は以下の通りです。
鍵長が32となっていますが、これはbyteが8bitと同値であることから、32 * 8 = 256で256ビットのAES暗号化を採用していることを指します。

const FILE_SIZE: usize = 1024;
const PASSWORD_SIZE: usize = 32;        // パスワードのバイトサイズ. aessafeパッケージのサイズ(128, 192, 256)に応じて変化させる必要がある

ファイルのバイト変換処理は以下のようにしました。

  • 暗号化処理
/// 暗号化<br>
/// 第1引数には読み込み元のファイル<br>
/// 第2引数には書き込み先のファイル<br>
/// 第3引数にはパスワード<br>
/// 戻り値は実行結果
fn encrypt(src: &str, dst: &str, pass: &str) -> Result<()>{
    let src_file = File::open(src)?;                        // ファイルを開く
    let mut reader = BufReader::new(&src_file);             // 読み込み用の機能を呼び出し
    let mut block: [u8; FILE_SIZE] = [0u8; FILE_SIZE];      // 空のバイト配列を用意
    reader.read(&mut block)?;                               // バイト配列にファイル情報を読み出し

    let key = pass.as_bytes();                              // 引数をバイト変換
    if key.len() > PASSWORD_SIZE {
        println!("Too long password!");
        return Err(std::io::Error::from(std::io::ErrorKind::Other));                      // 異常終了なのでエラーを出す
    }
    let mut key_array = [0u8; PASSWORD_SIZE];               // バイト用配列
    for i in 0..key.len() {
        key_array[i] = key[i];                              // スライスから配列へ変換
    }

    let dst_file = File::create(dst)?;                      // 出力先ファイルを指定
    let encryptor = AesSafe256Encryptor::new(&key_array);   // 暗号化の呼び出し
    let mut writer = AesWriter::new(dst_file, encryptor)?;  // ファイルへの暗号書き出しの呼び出し
    writer.write_all(&block)?;                              // 実行
    Ok(())
}
  • 復号化処理
/// 復号化<br>
/// 第1引数には読み込み元のファイル<br>
/// 第2引数には書き込み先のファイル<br>
/// 第3引数にはパスワード<br>
/// 戻り値は実行結果
fn decrypt(dst: &str, src: &str, pass: &str) -> Result<()>{
    let key = pass.as_bytes();                              // 引数をバイト変換
    if key.len() > PASSWORD_SIZE {
        println!("Too long password!");
        return Err(std::io::Error::from(std::io::ErrorKind::Other));                      // 異常終了なのでエラーを出す
    }
    let mut key_array = [0u8; PASSWORD_SIZE];               // バイト用配列
    for i in 0..key.len() {
        key_array[i] = key[i];                              // スライスから配列へ変換
    }

    let src_file = File::open(dst)?;                        // ファイルを開く
    let decryptor = AesSafe256Decryptor::new(&key_array);
    let mut reader = AesReader::new(&src_file, decryptor)?; // 読み込み用の機能を呼び出し
    let mut block: [u8; FILE_SIZE] = [0u8; FILE_SIZE];      // 空のバイト配列を用意
    reader.read(&mut block)?;                               // バイト配列にファイル情報を読み出し

    let dst_file = File::create(src)?;                      // 出力先ファイルを指定
    let mut writer = BufWriter::new(&dst_file);             // 書き込み用の機能を呼び出し
    writer.write(&block)?;                                  // 書き込みを実行
    Ok(())
}

実行結果

実行結果を示します。
まず適当なファイルを選択し、そのファイルに対して暗号化をかけます。

下図はその処理で生成されたファイルです。

次に、このファイルに対して復号化をかけます。

下図はその処理で生成されたファイルです。

これらの結果から、無事に暗号化と復号化がされていることが確認できました。

感想

今回、暗号化と復号化を試してみましたが、実際の暗号系の箇所は全てCargo(Rustのパッケージサービス)から取得したものをそのまま使っただけで自分ではバイトの読み込み部分しか作ることができていなかったので、時間があったら暗号部分の処理も作ってみたいところです。