Skip to content

Commit 1507689

Browse files
behratjyn514
andauthored
Add fips-3678 feature (#52)
* Add rerun-if-env-changed instructions for BORING_* variables * Use X509_get0_notBefore() and X509_get0_notAfter() instead of X509_getm_notBefore() and X509_getm_notAfter(). According to https://www.openssl.org/docs/man1.1.0/man3/X509_getm_notBefore.html, "X509_getm_notBefore() and X509_getm_notAfter() are similar to X509_get0_notBefore() and X509_get0_notAfter() except they return non-constant mutable references to the associated date field of the certificate". * Only update boringssl submodule if BORING_BSSL_PATH not provided * Allow BORING_BSSL_LIB_PATH to control link search * Add fips feature * Use X509_set_notAfter unconditionally for FIPS compatibility This is equivalent according to https://boringssl.googlesource.com/boringssl/+/c947efabcbc38dcf93e8ad0e6a76206cf0ec8072 The version of boringssl that's FIPS-certified doesn't have `X509_set1_notAfter`. The only difference between that and `X509_set_notAfter` is whether they're const-correct, which doesn't seem worth having two different code-paths. * Check out fips commit automatically * Verify the version of the compiler used for building boringssl NIST specifies that it needs to be 7.0.1; I originally tried building with clang 10 and it failed. Theoretically this should check the versions of Go and Ninja too, but they haven't given me trouble in practice. Example error: ``` Compiling boring-sys v1.1.1 (/home/jnelson/work/boring/boring-sys) error: failed to run custom build command for `boring-sys v1.1.1 (/home/jnelson/work/boring/boring-sys)` Caused by: process didn't exit successfully: `/home/jnelson/work/boring/target/debug/build/boring-sys-31b8ce53031cfd83/build-script-build` (exit status: 101) --- stdout cargo:rerun-if-env-changed=BORING_BSSL_PATH --- stderr warning: missing clang-7, trying other compilers: Permission denied (os error 13) warning: FIPS requires clang version 7.0.1, skipping incompatible version "clang version 10.0.0-4ubuntu1 " thread 'main' panicked at 'unsupported clang version "cc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0": FIPS requires clang 7.0.1', boring-sys/build.rs:216:13 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ``` * Add Github actions workflow testing FIPS Co-authored-by: Joshua Nelson <[email protected]>
1 parent 5f327ab commit 1507689

File tree

16 files changed

+209
-45
lines changed

16 files changed

+209
-45
lines changed

.github/workflows/ci.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,18 @@ jobs:
159159
- if: "!startsWith(matrix.os, 'windows')"
160160
run: cargo test
161161
name: Run tests (not Windows)
162+
163+
test-fips:
164+
name: Test FIPS integration
165+
runs-on: ubuntu-latest
166+
steps:
167+
- uses: actions/checkout@v2
168+
with:
169+
submodules: 'recursive'
170+
- name: Install Rust (rustup)
171+
run: rustup update stable --no-self-update && rustup default stable
172+
shell: bash
173+
- name: Install Clang-7
174+
run: sudo apt-get install -y clang-7
175+
- run: cargo test --features fips
176+
name: Run tests

.gitmodules

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
[submodule "boring-sys/deps/boringssl"]
22
path = boring-sys/deps/boringssl
33
url = https://github.com/google/boringssl.git
4-
ignore = dirty
4+
ignore = dirty
5+
[submodule "boring-sys/deps/boringssl-fips"]
6+
path = boring-sys/deps/boringssl-fips
7+
url = https://github.com/google/boringssl.git

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ _Notes_: The crate will look for headers in the `$BORING_BSSL_INCLUDE_PATH/opens
2222

2323
_Warning_: When providing a different version of BoringSSL make sure to use a compatible one, the crate relies on the presence of certain functions.
2424

25+
## Building with a FIPS-validated module
26+
27+
Only BoringCrypto module version ae223d6138807a13006342edfeef32e813246b39, as
28+
certified with [certificate
29+
3678](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/3678)
30+
is supported by this crate. Support is enabled by this crate's `fips` feature.
31+
32+
`boring-sys` comes with a test that FIPS is enabled/disabled depending on the feature flag. You can run it as follows:
33+
```bash
34+
$ cargo test --features fips fips::is_enabled
35+
```
36+
2537
## Contribution
2638

2739
Unless you explicitly state otherwise, any contribution intentionally

boring-sys/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,7 @@ include = [
2929
[build-dependencies]
3030
bindgen = { version = "0.59", default-features = false, features = ["runtime"] }
3131
cmake = "0.1"
32+
33+
[features]
34+
# Use a FIPS-validated version of boringssl.
35+
fips = []

boring-sys/build.rs

Lines changed: 116 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use std::path::{Path, PathBuf};
2+
use std::process::Command;
3+
14
// NOTE: this build script is adopted from quiche (https://github.com/cloudflare/quiche)
25

36
// Additional parameters for Android build of BoringSSL.
@@ -85,6 +88,11 @@ fn get_boringssl_platform_output_path() -> String {
8588
}
8689
}
8790

91+
#[cfg(feature = "fips")]
92+
const BORING_SSL_PATH: &str = "deps/boringssl-fips";
93+
#[cfg(not(feature = "fips"))]
94+
const BORING_SSL_PATH: &str = "deps/boringssl";
95+
8896
/// Returns a new cmake::Config for building BoringSSL.
8997
///
9098
/// It will add platform-specific parameters if needed.
@@ -93,7 +101,7 @@ fn get_boringssl_cmake_config() -> cmake::Config {
93101
let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
94102
let pwd = std::env::current_dir().unwrap();
95103

96-
let mut boringssl_cmake = cmake::Config::new("deps/boringssl");
104+
let mut boringssl_cmake = cmake::Config::new(BORING_SSL_PATH);
97105

98106
// Add platform-specific parameters.
99107
match os.as_ref() {
@@ -105,6 +113,7 @@ fn get_boringssl_cmake_config() -> cmake::Config {
105113
};
106114

107115
// We need ANDROID_NDK_HOME to be set properly.
116+
println!("cargo:rerun-if-env-changed=ANDROID_NDK_HOME");
108117
let android_ndk_home = std::env::var("ANDROID_NDK_HOME")
109118
.expect("Please set ANDROID_NDK_HOME for Android build");
110119
let android_ndk_home = std::path::Path::new(&android_ndk_home);
@@ -161,7 +170,8 @@ fn get_boringssl_cmake_config() -> cmake::Config {
161170
if arch == "x86" && os != "windows" {
162171
boringssl_cmake.define(
163172
"CMAKE_TOOLCHAIN_FILE",
164-
pwd.join("deps/boringssl/src/util/32-bit-toolchain.cmake")
173+
pwd.join(BORING_SSL_PATH)
174+
.join("src/util/32-bit-toolchain.cmake")
165175
.as_os_str(),
166176
);
167177
}
@@ -171,42 +181,107 @@ fn get_boringssl_cmake_config() -> cmake::Config {
171181
}
172182
}
173183

174-
fn main() {
175-
use std::env;
176-
use std::path::{Path, PathBuf};
177-
use std::process::Command;
178-
179-
if !Path::new("deps/boringssl/CMakeLists.txt").exists() {
180-
println!("cargo:warning=fetching boringssl git submodule");
181-
// fetch the boringssl submodule
182-
let status = Command::new("git")
183-
.args(&[
184-
"submodule",
185-
"update",
186-
"--init",
187-
"--recursive",
188-
"deps/boringssl",
189-
])
190-
.status();
191-
if !status.map_or(false, |status| status.success()) {
192-
panic!("failed to fetch submodule - consider running `git submodule update --init --recursive deps/boringssl` yourself");
184+
/// Verify that the toolchains match https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp3678.pdf
185+
/// See "Installation Instructions" under section 12.1.
186+
// TODO: maybe this should also verify the Go and Ninja versions? But those haven't been an issue in practice ...
187+
fn verify_fips_clang_version() -> (&'static str, &'static str) {
188+
fn version(tool: &str) -> String {
189+
let output = match Command::new(tool).arg("--version").output() {
190+
Ok(o) => o,
191+
Err(e) => {
192+
eprintln!("warning: missing {}, trying other compilers: {}", tool, e);
193+
// NOTE: hard-codes that the loop below checks the version
194+
return String::new();
195+
}
196+
};
197+
assert!(output.status.success());
198+
let output = std::str::from_utf8(&output.stdout).expect("invalid utf8 output");
199+
output.lines().next().expect("empty output").to_string()
200+
}
201+
202+
const REQUIRED_CLANG_VERSION: &str = "7.0.1";
203+
for (cc, cxx) in [
204+
("clang-7", "clang++-7"),
205+
("clang", "clang++"),
206+
("cc", "c++"),
207+
] {
208+
let cc_version = version(cc);
209+
if cc_version.contains(REQUIRED_CLANG_VERSION) {
210+
assert!(
211+
version(cxx).contains(REQUIRED_CLANG_VERSION),
212+
"mismatched versions of cc and c++"
213+
);
214+
return (cc, cxx);
215+
} else if cc == "cc" {
216+
panic!(
217+
"unsupported clang version \"{}\": FIPS requires clang {}",
218+
cc_version, REQUIRED_CLANG_VERSION
219+
);
220+
} else if !cc_version.is_empty() {
221+
eprintln!(
222+
"warning: FIPS requires clang version {}, skipping incompatible version \"{}\"",
223+
REQUIRED_CLANG_VERSION, cc_version
224+
);
193225
}
194226
}
227+
unreachable!()
228+
}
229+
230+
fn main() {
231+
use std::env;
195232

233+
println!("cargo:rerun-if-env-changed=BORING_BSSL_PATH");
196234
let bssl_dir = std::env::var("BORING_BSSL_PATH").unwrap_or_else(|_| {
235+
if !Path::new(BORING_SSL_PATH).join("CMakeLists.txt").exists() {
236+
println!("cargo:warning=fetching boringssl git submodule");
237+
// fetch the boringssl submodule
238+
let status = Command::new("git")
239+
.args(&[
240+
"submodule",
241+
"update",
242+
"--init",
243+
"--recursive",
244+
BORING_SSL_PATH,
245+
])
246+
.status();
247+
if !status.map_or(false, |status| status.success()) {
248+
panic!("failed to fetch submodule - consider running `git submodule update --init --recursive deps/boringssl` yourself");
249+
}
250+
}
251+
197252
let mut cfg = get_boringssl_cmake_config();
198253

199254
if cfg!(feature = "fuzzing") {
200255
cfg.cxxflag("-DBORINGSSL_UNSAFE_DETERMINISTIC_MODE")
201256
.cxxflag("-DBORINGSSL_UNSAFE_FUZZER_MODE");
202257
}
258+
if cfg!(feature = "fips") {
259+
let (clang, clangxx) = verify_fips_clang_version();
260+
cfg.define("CMAKE_C_COMPILER", clang);
261+
cfg.define("CMAKE_CXX_COMPILER", clangxx);
262+
cfg.define("CMAKE_ASM_COMPILER", clang);
263+
cfg.define("FIPS", "1");
264+
}
203265

204266
cfg.build_target("bssl").build().display().to_string()
205267
});
206268

207269
let build_path = get_boringssl_platform_output_path();
208-
let build_dir = format!("{}/build/{}", bssl_dir, build_path);
209-
println!("cargo:rustc-link-search=native={}", build_dir);
270+
if cfg!(feature = "fips") {
271+
println!(
272+
"cargo:rustc-link-search=native={}/build/crypto/{}",
273+
bssl_dir, build_path
274+
);
275+
println!(
276+
"cargo:rustc-link-search=native={}/build/ssl/{}",
277+
bssl_dir, build_path
278+
);
279+
} else {
280+
println!(
281+
"cargo:rustc-link-search=native={}/build/{}",
282+
bssl_dir, build_path
283+
);
284+
}
210285

211286
println!("cargo:rustc-link-lib=static=crypto");
212287
println!("cargo:rustc-link-lib=static=ssl");
@@ -216,10 +291,14 @@ fn main() {
216291
println!("cargo:rustc-cdylib-link-arg=-Wl,-undefined,dynamic_lookup");
217292
}
218293

219-
let include_path = PathBuf::from(
220-
std::env::var("BORING_BSSL_INCLUDE_PATH")
221-
.unwrap_or_else(|_| String::from("deps/boringssl/src/include")),
222-
);
294+
println!("cargo:rerun-if-env-changed=BORING_BSSL_INCLUDE_PATH");
295+
let include_path = std::env::var("BORING_BSSL_INCLUDE_PATH").unwrap_or_else(|_| {
296+
if cfg!(feature = "fips") {
297+
format!("{}/include", BORING_SSL_PATH)
298+
} else {
299+
format!("{}/src/include", BORING_SSL_PATH)
300+
}
301+
});
223302

224303
let mut builder = bindgen::Builder::default()
225304
.derive_copy(true)
@@ -234,12 +313,13 @@ fn main() {
234313
.layout_tests(true)
235314
.prepend_enum_name(true)
236315
.rustfmt_bindings(true)
237-
.clang_args(&["-I", include_path.to_str().unwrap()]);
316+
.clang_args(&["-I", &include_path]);
238317

239318
let headers = [
240319
"aes.h",
241320
"asn1_mac.h",
242321
"asn1t.h",
322+
#[cfg(not(feature = "fips"))]
243323
"blake2.h",
244324
"blowfish.h",
245325
"cast.h",
@@ -264,11 +344,18 @@ fn main() {
264344
"ripemd.h",
265345
"siphash.h",
266346
"srtp.h",
347+
#[cfg(not(feature = "fips"))]
267348
"trust_token.h",
268349
"x509v3.h",
269350
];
270351
for header in &headers {
271-
builder = builder.header(include_path.join("openssl").join(header).to_str().unwrap());
352+
builder = builder.header(
353+
Path::new(&include_path)
354+
.join("openssl")
355+
.join(header)
356+
.to_str()
357+
.unwrap(),
358+
);
272359
}
273360

274361
let bindings = builder.generate().expect("Unable to generate bindings");

boring-sys/deps/boringssl-fips

Submodule boringssl-fips added at ae223d6

boring/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ boring-sys = { version = ">=1.1.0,<3.0.0", path = "../boring-sys" }
2121
[dev-dependencies]
2222
hex = "0.4"
2323
rusty-hook = "^0.11"
24+
25+
[features]
26+
# Use a FIPS-validated version of boringssl.
27+
fips = ["boring-sys/fips"]

boring/examples/fips_enabled.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
println!("boring::fips::enabled(): {}", boring::fips::enabled());
3+
}

boring/examples/mk_certs.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ fn mk_request(privkey: &PKey<Private>) -> Result<X509Req, ErrorStack> {
8181
}
8282

8383
/// Make a certificate and private key signed by the given CA cert and private key
84+
#[cfg_attr(feature = "fips", allow(unreachable_code, unused_variables))]
8485
fn mk_ca_signed_cert(
8586
ca_cert: &X509Ref,
8687
ca_privkey: &PKeyRef<Private>,
@@ -98,7 +99,15 @@ fn mk_ca_signed_cert(
9899
serial.to_asn1_integer()?
99100
};
100101
cert_builder.set_serial_number(&serial_number)?;
102+
103+
#[cfg(not(feature = "fips"))]
101104
cert_builder.set_subject_name(req.subject_name())?;
105+
#[cfg(feature = "fips")]
106+
{
107+
eprintln!("mk_certs not supported with FIPS module");
108+
std::process::exit(1);
109+
}
110+
102111
cert_builder.set_issuer_name(ca_cert.subject_name())?;
103112
cert_builder.set_pubkey(&privkey)?;
104113
let not_before = Asn1Time::days_from_now(0)?;

boring/src/fips.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,11 @@ pub fn enable(enabled: bool) -> Result<(), ErrorStack> {
2020
pub fn enabled() -> bool {
2121
unsafe { ffi::FIPS_mode() != 0 }
2222
}
23+
24+
#[test]
25+
fn is_enabled() {
26+
#[cfg(feature = "fips")]
27+
assert!(enabled());
28+
#[cfg(not(feature = "fips"))]
29+
assert!(!enabled());
30+
}

0 commit comments

Comments
 (0)