Encrypting Static Site Content with WASM
April 2026
Overview
This is a counterpart to
/rust/wasm/static-site-file-decryption/.
It works fine, but it's a manual process. It
exists mainly to support the decryption example.
Generally speaking I'll use an offline process during
site generation to encrypt files. For example:
/rust/general/encrypt-a-file-with-a-password/
Output
Goal: Provide a way to encrypt and decrypt
files so they can be served
on static sites
Cargo.toml
[package]
name = "static-site-file-encryption"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
getrandom = { version = "0.4.2", features = ["wasm_js"] }
orion = "0.17.13"
wasm-bindgen = "0.2.117"
src/lib.rs
use orion::{aead, kdf};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn encrypt_file(
bytes: Vec<u8>,
password: String,
) -> Vec<u8> {
let kdf_password =
kdf::Password::from_slice(password.trim().as_bytes()).unwrap();
let salt =
kdf::Salt::from_slice(b"ca0beb1d-5a8a-4da9-b278-058087d00125")
.unwrap();
let kdf_key =
kdf::derive_key(&kdf_password, &salt, 3, 1 << 16, 32).unwrap();
aead::seal(&kdf_key, &bytes).unwrap()
}
HTML
<form id="encryptForm">
<label>
<div>File To Encrypt:</div>
<input type="file" id="fileToEncrypt" />
</label>
<label>
<div>Password to use:</div>
<input type="password" id="password" />
</label>
<div>
<input type="submit" value="Encrypt File" />
</div>
</form>
JavaScript
<script type="module">
import init, {encrypt_file} from "./pkg/static_site_file_encryption.js";
async function handleEncryption(event) {
event.preventDefault();
const password = document.querySelector("#password").value;
const fileInput = document.querySelector("#fileToEncrypt");
const file = fileInput.files[0];
if (!file || !password) {
console.error("Handle missing inputs here");
return;
}
const arrayBuffer = await file.arrayBuffer();
const bytes = new Uint8Array(arrayBuffer);
const responseBytes = await encrypt_file(bytes, password);
const blob = new Blob([responseBytes], { type: "application/octet-stream" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `${file.name}.bin`;
a.click();
URL.revokeObjectURL(url);
}
async function main() {
await init();
const fileInput = document.querySelector("#encryptForm");
fileInput.addEventListener("submit", (event) => {
handleEncryption(event);
});
}
main();
</script>
Build Script
[ -e pkg ] && rm -rf pkg
wasm-pack build --target web
[ -e "pkg/.gitignore" ] && rm "pkg/.gitignore"
References:
https://docs.rs/orion/latest/orion/
https://docs.rs/orion/latest/orion/aead/index.html
https://docs.rs/orion/latest/orion/kdf/index.html
https://docs.rs/orion/latest/orion/kdf/struct.Salt.html
https://docs.rs/wasm-bindgen/latest/wasm_bindgen/
https://doc.libsodium.org/password_hashing/default_phf#guidelines-for-choosing-the-parameters
https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html