anubis-age 1.4.0

Post-quantum secure encryption library with hybrid X25519+ML-KEM-1024 mode (internal dependency for anubis-rage)
Documentation
//! Build script for FIPS 140-3 module integrity verification
//!
//! Computes SHA-256 hash of critical cryptographic source files
//! and embeds it in the binary for runtime verification.

use sha2::{Digest, Sha256};
use std::env;
use std::fs;
use std::io::{self, Read};
use std::path::{Path, PathBuf};

/// Cryptographic module source files that must be integrity-checked
const MODULE_FILES: &[&str] = &[
    "src/lib.rs",
    "src/primitives/mod.rs",
    "src/primitives/aead.rs",
    "src/primitives/stream.rs",
    "src/fips/mod.rs",
    "src/fips/selftests.rs",
    "src/pqc/mod.rs",
    "src/pqc/mlkem.rs",
    "src/pqc/mldsa.rs",
    "src/protocol.rs",
    "src/format.rs",
    "src/keys.rs",
];

fn hash_file(path: &Path) -> io::Result<Vec<u8>> {
    let mut file = fs::File::open(path)?;
    let mut hasher = Sha256::new();
    let mut buffer = [0u8; 8192];

    loop {
        let n = file.read(&mut buffer)?;
        if n == 0 {
            break;
        }
        hasher.update(&buffer[..n]);
    }

    Ok(hasher.finalize().to_vec())
}

fn compute_module_hash() -> io::Result<String> {
    let manifest_dir = env::var("CARGO_MANIFEST_DIR")
        .unwrap_or_else(|_| ".".to_string());
    let base_path = PathBuf::from(manifest_dir);

    // Hash all critical module files in sorted order for reproducibility
    let mut combined_hasher = Sha256::new();

    for file_path in MODULE_FILES {
        let full_path = base_path.join(file_path);

        if !full_path.exists() {
            eprintln!("Warning: Module file not found: {}", full_path.display());
            continue;
        }

        let file_hash = hash_file(&full_path)?;

        // Include file path and hash in combined hash
        combined_hasher.update(file_path.as_bytes());
        combined_hasher.update(&file_hash);

        println!("cargo:rerun-if-changed={}", full_path.display());
    }

    let final_hash = combined_hasher.finalize();
    Ok(hex::encode(final_hash))
}

fn main() {
    // Compute module integrity hash
    match compute_module_hash() {
        Ok(hash) => {
            println!("cargo:rustc-env=FIPS_MODULE_HASH={}", hash);
            println!("Module integrity hash: {}", hash);
        }
        Err(e) => {
            eprintln!("Warning: Failed to compute module hash: {}", e);
            println!("cargo:rustc-env=FIPS_MODULE_HASH=UNKNOWN");
        }
    }

    // Embed build timestamp
    let timestamp = chrono::Utc::now().to_rfc3339();
    println!("cargo:rustc-env=FIPS_BUILD_TIMESTAMP={}", timestamp);
}