use std::env;
use std::panic::set_hook;
use std::path::{Path, PathBuf};
#[derive(Debug)]
pub struct OsslCallbacks;
impl bindgen::callbacks::ParseCallbacks for OsslCallbacks {
fn int_macro(
&self,
name: &str,
value: i64,
) -> Option<bindgen::callbacks::IntKind> {
if name == "OPENSSL_VERSION_NUMBER" {
println!("cargo:version_number={value:x}");
}
None
}
}
fn ossl_bindings(args: &mut Vec<String>, out_file: &Path) {
if let Some(var) = env::var("OSSL_BINDGEN_CLANG_ARGS").ok() {
for arg in var.split_whitespace() {
args.push(arg.to_string());
}
}
bindgen::Builder::default()
.header("ossl.h")
.clang_args(args)
.derive_default(true)
.formatter(bindgen::Formatter::Prettyplease)
.allowlist_item("ossl_.*")
.allowlist_item("OSSL_.*")
.allowlist_item("openssl_.*")
.allowlist_item("OPENSSL_.*")
.allowlist_item("CRYPTO_.*")
.allowlist_item("c_.*")
.allowlist_item("EVP_.*")
.allowlist_item("evp_.*")
.allowlist_item("BN_.*")
.allowlist_item("LN_aes.*")
.allowlist_item("ERR.*")
.allowlist_item("BIO.*")
.blocklist_item("evp_pkey_ctx_st__.*")
.opaque_type("ecx_key_st")
.opaque_type("evp_pkey_ctx_st")
.parse_callbacks(Box::new(OsslCallbacks))
.generate()
.expect("Unable to generate bindings")
.write_to_file(out_file)
.expect("Couldn't write bindings!");
}
fn build_ossl(out_file: &Path) {
let sources = std::env::var("KRYOPTIC_OPENSSL_SOURCES")
.expect("Env var KRYOPTIC_OPENSSL_SOURCES is not defined");
let openssl_path = std::path::PathBuf::from(sources)
.canonicalize()
.expect("cannot canonicalize OpenSSL path");
let mut buildargs = vec![
"no-deprecated",
"no-aria",
"no-argon2",
"no-atexit",
"no-des",
"no-dsa",
"no-cast",
"no-mdc2",
"no-ec2m",
"no-rc2",
"no-rc4",
"no-rc5",
"no-rmd160",
"no-seed",
"no-sm2",
"no-sm3",
"no-sm4",
"no-docs",
"no-tests",
];
match std::env::var("CARGO_CFG_TARGET_ARCH") {
Ok(arch) => match arch.as_str() {
"x86" => {
buildargs.insert(0, "linux-elf");
buildargs.push("-m32");
buildargs.push("-latomic");
}
"x86_64" => buildargs.push("enable-ec_nistp_64_gcc_128"),
"aarch64" => buildargs.push("enable-ec_nistp_64_gcc_128"),
"powerpc64" => buildargs.push("enable-ec_nistp_64_gcc_128"),
"s390x" => buildargs.push("no-ec_nistp_64_gcc_128"),
_ => (),
},
_ => panic!("No arch available in CARGO_CFG_TARGET_ARCH"),
}
if env::var("PROFILE").unwrap().as_str() == "debug" {
buildargs.push("--debug");
}
let mut defines = "-DDEVRANDOM=\\\"/dev/urandom\\\"".to_string();
let ar_path: std::path::PathBuf;
let ar_name: &str;
if cfg!(feature = "fips") {
buildargs.push("enable-fips");
defines.push_str(" -DOPENSSL_PEDANTIC_ZEROIZATION");
let fips_name = match std::env::var("KRYOPTIC_FIPS_VENDOR") {
Ok(name) => name,
Err(_) => env!("CARGO_PKG_NAME").to_string(),
};
defines.push_str(&format!(
" -DKRYOPTIC_FIPS_VENDOR=\\\"{}\\\"",
fips_name,
));
let fips_ver = match std::env::var("KRYOPTIC_FIPS_VERSION") {
Ok(ver) => ver,
Err(_) => env!("CARGO_PKG_VERSION").to_string(),
};
defines.push_str(&format!(
" -DKRYOPTIC_FIPS_VERSION=\\\"{}\\\"",
fips_ver,
));
let fips_build = match std::env::var("KRYOPTIC_FIPS_BUILD") {
Ok(bd) => bd,
Err(_) => "test".to_string(),
};
defines.push_str(&format!(
" -DKRYOPTIC_FIPS_BUILD=\\\"{}\\\"",
fips_build,
));
ar_name = "fips";
ar_path = openssl_path
.join("providers")
.canonicalize()
.expect("OpenSSL providers path unavailable");
} else {
ar_path = openssl_path.clone();
ar_name = "crypto";
}
buildargs.push(&defines);
let libpath = format!("{}/lib{}.a", ar_path.to_string_lossy(), ar_name);
println!("cargo:rustc-link-search={}", ar_path.to_string_lossy());
println!("cargo:rustc-link-lib=static={}", ar_name);
println!("cargo:rerun-if-changed={}", libpath);
match std::env::var("CARGO_CFG_TARGET_ARCH") {
Ok(arch) => match arch.as_str() {
"x86" => {
println!("cargo::rustc-link-lib=atomic");
}
_ => (),
},
_ => panic!("No arch available in CARGO_CFG_TARGET_ARCH"),
}
match std::path::Path::new(&libpath).try_exists() {
Ok(true) => (),
_ => {
if !std::process::Command::new("./Configure")
.current_dir(&openssl_path)
.args(buildargs)
.stdout(std::process::Stdio::inherit())
.stderr(std::process::Stdio::inherit())
.output()
.expect("could not run openssl `Configure`")
.status
.success()
{
panic!("could not configure OpenSSL");
}
let jobs = std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1);
let make_args = ["-j", &jobs.to_string()];
if !std::process::Command::new("make")
.current_dir(&openssl_path)
.args(make_args)
.stdout(std::process::Stdio::inherit())
.stderr(std::process::Stdio::inherit())
.output()
.expect("could not run openssl `make`")
.status
.success()
{
panic!("could not build OpenSSL");
}
}
}
let include_path = format!(
"-I{}",
openssl_path
.join("include")
.canonicalize()
.expect("OpenSSL include path unavailable")
.to_str()
.unwrap()
);
let mut args: Vec<String> = Vec::new();
args.push(include_path);
if cfg!(feature = "fips") {
args.push("-D_KRYOPTIC_FIPS_".to_string());
}
ossl_bindings(&mut args, out_file);
}
fn use_system_ossl(out_file: &Path) {
let library = pkg_config::Config::new()
.atleast_version("3.0.7")
.probe("openssl")
.unwrap();
let mut args: Vec<String> = Vec::new();
for include_path in library.include_paths {
args.push(["-I", include_path.to_str().unwrap()].concat());
}
ossl_bindings(&mut args, out_file);
}
fn set_pretty_panic() {
set_hook(Box::new(|panic_info| {
if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
if s != &"panic in a function that cannot unwind" {
println!("Compile Error: {s:?}");
}
} else if let Some(s) = panic_info.payload().downcast_ref::<String>() {
if s != "panic in a function that cannot unwind" {
println!("Compile Error: {s:?}");
}
} else {
if let Some(location) = panic_info.location() {
println!(
"Unrecognized compile error in file '{}' at line {}",
location.file(),
location.line(),
);
} else {
println!("Unknown panic with no location information...");
}
}
}));
}
fn main() {
set_pretty_panic();
if cfg!(all(feature = "dynamic", feature = "fips")) {
panic!("You can't combine dynamic and fips features.")
}
println!(
"cargo::rustc-check-cfg=cfg(ossl_v307,ossl_v320,ossl_v350,ossl_v400)"
);
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let ossl_bindings = out_path.join("ossl_bindings.rs");
if cfg!(feature = "dynamic") {
use_system_ossl(&ossl_bindings);
} else {
build_ossl(&ossl_bindings);
}
println!("cargo:rerun-if-changed=build.rs");
}