lum_ccc_rust/crates/ccc-crypto-wolfssl/build.rs

205 lines
8.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Build script for ccc-crypto-wolfssl.
//!
//! Responsibilities:
//! 1. Locate the wolfSSL source tree (vendored submodule).
//! 2. Build wolfSSL as a static library using CMake.
//! 3. Generate Rust FFI bindings to the wolfCrypt headers via bindgen.
//!
//! # Environment variables
//!
//! * `WOLFSSL_SOURCE_DIR` Override the default wolfSSL source path.
//! Useful for CI environments where the submodule may be at a non-standard
//! location. Defaults to `<workspace_root>/vendors/wolfssl`.
//! * `WOLFSSL_INSTALL_DIR` If set, skip the CMake build and link against
//! a pre-installed wolfSSL at this path. The path must contain
//! `include/wolfssl/` and `lib/libwolfssl.a`.
use std::{env, path::PathBuf};
fn main() {
// When the `stub_ffi` feature is enabled (e.g. for type-checking without
// the wolfSSL C library), skip all build steps and write an empty bindings
// file so the `include!()` in lib.rs still compiles.
if std::env::var("CARGO_FEATURE_STUB_FFI").is_ok() {
let out_dir = std::env::var("OUT_DIR").unwrap();
let bindings_path = std::path::Path::new(&out_dir).join("wolfcrypt_bindings.rs");
std::fs::write(&bindings_path, "// stub — no wolfSSL headers\n").unwrap();
return;
}
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
// Workspace root is two levels up from crates/ccc-crypto-wolfssl/
let workspace_root = manifest_dir.join("../..").canonicalize().unwrap();
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
// ── 1. Locate wolfSSL ────────────────────────────────────────────────────
let (include_dir, lib_dir) =
if let Ok(install_dir) = env::var("WOLFSSL_INSTALL_DIR") {
// Use a pre-installed wolfSSL — skip the CMake build.
let p = PathBuf::from(&install_dir);
println!("cargo:warning=Using pre-installed wolfSSL at {}", install_dir);
(p.join("include"), p.join("lib"))
} else {
// Build from source (default — uses our git submodule).
let source_dir = env::var("WOLFSSL_SOURCE_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| workspace_root.join("vendors/wolfssl"));
if !source_dir.join("CMakeLists.txt").exists() {
panic!(
"\n\nwolfSSL source not found at {}.\n\
Run `git submodule update --init --recursive` to fetch it,\n\
or set WOLFSSL_SOURCE_DIR to an alternative path.\n",
source_dir.display()
);
}
build_wolfssl_cmake(&source_dir, &out_dir)
};
// ── 2. Tell Cargo how to link ────────────────────────────────────────────
println!("cargo:rustc-link-search=native={}", lib_dir.display());
println!("cargo:rustc-link-lib=static=wolfssl");
// Link system libraries required by wolfSSL on each platform.
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
match target_os.as_str() {
"macos" | "ios" => {
println!("cargo:rustc-link-lib=framework=Security");
println!("cargo:rustc-link-lib=c++");
}
"linux" | "android" => {
println!("cargo:rustc-link-lib=stdc++");
}
_ => {}
}
// Re-run the build script if any of these change.
println!("cargo:rerun-if-env-changed=WOLFSSL_SOURCE_DIR");
println!("cargo:rerun-if-env-changed=WOLFSSL_INSTALL_DIR");
// ── 3. Generate FFI bindings via bindgen ─────────────────────────────────
generate_bindings(&include_dir, &out_dir);
}
/// Build wolfSSL from source using CMake. Returns `(include_dir, lib_dir)`.
fn build_wolfssl_cmake(source_dir: &PathBuf, out_dir: &PathBuf) -> (PathBuf, PathBuf) {
println!(
"cargo:warning=Building wolfSSL from source at {}",
source_dir.display()
);
let mut cfg = cmake::Config::new(source_dir);
// ── Core algorithm selection ─────────────────────────────────────────────
// Enable exactly the algorithms the CCC system needs in Phase 4.
// Additional algorithms can be enabled here for Phase 5+ PQ support.
cfg
// Build as a static library.
.define("BUILD_SHARED_LIBS", "OFF")
.define("WOLFSSL_BUILD_SHARED_LIBS", "OFF")
// Disable TLS/SSL stack — we only need the wolfCrypt algorithms.
.define("WOLFSSL_NO_TLS", "ON")
// AEAD
.define("WOLFSSL_AES_GCM", "ON")
.define("WOLFSSL_CHACHA", "ON")
.define("WOLFSSL_POLY1305", "ON")
// Hash
.define("WOLFSSL_SHA224", "ON")
.define("WOLFSSL_SHA384", "ON")
.define("WOLFSSL_SHA512", "ON")
.define("WOLFSSL_SHA3", "ON")
.define("WOLFSSL_BLAKE2", "ON")
// KDF
.define("WOLFSSL_HKDF", "ON")
.define("WOLFSSL_PWDBASED", "ON") // PBKDF2, Argon2
// MAC
.define("WOLFSSL_HMAC", "ON")
// Asymmetric (needed for X25519 ratchet)
.define("WOLFSSL_CURVE25519", "ON")
.define("WOLFSSL_CURVE448", "ON")
// RNG (needed internally)
.define("WOLFSSL_RNG", "ON")
// Minimise binary size.
.define("WOLFSSL_CRYPTONLY", "ON")
.define("WOLFSSL_MIN_RSA_BITS", "2048")
.define("WOLFSSL_NO_FILESYSTEM", "ON");
let install_path = cfg.build();
let include_dir = install_path.join("include");
let lib_dir = install_path.join("lib");
(include_dir, lib_dir)
}
/// Run bindgen over the wolfCrypt headers we actually use.
fn generate_bindings(include_dir: &PathBuf, out_dir: &PathBuf) {
let header = include_dir.join("wolfssl/wolfcrypt/aes.h");
if !header.exists() {
// Bindings are only generated when wolfSSL headers are present.
// In environments without the C library (e.g. pure Rust CI), a
// pre-generated bindings file can be committed at src/bindings.rs.
println!(
"cargo:warning=wolfSSL headers not found at {}; \
using pre-generated bindings if available.",
include_dir.display()
);
return;
}
let bindings = bindgen::Builder::default()
// Include all wolfCrypt headers we need.
.header(include_dir.join("wolfssl/wolfcrypt/aes.h").to_str().unwrap())
.header(include_dir.join("wolfssl/wolfcrypt/chacha20_poly1305.h").to_str().unwrap())
.header(include_dir.join("wolfssl/wolfcrypt/sha256.h").to_str().unwrap())
.header(include_dir.join("wolfssl/wolfcrypt/sha512.h").to_str().unwrap())
.header(include_dir.join("wolfssl/wolfcrypt/sha3.h").to_str().unwrap())
.header(include_dir.join("wolfssl/wolfcrypt/blake2.h").to_str().unwrap())
.header(include_dir.join("wolfssl/wolfcrypt/hmac.h").to_str().unwrap())
.header(include_dir.join("wolfssl/wolfcrypt/kdf.h").to_str().unwrap())
.header(include_dir.join("wolfssl/wolfcrypt/curve25519.h").to_str().unwrap())
.header(include_dir.join("wolfssl/wolfcrypt/curve448.h").to_str().unwrap())
.header(include_dir.join("wolfssl/wolfcrypt/random.h").to_str().unwrap())
.clang_arg(format!("-I{}", include_dir.display()))
// Only generate bindings for the types/functions we whitelist.
.allowlist_function("wc_Aes.*")
.allowlist_function("wc_ChaCha20Poly1305.*")
.allowlist_function("wc_Sha.*")
.allowlist_function("wc_Blake2.*")
.allowlist_function("wc_Hmac.*")
.allowlist_function("wc_HKDF.*")
.allowlist_function("wc_Pbkdf2.*")
.allowlist_function("wc_Argon2.*")
.allowlist_function("wc_curve25519.*")
.allowlist_function("wc_curve448.*")
.allowlist_function("wc_InitRng.*")
.allowlist_function("wc_RNG.*")
.allowlist_function("wc_FreeRng.*")
.allowlist_type("Aes")
.allowlist_type("Hmac")
.allowlist_type("WC_RNG")
.allowlist_type("curve25519.*")
.allowlist_type("curve448.*")
.allowlist_var("AES_.*")
.allowlist_var("WC_.*")
.allowlist_var("SHA.*")
.allowlist_var("BLAKE2.*")
// Silence warnings for types we don't control.
.blocklist_type("__.*")
.derive_debug(false)
.layout_tests(false)
.generate()
.expect("bindgen failed to generate wolfCrypt bindings");
bindings
.write_to_file(out_dir.join("wolfcrypt_bindings.rs"))
.expect("failed to write wolfCrypt bindings");
println!("cargo:rerun-if-changed=build.rs");
}