From 17478d9efbde792a3bbea34a719a67256ec8ab21 Mon Sep 17 00:00:00 2001 From: Thomas Epperson Date: Sat, 20 Apr 2024 16:02:50 -0500 Subject: [PATCH 1/2] Add bundled feature and improve support for windows. Signed-off-by: Thomas Epperson --- tss-esapi-sys/Cargo.toml | 8 +- tss-esapi-sys/build.rs | 211 +++++++++++++++++++++++++++++++++++++- tss-esapi/Cargo.toml | 1 + tss-esapi/build.rs | 4 + tss-esapi/src/tcti_ldr.rs | 5 + 5 files changed, 226 insertions(+), 3 deletions(-) diff --git a/tss-esapi-sys/Cargo.toml b/tss-esapi-sys/Cargo.toml index 0e8615fc..7bbf2e11 100644 --- a/tss-esapi-sys/Cargo.toml +++ b/tss-esapi-sys/Cargo.toml @@ -14,11 +14,17 @@ links = "tss2-esys" rust-version = "1.66.0" [build-dependencies] -bindgen = { version = "0.66.1", optional = true } +autotools = { version = "0.2.6", optional = true } +bindgen = { version = "0.69.4", optional = true } pkg-config = "0.3.18" target-lexicon = "0.12.0" cfg-if = "1.0.0" semver = "1.0.7" +[target.'cfg(windows)'.build-dependencies] +msbuild = { git = "https://github.com/uglyoldbob/msbuild.git", optional = true } +winreg = {version = "0.52", optional = true } + [features] generate-bindings = ["bindgen"] +bundled = ["dep:autotools", "dep:msbuild", "dep:winreg"] diff --git a/tss-esapi-sys/build.rs b/tss-esapi-sys/build.rs index d261d567..f3c14520 100644 --- a/tss-esapi-sys/build.rs +++ b/tss-esapi-sys/build.rs @@ -9,11 +9,21 @@ fn main() { cfg_if::cfg_if! { if #[cfg(feature = "generate-bindings")] { + #[cfg(feature = "bundled")] + let installation = tpm2_tss::Installation::bundled(); + #[cfg(not(feature = "bundled"))] let installation = tpm2_tss::Installation::probe(true); let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()); installation.generate_bindings(&out_dir.join("tss_esapi_bindings.rs")); + installation.output_linker_arguments(); } else { target::ensure_supported(); + #[cfg(feature = "bundled")] + { + let installation = tpm2_tss::Installation::bundled(); + installation.pkg_config(); + } + #[cfg(not(feature = "bundled"))] let _ = tpm2_tss::Installation::probe(false); } } @@ -59,7 +69,7 @@ pub mod tpm2_tss { /// The installed tpm2-tss libraries that are of /// interest. pub struct Installation { - _tss2_sys: Library, + tss2_sys: Library, tss2_esys: Library, tss2_tctildr: Library, tss2_mu: Library, @@ -67,11 +77,150 @@ pub mod tpm2_tss { } impl Installation { + fn platform_args() -> Option> { + cfg_if::cfg_if! { + if #[cfg(windows)] { + let mut clang_args: Vec = Vec::new(); + let hklm = winreg::RegKey::predef(winreg::enums::HKEY_LOCAL_MACHINE); + let sdk_entry = hklm.open_subkey("SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0").unwrap(); + let installation_path: String = sdk_entry.get_value("InstallationFolder").unwrap(); + let ip_pb = PathBuf::from(installation_path).join("Include"); + let windows_sdk = ip_pb.join("10.0.17134.0"); + clang_args.push(format!("-I{}", windows_sdk.join("ucrt").display())); + clang_args.push(format!("-I{}", windows_sdk.join("um").display())); + clang_args.push(format!("-I{}", windows_sdk.join("shared").display())); + Some(clang_args) + } + else { + None + } + } + } + + #[cfg(feature = "bundled")] + /// Fetch the given source repo using git + fn fetch_source( + dest_path: impl AsRef, + name: &str, + repo: &str, + branch: &str, + ) -> std::path::PathBuf { + let parent_path = dest_path.as_ref(); + let repo_path = parent_path.join(name); + if !repo_path.join("Makefile.am").exists() { + let output = std::process::Command::new("git") + .args(["clone", repo, "--depth", "1", "--branch", branch]) + .current_dir(parent_path) + .output() + .unwrap_or_else(|_| panic!("git clone for {} failed", name)); + let status = output.status; + if !status.success() { + panic!( + "git clone for {} returned failure status {}:\n{:?}", + name, status, output + ); + } + } + + repo_path + } + + #[cfg(feature = "bundled")] + fn compile_with_autotools(p: PathBuf) -> PathBuf { + let output1 = std::process::Command::new("./bootstrap") + .current_dir(&p) + .output() + .expect("bootstrap script failed"); + let status = output1.status; + if !status.success() { + panic!("bootstrap script failed with {}:\n{:?}", status, output1); + } + + let mut config = autotools::Config::new(p); + config.fast_build(true).reconf("-ivf").build() + } + + #[cfg(feature = "bundled")] + /// Uses a bundled build for an installation + pub fn bundled() -> Self { + use std::io::Write; + let out_path = std::env::var("OUT_DIR").expect("No output directory given"); + let source_path = Self::fetch_source( + out_path, + "tpm2-tss", + "https://github.com/tpm2-software/tpm2-tss.git", + MINIMUM_VERSION, + ); + let version_file_name = source_path.join("VERSION"); + let mut version_file = std::fs::File::create(version_file_name) + .expect("Unable to create version file for tpm2-tss"); + write!(version_file, "{}", MINIMUM_VERSION) + .unwrap_or_else(|e| panic!("Failed to write version file: {}", e)); + + cfg_if::cfg_if! { + if #[cfg(windows)] { + let mut msbuild = msbuild::MsBuild::find_msbuild(Some("2017")).unwrap(); + let profile = std::env::var("PROFILE").unwrap(); + let build_string = match profile.as_str() { + "debug" => "", + "release" => "/p:Configuration=Release", + _ => panic!("Unknown cargo profile:"), + }; + + msbuild.run(source_path.clone(), &[ + build_string, + "tpm2-tss.sln"]); + } + else { + let install_path = Self::compile_with_autotools(source_path.clone()); + std::env::set_var( + "PKG_CONFIG_PATH", + format!("{}", install_path.join("lib").join("pkgconfig").display()), + ); + } + } + std::env::set_var(PATH_ENV_VAR_NAME, source_path.clone()); + + let include_path = source_path.join("include").join("tss2"); + + #[cfg(windows)] + let tbs = Some(Library { + header_file: Some(include_path.join("tss2_tcti_tbs.h")), + version: MINIMUM_VERSION.into(), + name: "tss2-tbs".into(), + }); + #[cfg(not(windows))] + let tbs = None; + Self { + tss2_sys: Library { + header_file: Some(include_path.join("tss2_sys.h")), + version: MINIMUM_VERSION.into(), + name: "tss2-sys".into(), + }, + tss2_esys: Library { + header_file: Some(include_path.join("tss2_esys.h")), + version: MINIMUM_VERSION.into(), + name: "tss2-esys".into(), + }, + tss2_tctildr: Library { + header_file: Some(include_path.join("tss2_tctildr.h")), + version: MINIMUM_VERSION.into(), + name: "tss2-tctildr".into(), + }, + tss2_mu: Library { + header_file: Some(include_path.join("tss2_mu.h")), + version: MINIMUM_VERSION.into(), + name: "tss2-mu".into(), + }, + tss2_tcti_tbs: tbs, + } + } + /// Probes the system for an installation. pub fn probe(with_header_files: bool) -> Self { let install_path = Installation::installation_path_from_env_var(); Installation { - _tss2_sys: Library::probe_required( + tss2_sys: Library::probe_required( "tss2-sys", install_path.as_ref(), with_header_files, @@ -149,10 +298,58 @@ pub mod tpm2_tss { .clang_arg(tss2_tcti_tbs.include_dir_arg()) .header(tss2_tcti_tbs.header_file_arg()); } + if let Some(clang_args) = Self::platform_args() { + for arg in clang_args { + builder = builder.clang_arg(arg); + } + } builder } } } + + /// Run pkgconfig for bundled installations + #[cfg(feature = "bundled")] + pub fn pkg_config(&self) { + self.tss2_sys.pkg_config(); + self.tss2_esys.pkg_config(); + self.tss2_tctildr.pkg_config(); + self.tss2_mu.pkg_config(); + if let Some(lib) = &self.tss2_tcti_tbs { + lib.pkg_config(); + } + } + + pub fn output_linker_arguments(&self) { + #[cfg(windows)] + { + println!("cargo:rustc-link-lib=dylib=tss2-esys"); + println!("cargo:rustc-link-lib=dylib=tss2-mu"); + println!("cargo:rustc-link-lib=dylib=tss2-sys"); + println!("cargo:rustc-link-lib=dylib=tss2-tctildr"); + println!("cargo:rustc-link-lib=dylib=tss2-tcti-tbs"); + let profile = std::env::var("PROFILE").unwrap(); + let build_string = match profile.as_str() { + "debug" => "Debug", + "release" => "Release", + _ => panic!("Unknown cargo profile:"), + }; + let mut source_path = self + .tss2_esys + .header_file + .clone() + .expect("Expected a header file path"); + source_path.pop(); + source_path.pop(); + source_path.pop(); + println!("Source path is {}", source_path.display()); + println!( + "cargo:rustc-link-search=dylib={}", + source_path.join("x64").join(build_string).display() + ); + } + } + /// Retrieves the installation path from the environment variable and validates it. fn installation_path_from_env_var() -> Option<(PathBuf, String)> { std::env::var(PATH_ENV_VAR_NAME).map_or_else( @@ -372,6 +569,16 @@ pub mod tpm2_tss { ) } + /// Use the pkg config file for a bundled installation + #[cfg(feature = "bundled")] + fn pkg_config(&self) { + pkg_config::Config::new() + .atleast_version(MINIMUM_VERSION) + .statik(true) + .probe(&self.name) + .unwrap_or_else(|_| panic!("Failed to run pkg-config on {}", self.name)); + } + /// Probe the system for an optional library using pkg-config. /// /// # Args diff --git a/tss-esapi/Cargo.toml b/tss-esapi/Cargo.toml index f89fee56..96f4142c 100644 --- a/tss-esapi/Cargo.toml +++ b/tss-esapi/Cargo.toml @@ -49,3 +49,4 @@ default = ["abstraction"] generate-bindings = ["tss-esapi-sys/generate-bindings"] abstraction = ["oid", "picky-asn1", "picky-asn1-x509"] integration-tests = ["strum", "strum_macros"] +bundled = [ "tss-esapi-sys/bundled" ] \ No newline at end of file diff --git a/tss-esapi/build.rs b/tss-esapi/build.rs index 6831dbdf..3e185aa3 100644 --- a/tss-esapi/build.rs +++ b/tss-esapi/build.rs @@ -9,6 +9,10 @@ fn main() { println!("cargo:rustc-check-cfg=cfg(has_tpmu_sensitive_create)"); println!("cargo:rustc-check-cfg=cfg(has_esys_tr_get_tpm_handle)"); + #[cfg(feature = "bundled")] + { + std::env::set_var("DEP_TSS2_ESYS_VERSION", "3.2.2"); + } let tss_version_string = std::env::var("DEP_TSS2_ESYS_VERSION") .expect("Failed to parse ENV variable DEP_TSS2_ESYS_VERSION as string"); diff --git a/tss-esapi/src/tcti_ldr.rs b/tss-esapi/src/tcti_ldr.rs index f564baf8..814f80c3 100644 --- a/tss-esapi/src/tcti_ldr.rs +++ b/tss-esapi/src/tcti_ldr.rs @@ -21,6 +21,7 @@ const DEVICE: &str = "device"; const MSSIM: &str = "mssim"; const SWTPM: &str = "swtpm"; const TABRMD: &str = "tabrmd"; +const TBS: &str = "tbs"; /// TCTI Context created via a TCTI Loader Library. /// Wrapper around the TSS2_TCTI_CONTEXT structure. @@ -143,6 +144,8 @@ pub enum TctiNameConf { /// /// For more information about configuration, see [this page](https://www.mankier.com/3/Tss2_Tcti_Tabrmd_Init) Tabrmd(TabrmdConfig), + /// Connect to a TPM using windows services + Tbs, } impl TctiNameConf { @@ -174,6 +177,7 @@ impl TryFrom for CString { TctiNameConf::Mssim(..) => MSSIM, TctiNameConf::Swtpm(..) => SWTPM, TctiNameConf::Tabrmd(..) => TABRMD, + TctiNameConf::Tbs => TBS, }; let tcti_conf = match tcti { @@ -204,6 +208,7 @@ impl TryFrom for CString { TctiNameConf::Tabrmd(config) => { format!("bus_name={},bus_type={}", config.bus_name, config.bus_type) } + TctiNameConf::Tbs => String::new(), }; if tcti_conf.is_empty() { From 2763ce40dac1e63f23ea63a3a3c767270fa612b6 Mon Sep 17 00:00:00 2001 From: Thomas Epperson Date: Fri, 31 May 2024 20:25:33 -0500 Subject: [PATCH 2/2] Update documentation Update bundled documentation and include macos support Signed-off-by: William Brown --- tss-esapi-sys/Cargo.toml | 2 +- tss-esapi-sys/README.md | 61 +++++++++++++++++++++++++++++++++++++--- tss-esapi-sys/build.rs | 61 ++++++++++++++++++++++++++++++++-------- tss-esapi/README.md | 4 +++ 4 files changed, 112 insertions(+), 16 deletions(-) diff --git a/tss-esapi-sys/Cargo.toml b/tss-esapi-sys/Cargo.toml index 7bbf2e11..5df469f2 100644 --- a/tss-esapi-sys/Cargo.toml +++ b/tss-esapi-sys/Cargo.toml @@ -22,7 +22,7 @@ cfg-if = "1.0.0" semver = "1.0.7" [target.'cfg(windows)'.build-dependencies] -msbuild = { git = "https://github.com/uglyoldbob/msbuild.git", optional = true } +msbuild = { version = "0.1.0", optional = true } winreg = {version = "0.52", optional = true } [features] diff --git a/tss-esapi-sys/README.md b/tss-esapi-sys/README.md index 6f87584f..ba3e2194 100644 --- a/tss-esapi-sys/README.md +++ b/tss-esapi-sys/README.md @@ -13,12 +13,12 @@ interface to Rust to [TSS](https://github.com/tpm2-software/tpm2-tss). This crate exposes an interface for the TSS Enhanced System API and thus links to libraries that expose this interface. In order to allow proper use -of the ESAPI, this FFI layer includes bindings to TCTI and MU headers, and +of the ESAPI, this FFI layer includes bindings to TCTI and MU headers, and must therefore link to all of them at build time. The paths to the libraries are discovered using `pkg-config` - make sure they -are discoverable in this way on your system. Our build script looks for -`tss2-esys`, `tss2-tctildr` and `tss2-mu`. A minimum version of `3.2.2` is +are discoverable in this way on your system. Our build script looks for +`tss2-esys`, `tss2-tctildr` and `tss2-mu`. A minimum version of `3.2.2` is required for all of them. Having installed the open-source implementation libraries at `/usr/local/lib` (by default), it @@ -41,9 +41,62 @@ available, feel free to raise a Pull Request to add it or to use build-time generation of bindings. All the committed bindings **MUST** be generated from the library version found under the `vendor` submodule. +## Bundling TPM-TSS + +tpm-tss is used by this library to communicate with TPMs. If this library +is not available on your system you may optionally bundle (vendor) tpm-tss +during builds. tpm-tss can be provided from a local source path with the +environment variable `TPM_TSS_SOURCE_PATH` or it will be retrieved from +github during the build. + +To enable this feature: + +```bash +cargo build --features=bundled +``` + +```bash +TPM_TSS_SOURCE_PATH=/path/to/tpm-tss cargo build --features=bundled +``` + +If using this feature from an external project + +``` +tss-esapi-sys = { version = "...", features = "bundled" } +``` + +### Windows + +Compiling for windows requires a bit of setup to work with the bundled feature. + +* Openssl must be installed to a non-standard location at C:\OpenSSL-v11-Win64 +* Visual studio 2017 must be installed with the Clang/C2 experimental component, +and windows sdk 10.0.17134.0. + +### MacOS + +Compiling on MacOS requires the bundling feature. This requires dependencies +from brew. + +```bashbre +brew install autoconf autoconf-archive automake json-c libtool m4 pkg-config +``` + +Optionally you may require these libraries for certain classes of TPM transport + +``` +brew install libftdi +``` + +### OpenSUSE / SUSE + +``` +sudo zypper in autoconf autoconf-archive automake libjson-c-devel libtool libtpms-devel gawk make +``` + ## Cross compiling -Cross-compilation can be done as long as you have on your build system the TSS +Cross-compilation can be done as long as you have on your build system the TSS libraries compiled for your target system of choice. We rely on `pkg-config` to identify the libraries which we link against. Installing `tpm2-tss` does yield `.pc` files which can be used for this purpose, but depending on the exact build diff --git a/tss-esapi-sys/build.rs b/tss-esapi-sys/build.rs index f3c14520..59f46eff 100644 --- a/tss-esapi-sys/build.rs +++ b/tss-esapi-sys/build.rs @@ -43,6 +43,7 @@ pub mod target { match (target.architecture, target.operating_system) { (Architecture::Arm(_), OperatingSystem::Linux) | (Architecture::Aarch64(_), OperatingSystem::Linux) + | (Architecture::Aarch64(_), OperatingSystem::Darwin) | (Architecture::X86_64, OperatingSystem::Darwin) | (Architecture::X86_64, OperatingSystem::Linux) => {} (arch, os) => { @@ -77,15 +78,20 @@ pub mod tpm2_tss { } impl Installation { + /// Return an optional list of clang arguments that are platform specific + #[cfg(feature = "bundled")] fn platform_args() -> Option> { cfg_if::cfg_if! { if #[cfg(windows)] { let mut clang_args: Vec = Vec::new(); let hklm = winreg::RegKey::predef(winreg::enums::HKEY_LOCAL_MACHINE); + // Find the windows sdk path from the windows registry let sdk_entry = hklm.open_subkey("SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0").unwrap(); + // add relevant paths to get to the windows 10.0.17134.0 sdk, which tpm2-tss uses on windows. let installation_path: String = sdk_entry.get_value("InstallationFolder").unwrap(); let ip_pb = PathBuf::from(installation_path).join("Include"); let windows_sdk = ip_pb.join("10.0.17134.0"); + // Add paths required for bindgen to find all required headers clang_args.push(format!("-I{}", windows_sdk.join("ucrt").display())); clang_args.push(format!("-I{}", windows_sdk.join("um").display())); clang_args.push(format!("-I{}", windows_sdk.join("shared").display())); @@ -125,7 +131,7 @@ pub mod tpm2_tss { repo_path } - #[cfg(feature = "bundled")] + #[cfg(all(feature = "bundled", not(windows)))] fn compile_with_autotools(p: PathBuf) -> PathBuf { let output1 = std::process::Command::new("./bootstrap") .current_dir(&p) @@ -133,11 +139,27 @@ pub mod tpm2_tss { .expect("bootstrap script failed"); let status = output1.status; if !status.success() { - panic!("bootstrap script failed with {}:\n{:?}", status, output1); + panic!( + "{:?}/bootstrap script failed with {}:\n{:?}", + p, status, output1 + ); } let mut config = autotools::Config::new(p); - config.fast_build(true).reconf("-ivf").build() + config + // Force configuration of the autotools env + .reconf("-fiv") + // skip ./configure if no parameter changes are made + .fast_build(true) + .enable("esys", None) + // Disable fapi as we only use esys + .disable("fapi", None) + .disable("fapi-async-tests", None) + // Disable integration tests + .disable("integration", None) + // Don't allow weak crypto + .disable("weakcrypto", None) + .build() } #[cfg(feature = "bundled")] @@ -145,12 +167,27 @@ pub mod tpm2_tss { pub fn bundled() -> Self { use std::io::Write; let out_path = std::env::var("OUT_DIR").expect("No output directory given"); - let source_path = Self::fetch_source( - out_path, - "tpm2-tss", - "https://github.com/tpm2-software/tpm2-tss.git", - MINIMUM_VERSION, - ); + let source_path = if let Ok(tpm_tss_source) = std::env::var("TPM_TSS_SOURCE_PATH") { + eprintln!("using local tpm2-tss from {}", tpm_tss_source); + let Ok(source_path) = PathBuf::from(tpm_tss_source).canonicalize() else { + panic!( + "Unable to canonicalize tpm2-tss source path. Does the source path exist?" + ); + }; + + source_path + } else { + eprintln!( + "using remote tpm2-tss from https://github.com/tpm2-software/tpm2-tss.git" + ); + Self::fetch_source( + out_path, + "tpm2-tss", + "https://github.com/tpm2-software/tpm2-tss.git", + MINIMUM_VERSION, + ) + }; + let version_file_name = source_path.join("VERSION"); let mut version_file = std::fs::File::create(version_file_name) .expect("Unable to create version file for tpm2-tss"); @@ -298,11 +335,14 @@ pub mod tpm2_tss { .clang_arg(tss2_tcti_tbs.include_dir_arg()) .header(tss2_tcti_tbs.header_file_arg()); } + + #[cfg(feature = "bundled")] if let Some(clang_args) = Self::platform_args() { for arg in clang_args { builder = builder.clang_arg(arg); } } + builder } } @@ -332,7 +372,7 @@ pub mod tpm2_tss { let build_string = match profile.as_str() { "debug" => "Debug", "release" => "Release", - _ => panic!("Unknown cargo profile:"), + _ => panic!("Unknown cargo profile: {}", profile), }; let mut source_path = self .tss2_esys @@ -342,7 +382,6 @@ pub mod tpm2_tss { source_path.pop(); source_path.pop(); source_path.pop(); - println!("Source path is {}", source_path.display()); println!( "cargo:rustc-link-search=dylib={}", source_path.join("x64").join(build_string).display() diff --git a/tss-esapi/README.md b/tss-esapi/README.md index 51c5cc92..df290763 100644 --- a/tss-esapi/README.md +++ b/tss-esapi/README.md @@ -17,6 +17,10 @@ time using the headers identified on the system. Our end-goal is to achieve a fully Rust-native interface that offers strong safety and security guarantees. Check out our [documentation](https://docs.rs/tss-esapi/*/tss_esapi/#notes-on-code-safety) for an overview of our code safety approach. +## Integration Tests + +See the [integration tests](https://github.com/parallaxsecond/rust-tss-esapi/tree/main/tss-esapi/tests) + ## Cargo Features The crate currently offers the following features: