Skip to content
Merged
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ members = [
"parsec-openssl2",
"parsec-openssl-provider",
"parsec-openssl-provider-shared",
"parsec-openssl-provider-shared-test",
]
6 changes: 6 additions & 0 deletions ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ if [ "$TEST" == "True" ]; then
fi

echo "Parsec OpenSSL Provider loaded successfully!!!!"

# The parsec-openssl-provider-shared-test/src/lib.rs contains some unit tests from the generated
# test bindings from bindgen. So run only the integration tests in the test crate.
pushd parsec-openssl-provider-shared-test/
cargo test --test '*'
popd
fi

if [ "$STATIC_CHECKS" == "True" ]; then
Expand Down
19 changes: 19 additions & 0 deletions parsec-openssl-provider-shared-test/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "parsec-openssl-provider-shared-test"
version = "0.1.0"
authors = ["Parsec Project Contributors"]
description = "An end to end test framework for the parsec provider"
license = "Apache-2.0"
readme = "README.md"
keywords = ["security", "service"]
categories = ["cryptography", "hardware-support"]
edition = "2021"
repository = "https://github.com/parallaxsecond/parsec-openssl-provider"

[dependencies]
foreign-types-shared = "0.1.1"
parsec-openssl-provider = { path ="../parsec-openssl-provider" }

[build-dependencies]
pkg-config = "0.3.18"
bindgen = { version = "0.66.1" }
39 changes: 39 additions & 0 deletions parsec-openssl-provider-shared-test/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2024 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0
use std::env;
use std::io::{Error, ErrorKind};
use std::path::PathBuf;

const MINIMUM_VERSION: &str = "3.0.0";

fn main() -> std::io::Result<()> {
// Use package config to ensure openssl version 3.0.0 or higher is installed
let openssl = pkg_config::Config::new()
.atleast_version(MINIMUM_VERSION)
.probe("openssl")
.expect("Failed to find openssl version above 3.0.0");

// The include path points to the openssl development headers installed by libss-dev
let openssl_include_path = openssl.include_paths[0]
.clone()
.into_os_string()
.into_string()
.expect("Error converting OsString to String.");

// Generate bindings for the required headers
let openssl_builder = bindgen::Builder::default()
.header(format!("{}/openssl/evp.h", openssl_include_path))
.header(format!("{}/openssl/provider.h", openssl_include_path))
.generate_comments(false)
.size_t_is_usize(true);

// Build the bindings
let openssl_bindings = openssl_builder
.generate()
.map_err(|_| Error::new(ErrorKind::Other, "Unable to generate bindings to openssl"))?;

let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
openssl_bindings.write_to_file(out_path.join("openssl_test_bindings.rs"))?;

Ok(())
}
53 changes: 53 additions & 0 deletions parsec-openssl-provider-shared-test/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2024 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0
#![allow(clippy::missing_safety_doc)]

#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
#[allow(non_snake_case)]
#[allow(improper_ctypes)]
// These are test bindings generated from the "evp.h" and "provider.h" header files which
// provide interfaces for openssl clients.
pub mod openssl_test_bindings {
include!(concat!(env!("OUT_DIR"), "/openssl_test_bindings.rs"));
}

// Needed to access as_ptr function for LibCtx
pub use foreign_types_shared::ForeignType;
pub use openssl_test_bindings::*;
pub use parsec_openssl_provider::parsec_openssl2::openssl::{lib_ctx::LibCtx, provider::Provider};

// These needs to be replaced with consts from the key management module
pub const PARSEC_PROVIDER_RSA: &[u8; 4] = b"RSA\0";
pub const PARSEC_PROVIDER_PROPERTY: &[u8; 17] = b"provider=default\0";

// Loads a provider into the given library context
pub fn load_provider(lib_ctx: &LibCtx, provider_name: &str, provider_path: String) -> Provider {
assert!(Provider::set_default_search_path(Some(lib_ctx), &provider_path).is_ok());
Provider::load(Some(lib_ctx), provider_name).unwrap()
}

