use std::sync::Arc;
use wolfcrypt_sys::*;
use crate::certificate::{Certificate, PrivateKey, RootCertStore};
use crate::error::{expect_wolfssl_success, Result, TlsError};
use crate::protocol::{self, ProtocolVersion};
use crate::ensure_init;
pub(crate) struct CtxInner {
pub(crate) ctx: *mut WOLFSSL_CTX,
}
unsafe impl Send for CtxInner {}
unsafe impl Sync for CtxInner {}
impl Drop for CtxInner {
fn drop(&mut self) {
unsafe {
wolfSSL_CTX_free(self.ctx);
}
}
}
#[derive(Clone)]
pub struct TlsClientConfig {
pub(crate) inner: Arc<CtxInner>,
}
pub struct TlsClientConfigBuilder {
protocol_versions: Option<Vec<ProtocolVersion>>,
root_store: Option<RootCertStore>,
client_cert: Option<Certificate>,
client_key: Option<PrivateKey>,
}
impl TlsClientConfig {
pub fn builder() -> TlsClientConfigBuilder {
TlsClientConfigBuilder {
protocol_versions: None,
root_store: None,
client_cert: None,
client_key: None,
}
}
}
impl TlsClientConfigBuilder {
pub fn with_protocol_versions(mut self, versions: &[ProtocolVersion]) -> Self {
self.protocol_versions = Some(versions.to_vec());
self
}
pub fn with_root_certificates(mut self, store: RootCertStore) -> Self {
self.root_store = Some(store);
self
}
pub fn with_no_client_auth(self) -> Self {
self
}
pub fn with_client_auth(mut self, cert: Certificate, key: PrivateKey) -> Self {
self.client_cert = Some(cert);
self.client_key = Some(key);
self
}
pub fn build(self) -> Result<TlsClientConfig> {
ensure_init();
let root_store = self
.root_store
.ok_or(TlsError::InvalidConfig("root certificates are required"))?;
let method =
unsafe { protocol::resolve_method(protocol::Side::Client, self.protocol_versions.as_deref())? };
let ctx = unsafe { wolfSSL_CTX_new(method) };
if ctx.is_null() {
return Err(TlsError::AllocFailed {
func: "wolfSSL_CTX_new",
});
}
let inner = Arc::new(CtxInner { ctx });
for (cert_data, format) in root_store.iter() {
let ret = unsafe {
wolfSSL_CTX_load_verify_buffer(
inner.ctx,
cert_data.as_ptr(),
cert_data.len() as core::ffi::c_long,
format.as_c_int(),
)
};
expect_wolfssl_success(ret, "wolfSSL_CTX_load_verify_buffer")?;
}
unsafe {
wolfSSL_CTX_set_verify(
inner.ctx,
WOLFSSL_VERIFY_PEER as core::ffi::c_int,
None,
);
}
if let (Some(cert), Some(key)) = (self.client_cert.as_ref(), self.client_key.as_ref()) {
let ret = unsafe {
wolfSSL_CTX_use_certificate_buffer(
inner.ctx,
cert.data().as_ptr(),
cert.data().len() as core::ffi::c_long,
cert.format().as_c_int(),
)
};
expect_wolfssl_success(ret, "wolfSSL_CTX_use_certificate_buffer")?;
let ret = unsafe {
wolfSSL_CTX_use_PrivateKey_buffer(
inner.ctx,
key.data().as_ptr(),
key.data().len() as core::ffi::c_long,
key.format().as_c_int(),
)
};
expect_wolfssl_success(ret, "wolfSSL_CTX_use_PrivateKey_buffer")?;
}
Ok(TlsClientConfig { inner })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builder_without_root_certs_fails() {
let result = TlsClientConfig::builder()
.with_no_client_auth()
.build();
assert!(result.is_err());
}
}