Skip to content

Commit 4d69ce0

Browse files
committed
Build against openssl-sys low-level bindings
Linking applications against different bindings to the same library (openssl) is undefined behavior in rust ecosystem. This is removing the low-level bindings and bindgen dependency, delefating this to the openssl-sys crate providing all the low-level functions. Unfortunately, it does not expose all the constants and defines, that need to be defined manually. The direct static linking to libfips is preserved. Signed-off-by: Jakub Jelen <jjelen@redhat.com>
1 parent 8a29f39 commit 4d69ce0

6 files changed

Lines changed: 319 additions & 109 deletions

File tree

.github/workflows/rpm.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jobs:
5656
'crate(num-integer/default)' 'crate(num-traits/default)' \
5757
'crate(pkg-config/default)' 'crate(rusqlite/default)' \
5858
'crate(serde/default)' 'crate(serde/derive)' 'crate(serde_json/default)' \
59-
'crate(serial_test/default)' \
59+
'crate(serial_test/default)' 'crate(itertools/default)' 'crate(openssl-sys/bindgen)' \
6060
'crate(toml)' 'crate(toml/display)' 'crate(toml/parse)' 'crate(toml/serde)' \
6161
'crate(uuid/default)' 'crate(uuid/v4)' 'crate(uuid/v8)' 'crate(cryptoki/default)' \
6262
'crate(vsprintf/default)' 'crate(quick-xml/default)' 'crate(quick-xml/serialize)'

ossl/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
[package]
22
name = "ossl"
3-
links = "openssl"
43
version.workspace = true
54
edition.workspace = true
65
description = "OpenSSL version 3+ bindings to modern EVP APIs"
@@ -12,6 +11,7 @@ license = "Apache-2.0"
1211
test = true
1312

1413
[build-dependencies]
14+
# this is technically optional, but we either use it directly or through openssl-sys
1515
bindgen = "0.72"
1616
pkg-config = "0.3"
1717

@@ -20,6 +20,7 @@ cfg-if = "1.0.0"
2020
libc = "0.2.151"
2121
log = { version = "0.4.27", default-features = false, features = ["std"], optional = true }
2222
vsprintf = { version = "2.0.0", optional = true }
23+
openssl-sys = { version = "0.9", features = ["bindgen"], optional = true }
2324

2425
[dev-dependencies]
2526
hex = "0.4.3"
@@ -29,7 +30,7 @@ serial_test = "3.1.1"
2930
ossl320 = [] # Requires at a minimum OpenSSL 3.2.0
3031
ossl350 = [] # Requires at a minimum OpenSSL 3.5.0
3132
ossl400 = [] # Requires at minimum OpenSSL 4.0
32-
dynamic = [] # Builds against system libcrypto.so
33+
dynamic = ["dep:openssl-sys"] # Builds against system libcrypto.so
3334
fips = ["ossl350"] # Builds against sources and libfips.a instead of libcrypto
3435
log = ["dep:log", "dep:vsprintf"] # Error tracing using log crate
3536
rfc9580 = [] # Enables features required for OpenPGP implementations

ossl/build.rs

Lines changed: 63 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use std::env;
55
use std::panic::set_hook;
6+
#[cfg(not(feature = "dynamic"))]
67
use std::path::{Path, PathBuf};
78

89
#[derive(Debug)]
@@ -12,68 +13,63 @@ const OPENSSL_3_2_0: i64 = 0x30200000;
1213
const OPENSSL_3_5_0: i64 = 0x30500000;
1314
const OPENSSL_4_0_0: i64 = 0x40000000;
1415