// Loads a key using the given library context with loaded provider. The param should contain the necessary
// parameters based on the provider that we are loading.
pub unsafe fn load_key(lib_ctx: &LibCtx, param: *mut OSSL_PARAM, parsec_pkey: *mut *mut EVP_PKEY) {
let evp_ctx: *mut EVP_PKEY_CTX = EVP_PKEY_CTX_new_from_name(
lib_ctx.as_ptr() as *mut ossl_lib_ctx_st,
PARSEC_PROVIDER_RSA.as_ptr() as *const ::std::os::raw::c_char,
PARSEC_PROVIDER_PROPERTY.as_ptr() as *const ::std::os::raw::c_char,
);
assert_ne!(evp_ctx, std::ptr::null_mut());
assert_eq!(EVP_PKEY_fromdata_init(evp_ctx), 1);
assert_eq!(
EVP_PKEY_fromdata(
evp_ctx,
parsec_pkey as _,
// Change 3: Select the appropriate macro here to load the param value
EVP_PKEY_KEY_PARAMETERS.try_into().unwrap(),
param,
),
1
);
assert_ne!(*parsec_pkey, std::ptr::null_mut());

EVP_PKEY_CTX_free(evp_ctx);
}
199 changes: 199 additions & 0 deletions parsec-openssl-provider-shared-test/tests/provider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// Copyright 2024 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0

use parsec_openssl_provider::parsec_openssl2::{openssl_binding, ossl_param};
use parsec_openssl_provider_shared_test::*;
use std::ffi::CStr;

// Simple test to load a provider. Test fails if load_provider function reports error
#[test]
fn test_loading_parsec_provider() {
let provider_path = String::from("../target/debug");
let provider_name = String::from("libparsec_openssl_provider_shared");
let lib_ctx: LibCtx = LibCtx::new().unwrap();
let _provider: Provider = load_provider(&lib_ctx, &provider_name, provider_path);
}

// Fetch the provider name from the OSSL interface "OSSL_PROVIDER_get0_name"
#[test]
fn test_parsec_provider_name() {
let provider_path = String::from("../target/debug/");
let provider_name = String::from("libparsec_openssl_provider_shared");
let lib_ctx: LibCtx = LibCtx::new().unwrap();
let provider: Provider = load_provider(&lib_ctx, &provider_name, provider_path);

unsafe {
let prov_name = OSSL_PROVIDER_get0_name(provider.as_ptr() as *const ossl_provider_st);
let prov_name = CStr::from_ptr(prov_name);
assert_eq!(prov_name.to_str().unwrap(), provider_name);
}
}

// Loads a keys from the provider and returns an EVP_PKEY object with the details.
// ToDo: This is working with the default provider and currently loads a key with
// no parameters. In order to test it with the parsec provider 3 changes are needed
// explained in comments below.
#[test]
fn test_loading_keys() {
let provider_path = String::from("../target/debug/");
// Change 1: name the parsec provider here
let provider_name = String::from("default");

let lib_ctx: LibCtx = LibCtx::new().unwrap();
let provider: Provider = load_provider(&lib_ctx, &provider_name, provider_path);

// Change 2: Setup the param as needed for the parsec provider.
// Create a key beforehand using the parsec-tool and then run the test.
let mut param = ossl_param!();
unsafe {
let mut parsec_pkey: *mut EVP_PKEY = std::ptr::null_mut();
load_key(&lib_ctx, &mut param, &mut parsec_pkey);
EVP_PKEY_free(parsec_pkey);
}
}

// Checks if the parsec provider returns the expected list in the gettable param
// structure
#[test]
fn test_parsec_provider_gettable_param() {
let provider_path = String::from("../target/debug/");
let provider_name = String::from("libparsec_openssl_provider_shared");
let lib_ctx: LibCtx = LibCtx::new().unwrap();
let provider: Provider = load_provider(&lib_ctx, &provider_name, provider_path);
unsafe {
let gettable_params: *const OSSL_PARAM =
OSSL_PROVIDER_gettable_params(provider.as_ptr() as *const ossl_provider_st);

// Checks if the returned structure contains OSSL_PROV_PARAM_NAME
assert_ne!(
openssl_binding::OSSL_PARAM_locate(
gettable_params as _,
openssl_binding::OSSL_PROV_PARAM_NAME.as_ptr() as *const std::os::raw::c_char,
),
std::ptr::null_mut()
);

// Checks if the returned structure contains OSSL_PROV_PARAM_VERSION
assert_ne!(
openssl_binding::OSSL_PARAM_locate(
gettable_params as _,
openssl_binding::OSSL_PROV_PARAM_VERSION.as_ptr() as *const std::os::raw::c_char,
),
std::ptr::null_mut()
);

// Checks if the returned structure contains OSSL_PROV_PARAM_STATUS
assert_ne!(
openssl_binding::OSSL_PARAM_locate(
gettable_params as _,
openssl_binding::OSSL_PROV_PARAM_STATUS.as_ptr() as *const std::os::raw::c_char,
),
std::ptr::null_mut()
);
}
}

