Back to list
dev_to 2026年4月25日

なぜ Argon2id を PDF 暗号化鍵として bcrypt より選ぶのか

Why I Use Argon2id Instead of bcrypt for PDF Encryption Keys

Translated: 2026/4/25 2:01:09
argon2idbcryptpdf-encryptionkey-derivationsecurity-rust

Japanese Translation

すべてのテストは 8 年目の MacBook Air を実行しています。 PDF をパスワードで暗号化する際、そのパスワードは 32 バイトの鍵に変換される必要があります。 この変換方法の重要性は多くの方が気づいていません。 bcrypt はパスワードハッシュ化には適していますが、鍵生成設計には適していません。 出力は固定された 60 文字であり、生みの暗号化鍵として不適切です。 メモリアス使いは低く、GPU によるブルートフォース攻撃が安価です。 任意長さの鍵生成のための組み込みサポートがありません。 PBKDF2 も良いですが、メモリーライトのままです。GPU ファームは 1 秒間に数十億回の反復を実行できます。 Argon2id は 2015 年のパスワードハッシュ化コンテストで勝利しました。それは設計上のメモリーハードです。 メモリーハードとは、ブルートフォースを行うには計算だけでなく RAM も必要です。数千のコアを持ちながら各コアに限定されたメモリしかない GPU は突如としてはるかに役立ちません。 use argon2::{Argon2, Params}; pub fn derive_key(password: &str, salt: &[u8]) -> [u8; 32] { let mut key = [0u8; 32]; let params = Params::new( 64 * 1024, // 64MB memory 3, // 3 iterations 1, // 1 thread Some(32), // 32-byte output ).expect("invalid params"); Argon2::new(argon2::Algorithm::Argon2id, argon2::Version::V0x13, params) .hash_password_into(password.as_bytes(), salt, &mut key) .expect("key derivation failed"); key } 1 回の導出試行につき 64MB のメモリ。これは大規模 GPU 攻撃を高価にします。 塩は決して再利用しないでください。各暗号化で新鮮なランダムなものを生成してください: use aes_gcm::aead::rand_core::RngCore; use aes_gcm::aead::OsRng; pub fn generate_salt() -> [u8; 16] { let mut salt = [0u8; 16]; OsRng.fill_bytes(&mut salt); salt } 暗号テキストと共に塩を保存してください。それは秘密ではありません、ただユニークである必要があります。 bcrypt は 1999 年に設計されました。脅威モデルは変化しました。 Hiyoko PDF Vault → https://hiyokoko.gumroad.com/l/HiyokoPDFVault @hiyoyok

Original Content

All tests run on an 8-year-old MacBook Air. When you encrypt a PDF with a password, that password needs to become a 32-byte key. How you do that conversion matters more than most people realize. bcrypt is fine for password hashing. It's not designed for key derivation. Output is fixed at 60 characters — not suitable as a raw encryption key Memory usage is low, making GPU-based brute force cheap No built-in support for generating arbitrary-length keys PBKDF2 is better but still memory-light. A GPU farm can run billions of iterations per second against it. Argon2id won the Password Hashing Competition in 2015. It's memory-hard by design. Memory-hard means: to brute force it, you need not just compute but RAM. A GPU with thousands of cores but limited memory per core is suddenly much less useful. use argon2::{Argon2, Params}; pub fn derive_key(password: &str, salt: &[u8]) -> [u8; 32] { let mut key = [0u8; 32]; let params = Params::new( 64 * 1024, // 64MB memory 3, // 3 iterations 1, // 1 thread Some(32), // 32-byte output ).expect("invalid params"); Argon2::new(argon2::Algorithm::Argon2id, argon2::Version::V0x13, params) .hash_password_into(password.as_bytes(), salt, &mut key) .expect("key derivation failed"); key } 64MB of memory per derivation attempt. That makes large-scale GPU attacks expensive. Never reuse a salt. Generate a fresh random one per encryption: use aes_gcm::aead::rand_core::RngCore; use aes_gcm::aead::OsRng; pub fn generate_salt() -> [u8; 16] { let mut salt = [0u8; 16]; OsRng.fill_bytes(&mut salt); salt } Store the salt alongside the ciphertext — it's not secret, just needs to be unique. bcrypt was designed in 1999. The threat model has changed. Hiyoko PDF Vault → https://hiyokoko.gumroad.com/l/HiyokoPDFVault @hiyoyok