205 lines
8.8 KiB
Rust
205 lines
8.8 KiB
Rust
//! 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");
|
||
}
|