16+
fn check_ossl_version(version: i64) {
17+
if version < OPENSSL_4_0_0 {
18+
#[cfg(feature = "ossl400")]
19+
panic!("OpenSSL 4.0.0 or later is required");
20+
}
21+
if version < OPENSSL_3_5_0 {
22+
#[cfg(feature = "ossl350")]
23+
panic!("OpenSSL 3.5.0 or later is required");
24+
}
25+
if version < OPENSSL_3_2_0 {
26+
#[cfg(feature = "ossl320")]
27+
panic!("OpenSSL 3.2.0 or later is required");
28+
}
29+
if version < OPENSSL_3_0_7 {
30+
panic!(
31+
"OpenSSL 3.0.7 is the minimum viable version. Found {:x}",
32+
version
33+
);
34+
}
35+
/* Emit versions we found, versions stack, so code
36+
* just need to build conditionalized just to the older version
37+
* that introduced the desired feature */
38+
println!("cargo::rustc-cfg=ossl_v307");
39+
if version >= OPENSSL_3_2_0 {
40+
println!("cargo::rustc-cfg=ossl_v320");
41+
}
42+
if version >= OPENSSL_3_5_0 {
43+
println!("cargo::rustc-cfg=ossl_v350");
44+
}
45+
if version >= OPENSSL_4_0_0 {
46+
println!("cargo::rustc-cfg=ossl_v400");
47+
}
48+
}
49+
50+
#[cfg(not(feature = "dynamic"))]
1551
impl bindgen::callbacks::ParseCallbacks for OsslCallbacks {
1652
fn int_macro(
1753
&self,
1854
name: &str,
1955
value: i64,
2056
) -> Option<bindgen::callbacks::IntKind> {
2157
if name == "OPENSSL_VERSION_NUMBER" {
22-
if value < OPENSSL_4_0_0 {
23-
#[cfg(feature = "ossl400")]
24-
panic!("OpenSSL 4.0.0 or later is required");
25-
}
26-
if value < OPENSSL_3_5_0 {
27-
#[cfg(feature = "ossl350")]
28-
panic!("OpenSSL 3.5.0 or later is required");
29-
}
30-
if value < OPENSSL_3_2_0 {
31-
#[cfg(feature = "ossl320")]
32-
panic!("OpenSSL 3.2.0 or later is required");
33-
}
34-
if value < OPENSSL_3_0_7 {
35-
panic!(
36-
"OpenSSL 3.0.7 is the minimum viable version. Found {:x}",
37-
value
38-
);
39-
}
40-
/* Emit versions we found, versions stack, so code
41-
* just need to build conditionalized just to the older version
42-
* that introduced the desired feature */
43-
println!("cargo::rustc-cfg=ossl_v307");
44-
if value >= OPENSSL_3_2_0 {
45-
println!("cargo::rustc-cfg=ossl_v320");
46-
}
47-
if value >= OPENSSL_3_5_0 {
48-
println!("cargo::rustc-cfg=ossl_v350");
49-
}
50-
if value >= OPENSSL_4_0_0 {
51-
println!("cargo::rustc-cfg=ossl_v400");
52-
}
58+
check_ossl_version(value);
5359
}
5460

5561
None
5662
}
5763

58-
fn str_macro(&self, name: &str, _value: &[u8]) {
59-
if name == "OSSL_PKEY_PARAM_SLH_DSA_SEED" {
60-
println!("cargo::rustc-cfg=ossl_slhdsa")
61-
}
62-
if name == "OSSL_PKEY_PARAM_ML_DSA_SEED" {
63-
println!("cargo::rustc-cfg=ossl_mldsa")
64-
}
65-
if name == "OSSL_PKEY_PARAM_ML_KEM_SEED" {
66-
println!("cargo::rustc-cfg=ossl_mlkem")
67-
}
68-
}
69-
64+
// FIXME for openssl-sys
7065
fn func_macro(&self, name: &str, _value: &[&[u8]]) {
7166
if name == "OSSL_PARAM_clear_free" {
7267
println!("cargo::rustc-cfg=param_clear_free")
7368
}
7469
}
7570
}
7671

