initial commit
Some checks failed
CI / lint (push) Successful in 1m37s
CI / test-python (push) Successful in 1m49s
CI / test-zig (push) Successful in 1m39s
CI / test-wasm (push) Successful in 1m54s
CI / test (push) Successful in 14m44s
CI / miri (push) Successful in 14m18s
CI / build (push) Successful in 1m9s
CI / fuzz-regression (push) Successful in 9m9s
CI / publish (push) Failing after 1m10s
CI / publish-python (push) Failing after 1m46s
CI / publish-wasm (push) Has been cancelled

Signed-off-by: Kamal Tufekcic <kamal@lo.sh>
This commit is contained in:
Kamal Tufekcic 2026-04-02 23:48:10 +03:00
commit 1d99048c95
No known key found for this signature in database
165830 changed files with 79062 additions and 0 deletions

113
soliton/build.rs Normal file
View file

@ -0,0 +1,113 @@
//! Build script for soliton.
//!
//! Embeds the EFF large wordlist for verification phrase generation.
//! The wordlist is checked into the repo at `src/eff_large_wordlist.txt`
//! and SHA-256 verified at build time.
//!
//! All cryptographic dependencies are pure Rust — no C compilation or linking needed.
use sha2::{Digest, Sha256};
use std::env;
use std::fs;
use std::io::Write;
use std::path::PathBuf;
/// SHA-256 hash of the canonical EFF large wordlist (7776 lines).
/// <https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt>
const EFF_WORDLIST_SHA256: &str =
"addd35536511597a02fa0a9ff1e5284677b8883b83e986e43f15a3db996b903e";
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src/eff_large_wordlist.txt");
generate_wordlist();
}
/// Read the EFF large wordlist from the in-repo cached copy and generate
/// a Rust source file with the words as a static array. The file is
/// written to OUT_DIR and included via `include!` in the verification
/// module.
///
/// The EFF large wordlist contains 7776 words (6^5), one per line, prefixed
/// with a dice roll number and a tab. We strip the prefix and keep only the
/// words.
fn generate_wordlist() {
// Cargo guarantees OUT_DIR and CARGO_MANIFEST_DIR for build scripts.
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let wordlist_rs = out_dir.join("eff_wordlist.rs");
let cache_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
.join("src")
.join("eff_large_wordlist.txt");
// Skip regeneration if the output already exists AND the cached source
// passes SHA-256 verification. Safe because the output is deterministically
// generated from the source — same input always produces identical output.
// The SHA-256 check protects against tampered build caches.
if wordlist_rs.exists() {
if let Ok(cached) = fs::read_to_string(&cache_path) {
verify_wordlist_hash(&cached);
return;
}
}
let raw = fs::read_to_string(&cache_path)
.expect("EFF wordlist not available: src/eff_large_wordlist.txt missing from repo");
verify_wordlist_hash(&raw);
// Parse: each line is "DDDDD\tword\n" (5-digit dice roll, tab, word).
let words: Vec<&str> = raw
.lines()
.filter_map(|line| {
let line = line.trim();
if line.is_empty() {
return None;
}
line.split('\t').nth(1)
})
.collect();
// Panic: build scripts communicate errors by panicking. A count mismatch
// indicates a corrupted or non-canonical wordlist that passed SHA-256
// (hash collision or wrong file).
assert_eq!(
words.len(),
7776,
"EFF wordlist should have 7776 entries, got {}",
words.len()
);
// Panic on I/O failure: build scripts have no recovery path — Cargo
// treats a panic as a build error and reports the message to the user.
let mut out = fs::File::create(&wordlist_rs).unwrap();
writeln!(out, "/// EFF large wordlist (7776 words).").unwrap();
writeln!(
out,
"/// Source: <https://www.eff.org/dice>, checked into repo at src/eff_large_wordlist.txt."
)
.unwrap();
writeln!(out, "pub(crate) static EFF_WORDLIST: [&str; 7776] = [").unwrap();
for word in &words {
writeln!(out, " \"{word}\",").unwrap();
}
writeln!(out, "];").unwrap();
}
/// Verify the SHA-256 hash of the EFF wordlist content.
///
/// # Security
///
/// Panics with a clear message if the hash doesn't match, preventing
/// use of a tampered or corrupted wordlist in verification phrases.
/// This is the sole integrity gate for the in-repo wordlist.
fn verify_wordlist_hash(content: &str) {
let computed = format!("{:x}", Sha256::digest(content.as_bytes()));
assert_eq!(
computed, EFF_WORDLIST_SHA256,
"EFF wordlist SHA-256 mismatch!\n expected: {}\n computed: {}\n\
The wordlist may be corrupted or tampered with.",
EFF_WORDLIST_SHA256, computed
);
}