2025年2月3日
ブラウザだけで完結する暗号化ファイルマネージャを作ってみた
ちょっとした思いつきで、ブラウザだけでファイルを暗号化して保管できるアプリを作れるのではないかと思い作ってみました。使い道はよくわからないですが、何かの役に立つこともあるかもしれません。
アプリ: https://sogo.dev/local-file-locker/
ソースコード: https://github.com/SogoKato/local-file-locker
ファイルを選んで encrypt ボタンを押すと……
ファイルが暗号化されて保存されます!
ファイル名をクリックすると復号して表示できます。
以下のブラウザで動作確認しています。
- Firefox 134.0.2 (macOS, Android)
- Chrome 132.0.6834 (macOS, Android)
ポイント
- WASM でファイルを AES 暗号化
- OPFS (Origin Private File System) でファイルを保管
WASM でファイルを AES 暗号化
JS にも SubtleCrypto API が用意されていますが、今回は Rust の暗号化ライブラリである aes_gcm を使用してファイルを暗号化する関数を作り、WebAssembly 化して JS から呼び出すようにしてみました。WASM を使ったのは使ってみたかった理由が大きいですが、その方が高速かもという期待もあります。これから SubtleCrypto とのパフォーマンス比較とかもやってみたいなと思います。
処理自体は一般的な AES-GCM 暗号化です。ライブラリを呼んでいるだけなのでコードはこれだけです。
use aes_gcm::{AeadCore, Aes256Gcm, Key}; use aes_gcm::aead::{Aead, KeyInit, OsRng}; use sha2::digest::generic_array::GenericArray; use sha2::{Sha256, Digest}; use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn encrypt(password: &str, file: &[u8]) -> Box<[u8]> { let key = hash_password(password); let cipher = Aes256Gcm::new(&key.into()); let nonce = Aes256Gcm::generate_nonce(&mut OsRng); let ciphertext = cipher.encrypt(&nonce, file).expect("encryption failure!"); [nonce.as_slice(), &ciphertext].concat().into_boxed_slice() } #[wasm_bindgen] pub fn decrypt(password: &str, file: &[u8]) -> Box<[u8]> { let key = hash_password(password); let cipher = Aes256Gcm::new(&key.into()); let (nonce_bytes, ciphertext) = file.split_at(12); let nonce = GenericArray::from_slice(nonce_bytes); let plaintext = cipher.decrypt(&nonce, ciphertext).expect("decryption failure!"); plaintext.into_boxed_slice() } fn hash_password(password: &str) -> Key<Aes256Gcm> { let hash = Sha256::digest(password.as_bytes()); Key::<Aes256Gcm>::from_slice(&hash).clone() }
wasm_bindgen が Uint8Array
↔️ &[u8]
Box[u8]
のような複雑な型の変換までしてくれるので感動しました。
OPFS でファイルを保管
OPFS (Origin Private File System) は、モダンなブラウザで利用できる origin ごとに隔離された、パフォーマンスとセキュリティに優れたファイルシステムです。
TypeScript から利用するときに tsconfig.json
の compilerOptions.lib に dom.asynciterable
を追加しないと FileSystemDirectoryHandle
の entries()
メソッド等が使えないという罠があって少し引っかかりました。
今後やりたいこと
- SubtleCrypto とのパフォーマンス比較
- Web Worker 化?
- 2025年2月現在、Safari では
FileSystemSyncAccessHandle
しかサポートされていないが、これは Web Worker 内でしか利用できない
- 2025年2月現在、Safari では