extern crate bindgen;
use autotools::Config;
use std::collections::HashSet;
use std::env;
use std::fs::File;
use std::path::{Path, PathBuf};
use std::process::Command;
#[derive(Debug)]
struct IgnoreMacros(HashSet<String>);
impl bindgen::callbacks::ParseCallbacks for IgnoreMacros {
fn will_parse_macro(&self, name: &str) -> bindgen::callbacks::MacroParsingBehavior {
if self.0.contains(name) {
bindgen::callbacks::MacroParsingBehavior::Ignore
} else {
bindgen::callbacks::MacroParsingBehavior::Default
}
}
}
fn copy_wolfssl(dest: &Path) -> std::io::Result<PathBuf> {
println!("cargo:rerun-if-changed=wolfssl-src");
Command::new("cp")
.arg("-rf")
.arg("wolfssl-src")
.arg(dest)
.status()
.unwrap();
Ok(dest.join("wolfssl-src"))
}
const PATCH_DIR: &str = "patches";
const PATCHES: &[&str] = &[
"include-private-key-fields-for-kyber.patch",
"make-kyber-mlkem-available.patch",
"fix-kyber-mlkem-benchmark.patch",
"fix-mlkem-get-curve-name.patch",
"fix-kyber-get-curve-name.patch",
];
fn apply_patch(wolfssl_path: &Path, patch: impl AsRef<Path>) {
let full_patch = Path::new(PATCH_DIR).join(patch.as_ref());
println!("cargo:rerun-if-changed={}", full_patch.display());
let patch_buffer = File::open(full_patch).unwrap();
let status = Command::new("patch")
.arg("-d")
.arg(wolfssl_path)
.arg("-p1")
.stdin(patch_buffer)
.status()
.unwrap();
assert!(
status.success(),
"Failed to apply {}",
patch.as_ref().display()
);
}
fn build_wolfssl(wolfssl_src: &Path) -> PathBuf {
let mut conf = Config::new(wolfssl_src);
conf.reconf("-ivf")
.disable("benchmark", None)
.disable("dh", None)
.disable("examples", None)
.disable("oldtls", None)
.disable("sha3", None)
.disable_shared()
.disable("sys-ca-certs", None)
.disable("dilithium", None)
.enable("aes-bitsliced", None)
.enable("curve25519", None)
.enable("dtls", None)
.enable("dtls13", None)
.enable("dtls-frag-ch", None)
.enable("dtls-mtu", None)
.enable("secure-renegotiation", None)
.enable("singlethreaded", None)
.enable("sni", None)
.enable("sp", None)
.enable("sp-asm", None)
.enable_static()
.enable("supportedcurves", None)
.enable("tls13", None)
.enable("experimental", None)
.cflag("-g")
.cflag("-fPIC")
.cflag("-DWOLFSSL_DTLS_ALLOW_FUTURE")
.cflag("-DWOLFSSL_MIN_RSA_BITS=2048")
.cflag("-DWOLFSSL_MIN_ECC_BITS=256")
.cflag("-DUSE_CERT_BUFFERS_4096")
.cflag("-DUSE_CERT_BUFFERS_256")
.cflag("-DWOLFSSL_NO_SPHINCS")
.cflag("-DWOLFSSL_TLS13_MIDDLEBOX_COMPAT");
if cfg!(feature = "debug") {
conf.enable("debug", None);
conf.cflag("-DHAVE_SECRET_CALLBACK");
}
if cfg!(feature = "postquantum") {
let flags = if cfg!(feature = "kyber_only") {
"all,original"
} else {
"all,original,ml-kem"
};
conf.enable("kyber", Some(flags))
.enable("sha3", None);
}
match build_target::target_arch().unwrap() {
build_target::Arch::AARCH64 => {
conf.enable("armasm", None);
}
build_target::Arch::ARM => {
if build_target::target_os().unwrap() != build_target::Os::Android {
conf.enable("armasm", None);
}
}
build_target::Arch::X86 => {
conf.disable("sp-asm", None);
}
build_target::Arch::X86_64 => {
conf.enable("intelasm", None);
conf.enable("aesni", None);
}
_ => {}
}
if build_target::target_os().unwrap() == build_target::Os::Android {
let (chost, arch_flags, arch, configure_platform) =
match build_target::target_arch().unwrap() {
build_target::Arch::ARM => (
"armv7a-linux-androideabi",
"-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -O3",
"armeabi-v7a",
"android-arm",
),
build_target::Arch::AARCH64 => (
"aarch64-linux-android",
"-march=armv8-a+crypto -O3",
"arm64-v8a",
"android-arm64",
),
build_target::Arch::X86 => (
"i686-linux-android",
"-march=i686 -msse3 -m32 -O3",
"x86",
"android-x86",
),
build_target::Arch::X86_64 => (
"x86_64-linux-android",
"-march=x86-64 -msse4.2 -mpopcnt -m64 -O3",
"x86_64",
"android64-x86_64",
),
_ => panic!("Unsupported build_target for Android"),
};
conf.config_option("host", Some(chost));
conf.cflag(arch_flags);
conf.env("ARCH", arch);
conf.env("CONFIGURE_PLATFORM", configure_platform);
conf.disable("crypttests", None);
conf.cflag("-DFP_MAX_BITS=8192");
conf.cflag("-fomit-frame-pointer");
conf.env("LIBS", "-llog -landroid");
}
conf.build()
}
fn main() -> std::io::Result<()> {
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let wolfssl_src = copy_wolfssl(&out_dir)?;
PATCHES.iter().for_each(|&f| apply_patch(&wolfssl_src, f));
println!("cargo:rerun-if-changed={}", PATCH_DIR);
let wolfssl_install_dir = build_wolfssl(&wolfssl_src);
let mut hash_ignored_macros = HashSet::new();
for i in &[
"IPPORT_RESERVED",
"EVP_PKEY_DH",
"BIO_CLOSE",
"BIO_NOCLOSE",
"CRYPTO_LOCK",
"ASN1_STRFLGS_ESC_MSB",
"SSL_MODE_RELEASE_BUFFERS",
"GEN_IPADD",
"EVP_PKEY_RSA",
] {
hash_ignored_macros.insert(i.to_string());
}
let ignored_macros = IgnoreMacros(hash_ignored_macros);
let wolfssl_include_dir = wolfssl_install_dir.join("include");
let builder = bindgen::Builder::default()
.header("wrapper.h")
.clang_arg(format!("-I{}/", wolfssl_include_dir.to_str().unwrap()))
.parse_callbacks(Box::new(ignored_macros))
.formatter(bindgen::Formatter::Rustfmt);
let builder = [
"wolfssl/.*.h",
"wolfssl/wolfcrypt/.*.h",
"wolfssl/openssl/compat_types.h",
]
.iter()
.fold(builder, |b, p| {
b.allowlist_file(wolfssl_include_dir.join(p).to_str().unwrap())
});
let builder = builder.blocklist_function("wolfSSL_BIO_vprintf");
let bindings: bindgen::Bindings = builder.generate().expect("Unable to generate bindings");
bindings
.write_to_file(out_dir.join("bindings.rs"))
.expect("Couldn't write bindings!");
println!("cargo:rustc-link-lib=static=wolfssl");
println!(
"cargo:rustc-link-search=native={}",
wolfssl_install_dir.join("lib").to_str().unwrap()
);
println!("cargo:rerun-if-changed=wrapper.h");
Ok(())
}