use std::slice;
use std::sync::Arc;
use libc::size_t;
use rustls::SupportedCipherSuite;
#[cfg(feature = "aws-lc-rs")]
use rustls::crypto::aws_lc_rs;
#[cfg(feature = "ring")]
use rustls::crypto::ring;
use rustls::crypto::{CryptoProvider, hpke};
use rustls::pki_types::PrivateKeyDer;
use rustls::pki_types::pem::PemObject;
use rustls::sign::SigningKey;
use crate::cipher::rustls_supported_ciphersuite;
use crate::error::{map_error, rustls_result};
use crate::ffi::{
Castable, OwnershipArc, OwnershipBox, OwnershipRef, free_arc, free_box, set_arc_mut_ptr,
set_boxed_mut_ptr, to_boxed_mut_ptr, try_clone_arc, try_mut_from_ptr, try_mut_from_ptr_ptr,
try_ref_from_ptr, try_ref_from_ptr_ptr, try_slice, try_slice_mut, try_take,
};
use crate::panic::ffi_panic_boundary;
pub struct rustls_crypto_provider_builder {
_private: [u8; 0],
}
impl Castable for rustls_crypto_provider_builder {
type Ownership = OwnershipBox;
type RustType = Option<CryptoProviderBuilder>;
}
#[derive(Debug)]
pub struct CryptoProviderBuilder {
base: Arc<CryptoProvider>,
cipher_suites: Vec<SupportedCipherSuite>,
}
impl CryptoProviderBuilder {
fn build_provider(self) -> CryptoProvider {
let cipher_suites = match self.cipher_suites.is_empty() {
true => self.base.cipher_suites.clone(),
false => self.cipher_suites,
};
CryptoProvider {
cipher_suites,
kx_groups: self.base.kx_groups.clone(),
signature_verification_algorithms: self.base.signature_verification_algorithms,
secure_random: self.base.secure_random,
key_provider: self.base.key_provider,
}
}
}
#[no_mangle]
pub extern "C" fn rustls_crypto_provider_builder_new_from_default(
builder_out: *mut *mut rustls_crypto_provider_builder,
) -> rustls_result {
ffi_panic_boundary! {
let provider_out = try_mut_from_ptr_ptr!(builder_out);
let base = match get_default_or_install_from_crate_features() {
Some(provider) => provider,
None => return rustls_result::NoDefaultCryptoProvider,
};
set_boxed_mut_ptr(
provider_out,
Some(CryptoProviderBuilder {
base,
cipher_suites: Vec::default(),
}),
);
rustls_result::Ok
}
}
#[no_mangle]
pub extern "C" fn rustls_crypto_provider_builder_new_with_base(
base: *const rustls_crypto_provider,
) -> *mut rustls_crypto_provider_builder {
ffi_panic_boundary! {
to_boxed_mut_ptr(Some(CryptoProviderBuilder {
base: try_clone_arc!(base),
cipher_suites: Vec::default(),
}))
}
}
#[no_mangle]
pub extern "C" fn rustls_crypto_provider_builder_set_cipher_suites(
builder: *mut rustls_crypto_provider_builder,
cipher_suites: *const *const rustls_supported_ciphersuite,
cipher_suites_len: size_t,
) -> rustls_result {
ffi_panic_boundary! {
let builder = try_mut_from_ptr!(builder);
let builder = match builder {
Some(builder) => builder,
None => return rustls_result::AlreadyUsed,
};
let cipher_suites = try_slice!(cipher_suites, cipher_suites_len);
let mut supported_cipher_suites = Vec::new();
for cs in cipher_suites {
let cs = *cs;
let cs = try_ref_from_ptr!(cs);
supported_cipher_suites.push(*cs);
}
builder.cipher_suites = supported_cipher_suites;
rustls_result::Ok
}
}
#[no_mangle]
pub extern "C" fn rustls_crypto_provider_builder_build(
builder: *mut rustls_crypto_provider_builder,
provider_out: *mut *const rustls_crypto_provider,
) -> rustls_result {
ffi_panic_boundary! {
let builder = try_mut_from_ptr!(builder);
set_arc_mut_ptr(
try_ref_from_ptr_ptr!(provider_out),
try_take!(builder).build_provider(),
);
rustls_result::Ok
}
}
#[no_mangle]
pub extern "C" fn rustls_crypto_provider_builder_build_as_default(
builder: *mut rustls_crypto_provider_builder,
) -> rustls_result {
let builder = try_mut_from_ptr!(builder);
match try_take!(builder).build_provider().install_default() {
Ok(_) => rustls_result::Ok,
Err(_) => rustls_result::AlreadyUsed,
}
}
#[no_mangle]
pub extern "C" fn rustls_crypto_provider_builder_free(
builder: *mut rustls_crypto_provider_builder,
) {
ffi_panic_boundary! {
free_box(builder);
}
}
#[no_mangle]
#[cfg(feature = "ring")]
pub extern "C" fn rustls_ring_crypto_provider() -> *const rustls_crypto_provider {
ffi_panic_boundary! {
Arc::into_raw(Arc::new(ring::default_provider())) as *const rustls_crypto_provider
}
}
#[no_mangle]
#[cfg(feature = "aws-lc-rs")]
pub extern "C" fn rustls_aws_lc_rs_crypto_provider() -> *const rustls_crypto_provider {
ffi_panic_boundary! {
Arc::into_raw(Arc::new(aws_lc_rs::default_provider())) as *const rustls_crypto_provider
}
}
#[no_mangle]
#[cfg(feature = "fips")]
pub extern "C" fn rustls_default_fips_provider() -> *const rustls_crypto_provider {
ffi_panic_boundary! {
Arc::into_raw(Arc::new(rustls::crypto::default_fips_provider()))
as *const rustls_crypto_provider
}
}
#[no_mangle]
pub extern "C" fn rustls_crypto_provider_default() -> *const rustls_crypto_provider {
ffi_panic_boundary! {
match CryptoProvider::get_default() {
Some(provider) => Arc::into_raw(provider.clone()) as *const rustls_crypto_provider,
None => core::ptr::null(),
}
}
}
pub struct rustls_crypto_provider {
_private: [u8; 0],
}
impl Castable for rustls_crypto_provider {
type Ownership = OwnershipArc;
type RustType = CryptoProvider;
}
#[no_mangle]
pub extern "C" fn rustls_crypto_provider_ciphersuites_len(
provider: *const rustls_crypto_provider,
) -> usize {
ffi_panic_boundary! {
try_clone_arc!(provider).cipher_suites.len()
}
}
#[no_mangle]
pub extern "C" fn rustls_crypto_provider_ciphersuites_get(
provider: *const rustls_crypto_provider,
index: usize,
) -> *const rustls_supported_ciphersuite {
ffi_panic_boundary! {
match try_clone_arc!(provider).cipher_suites.get(index) {
Some(ciphersuite) => ciphersuite as *const SupportedCipherSuite as *const _,
None => core::ptr::null(),
}
}
}
#[no_mangle]
pub extern "C" fn rustls_crypto_provider_load_key(
provider: *const rustls_crypto_provider,
private_key: *const u8,
private_key_len: size_t,
signing_key_out: *mut *mut rustls_signing_key,
) -> rustls_result {
ffi_panic_boundary! {
let provider = try_clone_arc!(provider);
let private_key_pem = try_slice!(private_key, private_key_len);
let signing_key_out = try_mut_from_ptr_ptr!(signing_key_out);
let private_key_der = match PrivateKeyDer::from_pem_slice(private_key_pem) {
Ok(der) => der,
Err(_) => return rustls_result::PrivateKeyParseError,
};
let private_key = match provider.key_provider.load_private_key(private_key_der) {
Ok(key) => key,
Err(e) => return map_error(e),
};
set_boxed_mut_ptr(signing_key_out, private_key);
rustls_result::Ok
}
}
#[no_mangle]
pub extern "C" fn rustls_crypto_provider_random(
provider: *const rustls_crypto_provider,
buff: *mut u8,
len: size_t,
) -> rustls_result {
ffi_panic_boundary! {
match try_clone_arc!(provider)
.secure_random
.fill(try_slice_mut!(buff, len))
{
Ok(_) => rustls_result::Ok,
Err(_) => rustls_result::GetRandomFailed,
}
}
}
#[no_mangle]
pub extern "C" fn rustls_crypto_provider_fips(provider: *const rustls_crypto_provider) -> bool {
ffi_panic_boundary! {
try_ref_from_ptr!(provider).fips()
}
}
#[no_mangle]
pub extern "C" fn rustls_crypto_provider_free(provider: *const rustls_crypto_provider) {
ffi_panic_boundary! {
free_arc(provider);
}
}
#[no_mangle]
pub extern "C" fn rustls_default_crypto_provider_ciphersuites_len() -> usize {
ffi_panic_boundary! {
match get_default_or_install_from_crate_features() {
Some(provider) => provider.cipher_suites.len(),
None => return 0,
}
}
}
#[no_mangle]
pub extern "C" fn rustls_default_crypto_provider_ciphersuites_get(
index: usize,
) -> *const rustls_supported_ciphersuite {
ffi_panic_boundary! {
let default_provider = match get_default_or_install_from_crate_features() {
Some(provider) => provider,
None => return core::ptr::null(),
};
match default_provider.cipher_suites.get(index) {
Some(ciphersuite) => ciphersuite as *const SupportedCipherSuite as *const _,
None => core::ptr::null(),
}
}
}
#[no_mangle]
pub extern "C" fn rustls_default_crypto_provider_random(
buff: *mut u8,
len: size_t,
) -> rustls_result {
ffi_panic_boundary! {
match get_default_or_install_from_crate_features() {
Some(provider) => match provider.secure_random.fill(try_slice_mut!(buff, len)) {
Ok(_) => rustls_result::Ok,
Err(_) => rustls_result::GetRandomFailed,
},
None => rustls_result::NoDefaultCryptoProvider,
}
}
}
pub struct rustls_signing_key {
_private: [u8; 0],
}
impl Castable for rustls_signing_key {
type Ownership = OwnershipBox;
type RustType = Arc<dyn SigningKey>;
}
impl rustls_signing_key {
#[no_mangle]
pub extern "C" fn rustls_signing_key_free(signing_key: *mut rustls_signing_key) {
ffi_panic_boundary! {
free_box(signing_key);
}
}
}
pub struct rustls_hpke {
_private: [u8; 0],
}
impl Castable for rustls_hpke {
type Ownership = OwnershipRef;
type RustType = Hpke;
}
pub(crate) struct Hpke {
pub(crate) suites: &'static [&'static dyn hpke::Hpke],
}
impl Hpke {
pub(crate) fn grease_public_key(
&self,
provider: &CryptoProvider,
) -> Option<(&dyn hpke::Hpke, hpke::HpkePublicKey)> {
let num_suites = self.suites.len();
if num_suites == 0 {
return None;
}
debug_assert!(num_suites < u32::MAX as usize);
let mut buf = [0u8; 4];
let threshold = u32::MAX - (u32::MAX % num_suites as u32);
let suite = loop {
provider.secure_random.fill(&mut buf).ok()?;
let value = u32::from_ne_bytes(buf);
if value < threshold {
break self.suites[value as usize / (threshold as usize / num_suites)];
}
};
let pk = suite.generate_key_pair().map(|pair| pair.0).ok()?;
Some((suite, pk))
}
}
#[cfg(feature = "aws-lc-rs")]
static AWS_LC_RS_HPKE: &Hpke = &Hpke {
suites: aws_lc_rs::hpke::ALL_SUPPORTED_SUITES,
};
#[no_mangle]
pub extern "C" fn rustls_supported_hpke() -> *const rustls_hpke {
ffi_panic_boundary! {
#[cfg(feature = "aws-lc-rs")]
{
AWS_LC_RS_HPKE as *const _ as _
}
#[cfg(not(feature = "aws-lc-rs"))]
{
core::ptr::null()
}
}
}
pub(crate) fn get_default_or_install_from_crate_features() -> Option<Arc<CryptoProvider>> {
if let Some(provider) = CryptoProvider::get_default() {
return Some(provider.clone());
}
let _ = provider_from_crate_features()?.install_default();
Some(CryptoProvider::get_default().unwrap().clone())
}
fn provider_from_crate_features() -> Option<CryptoProvider> {
#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
{
return Some(aws_lc_rs::default_provider());
}
#[cfg(all(feature = "ring", not(feature = "aws-lc-rs")))]
{
return Some(ring::default_provider());
}
#[allow(unreachable_code)]
None
}
#[cfg(all(test, not(miri), any(feature = "aws-lc-rs", feature = "ring")))]
mod tests {
use std::ptr;
use crate::rustls_result;
use super::*;
#[test]
fn random_data() {
let provider = rustls_crypto_provider_default();
assert!(!provider.is_null());
let result = rustls_crypto_provider_random(provider, ptr::null_mut(), 1337);
assert_eq!(result, rustls_result::NullParameter);
let mut buff = vec![0; 32];
let result = rustls_crypto_provider_random(ptr::null(), buff.as_mut_ptr(), buff.len());
assert_eq!(buff, vec![0; 32]);
assert_eq!(result, rustls_result::NullParameter);
let result = rustls_crypto_provider_random(provider, buff.as_mut_ptr(), buff.len());
assert_eq!(result, rustls_result::Ok);
assert_ne!(buff, vec![0; 32]);
}
#[test]
fn default_random_data() {
let result = rustls_default_crypto_provider_random(ptr::null_mut(), 1337);
assert_eq!(result, rustls_result::NullParameter);
let mut buff = vec![0; 32];
let result = rustls_default_crypto_provider_random(buff.as_mut_ptr(), buff.len());
assert_eq!(result, rustls_result::Ok);
assert_ne!(buff, vec![0; 32]);
}
#[test]
#[cfg(feature = "aws-lc-rs")]
fn test_hpke_aws_lc_rs() {
let provider = rustls_crypto_provider_default();
assert!(!provider.is_null());
let provider = try_clone_arc!(provider);
let hpke = rustls_supported_hpke();
assert!(!hpke.is_null());
let hpke = try_ref_from_ptr!(hpke);
let (suite, pk) = hpke.grease_public_key(&provider).unwrap();
let (_, _) = suite.setup_sealer(&[0xC0, 0xFF, 0xEE], &pk).unwrap();
}
#[test]
#[cfg(not(feature = "aws-lc-rs"))]
fn test_hpke_not_aws_lc_rs() {
assert!(rustls_supported_hpke().is_null());
}
}