Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement bundled feature and improve windows support #523

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion tss-esapi-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = { version = "0.1.0", optional = true }
winreg = {version = "0.52", optional = true }

[features]
generate-bindings = ["bindgen"]
bundled = ["dep:autotools", "dep:msbuild", "dep:winreg"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't bundled need to imply bindgen too? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had thought that, but on my Ubuntu installation, bindgen didn't work. I had to do bundled without bindgen there.
On windows I needed bindgen and bundled.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No bundled does need to imply bindgen. Bundled can use the bindings that are committed in the sys crate and just build the libraries from the specified version.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add some text in the README file and/or in the code documentation to describe the use of the new feature?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I can add some documentation.

11 changes: 11 additions & 0 deletions tss-esapi-sys/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ 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.

## Bundled feature

There is a feature called bundled which downloads tpm2-tss source from github and compiles it using visual studio. It will usually be paired with the generate-bindings feature.

# 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.

## Cross compiling

Cross-compilation can be done as long as you have on your build system the TSS
Expand Down
215 changes: 213 additions & 2 deletions tss-esapi-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -59,19 +69,163 @@ 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,
tss2_tcti_tbs: Option<Library>,
}

impl Installation {
/// Return an optional list of clang arguments that are platform specific
#[cfg(feature = "bundled")]
fn platform_args() -> Option<Vec<String>> {
cfg_if::cfg_if! {
if #[cfg(windows)] {
let mut clang_args: Vec<String> = 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()));
Some(clang_args)
}
else {
None
}
}
}

#[cfg(feature = "bundled")]
/// Fetch the given source repo using git
fn fetch_source(
dest_path: impl AsRef<std::path::Path>,
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(all(feature = "bundled",not(windows)))]
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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth making it explicit in documentation about how this behaves in relationship with the build environment, i.e. that if you don't have a local copy of tpm2-tss then you need access to the internet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, that makes sense.

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,
Expand Down Expand Up @@ -149,10 +303,57 @@ 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: {}", 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!(
"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(
Expand Down Expand Up @@ -372,6 +573,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
Expand Down
1 change: 1 addition & 0 deletions tss-esapi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" ]
5 changes: 4 additions & 1 deletion tss-esapi/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ fn main() {
println!("cargo:rustc-check-cfg=cfg(has_tss_base_rc_values_52_to_53)");
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");

Expand Down
5 changes: 5 additions & 0 deletions tss-esapi/src/tcti_ldr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -174,6 +177,7 @@ impl TryFrom<TctiNameConf> for CString {
TctiNameConf::Mssim(..) => MSSIM,
TctiNameConf::Swtpm(..) => SWTPM,
TctiNameConf::Tabrmd(..) => TABRMD,
TctiNameConf::Tbs => TBS,
};

let tcti_conf = match tcti {
Expand Down Expand Up @@ -204,6 +208,7 @@ impl TryFrom<TctiNameConf> 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() {
Expand Down
Loading