Skip to content

Commit

Permalink
Add fips-link-precompiled feature
Browse files Browse the repository at this point in the history
  • Loading branch information
inikulin committed Jul 26, 2023
1 parent 09d92e5 commit a126208
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 27 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,34 @@ jobs:
name: Run `pq-experimental` tests
- run: cargo test --features pq-experimental,rpk
name: Run `pq-experimental,rpk` tests

test-fips-link-precompiled:
name: Test precompiled bcm.o linking
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
submodules: 'recursive'
- name: Install Rust (rustup)
run: rustup update stable --no-self-update && rustup default stable
shell: bash
- name: Install Clang-12
uses: KyleMayes/install-llvm-action@v1
with:
version: "12.0.0"
directory: ${{ runner.temp }}/llvm
- name: Add clang++-12 link
working-directory: ${{ runner.temp }}/llvm/bin
run: ln -s clang clang++-12
- name: Build FIPS boring
run: (cd boring-sys && cargo build --features fips)
- name: Copy bcm.o to tmp
run: find . -name "bcm.o" -exec cp "{}" ${{ runner.temp }} \;
- name: Clean the build dir
run: cargo clean
- name: Run tests
run: cargo test --features fips-link-precompiled
env:
BORING_SSL_PRECOMPILED_BCM_O: ${{ runner.temp }}/bcm.o


3 changes: 3 additions & 0 deletions boring-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ rustdoc-args = ["--cfg", "docsrs"]
# Use a FIPS-validated version of boringssl.
fips = []

# Link with precompiled FIPS-validated `bcm.o` module.
fips-link-precompiled = ["fips"]

# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250)
rpk = []

Expand Down
110 changes: 83 additions & 27 deletions boring-sys/build.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use fslock::LockFile;
use std::env;
use std::fs;
use std::io;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::process::{Command, Output};

// NOTE: this build script is adopted from quiche (https://github.com/cloudflare/quiche)

Expand Down Expand Up @@ -38,7 +39,7 @@ const CMAKE_PARAMS_ANDROID_NDK: &[(&str, &[(&str, &str)])] = &[
];

fn cmake_params_android() -> &'static [(&'static str, &'static str)] {
let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let cmake_params_android = if cfg!(feature = "ndk-old-gcc") {
CMAKE_PARAMS_ANDROID_NDK_OLD_GCC
} else {
Expand Down Expand Up @@ -77,7 +78,7 @@ const CMAKE_PARAMS_IOS: &[(&str, &[(&str, &str)])] = &[
];

fn cmake_params_ios() -> &'static [(&'static str, &'static str)] {
let target = std::env::var("TARGET").unwrap();
let target = env::var("TARGET").unwrap();
for (ios_target, params) in CMAKE_PARAMS_IOS {
if *ios_target == target {
return params;
Expand All @@ -92,18 +93,18 @@ fn get_ios_sdk_name() -> &'static str {
return value;
}
}
let target = std::env::var("TARGET").unwrap();
let target = env::var("TARGET").unwrap();
panic!("cannot find iOS SDK for {} in CMAKE_PARAMS_IOS", target);
}

/// Returns an absolute path to the BoringSSL source.
fn get_boringssl_source_path() -> String {
#[cfg(feature = "fips")]
#[cfg(all(feature = "fips", not(feature = "fips-link-precompiled")))]
const BORING_SSL_SOURCE_PATH: &str = "deps/boringssl-fips";
#[cfg(not(feature = "fips"))]
#[cfg(any(not(feature = "fips"), feature = "fips-link-precompiled"))]
const BORING_SSL_SOURCE_PATH: &str = "deps/boringssl";

std::env::var("BORING_BSSL_SOURCE_PATH")
env::var("BORING_BSSL_SOURCE_PATH")
.unwrap_or(env!("CARGO_MANIFEST_DIR").to_owned() + "/" + BORING_SSL_SOURCE_PATH)
}

