use crate::ffi::errors::{ErrorHandle, Status};
use crate::ffi::{Buffer, Slice};
use crate::tls::{ECHConfigList, ECHKeys, Identity};
#[repr(C)]
#[derive(Clone, Copy)]
pub struct TLSConfig {
pub ciphers: Slice,
pub groups: Slice,
pub signature_algorithms: Slice,
pub prefer_server_ciphers: bool,
pub session_tickets: bool,
pub early_data: bool,
pub certificate_compression: bool,
}
impl TLSConfig {
pub unsafe fn parse(&self) -> Option<crate::tls::TLSConfig> {
let text = |slice: Slice| match slice.data.is_null() {
true => Some(None),
false => unsafe { Slice::borrow_text(slice.data, slice.len) }.map(|text| Some(text.to_owned())),
};
Some(crate::tls::TLSConfig {
ciphers: text(self.ciphers)?,
groups: text(self.groups)?,
signature_algorithms: text(self.signature_algorithms)?,
prefer_server_ciphers: self.prefer_server_ciphers,
session_tickets: self.session_tickets,
early_data: self.early_data,
certificate_compression: self.certificate_compression,
})
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_tls_config_default() -> TLSConfig {
let config = crate::tls::TLSConfig::default();
TLSConfig {
ciphers: Slice::ABSENT,
groups: Slice::ABSENT,
signature_algorithms: Slice::ABSENT,
prefer_server_ciphers: config.prefer_server_ciphers,
session_tickets: config.session_tickets,
early_data: config.early_data,
certificate_compression: config.certificate_compression,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_identity_new(certificates: *const Slice, certificate_count: usize, key: *const u8, key_len: usize) -> *mut Identity {
if certificates.is_null() {
return std::ptr::null_mut();
}
let Some(key) = (unsafe { Slice::borrow(key, key_len) }) else {
return std::ptr::null_mut();
};
let mut chain = Vec::with_capacity(certificate_count);
for index in 0..certificate_count {
let slice = unsafe { *certificates.add(index) };
let Some(blob) = (unsafe { Slice::borrow(slice.data, slice.len) }) else {
return std::ptr::null_mut();
};
chain.push(blob.to_vec());
}
Box::into_raw(Box::new(Identity::new(chain, key.to_vec())))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_identity_from_pkcs12(data: *const u8, data_len: usize, passphrase: *const u8, passphrase_len: usize, out: *mut *mut Identity, error: *mut *mut ErrorHandle) -> Status {
if out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
let (Some(data), Some(passphrase)) = (unsafe { Slice::borrow(data, data_len) }, unsafe { Slice::borrow_text(passphrase, passphrase_len) }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match Identity::from_pkcs12(data, passphrase) {
Ok(identity) => {
unsafe { *out = Box::into_raw(Box::new(identity)) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_identity_free(identity: *mut Identity) {
if !identity.is_null() {
drop(unsafe { Box::from_raw(identity) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_ech_keys_generate(public_name: *const u8, public_name_len: usize, config_id: u8, out: *mut *mut ECHKeys, error: *mut *mut ErrorHandle) -> Status {
if out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
let Some(public_name) = (unsafe { Slice::borrow_text(public_name, public_name_len) }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
if public_name.len() > 255 {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
match ECHKeys::generate(public_name, config_id) {
Ok(keys) => {
unsafe { *out = Box::into_raw(Box::new(keys)) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_ech_keys_new(config: *const u8, config_len: usize, private_key: *const u8, private_key_len: usize) -> *mut ECHKeys {
let (Some(config), Some(private_key)) = (unsafe { Slice::borrow(config, config_len) }, unsafe { Slice::borrow(private_key, private_key_len) }) else {
return std::ptr::null_mut();
};
Box::into_raw(Box::new(ECHKeys { config: config.to_vec(), private_key: private_key.to_vec() }))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_ech_keys_free(keys: *mut ECHKeys) {
if !keys.is_null() {
drop(unsafe { Box::from_raw(keys) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_ech_keys_config(keys: *const ECHKeys) -> Slice {
match unsafe { keys.as_ref() } {
Some(keys) => Slice::new(&keys.config),
None => Slice::ABSENT,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_ech_keys_private_key(keys: *const ECHKeys) -> Slice {
match unsafe { keys.as_ref() } {
Some(keys) => Slice::new(&keys.private_key),
None => Slice::ABSENT,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_ech_keys_config_list(keys: *const ECHKeys) -> Buffer {
match unsafe { keys.as_ref() } {
Some(keys) => Buffer::new(keys.config_list()),
None => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_ech_config_list_parse(data: *const u8, data_len: usize, out: *mut *mut ECHConfigList, error: *mut *mut ErrorHandle) -> Status {
if out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
let Some(data) = (unsafe { Slice::borrow(data, data_len) }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match ECHConfigList::parse(data) {
Ok(list) => {
unsafe { *out = Box::into_raw(Box::new(list)) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_ech_config_list_free(list: *mut ECHConfigList) {
if !list.is_null() {
drop(unsafe { Box::from_raw(list) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_ech_config_list_count(list: *const ECHConfigList) -> usize {
unsafe { list.as_ref() }.map_or(0, |list| list.configs.len())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_ech_config_version(list: *const ECHConfigList, index: usize) -> u16 {
unsafe { list.as_ref() }.and_then(|list| list.configs.get(index)).map_or(0, |config| config.version)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_ech_config_public_name(list: *const ECHConfigList, index: usize) -> Slice {
Slice::maybe(unsafe { list.as_ref() }.and_then(|list| list.configs.get(index)).map(|config| config.public_name.as_str()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_ech_config_maximum_name_length(list: *const ECHConfigList, index: usize) -> i32 {
unsafe { list.as_ref() }
.and_then(|list| list.configs.get(index))
.map_or(-1, |config| config.maximum_name_length as i32)
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ECHEntry {
pub host: Slice,
pub config_list: Slice,
}