// Fetch the supported params from the parsec provider and compares if its as expected
#[test]
fn test_parsec_provider_get_param() {
let provider_path = String::from("../target/debug/");
let provider_name = String::from("libparsec_openssl_provider_shared");
let lib_ctx: LibCtx = LibCtx::new().unwrap();
let provider: Provider = load_provider(&lib_ctx, &provider_name, provider_path);

let mut prov_name: *mut i8 = std::ptr::null_mut();
let mut prov_version: *mut i8 = std::ptr::null_mut();
let mut prov_status: i32 = 0;
unsafe {
let mut params: [OSSL_PARAM; 4] =
[ossl_param!(), ossl_param!(), ossl_param!(), ossl_param!()];

// Construct the 3 parameters
params[0] = OSSL_PARAM_construct_utf8_ptr(
openssl_binding::OSSL_PROV_PARAM_NAME.as_ptr() as _,
&mut prov_name,
0,
);
params[1] = OSSL_PARAM_construct_utf8_ptr(
openssl_binding::OSSL_PROV_PARAM_VERSION.as_ptr() as _,
&mut prov_version,
0,
);
params[2] = OSSL_PARAM_construct_int(
openssl_binding::OSSL_PROV_PARAM_STATUS.as_ptr() as _,
&mut prov_status as *mut i32,
);

// Ensure the structure is unpopulated
assert_eq!(OSSL_PARAM_modified(&params as _), 0);
assert_eq!(OSSL_PARAM_modified(&params[1] as _), 0);
assert_eq!(OSSL_PARAM_modified(&params[2] as _), 0);

// Fetch the providers
assert_eq!(
OSSL_PROVIDER_get_params(
provider.as_ptr() as *const ossl_provider_st,
params.as_ptr() as *mut OSSL_PARAM,
),
1
);

// Ensure the structure is populated by the provider
assert_eq!(OSSL_PARAM_modified(&params as _), 1);
assert_eq!(OSSL_PARAM_modified(&params[1] as _), 1);
assert_eq!(OSSL_PARAM_modified(&params[2] as _), 1);

// Verify the returned provider parameters
let prov_name = CStr::from_ptr(prov_name);
let prov_name = prov_name.to_str().unwrap();
assert_eq!(prov_name, "Parsec OpenSSL Provider");

let prov_version = CStr::from_ptr(prov_version);
let prov_version = prov_version.to_str().unwrap();
assert_eq!(prov_version, "0.1.0");
}
}

// Verifies that the provider is able to return a non NULL pointer when queried for
// a supported function
#[test]
fn test_provider_query_supported() {
let provider_path = String::from("../target/debug");
let provider_name = String::from("libparsec_openssl_provider_shared");
let lib_ctx: LibCtx = LibCtx::new().unwrap();
let provider: Provider = load_provider(&lib_ctx, &provider_name, provider_path);

let mut no_cache: i32 = 0;
unsafe {
assert_ne!(
OSSL_PROVIDER_query_operation(
provider.as_ptr() as _,
OSSL_OP_KEYMGMT.try_into().unwrap(),
&mut no_cache as _,
),
std::ptr::null_mut()
);
}
}

// Verifies that the provider is able to return a NULL pointer when queried for
// an unsupported function
#[test]
fn test_provider_query_unsupported() {
let provider_path = String::from("../target/debug");
let provider_name = String::from("libparsec_openssl_provider_shared");
let lib_ctx: LibCtx = LibCtx::new().unwrap();
let provider: Provider = load_provider(&lib_ctx, &provider_name, provider_path);

let mut no_cache: i32 = 0;
unsafe {
assert_eq!(
OSSL_PROVIDER_query_operation(
provider.as_ptr() as _,
OSSL_OP_RAND.try_into().unwrap(),
&mut no_cache as _,
),
std::ptr::null_mut()
);
}
}
4 changes: 2 additions & 2 deletions parsec-openssl-provider/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ use parsec_client::BasicClient;
use std::sync::Arc;

use crate::openssl_binding::{
OSSL_ALGORITHM, OSSL_OP_KEYMGMT, OSSL_PARAM, OSSL_PARAM_INTEGER, OSSL_PARAM_UTF8_PTR,
OSSL_PROV_PARAM_BUILDINFO, OSSL_PROV_PARAM_NAME, OSSL_PROV_PARAM_STATUS,
OSSL_ALGORITHM, OSSL_OP_KEYMGMT, OSSL_OP_SIGNATURE, OSSL_PARAM, OSSL_PARAM_INTEGER,
OSSL_PARAM_UTF8_PTR, OSSL_PROV_PARAM_BUILDINFO, OSSL_PROV_PARAM_NAME, OSSL_PROV_PARAM_STATUS,
OSSL_PROV_PARAM_VERSION,
};

Expand Down