Expand All @@ -115,16 +116,15 @@ fn get_boringssl_source_path() -> String {
fn get_boringssl_platform_output_path() -> String {
if cfg!(target_env = "msvc") {
// Code under this branch should match the logic in cmake-rs
let debug_env_var = std::env::var("DEBUG").expect("DEBUG variable not defined in env");
let debug_env_var = env::var("DEBUG").expect("DEBUG variable not defined in env");

let deb_info = match &debug_env_var[..] {
"false" => false,
"true" => true,
unknown => panic!("Unknown DEBUG={} env var.", unknown),
};

let opt_env_var =
std::env::var("OPT_LEVEL").expect("OPT_LEVEL variable not defined in env");
let opt_env_var = env::var("OPT_LEVEL").expect("OPT_LEVEL variable not defined in env");

let subdir = match &opt_env_var[..] {
"0" => "Debug",
Expand All @@ -149,10 +149,10 @@ fn get_boringssl_platform_output_path() -> String {
///
/// It will add platform-specific parameters if needed.
fn get_boringssl_cmake_config() -> cmake::Config {
let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let host = std::env::var("HOST").unwrap();
let target = std::env::var("TARGET").unwrap();
let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
let host = env::var("HOST").unwrap();
let target = env::var("TARGET").unwrap();
let pwd = std::env::current_dir().unwrap();
let src_path = get_boringssl_source_path();

Expand All @@ -163,7 +163,7 @@ fn get_boringssl_cmake_config() -> cmake::Config {
"android" => {
// We need ANDROID_NDK_HOME to be set properly.
println!("cargo:rerun-if-env-changed=ANDROID_NDK_HOME");
let android_ndk_home = std::env::var("ANDROID_NDK_HOME")
let android_ndk_home = env::var("ANDROID_NDK_HOME")
.expect("Please set ANDROID_NDK_HOME for Android build");
let android_ndk_home = std::path::Path::new(&android_ndk_home);
for (name, value) in cmake_params_android() {
Expand Down Expand Up @@ -288,7 +288,7 @@ fn verify_fips_clang_version() -> (&'static str, &'static str) {
}

fn get_extra_clang_args_for_bindgen() -> Vec<String> {
let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let os = env::var("CARGO_CFG_TARGET_OS").unwrap();

let mut params = Vec::new();

Expand Down Expand Up @@ -320,13 +320,13 @@ fn get_extra_clang_args_for_bindgen() -> Vec<String> {
params.push(sysroot);
}
"android" => {
let android_ndk_home = std::env::var("ANDROID_NDK_HOME")
let android_ndk_home = env::var("ANDROID_NDK_HOME")
.expect("Please set ANDROID_NDK_HOME for Android build");
let mut android_sysroot = std::path::PathBuf::from(android_ndk_home);
android_sysroot.push("sysroot");
params.push("--sysroot".to_string());
// If ANDROID_NDK_HOME weren't a valid UTF-8 string,
// we'd already know from std::env::var.
// we'd already know from env::var.
params.push(android_sysroot.into_os_string().into_string().unwrap());
}
_ => {}
Expand Down Expand Up @@ -367,19 +367,19 @@ fn ensure_patches_applied() -> io::Result<()> {
Ok(())
}

fn run_command(command: &mut Command) -> io::Result<()> {
let exit_status = command.spawn()?.wait()?;
fn run_command(command: &mut Command) -> io::Result<Output> {
let out = command.output()?;

if !exit_status.success() {
let err = match exit_status.code() {
if !out.status.success() {
let err = match out.status.code() {
Some(code) => format!("{:?} exited with status: {}", command, code),
None => format!("{:?} was terminated by signal", command),
};

return Err(io::Error::new(io::ErrorKind::Other, err));
}

Ok(())
Ok(out)
}

fn run_apply_patch_script(script_path: impl AsRef<Path>) -> io::Result<()> {
Expand Down Expand Up @@ -417,30 +417,82 @@ fn build_boring_from_sources() -> String {
cfg.cxxflag("-DBORINGSSL_UNSAFE_DETERMINISTIC_MODE")
.cxxflag("-DBORINGSSL_UNSAFE_FUZZER_MODE");
}
if cfg!(feature = "fips") {

if cfg!(all(
feature = "fips",
not(feature = "fips-link-precompiled")
)) {
let (clang, clangxx) = verify_fips_clang_version();
cfg.define("CMAKE_C_COMPILER", clang);
cfg.define("CMAKE_CXX_COMPILER", clangxx);
cfg.define("CMAKE_ASM_COMPILER", clang);
cfg.define("FIPS", "1");
}

if cfg!(feature = "fips-link-precompiled") {
cfg.define("FIPS", "1");
}

cfg.build_target("ssl").build();
cfg.build_target("crypto").build().display().to_string()
}

fn link_in_precompiled_bcm_o(bssl_dir: &str) -> io::Result<()> {
println!("cargo:warning=linking in precompiled `bcm.o` module");

let bcm_o_src_path = env::var("BORING_SSL_PRECOMPILED_BCM_O")
.expect("`fips-link-precompiled` requires `BORING_SSL_PRECOMPILED_BCM_O` env variable to be specified");

let libcrypto_path = PathBuf::from(bssl_dir)
.join("build/crypto/libcrypto.a")
.canonicalize()?
.display()
.to_string();

let bcm_o_dst_path = PathBuf::from(bssl_dir).join("build/bcm-fips.o");

fs::copy(bcm_o_src_path, &bcm_o_dst_path).unwrap();

// check that fips module is named as expected
let out = run_command(Command::new("ar").args(["t", &libcrypto_path, "bcm.o"]))?;

assert_eq!(
String::from_utf8(out.stdout).unwrap(),
"bcm.o",
"failed to verify FIPS module name"
);

// insert fips bcm.o before bcm.o into libcrypto.a,
// so for all duplicate symbols the older fips bcm.o is used
// (this causes the need for extra linker flags to deal with duplicate symbols)
// (as long as the newer module does not define new symbols, one may also remove it,
// but once there are new symbols it would cause missing symbols at linking stage)
run_command(Command::new("ar").args([
"rb",
"bcm.o",
&libcrypto_path,
bcm_o_dst_path.display().to_string().as_str(),
]))?;

Ok(())
}

fn main() {
println!("cargo:rerun-if-env-changed=BORING_BSSL_PATH");

#[cfg(all(feature = "fips", feature = "rpk"))]
compile_error!("`fips` and `rpk` features are mutually exclusive");

let bssl_dir = std::env::var("BORING_BSSL_PATH");
let bssl_dir = env::var("BORING_BSSL_PATH");

if bssl_dir.is_ok() && cfg!(any(feature = "rpk", feature = "pq-experimental")) {
panic!("precompiled BoringSSL was provided, optional patches can't be applied to it");
}

if bssl_dir.is_ok() && cfg!(feature = "fips") {
panic!("precompiled BoringSSL was provided, so FIPS configuration can't be applied");
}

let bssl_dir = bssl_dir.unwrap_or_else(|_| build_boring_from_sources());

let build_path = get_boringssl_platform_output_path();
Expand All @@ -461,11 +513,15 @@ fn main() {
);
}

if cfg!(feature = "fips-link-precompiled") {
link_in_precompiled_bcm_o(&bssl_dir).unwrap();
}

println!("cargo:rustc-link-lib=static=crypto");
println!("cargo:rustc-link-lib=static=ssl");

println!("cargo:rerun-if-env-changed=BORING_BSSL_INCLUDE_PATH");
let include_path = std::env::var("BORING_BSSL_INCLUDE_PATH").unwrap_or_else(|_| {
let include_path = env::var("BORING_BSSL_INCLUDE_PATH").unwrap_or_else(|_| {
let src_path = get_boringssl_source_path();
if cfg!(feature = "fips") {
format!("{}/include", &src_path)
Expand All @@ -492,7 +548,7 @@ fn main() {
.clang_args(get_extra_clang_args_for_bindgen())
.clang_args(&["-I", &include_path]);

let target = std::env::var("TARGET").unwrap();
let target = env::var("TARGET").unwrap();
match target.as_ref() {
// bindgen produces alignment tests that cause undefined behavior [1]
// when applied to explicitly unaligned types like OSUnalignedU64.
Expand Down
3 changes: 3 additions & 0 deletions boring/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ rustdoc-args = ["--cfg", "docsrs"]
# Use a FIPS-validated version of boringssl.
fips = ["boring-sys/fips"]

# Link with precompiled FIPS-validated `bcm.o` module.
fips-link-precompiled = ["fips"]

# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250)
rpk = ["boring-sys/rpk"]

Expand Down
6 changes: 6 additions & 0 deletions boring/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@
//! $ cargo test --features fips fips::is_enabled
//! ```
//!
//! ## Linking current BoringSSL version with precompiled FIPS-validated module (`bcm.o`)
//! It's possible to link latest supported version of BoringSSL with FIPS-validated crypto module
//! (`bcm.o`). To enable this compilation option one should enable `fips-link-precompiled`
//! compilation feature and provide a `BORING_SSL_PRECOMPILED_BCM_O` env variable with a path to the
//! precompiled FIPS-validated `bcm.o` module.
//!
//! # Optional patches
//!
//! ## Raw Public Key
Expand Down
3 changes: 3 additions & 0 deletions hyper-boring/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ runtime = ["hyper/runtime"]
# Use a FIPS-validated version of boringssl.
fips = ["tokio-boring/fips"]

# Link with precompiled FIPS-validated `bcm.o` module.
fips-link-precompiled = ["fips"]

# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250)
rpk = ["tokio-boring/rpk"]

Expand Down
3 changes: 3 additions & 0 deletions tokio-boring/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ rustdoc-args = ["--cfg", "docsrs"]
# Use a FIPS-validated version of boringssl.
fips = ["boring/fips"]

# Link with precompiled FIPS-validated `bcm.o` module.
fips-link-precompiled = ["fips"]

# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250)
rpk = ["boring/rpk"]

Expand Down

0 comments on commit a126208

Please sign in to comment.