72+
#[cfg(not(feature = "dynamic"))]
7773
fn ossl_bindings(args: &mut Vec<String>, out_file: &Path) {
7874
if let Some(var) = env::var("OSSL_BINDGEN_CLANG_ARGS").ok() {
7975
for arg in var.split_whitespace() {
@@ -108,6 +104,7 @@ fn ossl_bindings(args: &mut Vec<String>, out_file: &Path) {
108104
.expect("Couldn't write bindings!");
109105
}
110106

107+
#[cfg(not(feature = "dynamic"))]
111108
fn build_ossl(out_file: &Path) {
112109
let sources = std::env::var("KRYOPTIC_OPENSSL_SOURCES")
113110
.expect("Env var KRYOPTIC_OPENSSL_SOURCES is not defined");
@@ -281,20 +278,6 @@ fn build_ossl(out_file: &Path) {
281278
ossl_bindings(&mut args, out_file);
282279
}
283280

284-
fn use_system_ossl(out_file: &Path) {
285-
let library = pkg_config::Config::new()
286-
.atleast_version("3.0.7")
287-
.probe("openssl")
288-
.unwrap();
289-
290-
let mut args: Vec<String> = Vec::new();
291-
for include_path in library.include_paths {
292-
args.push(["-I", include_path.to_str().unwrap()].concat());
293-
}
294-
295-
ossl_bindings(&mut args, out_file);
296-
}
297-
298281
fn set_pretty_panic() {
299282
set_hook(Box::new(|panic_info| {
300283
if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
@@ -322,20 +305,36 @@ fn set_pretty_panic() {
322305
fn main() {
323306
set_pretty_panic();
324307

325-
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
326-
let ossl_bindings = out_path.join("ossl_bindings.rs");
327-
328308
/* Always emit known configs */
329309
println!(
330-
"cargo::rustc-check-cfg=cfg(ossl_v307,ossl_v320,ossl_v350,ossl_v400,ossl_mldsa,ossl_mlkem,ossl_slhdsa,param_clear_free)"
310+
"cargo::rustc-check-cfg=cfg(ossl_v307,ossl_v320,ossl_v350,ossl_v400,param_clear_free)"
331311
);
332312

333313
/* OpenSSL Cryptography */
334-
if cfg!(feature = "dynamic") {
335-
use_system_ossl(&ossl_bindings);
336-
} else {
314+
#[cfg(not(feature = "dynamic"))]
315+
{
316+
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
317+
let ossl_bindings = out_path.join("ossl_bindings.rs");
337318
build_ossl(&ossl_bindings);
338319
}
339320

321+
#[cfg(feature = "dynamic")]
322+
if let Ok(v) = env::var("DEP_OPENSSL_VERSION_NUMBER") {
323+
let version = i64::from_str_radix(&v, 16).unwrap();
324+
check_ossl_version(version);
325+
// Proxy it also to the code for the api_level() API
326+
println!("cargo:rustc-env=DEP_OPENSSL_VERSION_NUMBER={}", v);
327+
328+
// backward compatible OPENSSL_FULL_VERSION_STR
329+
let major = (version >> 28) & 0xF;
330+
let minor = (version >> 20) & 0xFF;
331+
let patch = (version >> 4) & 0xFF;
332+
let version_string = format!("{}.{}.{}", major, minor, patch);
333+
println!(
334+
"cargo:rustc-env=OPENSSL_FULL_VERSION_STR={}",
335+
version_string
336+
);
337+
}
338+
340339
println!("cargo:rerun-if-changed=build.rs");
341340
}

ossl/src/lib.rs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
/// Part of this module is automatically generated by bindgen from the OpenSSL
99
/// Headers and includes a selection of functions and other items needed to
1010
/// access the libcrypto/libfips functions needed.
11+
#[cfg(not(feature = "dynamic"))]
1112
pub mod bindings {
1213
#![allow(non_upper_case_globals)]
1314
#![allow(non_camel_case_types)]
@@ -16,6 +17,15 @@ pub mod bindings {
1617
#![allow(unnecessary_transmutes)]
1718
include!(concat!(env!("OUT_DIR"), "/ossl_bindings.rs"));
1819
}
20+
#[cfg(feature = "dynamic")]
21+
pub mod bindings {
22+
#![allow(non_upper_case_globals)]
23+
include!("missing_ossl_bindings.rs");
24+
pub use openssl_sys::*;
25+
// backward compatible definitions
26+
pub const OPENSSL_FULL_VERSION_STR: &[u8] =
27+
concat!(env!("OPENSSL_FULL_VERSION_STR"), "\0").as_bytes();
28+
}
1929

2030
use std::borrow::Cow;
2131
use std::ffi::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_void, CStr};
@@ -43,10 +53,27 @@ pub mod fips;
4353
/// This returns a triplet of u8 values representing Major, Minor, and
4454
/// Patch version.
4555
pub fn api_level() -> (u8, u8, u8) {
46-
let patch: u8 = (OPENSSL_API_LEVEL & 0xff) as u8;
47-
let minor: u8 = ((OPENSSL_API_LEVEL & 0xff00) >> 8) as u8;
48-
let major: u8 = ((OPENSSL_API_LEVEL & 0xff0000) >> 16) as u8;
49-
(major, minor, patch)
56+
#[cfg(not(feature = "dynamic"))]
57+
{
58+
// This is the 0xMMNNPP (Major, miNor, Patch) scheme
59+
let version = OPENSSL_API_LEVEL;
60+
let patch: u8 = (version & 0xff) as u8;
61+
let minor: u8 = ((version & 0xff00) >> 8) as u8;
62+
let major: u8 = ((version & 0xff0000) >> 16) as u8;
63+
(major, minor, patch)
64+
}
65+
66+
#[cfg(feature = "dynamic")]
67+
{
68+
// This is the 0xMNN00PP0 (Major, miNor, Patch) scheme
69+
let v = env!("DEP_OPENSSL_VERSION_NUMBER");
70+
let version = u64::from_str_radix(&v, 16).unwrap();
71+
72+
let patch: u8 = ((version & 0xff0) >> 4) as u8;
73+
let minor: u8 = ((version & 0xff00000) >> 20) as u8;
74+
let major: u8 = ((version & 0xff0000000) >> 28) as u8;
75+
(major, minor, patch)
76+
}
5077
}
5178

5279
/// Securely zeroizes a memory slice using `OPENSSL_cleanse`.

0 commit comments

Comments
 (0)