
ギガバイト毎秒のマルチスレッドファイル暗号エンジン。ロックフリーでトリプルバッファリングされたio_uringパイプライン、Rayonの並列チャンク処理、ハードウェアアクセラレーションAEAD(AES-256-GCM / ChaCha20)を使用して極限のスループットを実現。
Rustで構築されたマルチスレッドAEAD暗号化エンジンです。トリプルバッファリングされたio_uringパイプライン、Rayonによる並列チャンク処理、ringによるアセンブリ最適化暗号を利用して、ギガバイト/秒のスループットでファイルを暗号化・復号します。
⚠️ 免責事項:実験的ソフトウェア ⚠️
このプロジェクトは非常に新しく、現在、本番環境やミッションクリティカルな使用には推奨されていません。 暗号プリミティブ(ringによるAES-256-GCM、ChaCha20-Poly1305)と形式設計は妥当ですが、コードベースは正式なセキュリティ監査や広範な実世界テストを受けていません。自己責任で使用してください。機密データを保護するには、このプロジェクトが成熟するまでGnuPG、age、OpenSSLなどの実績のあるツールを使用することを検討してください。
ringによるAES-256-GCM(ハードウェアAES-NI)およびChaCha20-Poly1305(アセンブリ最適化)--memoryで設定可能)ringによるseal_in_place_separate_tag / open_in_placeを使用し、ホットループでのアロケーションを最小化O_DIRECT I/Oを有効にし、NVMe上でDMA速度の読み取り/書き込みのためにカーネルページキャッシュをバイパスします。バッファプールは4096バイトアライメントでstd::allocを使用します。cargo bench(Criterion、測定ごとに10サンプル)でベンチマーク。鍵導出は除外 — 数値は純粋な暗号スループットのみを示します。
ハードウェア:
I/Oに関する注記: Criterionは一時ファイルを/tmpに書き込みます。このシステムではtmpfs(RAMバッキング)です。O_DIRECTを使用する場合、カーネルはtmpfs上で実際の非同期DMAを使用できないため、これらの数値はDMAバイパスの利点なしでの暗号スループットとio_uringオーバーヘッドを反映しています。実際のGen4 NVMeドライブでは、O_DIRECTはページキャッシュのダブルバッファリングを排除し、アラインされたバッファプールへのDMAを可能にするため、大幅に高いスループットが期待できます。
| ファイルサイズ | AES-256-GCM 暗号化 | ChaCha20 暗号化 | AES-256-GCM 復号 | ChaCha20 復号 |
|---|---|---|---|---|
| 64 KiB | 244 MiB/s | 233 MiB/s | 233 MiB/s | 234 MiB/s |
| 1 MiB | 1.08 GiB/s | 882 MiB/s | 1010 MiB/s | 876 MiB/s |
| 16 MiB | 1.10 GiB/s | 923 MiB/s | 1.06 GiB/s | 988 MiB/s |
| 64 MiB | 984 MiB/s | 935 MiB/s | 988 MiB/s | 973 MiB/s |
| 256 MiB | 1.00 GiB/s | 1015 MiB/s | 1.01 GiB/s | 1.02 GiB/s |
チャンクサイズスイープ(AES-256-GCM、64 MiBファイル):
| チャンクサイズ | スループット |
|---|---|
| 64 KiB | 1.01 GiB/s |
| 256 KiB | 1.05 GiB/s |
| 1 MiB | 1.07 GiB/s |
| 4 MiB | 988 MiB/s |
| 8 MiB | 988 MiB/s |
| 16 MiB | 1.00 GiB/s |
このエンジンは、暗号操作にring(アセンブリ最適化AES-NI / NEON / ARMv8-CE)を使用し、I/Oにトリプルバッファリングされたio_uringパイプラインを使用します。3つの事前割り当てバッファプールがパイプラインを循環します。プールAの書き込みがカーネルで完了している間に、プールBはCPU上でRayonによって暗号化され、プールCの読み取りがカーネルに送信されます。これにより、I/Oレイテンシと暗号計算がオーバーラップします。
なぜAES-256-GCMが小さいファイルでChaCha20-Poly1305より速いのか:
ringのAES-GCMバックエンドは、x86-64で利用可能なAES-NI + CLMULハードウェア命令を活用するため、ChaCha20(ソフトウェア暗号)よりもハードウェア上の利点があります。大きいサイズでは両方の暗号が約1.0 GiB/sに収束し、ボトルネックが暗号スループットからI/O送信オーバーヘッドに移行することを示しています。
なぜピークスループットが256 MiBではなく1〜16 MiBなのか: 小さいファイル(1〜16 MiB)はチャンク数が少ないため、Rayonの並列性が効率的で、ワーキングセットがキャッシュに収まります。64〜256 MiBでは、io_uringパイプラインが完全にアクティブ(3つのバッチが進行中)ですが、SQEあたりの送信およびCQE完了オーバーヘッドはチャンク数に比例して増加します。トリプルバッファ設計によりI/Oと暗号処理がオーバーラップし、このコストを部分的に隠蔽します。
なぜ約1.0 GiB/sであり、10+ GiB/sではないのか: 最新のAES-NIはコアあたり2〜4 GiB/sを実現できます。12スレッドでは、生の暗号スループットは10 GiB/sを超える可能性があります。ギャップを説明する3つの要因:
PIPELINE_DEPTH=3では、一度に3つのバッチのみがパイプラインを循環します。真の定常状態オーバーラップには少なくとも3つのバッチが必要です。1つまたは2つのバッチに収まるファイルは、パイプライン化の恩恵を受けません。バッファのライフサイクルと安全性:
バッファプールは、io_uringリングが作成される前にstd::alloc::alloc_zeroedとLayout::from_size_align(size, 4096)を使用して一度だけ割り当てられ、すべてのパイプライン反復で再利用され、再割り当ては行われません。各暗号化チャンクは、O_DIRECT書き込みの前にセクターアライメントにゼロパディングされます。リングはバッファプールの前に明示的にドロップされ、カーネルが解放されたメモリを参照しないようにします(UAFなし)。
git clone https://github.com/frogsnot/concryptor.git
cd concryptor
cargo build --release
バイナリはtarget/release/concryptorにあります。
# AES-256-GCM(デフォルト)、出力先 myfile.dat.enc
concryptor encrypt myfile.dat
# ChaCha20-Poly1305、カスタム出力パス
concryptor encrypt myfile.dat --cipher chacha -o encrypted.enc
# カスタムチャンクサイズ(MiB単位)
concryptor encrypt largefile.iso --chunk-size 8
# より強力なKDF(512 MiBメモリコスト)
concryptor encrypt secrets.tar --memory 512
# 非対話型(パスワードプロンプトをスキップ)
concryptor encrypt myfile.dat -p "password"
セキュリティノート:
--password/-pはパスワードをCLI引数として渡します。これはpsの出力やシェル履歴に表示されます。対話的に使用する場合は省略して、安全な非表示プロンプトを利用してください。スクリプトで使用する場合は、履歴を後で消去するか、ファイルディスクリプタから読み取るラッパーを使用することを推奨します。
# ディレクトリを暗号化(自動検出、mydir.tar.enc を生成)
concryptor encrypt mydir/
# カスタム暗号と出力
concryptor encrypt mydir/ --cipher chacha -o secrets.enc
ディレクトリ暗号化は、一時的なtarアーカイブ(.concryptor-*.tar、0600パーミッション、CSPRNG命名)を作成し、それを暗号化してから一時ファイルを自動削除します。ファイル名、ディレクトリ構造、パーミッション、タイムスタンプはすべて暗号化ペイロード内に含まれます。
# .enc 拡張子を自動除去
concryptor decrypt myfile.dat.enc
# カスタム出力パス
concryptor decrypt encrypted.enc -o restored.dat
# 非対話型
concryptor decrypt myfile.dat.enc -p "password"
# 復号と展開を1ステップで実行(.tar.enc を自動除去 -> ディレクトリ名)
concryptor decrypt mydir.tar.enc --extract
# 短いフラグ、カスタム出力ディレクトリ
concryptor decrypt mydir.tar.enc -x -o restored_dir/
--extract を指定しない場合、ディレクトリアーカイブの復号は中間的な .tar ファイルを生成します。これは手動で検査したり展開したりできます。
concryptor --help
concryptor encrypt --help
concryptor decrypt --help
すべての値はリトルエンディアンです。ヘッダーは完全な4 KiBセクターを占有し、各暗号化チャンクスロットは次の4 KiB境界にパディングされます。これにより、すべてのオフセットとI/OサイズがO_DIRECT用にセクターアライメントされます。
オフセット サイズ フィールド
------ ----- ---------------------
0 10 マジックバイト "CONCRYPTOR"
10 1 形式バージョン (4)
11 1 暗号タイプ (0 = AES-256-GCM, 1 = ChaCha20-Poly1305)
12 4 チャンクサイズ (バイト、LE)
16 8 元のファイルサイズ (バイト、LE)
24 16 Argon2 ソルト (暗号学的ランダム、ファイルごとに一意)
40 12 ベースノンス (暗号学的ランダム、ファイルごとに一意)
52 4 Argon2 m_cost (KiB単位、LE、0 = レガシー64 MiB)
56 4 Argon2 t_cost / 反復回数 (LE、0 = レガシー3)
60 4 Argon2 p_cost / 並列度 (LE、0 = レガシー4)
64 4032 予約 (4096バイトになるまでゼロパディング)
4096 ... [チャンク0: 暗号文 + 16バイトタグ + セクター境界までのゼロパディング]
[チャンク1: 暗号文 + 16バイトタグ + セクター境界までのゼロパディング]
...
4 MiBチャンクの場合: 各ディスクスロットは ceil((4194304 + 16) / 4096) * 4096 = 4198400 バイト(チャンクあたり4080バイトのパディング)です。ヘッダー内の4032バイトの予約領域は、将来の機能(非対称鍵スロット、メタデータなど)に使用できます。
ソルトとベースノンスは、暗号化ごとにrand::rng()(OSのCSPRNGにバッキング)から新たに生成されます。異なるソルトは異なるArgon2id鍵を生成し、異なるベースノンスは異なるチャンクごとのノンスを生成するため、パスワードをファイル間で再利用しても安全です。